external_validation_test.go raw

   1  //go:build !js
   2  
   3  package p256k1
   4  
   5  import (
   6  	"crypto/sha256"
   7  	"encoding/hex"
   8  	"testing"
   9  )
  10  
  11  // TestExternalValidationGeneratorMult validates k*G against libsecp256k1
  12  // This is the primary test for verifying our scalar multiplication is correct.
  13  func TestExternalValidationGeneratorMult(t *testing.T) {
  14  	lib, err := GetLibSecp256k1()
  15  	if err != nil {
  16  		t.Skipf("libsecp256k1 not available: %v", err)
  17  	}
  18  	if !lib.IsLoaded() {
  19  		t.Skip("libsecp256k1 not loaded")
  20  	}
  21  
  22  	testCases := []struct {
  23  		name   string
  24  		scalar string
  25  	}{
  26  		{"one", "0000000000000000000000000000000000000000000000000000000000000001"},
  27  		{"two", "0000000000000000000000000000000000000000000000000000000000000002"},
  28  		{"three", "0000000000000000000000000000000000000000000000000000000000000003"},
  29  		{"seven", "0000000000000000000000000000000000000000000000000000000000000007"},
  30  		{"small", "0000000000000000000000000000000000000000000000000000000000000100"},
  31  		{"random1", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"},
  32  		{"random2", "deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef"},
  33  		{"half_order", "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0"},
  34  		// Edge cases for GLV splitting
  35  		{"lambda_minus_one", "5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd71"},
  36  		{"near_lambda", "5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd73"},
  37  	}
  38  
  39  	for _, tc := range testCases {
  40  		t.Run(tc.name, func(t *testing.T) {
  41  			seckey, _ := hex.DecodeString(tc.scalar)
  42  
  43  			// Get expected result from libsecp256k1
  44  			expectedPubkey, err := lib.CreatePubkey(seckey)
  45  			if err != nil {
  46  				t.Fatalf("libsecp256k1.CreatePubkey failed: %v", err)
  47  			}
  48  
  49  			// Parse scalar
  50  			var s Scalar
  51  			s.setB32(seckey)
  52  
  53  			if s.isZero() {
  54  				t.Skip("zero scalar")
  55  			}
  56  
  57  			// Test EcmultConst (reference binary method)
  58  			t.Run("EcmultConst", func(t *testing.T) {
  59  				var result GroupElementJacobian
  60  				EcmultConst(&result, &Generator, &s)
  61  				var aff GroupElementAffine
  62  				aff.setGEJ(&result)
  63  				aff.x.normalize()
  64  
  65  				var gotX [32]byte
  66  				aff.x.getB32(gotX[:])
  67  
  68  				if hex.EncodeToString(gotX[:]) != hex.EncodeToString(expectedPubkey) {
  69  					t.Errorf("EcmultConst mismatch:\n  got:  %s\n  want: %s",
  70  						hex.EncodeToString(gotX[:]), hex.EncodeToString(expectedPubkey))
  71  				}
  72  			})
  73  
  74  			// Test ecmultGenGLV (GLV-optimized generator multiplication)
  75  			t.Run("ecmultGenGLV", func(t *testing.T) {
  76  				var result GroupElementJacobian
  77  				ecmultGenGLV(&result, &s)
  78  				var aff GroupElementAffine
  79  				aff.setGEJ(&result)
  80  				aff.x.normalize()
  81  
  82  				var gotX [32]byte
  83  				aff.x.getB32(gotX[:])
  84  
  85  				if hex.EncodeToString(gotX[:]) != hex.EncodeToString(expectedPubkey) {
  86  					t.Errorf("ecmultGenGLV mismatch:\n  got:  %s\n  want: %s",
  87  						hex.EncodeToString(gotX[:]), hex.EncodeToString(expectedPubkey))
  88  				}
  89  			})
  90  
  91  			// Test ecmultStraussWNAFGLV with Generator
  92  			t.Run("ecmultStraussWNAFGLV", func(t *testing.T) {
  93  				var result GroupElementJacobian
  94  				ecmultStraussWNAFGLV(&result, &Generator, &s)
  95  				var aff GroupElementAffine
  96  				aff.setGEJ(&result)
  97  				aff.x.normalize()
  98  
  99  				var gotX [32]byte
 100  				aff.x.getB32(gotX[:])
 101  
 102  				if hex.EncodeToString(gotX[:]) != hex.EncodeToString(expectedPubkey) {
 103  					t.Errorf("ecmultStraussWNAFGLV mismatch:\n  got:  %s\n  want: %s",
 104  						hex.EncodeToString(gotX[:]), hex.EncodeToString(expectedPubkey))
 105  				}
 106  			})
 107  
 108  			// Test ecmultWindowedVar
 109  			t.Run("ecmultWindowedVar", func(t *testing.T) {
 110  				var result GroupElementJacobian
 111  				ecmultWindowedVar(&result, &Generator, &s)
 112  				var aff GroupElementAffine
 113  				aff.setGEJ(&result)
 114  				aff.x.normalize()
 115  
 116  				var gotX [32]byte
 117  				aff.x.getB32(gotX[:])
 118  
 119  				if hex.EncodeToString(gotX[:]) != hex.EncodeToString(expectedPubkey) {
 120  					t.Errorf("ecmultWindowedVar mismatch:\n  got:  %s\n  want: %s",
 121  						hex.EncodeToString(gotX[:]), hex.EncodeToString(expectedPubkey))
 122  				}
 123  			})
 124  		})
 125  	}
 126  }
 127  
 128  // TestExternalValidationECDH validates arbitrary point multiplication via ECDH
 129  // libsecp256k1's ECDH computes hash(k*P), so if our k*P is correct, the hash should match.
 130  func TestExternalValidationECDH(t *testing.T) {
 131  	lib, err := GetLibSecp256k1()
 132  	if err != nil {
 133  		t.Skipf("libsecp256k1 not available: %v", err)
 134  	}
 135  	if !lib.IsLoaded() {
 136  		t.Skip("libsecp256k1 not loaded")
 137  	}
 138  
 139  	// Test with different secret keys and public key points
 140  	testCases := []struct {
 141  		name      string
 142  		seckey    string
 143  		pubkeyGen string // scalar to generate the pubkey (pubkey = pubkeyGen * G)
 144  	}{
 145  		{"simple", "0000000000000000000000000000000000000000000000000000000000000002",
 146  			"0000000000000000000000000000000000000000000000000000000000000003"},
 147  		{"random", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 148  			"deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef"},
 149  	}
 150  
 151  	for _, tc := range testCases {
 152  		t.Run(tc.name, func(t *testing.T) {
 153  			seckey, _ := hex.DecodeString(tc.seckey)
 154  			pubkeyGenBytes, _ := hex.DecodeString(tc.pubkeyGen)
 155  
 156  			// Generate the public key point using libsecp256k1
 157  			// First get the compressed pubkey for pubkeyGen * G
 158  			var pubkeyGenScalar Scalar
 159  			pubkeyGenScalar.setB32(pubkeyGenBytes)
 160  
 161  			// Compute pubkey = pubkeyGen * G using our reference implementation
 162  			var pubkeyJac GroupElementJacobian
 163  			EcmultConst(&pubkeyJac, &Generator, &pubkeyGenScalar)
 164  			var pubkeyAff GroupElementAffine
 165  			pubkeyAff.setGEJ(&pubkeyJac)
 166  			pubkeyAff.x.normalize()
 167  			pubkeyAff.y.normalize()
 168  
 169  			// Serialize as compressed pubkey (33 bytes)
 170  			pubkey33 := make([]byte, 33)
 171  			var xBytes [32]byte
 172  			pubkeyAff.x.getB32(xBytes[:])
 173  			copy(pubkey33[1:], xBytes[:])
 174  
 175  			// Determine prefix based on Y coordinate parity
 176  			var yBytes [32]byte
 177  			pubkeyAff.y.getB32(yBytes[:])
 178  			if yBytes[31]&1 == 0 {
 179  				pubkey33[0] = 0x02 // even Y
 180  			} else {
 181  				pubkey33[0] = 0x03 // odd Y
 182  			}
 183  
 184  			// Get expected ECDH result from libsecp256k1
 185  			expectedECDH, err := lib.ECDH(seckey, pubkey33)
 186  			if err != nil {
 187  				t.Fatalf("libsecp256k1.ECDH failed: %v", err)
 188  			}
 189  
 190  			// Now compute ECDH using our implementation
 191  			// ECDH = sha256(seckey * pubkey)
 192  			var seckeyScalar Scalar
 193  			seckeyScalar.setB32(seckey)
 194  
 195  			// Helper to compute ECDH hash matching libsecp256k1's default
 196  			// libsecp256k1 hashes sha256(0x02/0x03 || x) - the compressed point
 197  			computeECDH := func(result *GroupElementJacobian) [32]byte {
 198  				var resultAff GroupElementAffine
 199  				resultAff.setGEJ(result)
 200  				resultAff.x.normalize()
 201  				resultAff.y.normalize()
 202  
 203  				// Serialize as compressed point (33 bytes)
 204  				var compressed [33]byte
 205  				var xBytes [32]byte
 206  				resultAff.x.getB32(xBytes[:])
 207  				copy(compressed[1:], xBytes[:])
 208  
 209  				// Determine prefix based on Y parity
 210  				var yBytes [32]byte
 211  				resultAff.y.getB32(yBytes[:])
 212  				if yBytes[31]&1 == 0 {
 213  					compressed[0] = 0x02 // even Y
 214  				} else {
 215  					compressed[0] = 0x03 // odd Y
 216  				}
 217  
 218  				return sha256.Sum256(compressed[:])
 219  			}
 220  
 221  			// Test using EcmultConst (reference)
 222  			t.Run("EcmultConst", func(t *testing.T) {
 223  				var result GroupElementJacobian
 224  				EcmultConst(&result, &pubkeyAff, &seckeyScalar)
 225  				gotECDH := computeECDH(&result)
 226  
 227  				if hex.EncodeToString(gotECDH[:]) != hex.EncodeToString(expectedECDH) {
 228  					t.Errorf("ECDH mismatch:\n  got:  %s\n  want: %s",
 229  						hex.EncodeToString(gotECDH[:]), hex.EncodeToString(expectedECDH))
 230  				}
 231  			})
 232  
 233  			// Test using ecmultStraussWNAFGLV
 234  			t.Run("ecmultStraussWNAFGLV", func(t *testing.T) {
 235  				var result GroupElementJacobian
 236  				ecmultStraussWNAFGLV(&result, &pubkeyAff, &seckeyScalar)
 237  				gotECDH := computeECDH(&result)
 238  
 239  				if hex.EncodeToString(gotECDH[:]) != hex.EncodeToString(expectedECDH) {
 240  					t.Errorf("ECDH mismatch:\n  got:  %s\n  want: %s",
 241  						hex.EncodeToString(gotECDH[:]), hex.EncodeToString(expectedECDH))
 242  				}
 243  			})
 244  
 245  			// Test using ecmultWindowedVar
 246  			t.Run("ecmultWindowedVar", func(t *testing.T) {
 247  				var result GroupElementJacobian
 248  				ecmultWindowedVar(&result, &pubkeyAff, &seckeyScalar)
 249  				gotECDH := computeECDH(&result)
 250  
 251  				if hex.EncodeToString(gotECDH[:]) != hex.EncodeToString(expectedECDH) {
 252  					t.Errorf("ECDH mismatch:\n  got:  %s\n  want: %s",
 253  						hex.EncodeToString(gotECDH[:]), hex.EncodeToString(expectedECDH))
 254  				}
 255  			})
 256  		})
 257  	}
 258  }
 259  
 260  // TestExternalValidationRandomScalars tests with pseudo-random scalars
 261  func TestExternalValidationRandomScalars(t *testing.T) {
 262  	lib, err := GetLibSecp256k1()
 263  	if err != nil {
 264  		t.Skipf("libsecp256k1 not available: %v", err)
 265  	}
 266  	if !lib.IsLoaded() {
 267  		t.Skip("libsecp256k1 not loaded")
 268  	}
 269  
 270  	seed := uint64(0xdeadbeef12345678)
 271  
 272  	for i := 0; i < 100; i++ {
 273  		// Generate pseudo-random scalar
 274  		var seckey [32]byte
 275  		for j := 0; j < 32; j++ {
 276  			seed = seed*6364136223846793005 + 1442695040888963407
 277  			seckey[j] = byte(seed >> 56)
 278  		}
 279  
 280  		// Get expected from libsecp256k1
 281  		expectedPubkey, err := lib.CreatePubkey(seckey[:])
 282  		if err != nil {
 283  			continue // Skip invalid keys
 284  		}
 285  
 286  		var s Scalar
 287  		s.setB32(seckey[:])
 288  
 289  		if s.isZero() {
 290  			continue
 291  		}
 292  
 293  		// Test EcmultConst
 294  		var refResult GroupElementJacobian
 295  		EcmultConst(&refResult, &Generator, &s)
 296  		var refAff GroupElementAffine
 297  		refAff.setGEJ(&refResult)
 298  		refAff.x.normalize()
 299  
 300  		var refX [32]byte
 301  		refAff.x.getB32(refX[:])
 302  
 303  		if hex.EncodeToString(refX[:]) != hex.EncodeToString(expectedPubkey) {
 304  			t.Errorf("Random test %d: EcmultConst mismatch:\n  scalar: %s\n  got:    %s\n  want:   %s",
 305  				i, hex.EncodeToString(seckey[:]),
 306  				hex.EncodeToString(refX[:]), hex.EncodeToString(expectedPubkey))
 307  		}
 308  
 309  		// Test ecmultStraussWNAFGLV
 310  		var straussResult GroupElementJacobian
 311  		ecmultStraussWNAFGLV(&straussResult, &Generator, &s)
 312  		var straussAff GroupElementAffine
 313  		straussAff.setGEJ(&straussResult)
 314  		straussAff.x.normalize()
 315  
 316  		var straussX [32]byte
 317  		straussAff.x.getB32(straussX[:])
 318  
 319  		if hex.EncodeToString(straussX[:]) != hex.EncodeToString(expectedPubkey) {
 320  			t.Errorf("Random test %d: ecmultStraussWNAFGLV mismatch:\n  scalar: %s\n  got:    %s\n  want:   %s",
 321  				i, hex.EncodeToString(seckey[:]),
 322  				hex.EncodeToString(straussX[:]), hex.EncodeToString(expectedPubkey))
 323  		}
 324  
 325  		// Test ecmultGenGLV
 326  		var glvResult GroupElementJacobian
 327  		ecmultGenGLV(&glvResult, &s)
 328  		var glvAff GroupElementAffine
 329  		glvAff.setGEJ(&glvResult)
 330  		glvAff.x.normalize()
 331  
 332  		var glvX [32]byte
 333  		glvAff.x.getB32(glvX[:])
 334  
 335  		if hex.EncodeToString(glvX[:]) != hex.EncodeToString(expectedPubkey) {
 336  			t.Errorf("Random test %d: ecmultGenGLV mismatch:\n  scalar: %s\n  got:    %s\n  want:   %s",
 337  				i, hex.EncodeToString(seckey[:]),
 338  				hex.EncodeToString(glvX[:]), hex.EncodeToString(expectedPubkey))
 339  		}
 340  	}
 341  }
 342  
 343  // TestExternalValidationGLVSplit validates the GLV scalar splitting specifically
 344  // We can verify the split is correct by checking that k1 + k2*λ ≡ k (mod n)
 345  // AND that k1*G + k2*(λ*G) = k*G (via libsecp256k1)
 346  func TestExternalValidationGLVSplit(t *testing.T) {
 347  	lib, err := GetLibSecp256k1()
 348  	if err != nil {
 349  		t.Skipf("libsecp256k1 not available: %v", err)
 350  	}
 351  	if !lib.IsLoaded() {
 352  		t.Skip("libsecp256k1 not loaded")
 353  	}
 354  
 355  	testCases := []struct {
 356  		name   string
 357  		scalar string
 358  	}{
 359  		{"one", "0000000000000000000000000000000000000000000000000000000000000001"},
 360  		{"random1", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"},
 361  		{"near_order", "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e"},
 362  	}
 363  
 364  	for _, tc := range testCases {
 365  		t.Run(tc.name, func(t *testing.T) {
 366  			kBytes, _ := hex.DecodeString(tc.scalar)
 367  
 368  			var k Scalar
 369  			k.setB32(kBytes)
 370  
 371  			if k.isZero() {
 372  				t.Skip("zero scalar")
 373  			}
 374  
 375  			// Get expected k*G from libsecp256k1
 376  			expectedPubkey, err := lib.CreatePubkey(kBytes)
 377  			if err != nil {
 378  				t.Fatalf("libsecp256k1.CreatePubkey failed: %v", err)
 379  			}
 380  
 381  			// Split k into k1, k2
 382  			var k1, k2 Scalar
 383  			scalarSplitLambda(&k1, &k2, &k)
 384  
 385  			// Log split values for debugging
 386  			var k1Bytes, k2Bytes [32]byte
 387  			k1.getB32(k1Bytes[:])
 388  			k2.getB32(k2Bytes[:])
 389  			t.Logf("k  = %s", hex.EncodeToString(kBytes))
 390  			t.Logf("k1 = %s", hex.EncodeToString(k1Bytes[:]))
 391  			t.Logf("k2 = %s", hex.EncodeToString(k2Bytes[:]))
 392  
 393  			// Verify algebraically: k1 + k2*λ ≡ k (mod n)
 394  			var k2Lambda Scalar
 395  			k2Lambda.mul(&k2, &scalarLambda)
 396  
 397  			var reconstructed Scalar
 398  			reconstructed.add(&k1, &k2Lambda)
 399  
 400  			if !reconstructed.equal(&k) {
 401  				var recBytes [32]byte
 402  				reconstructed.getB32(recBytes[:])
 403  				t.Errorf("Scalar split reconstruction failed:\n  k1 + k2*λ = %s\n  k         = %s",
 404  					hex.EncodeToString(recBytes[:]), hex.EncodeToString(kBytes))
 405  			}
 406  
 407  			// Verify geometrically: k1*G + k2*(λ*G) = k*G
 408  			// Compute k1*G
 409  			var k1G GroupElementJacobian
 410  			EcmultConst(&k1G, &Generator, &k1)
 411  
 412  			// Compute λ*G (using the endomorphism: (β*Gx, Gy))
 413  			var lambdaG GroupElementAffine
 414  			lambdaG.mulLambda(&Generator)
 415  
 416  			// Compute k2*(λ*G)
 417  			var k2LambdaG GroupElementJacobian
 418  			EcmultConst(&k2LambdaG, &lambdaG, &k2)
 419  
 420  			// Add them
 421  			var sum GroupElementJacobian
 422  			sum.addVar(&k1G, &k2LambdaG)
 423  
 424  			var sumAff GroupElementAffine
 425  			sumAff.setGEJ(&sum)
 426  			sumAff.x.normalize()
 427  
 428  			var sumX [32]byte
 429  			sumAff.x.getB32(sumX[:])
 430  
 431  			if hex.EncodeToString(sumX[:]) != hex.EncodeToString(expectedPubkey) {
 432  				t.Errorf("Geometric verification failed:\n  k1*G + k2*(λ*G) = %s\n  k*G (expected)  = %s",
 433  					hex.EncodeToString(sumX[:]), hex.EncodeToString(expectedPubkey))
 434  			}
 435  		})
 436  	}
 437  }
 438  
 439  // TestExternalValidationSchnorr validates our Schnorr implementation against libsecp256k1
 440  func TestExternalValidationSchnorr(t *testing.T) {
 441  	lib, err := GetLibSecp256k1()
 442  	if err != nil {
 443  		t.Skipf("libsecp256k1 not available: %v", err)
 444  	}
 445  	if !lib.IsLoaded() {
 446  		t.Skip("libsecp256k1 not loaded")
 447  	}
 448  
 449  	// Test that signatures created by libsecp256k1 can be verified by our code
 450  	// and vice versa
 451  	testCases := []struct {
 452  		name   string
 453  		seckey string
 454  		msg    string
 455  	}{
 456  		{"simple", "0000000000000000000000000000000000000000000000000000000000000001",
 457  			"0000000000000000000000000000000000000000000000000000000000000000"},
 458  		{"random", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
 459  			"deadbeefcafebabe1234567890abcdef0123456789abcdef0123456789abcdef"},
 460  	}
 461  
 462  	for _, tc := range testCases {
 463  		t.Run(tc.name, func(t *testing.T) {
 464  			seckey, _ := hex.DecodeString(tc.seckey)
 465  			msg, _ := hex.DecodeString(tc.msg)
 466  
 467  			// Get pubkey from libsecp256k1
 468  			pubkey, err := lib.CreatePubkey(seckey)
 469  			if err != nil {
 470  				t.Fatalf("CreatePubkey failed: %v", err)
 471  			}
 472  
 473  			// Sign with libsecp256k1
 474  			sig, err := lib.SchnorrSign(msg, seckey)
 475  			if err != nil {
 476  				t.Fatalf("SchnorrSign failed: %v", err)
 477  			}
 478  
 479  			// Verify with libsecp256k1 (sanity check)
 480  			if !lib.SchnorrVerify(sig, msg, pubkey) {
 481  				t.Error("libsecp256k1 failed to verify its own signature")
 482  			}
 483  
 484  			// Verify with our implementation
 485  			xonlyPubkey, err := XOnlyPubkeyParse(pubkey)
 486  			if err != nil {
 487  				t.Fatalf("XOnlyPubkeyParse failed: %v", err)
 488  			}
 489  			valid := SchnorrVerify(sig, msg, xonlyPubkey)
 490  			if !valid {
 491  				t.Error("Our SchnorrVerify failed to verify libsecp256k1 signature")
 492  			}
 493  		})
 494  	}
 495  }
 496  
 497  // TestExternalValidationDebugSingleScalar is a focused debug test for a single scalar
 498  func TestExternalValidationDebugSingleScalar(t *testing.T) {
 499  	lib, err := GetLibSecp256k1()
 500  	if err != nil {
 501  		t.Skipf("libsecp256k1 not available: %v", err)
 502  	}
 503  	if !lib.IsLoaded() {
 504  		t.Skip("libsecp256k1 not loaded")
 505  	}
 506  
 507  	// Use scalar 2 for simple debugging
 508  	seckey, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000002")
 509  
 510  	expectedPubkey, err := lib.CreatePubkey(seckey)
 511  	if err != nil {
 512  		t.Fatalf("CreatePubkey failed: %v", err)
 513  	}
 514  	t.Logf("Expected (2*G).x = %s", hex.EncodeToString(expectedPubkey))
 515  
 516  	var s Scalar
 517  	s.setB32(seckey)
 518  
 519  	// EcmultConst
 520  	var constResult GroupElementJacobian
 521  	EcmultConst(&constResult, &Generator, &s)
 522  	var constAff GroupElementAffine
 523  	constAff.setGEJ(&constResult)
 524  	constAff.x.normalize()
 525  	var constX [32]byte
 526  	constAff.x.getB32(constX[:])
 527  	t.Logf("EcmultConst:         %s", hex.EncodeToString(constX[:]))
 528  
 529  	// ecmultWindowedVar
 530  	var windowedResult GroupElementJacobian
 531  	ecmultWindowedVar(&windowedResult, &Generator, &s)
 532  	var windowedAff GroupElementAffine
 533  	windowedAff.setGEJ(&windowedResult)
 534  	windowedAff.x.normalize()
 535  	var windowedX [32]byte
 536  	windowedAff.x.getB32(windowedX[:])
 537  	t.Logf("ecmultWindowedVar:   %s", hex.EncodeToString(windowedX[:]))
 538  
 539  	// ecmultGenGLV
 540  	var glvResult GroupElementJacobian
 541  	ecmultGenGLV(&glvResult, &s)
 542  	var glvAff GroupElementAffine
 543  	glvAff.setGEJ(&glvResult)
 544  	glvAff.x.normalize()
 545  	var glvX [32]byte
 546  	glvAff.x.getB32(glvX[:])
 547  	t.Logf("ecmultGenGLV:        %s", hex.EncodeToString(glvX[:]))
 548  
 549  	// ecmultStraussWNAFGLV
 550  	var straussResult GroupElementJacobian
 551  	ecmultStraussWNAFGLV(&straussResult, &Generator, &s)
 552  	var straussAff GroupElementAffine
 553  	straussAff.setGEJ(&straussResult)
 554  	straussAff.x.normalize()
 555  	var straussX [32]byte
 556  	straussAff.x.getB32(straussX[:])
 557  	t.Logf("ecmultStraussWNAFGLV: %s", hex.EncodeToString(straussX[:]))
 558  
 559  	// Check all match expected
 560  	if hex.EncodeToString(constX[:]) != hex.EncodeToString(expectedPubkey) {
 561  		t.Errorf("EcmultConst MISMATCH")
 562  	}
 563  	if hex.EncodeToString(windowedX[:]) != hex.EncodeToString(expectedPubkey) {
 564  		t.Errorf("ecmultWindowedVar MISMATCH")
 565  	}
 566  	if hex.EncodeToString(glvX[:]) != hex.EncodeToString(expectedPubkey) {
 567  		t.Errorf("ecmultGenGLV MISMATCH")
 568  	}
 569  	if hex.EncodeToString(straussX[:]) != hex.EncodeToString(expectedPubkey) {
 570  		t.Errorf("ecmultStraussWNAFGLV MISMATCH")
 571  	}
 572  }
 573