From 67123ae8f20ea7a3f607587a05536aaeb237e092 Mon Sep 17 00:00:00 2001 From: jholdstock Date: Sun, 5 Jul 2026 12:44:36 +0800 Subject: [PATCH 1/4] edwards: Run tests in parallel. None of the tests in this package were tagged with t.Parallel despite them sharing no global state and there being no risk of races. On my system with a i7-10750H CPU using t.Parallel reduces the total runtime for the edwards package tests from ~9.3s to ~4.7s. --- dcrec/edwards/ciphering_test.go | 10 +++++++++- dcrec/edwards/curve_test.go | 8 +++++++- dcrec/edwards/ecdsa_test.go | 6 +++++- dcrec/edwards/primitives_test.go | 6 +++++- dcrec/edwards/privkey_test.go | 4 +++- dcrec/edwards/threshold_test.go | 4 +++- 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/dcrec/edwards/ciphering_test.go b/dcrec/edwards/ciphering_test.go index 300e7cb036..e409119b69 100644 --- a/dcrec/edwards/ciphering_test.go +++ b/dcrec/edwards/ciphering_test.go @@ -1,5 +1,5 @@ // Copyright (c) 2015 The btcsuite developers -// Copyright (c) 2015-2024 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -13,6 +13,8 @@ import ( ) func TestGenerateSharedSecret(t *testing.T) { + t.Parallel() + privKey1, err := GeneratePrivateKey() if err != nil { t.Errorf("private key generation error: %s", err) @@ -39,6 +41,8 @@ func TestGenerateSharedSecret(t *testing.T) { // Test 1: Encryption and decryption. func TestCipheringBasic(t *testing.T) { + t.Parallel() + privkey, err := GeneratePrivateKey() if err != nil { t.Fatal("failed to generate private key") @@ -64,6 +68,8 @@ func TestCipheringBasic(t *testing.T) { } func TestCiphering(t *testing.T) { + t.Parallel() + c := Edwards() pb, _ := hex.DecodeString("fe38240982f313ae5afb3e904fb8215fb11af1200592b" + @@ -103,6 +109,8 @@ func TestCiphering(t *testing.T) { } func TestCipheringErrors(t *testing.T) { + t.Parallel() + privkey, err := GeneratePrivateKey() if err != nil { t.Fatal("failed to generate private key") diff --git a/dcrec/edwards/curve_test.go b/dcrec/edwards/curve_test.go index 9e5a0aa2cf..688e5549e5 100644 --- a/dcrec/edwards/curve_test.go +++ b/dcrec/edwards/curve_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2022 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -40,6 +40,8 @@ func testPointXRecoveryVectors() []XRecoveryVector { // - extendedToBigAffine // - EncodedBytesToBigIntPoint func TestXRecovery(t *testing.T) { + t.Parallel() + curve := Edwards() for _, vector := range testPointXRecoveryVectors() { @@ -97,6 +99,8 @@ func TestXRecovery(t *testing.T) { // extendedToBigAffine // encodedBytesToBigIntPoint func TestAdd(t *testing.T) { + t.Parallel() + pointHexStrIdx := 0 pointHexStrSet := []string{ "4a3f2684abc42977fe50adbb158a9939cc31b210a7c6e6ea4856395ef3e51bf4", @@ -214,6 +218,8 @@ func testVectorsScalarMult() []ScalarMultVector { // - Double // - ScalarMult func TestScalarMult(t *testing.T) { + t.Parallel() + curve := Edwards() for _, vector := range testVectorsScalarMult() { diff --git a/dcrec/edwards/ecdsa_test.go b/dcrec/edwards/ecdsa_test.go index 71d57ec6cc..952307e88c 100644 --- a/dcrec/edwards/ecdsa_test.go +++ b/dcrec/edwards/ecdsa_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -19,6 +19,8 @@ import ( ) func TestGolden(t *testing.T) { + t.Parallel() + // sign.input.gz is a selection of test cases from // https://ed25519.cr.yp.to/python/sign.input testDataZ, err := os.Open("testdata/sign.input.gz") @@ -200,6 +202,8 @@ func randPrivScalarKeyList(i int) []*PrivateKey { } func TestNonStandardSignatures(t *testing.T) { + t.Parallel() + tRand := rand.New(rand.NewSource(54321)) msg := []byte{ diff --git a/dcrec/edwards/primitives_test.go b/dcrec/edwards/primitives_test.go index 788cef3654..438e3c2bf0 100644 --- a/dcrec/edwards/primitives_test.go +++ b/dcrec/edwards/primitives_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2022 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -45,6 +45,8 @@ func testConversionVectors() []ConversionVector { // - FieldElementToBigInt // - EncodedBytesToFieldElement func TestConversion(t *testing.T) { + t.Parallel() + encodedNumToStrIdx := 0 encodedNumToStrSet := []string{ "20196841024736227335511321252453997055107605473446826399550527392484145048463", @@ -229,6 +231,8 @@ func testPointConversionVectors() []ConversionVector { // - extendedToBigAffine // - EncodedBytesToBigIntPoint func TestPointConversion(t *testing.T) { + t.Parallel() + decodedPointsIdx := 0 decodedPointsSet := []string{ "36342386295235510298682738805067969701306540594271578388800019131093341795154,12122921476001995645148951048614280991245620197289177635264906062452356396947", diff --git a/dcrec/edwards/privkey_test.go b/dcrec/edwards/privkey_test.go index a8a86532ed..1de60293bb 100644 --- a/dcrec/edwards/privkey_test.go +++ b/dcrec/edwards/privkey_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Decred developers +// Copyright (c) 2019-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -9,6 +9,8 @@ import ( ) func TestPrivKeySign(t *testing.T) { + t.Parallel() + message := []byte("the quick brown fox jumps over the lazy dog") privkey, err := GeneratePrivateKey() diff --git a/dcrec/edwards/threshold_test.go b/dcrec/edwards/threshold_test.go index 752c6d0e64..7047f909d7 100644 --- a/dcrec/edwards/threshold_test.go +++ b/dcrec/edwards/threshold_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2016 The Decred developers +// Copyright (c) 2015-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -13,6 +13,8 @@ import ( ) func TestSchnorrThreshold(t *testing.T) { + t.Parallel() + tRand := rand.New(rand.NewSource(543212345)) maxSignatories := 10 numTests := 5 From c5e02c8ef27a14e4d7a9f069fd8bf3d8d5785fe3 Mon Sep 17 00:00:00 2001 From: jholdstock Date: Sun, 5 Jul 2026 12:32:40 +0800 Subject: [PATCH 2/4] edwards: Run test cases concurrently. TestSchnorrThreshold and TestNonStandardSignatures were previously running 50 test cases sequentially. On my system with a i7-10750H CPU running them in parallel using goroutines reduces the total runtime for the edwards package tests from ~4.7s to ~1.7s. --- dcrec/edwards/ecdsa_test.go | 171 ++++++++------- dcrec/edwards/threshold_test.go | 370 +++++++++++++++++--------------- 2 files changed, 289 insertions(+), 252 deletions(-) diff --git a/dcrec/edwards/ecdsa_test.go b/dcrec/edwards/ecdsa_test.go index 952307e88c..7000ddea28 100644 --- a/dcrec/edwards/ecdsa_test.go +++ b/dcrec/edwards/ecdsa_test.go @@ -15,6 +15,7 @@ import ( "math/rand" "os" "strings" + "sync" "testing" ) @@ -204,8 +205,6 @@ func randPrivScalarKeyList(i int) []*PrivateKey { func TestNonStandardSignatures(t *testing.T) { t.Parallel() - tRand := rand.New(rand.NewSource(54321)) - msg := []byte{ 0xbe, 0x13, 0xae, 0xf4, 0xe8, 0xa2, 0x00, 0xb6, @@ -218,94 +217,112 @@ func TestNonStandardSignatures(t *testing.T) { } pks := randPrivScalarKeyList(50) - for _, pk := range pks { - r, s, err := Sign(pk, msg) - if err != nil { - t.Fatalf("unexpected error %s", err) - } - - pubX, pubY := pk.Public() - pub := NewPublicKey(pubX, pubY) - ok := Verify(pub, msg, r, s) - if !ok { - t.Fatalf("expected %v, got %v", true, ok) - } + var wg sync.WaitGroup + for i, pk := range pks { + wg.Add(1) + go func(i int, pk *PrivateKey) { + defer wg.Done() + + tRand := rand.New(rand.NewSource(54321)) + r, s, err := Sign(pk, msg) + if err != nil { + t.Errorf("unexpected error %s", err) + return + } - // Test serializing/deserializing. - privKeyDupTest, _, err := PrivKeyFromScalar( - copyBytes(pk.ecPk.D.Bytes())[:]) + pubX, pubY := pk.Public() + pub := NewPublicKey(pubX, pubY) + ok := Verify(pub, msg, r, s) + if !ok { + t.Errorf("expected %v, got %v", true, ok) + return + } - if err != nil { - t.Fatalf("unexpected error %s", err) - } + // Test serializing/deserializing. + privKeyDupTest, _, err := PrivKeyFromScalar( + copyBytes(pk.ecPk.D.Bytes())[:]) - cmp := privKeyDupTest.GetD().Cmp(pk.GetD()) == 0 - if !cmp { - t.Fatalf("expected %v, got %v", true, cmp) - } + if err != nil { + t.Errorf("unexpected error %s", err) + return + } - privKeyDupTest2, _, err := PrivKeyFromScalar(pk.Serialize()) - if err != nil { - t.Fatalf("unexpected error %s", err) - } + cmp := privKeyDupTest.GetD().Cmp(pk.GetD()) == 0 + if !cmp { + t.Errorf("expected %v, got %v", true, cmp) + return + } - cmp = privKeyDupTest2.GetD().Cmp(pk.GetD()) == 0 - if !cmp { - t.Fatalf("expected %v, got %v", true, cmp) - } + privKeyDupTest2, _, err := PrivKeyFromScalar(pk.Serialize()) + if err != nil { + t.Errorf("unexpected error %s", err) + return + } - // Screw up a random bit in the signature and - // make sure it still fails. - sig := NewSignature(r, s) - sigBad := sig.Serialize() - pos := tRand.Intn(63) - bitPos := tRand.Intn(7) - sigBad[pos] ^= 1 << uint8(bitPos) + cmp = privKeyDupTest2.GetD().Cmp(pk.GetD()) == 0 + if !cmp { + t.Errorf("expected %v, got %v", true, cmp) + return + } - bSig, err := ParseSignature(sigBad) - if err != nil { - // Signature failed to parse, continue. - continue - } - ok = Verify(pub, msg, bSig.GetR(), bSig.GetS()) - if ok { - t.Fatalf("expected %v, got %v", false, ok) - } + // Screw up a random bit in the signature and + // make sure it still fails. + sig := NewSignature(r, s) + sigBad := sig.Serialize() + pos := tRand.Intn(63) + bitPos := tRand.Intn(7) + sigBad[pos] ^= 1 << uint8(bitPos) - // Screw up a random bit in the pubkey and - // make sure it still fails. - pkBad := pub.Serialize() - pos = tRand.Intn(31) - if pos == 0 { - // 0th bit in first byte doesn't matter - bitPos = tRand.Intn(6) + 1 - } else { - bitPos = tRand.Intn(7) - } - pkBad[pos] ^= 1 << uint8(bitPos) - bPub, err := ParsePubKey(pkBad) - if err == nil && bPub != nil { - ok = Verify(bPub, msg, r, s) + bSig, err := ParseSignature(sigBad) + if err != nil { + // Signature failed to parse, nothing more to check. + return + } + ok = Verify(pub, msg, bSig.GetR(), bSig.GetS()) if ok { - t.Fatalf("expected %v, got %v", false, ok) + t.Errorf("expected %v, got %v", false, ok) + return } - } - // Append an extra byte and make sure the parse fails. - pkBad2 := append(pub.Serialize(), 0x01) - _, err = ParsePubKey(pkBad2) - if err == nil { - t.Fatal("expected err, got nil") - } + // Screw up a random bit in the pubkey and + // make sure it still fails. + pkBad := pub.Serialize() + pos = tRand.Intn(31) + if pos == 0 { + // 0th bit in first byte doesn't matter + bitPos = tRand.Intn(6) + 1 + } else { + bitPos = tRand.Intn(7) + } + pkBad[pos] ^= 1 << uint8(bitPos) + bPub, err := ParsePubKey(pkBad) + if err == nil && bPub != nil { + ok = Verify(bPub, msg, r, s) + if ok { + t.Errorf("expected %v, got %v", false, ok) + return + } + } - // Remove a random byte and make sure the parse fails. - pkBad3 := pub.Serialize() - pkBad3 = append(pkBad3[:pos], pkBad3[pos+1:]...) - _, err = ParsePubKey(pkBad3) - if err == nil { - t.Fatal("expected err, got nil") - } + // Append an extra byte and make sure the parse fails. + pkBad2 := append(pub.Serialize(), 0x01) + _, err = ParsePubKey(pkBad2) + if err == nil { + t.Errorf("expected err, got nil") + return + } + + // Remove a random byte and make sure the parse fails. + pkBad3 := pub.Serialize() + pkBad3 = append(pkBad3[:pos], pkBad3[pos+1:]...) + _, err = ParsePubKey(pkBad3) + if err == nil { + t.Errorf("expected err, got nil") + return + } + }(i, pk) } + wg.Wait() } func randPrivKeyList(i int) []*PrivateKey { diff --git a/dcrec/edwards/threshold_test.go b/dcrec/edwards/threshold_test.go index 7047f909d7..e6b0d6ffee 100644 --- a/dcrec/edwards/threshold_test.go +++ b/dcrec/edwards/threshold_test.go @@ -9,13 +9,13 @@ import ( "encoding/hex" "math/big" "math/rand" + "sync" "testing" ) func TestSchnorrThreshold(t *testing.T) { t.Parallel() - tRand := rand.New(rand.NewSource(543212345)) maxSignatories := 10 numTests := 5 numSignatories := maxSignatories * numTests @@ -24,197 +24,217 @@ func TestSchnorrThreshold(t *testing.T) { "d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa") privkeys := randPrivScalarKeyList(numSignatories) + var wg sync.WaitGroup for i := 0; i < numTests; i++ { - numKeysForTest := tRand.Intn(maxSignatories-2) + 2 - keyIndex := i * maxSignatories - keysToUse := make([]*PrivateKey, numKeysForTest) - for j := 0; j < numKeysForTest; j++ { - keysToUse[j] = privkeys[j+keyIndex] - } - - pubKeysToUse := make([]*PublicKey, numKeysForTest) - for j := 0; j < numKeysForTest; j++ { - _, pubkey, _ := PrivKeyFromScalar( - keysToUse[j].Serialize()) - pubKeysToUse[j] = pubkey - } - - // Combine pubkeys. - allPubkeys := make([]*PublicKey, numKeysForTest) - copy(allPubkeys, pubKeysToUse) - - allPksSum := combinePubkeys(allPubkeys) - - curve := Edwards() - privNoncesToUse := make([]*PrivateKey, numKeysForTest) - pubNoncesToUse := make([]*PublicKey, numKeysForTest) - for j := 0; j < numKeysForTest; j++ { - nonce := nonceRFC6979(keysToUse[j].Serialize(), msg, nil, - Sha512VersionStringRFC6979) - nonceBig := new(big.Int).SetBytes(nonce) - nonceBig.Mod(nonceBig, curve.N) - nonce = copyBytes(nonceBig.Bytes())[:] - nonce[31] &= 248 - - privNonce, pubNonce, err := PrivKeyFromScalar( - nonce[:]) - cmp := privNonce != nil - if !cmp { - t.Fatalf("expected %v, got %v", true, cmp) + wg.Add(1) + go func(i int) { + defer wg.Done() + + tRand := rand.New(rand.NewSource(543212345)) + numKeysForTest := tRand.Intn(maxSignatories-2) + 2 + keyIndex := i * maxSignatories + keysToUse := make([]*PrivateKey, numKeysForTest) + for j := 0; j < numKeysForTest; j++ { + keysToUse[j] = privkeys[j+keyIndex] + } + + pubKeysToUse := make([]*PublicKey, numKeysForTest) + for j := 0; j < numKeysForTest; j++ { + _, pubkey, _ := PrivKeyFromScalar( + keysToUse[j].Serialize()) + pubKeysToUse[j] = pubkey } - cmp = pubNonce != nil + // Combine pubkeys. + allPubkeys := make([]*PublicKey, numKeysForTest) + copy(allPubkeys, pubKeysToUse) + + allPksSum := combinePubkeys(allPubkeys) + + curve := Edwards() + privNoncesToUse := make([]*PrivateKey, numKeysForTest) + pubNoncesToUse := make([]*PublicKey, numKeysForTest) + for j := 0; j < numKeysForTest; j++ { + nonce := nonceRFC6979(keysToUse[j].Serialize(), msg, nil, + Sha512VersionStringRFC6979) + nonceBig := new(big.Int).SetBytes(nonce) + nonceBig.Mod(nonceBig, curve.N) + nonce = copyBytes(nonceBig.Bytes())[:] + nonce[31] &= 248 + + privNonce, pubNonce, err := PrivKeyFromScalar( + nonce[:]) + cmp := privNonce != nil + if !cmp { + t.Errorf("expected %v, got %v", true, cmp) + return + } + + cmp = pubNonce != nil + if !cmp { + t.Errorf("expected %v, got %v", true, cmp) + return + } + + if err != nil { + t.Errorf("unexpected error %s, ", err) + return + } + + privNoncesToUse[j] = privNonce + pubNoncesToUse[j] = pubNonce + } + + partialSignatures := make([]*Signature, numKeysForTest) + + // Partial signature generation. + publicNonceSum := combinePubkeys(pubNoncesToUse) + cmp := publicNonceSum != nil if !cmp { - t.Fatalf("expected %v, got %v", true, cmp) + t.Errorf("expected %v, got %v", true, cmp) + return + } + for j := range keysToUse { + r, s, err := schnorrPartialSign(msg, keysToUse[j].Serialize(), + allPksSum.Serialize(), privNoncesToUse[j].Serialize(), + publicNonceSum.Serialize()) + if err != nil { + t.Errorf("unexpected error %s, ", err) + return + } + + localSig := NewSignature(r, s) + partialSignatures[j] = localSig } + // Combine signatures. + combinedSignature, err := schnorrCombinePartialSigs(partialSignatures) if err != nil { - t.Fatalf("unexpected error %s, ", err) + t.Errorf("unexpected error %s, ", err) + return } - privNoncesToUse[j] = privNonce - pubNoncesToUse[j] = pubNonce - } + // Make sure the combined signatures are the same as the + // signatures that would be generated by simply adding + // the private keys and private nonces. + combinedPrivkeysD := new(big.Int).SetInt64(0) + for _, priv := range keysToUse { + combinedPrivkeysD = scalarAdd(combinedPrivkeysD, priv.GetD()) + combinedPrivkeysD = combinedPrivkeysD.Mod(combinedPrivkeysD, curve.N) + } - partialSignatures := make([]*Signature, numKeysForTest) + combinedNonceD := new(big.Int).SetInt64(0) + for _, priv := range privNoncesToUse { + combinedNonceD.Add(combinedNonceD, priv.GetD()) + combinedNonceD.Mod(combinedNonceD, curve.N) + } - // Partial signature generation. - publicNonceSum := combinePubkeys(pubNoncesToUse) - cmp := publicNonceSum != nil - if !cmp { - t.Fatalf("expected %v, got %v", true, cmp) - } - for j := range keysToUse { - r, s, err := schnorrPartialSign(msg, keysToUse[j].Serialize(), - allPksSum.Serialize(), privNoncesToUse[j].Serialize(), - publicNonceSum.Serialize()) + combinedPrivkey, _, err := PrivKeyFromScalar( + copyBytes(combinedPrivkeysD.Bytes())[:]) if err != nil { - t.Fatalf("unexpected error %s, ", err) - } - - localSig := NewSignature(r, s) - partialSignatures[j] = localSig - } - - // Combine signatures. - combinedSignature, err := schnorrCombinePartialSigs(partialSignatures) - if err != nil { - t.Fatalf("unexpected error %s, ", err) - } - - // Make sure the combined signatures are the same as the - // signatures that would be generated by simply adding - // the private keys and private nonces. - combinedPrivkeysD := new(big.Int).SetInt64(0) - for _, priv := range keysToUse { - combinedPrivkeysD = scalarAdd(combinedPrivkeysD, priv.GetD()) - combinedPrivkeysD = combinedPrivkeysD.Mod(combinedPrivkeysD, curve.N) - } - - combinedNonceD := new(big.Int).SetInt64(0) - for _, priv := range privNoncesToUse { - combinedNonceD.Add(combinedNonceD, priv.GetD()) - combinedNonceD.Mod(combinedNonceD, curve.N) - } - - combinedPrivkey, _, err := PrivKeyFromScalar( - copyBytes(combinedPrivkeysD.Bytes())[:]) - if err != nil { - t.Fatalf("unexpected error %s, ", err) - } - combinedNonce, _, err := PrivKeyFromScalar( - copyBytes(combinedNonceD.Bytes())[:]) - if err != nil { - t.Fatalf("unexpected error %s, ", err) - } - cSigR, cSigS, err := SignFromScalar(combinedPrivkey, - combinedNonce.Serialize(), msg) - sumSig := NewSignature(cSigR, cSigS) - cmp = bytes.Equal(sumSig.Serialize(), combinedSignature.Serialize()) - if !cmp { - t.Fatalf("expected %v, got %v", true, cmp) - } - - if err != nil { - t.Fatalf("unexpected error %s, ", err) - } - - // Verify the combined signature and public keys. - ok := Verify(allPksSum, msg, combinedSignature.GetR(), - combinedSignature.GetS()) - if !ok { - t.Fatalf("expected %v, got %v", true, ok) - } - - // Corrupt some memory and make sure it breaks something. - corruptWhat := tRand.Intn(3) - randItem := tRand.Intn(numKeysForTest - 1) - - // Corrupt private key. - if corruptWhat == 0 { - privSerCorrupt := keysToUse[randItem].Serialize() - pos := tRand.Intn(31) - bitPos := tRand.Intn(7) - privSerCorrupt[pos] ^= 1 << uint8(bitPos) - keysToUse[randItem].ecPk.D.SetBytes(privSerCorrupt) - } - // Corrupt public key. - if corruptWhat == 1 { - pubXCorrupt := bigIntToEncodedBytes(pubKeysToUse[randItem].GetX()) - pos := tRand.Intn(31) - bitPos := tRand.Intn(7) - pubXCorrupt[pos] ^= 1 << uint8(bitPos) - pubKeysToUse[randItem].GetX().SetBytes(pubXCorrupt[:]) - } - // Corrupt private nonce. - if corruptWhat == 2 { - privSerCorrupt := privNoncesToUse[randItem].Serialize() - pos := tRand.Intn(31) - bitPos := tRand.Intn(7) - privSerCorrupt[pos] ^= 1 << uint8(bitPos) - privNoncesToUse[randItem].ecPk.D.SetBytes(privSerCorrupt) - } - // Corrupt public nonce. - if corruptWhat == 3 { - pubXCorrupt := bigIntToEncodedBytes(pubNoncesToUse[randItem].GetX()) - pos := tRand.Intn(31) - bitPos := tRand.Intn(7) - pubXCorrupt[pos] ^= 1 << uint8(bitPos) - pubNoncesToUse[randItem].GetX().SetBytes(pubXCorrupt[:]) - } - - for j := range keysToUse { - thisPubNonce := pubNoncesToUse[j] - localPubNonces := make([]*PublicKey, numKeysForTest-1) - itr := 0 - for _, pubNonce := range pubNoncesToUse { - if bytes.Equal(thisPubNonce.Serialize(), pubNonce.Serialize()) { - continue - } - localPubNonces[itr] = pubNonce - itr++ + t.Errorf("unexpected error %s, ", err) + return + } + combinedNonce, _, err := PrivKeyFromScalar( + copyBytes(combinedNonceD.Bytes())[:]) + if err != nil { + t.Errorf("unexpected error %s, ", err) + return + } + cSigR, cSigS, err := SignFromScalar(combinedPrivkey, + combinedNonce.Serialize(), msg) + sumSig := NewSignature(cSigR, cSigS) + cmp = bytes.Equal(sumSig.Serialize(), combinedSignature.Serialize()) + if !cmp { + t.Errorf("expected %v, got %v", true, cmp) + return } - publicNonceSum := combinePubkeys(localPubNonces) - sigR, sigS, _ := schnorrPartialSign(msg, - keysToUse[j].Serialize(), allPksSum.Serialize(), - privNoncesToUse[j].Serialize(), - publicNonceSum.Serialize()) - localSig := NewSignature(sigR, sigS) + if err != nil { + t.Errorf("unexpected error %s, ", err) + return + } - partialSignatures[j] = localSig - } + // Verify the combined signature and public keys. + ok := Verify(allPksSum, msg, combinedSignature.GetR(), + combinedSignature.GetS()) + if !ok { + t.Errorf("expected %v, got %v", true, ok) + return + } - // combine signatures. - combinedSignature, _ = schnorrCombinePartialSigs(partialSignatures) + // Corrupt some memory and make sure it breaks something. + corruptWhat := tRand.Intn(3) + randItem := tRand.Intn(numKeysForTest - 1) + + // Corrupt private key. + if corruptWhat == 0 { + privSerCorrupt := keysToUse[randItem].Serialize() + pos := tRand.Intn(31) + bitPos := tRand.Intn(7) + privSerCorrupt[pos] ^= 1 << uint8(bitPos) + keysToUse[randItem].ecPk.D.SetBytes(privSerCorrupt) + } + // Corrupt public key. + if corruptWhat == 1 { + pubXCorrupt := bigIntToEncodedBytes(pubKeysToUse[randItem].GetX()) + pos := tRand.Intn(31) + bitPos := tRand.Intn(7) + pubXCorrupt[pos] ^= 1 << uint8(bitPos) + pubKeysToUse[randItem].GetX().SetBytes(pubXCorrupt[:]) + } + // Corrupt private nonce. + if corruptWhat == 2 { + privSerCorrupt := privNoncesToUse[randItem].Serialize() + pos := tRand.Intn(31) + bitPos := tRand.Intn(7) + privSerCorrupt[pos] ^= 1 << uint8(bitPos) + privNoncesToUse[randItem].ecPk.D.SetBytes(privSerCorrupt) + } + // Corrupt public nonce. + if corruptWhat == 3 { + pubXCorrupt := bigIntToEncodedBytes(pubNoncesToUse[randItem].GetX()) + pos := tRand.Intn(31) + bitPos := tRand.Intn(7) + pubXCorrupt[pos] ^= 1 << uint8(bitPos) + pubNoncesToUse[randItem].GetX().SetBytes(pubXCorrupt[:]) + } - // Nothing that makes it here should be valid. - if allPksSum != nil && combinedSignature != nil { - ok = Verify(allPksSum, msg, combinedSignature.GetR(), - combinedSignature.GetS()) - if ok { - t.Fatalf("expected %v, got %v", false, ok) + for j := range keysToUse { + thisPubNonce := pubNoncesToUse[j] + localPubNonces := make([]*PublicKey, numKeysForTest-1) + itr := 0 + for _, pubNonce := range pubNoncesToUse { + if bytes.Equal(thisPubNonce.Serialize(), pubNonce.Serialize()) { + continue + } + localPubNonces[itr] = pubNonce + itr++ + } + publicNonceSum := combinePubkeys(localPubNonces) + + sigR, sigS, _ := schnorrPartialSign(msg, + keysToUse[j].Serialize(), allPksSum.Serialize(), + privNoncesToUse[j].Serialize(), + publicNonceSum.Serialize()) + localSig := NewSignature(sigR, sigS) + + partialSignatures[j] = localSig + } + + // combine signatures. + combinedSignature, _ = schnorrCombinePartialSigs(partialSignatures) + + // Nothing that makes it here should be valid. + if allPksSum != nil && combinedSignature != nil { + ok = Verify(allPksSum, msg, combinedSignature.GetR(), + combinedSignature.GetS()) + if ok { + t.Errorf("expected %v, got %v", false, ok) + return + } } - } + }(i) } + wg.Wait() } From 41d6475bbc56b96163c6f63814f6d82528faec67 Mon Sep 17 00:00:00 2001 From: jholdstock Date: Sun, 5 Jul 2026 12:43:36 +0800 Subject: [PATCH 3/4] edwards: Fix badly formatted test error msg. --- dcrec/edwards/threshold_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dcrec/edwards/threshold_test.go b/dcrec/edwards/threshold_test.go index e6b0d6ffee..e83e2196ca 100644 --- a/dcrec/edwards/threshold_test.go +++ b/dcrec/edwards/threshold_test.go @@ -77,7 +77,7 @@ func TestSchnorrThreshold(t *testing.T) { } if err != nil { - t.Errorf("unexpected error %s, ", err) + t.Errorf("unexpected error %s", err) return } @@ -99,7 +99,7 @@ func TestSchnorrThreshold(t *testing.T) { allPksSum.Serialize(), privNoncesToUse[j].Serialize(), publicNonceSum.Serialize()) if err != nil { - t.Errorf("unexpected error %s, ", err) + t.Errorf("unexpected error %s", err) return } @@ -110,7 +110,7 @@ func TestSchnorrThreshold(t *testing.T) { // Combine signatures. combinedSignature, err := schnorrCombinePartialSigs(partialSignatures) if err != nil { - t.Errorf("unexpected error %s, ", err) + t.Errorf("unexpected error %s", err) return } @@ -132,13 +132,13 @@ func TestSchnorrThreshold(t *testing.T) { combinedPrivkey, _, err := PrivKeyFromScalar( copyBytes(combinedPrivkeysD.Bytes())[:]) if err != nil { - t.Errorf("unexpected error %s, ", err) + t.Errorf("unexpected error %s", err) return } combinedNonce, _, err := PrivKeyFromScalar( copyBytes(combinedNonceD.Bytes())[:]) if err != nil { - t.Errorf("unexpected error %s, ", err) + t.Errorf("unexpected error %s", err) return } cSigR, cSigS, err := SignFromScalar(combinedPrivkey, @@ -151,7 +151,7 @@ func TestSchnorrThreshold(t *testing.T) { } if err != nil { - t.Errorf("unexpected error %s, ", err) + t.Errorf("unexpected error %s", err) return } From 2c25bac6fad97faf1d61ff809e5c4396235c0a58 Mon Sep 17 00:00:00 2001 From: jholdstock Date: Sun, 5 Jul 2026 12:43:16 +0800 Subject: [PATCH 4/4] edwards: Remove unnecessary func wrapping in test. --- dcrec/edwards/ciphering_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dcrec/edwards/ciphering_test.go b/dcrec/edwards/ciphering_test.go index e409119b69..522558fdf7 100644 --- a/dcrec/edwards/ciphering_test.go +++ b/dcrec/edwards/ciphering_test.go @@ -186,15 +186,9 @@ func TestCipheringErrors(t *testing.T) { {bytes.Repeat([]byte{0x07}, 15)}, } for i, test := range tests2 { - _, err = TstRemovePKCSPadding(test.in) + _, err = removePKCSPadding(test.in) if err == nil { t.Errorf("removePKCSPadding #%d did not get error", i) } } } - -// TstRemovePKCSPadding makes the internal removePKCSPadding function available -// to the test package. -func TstRemovePKCSPadding(src []byte) ([]byte, error) { - return removePKCSPadding(src) -}