diff --git a/btcec/go.mod b/btcec/go.mod index 8c7cc1d6..452399cf 100644 --- a/btcec/go.mod +++ b/btcec/go.mod @@ -6,6 +6,11 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 + github.com/stretchr/testify v1.8.0 ) -require github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect +require ( + github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/btcec/go.sum b/btcec/go.sum index 0004a783..73b8f2b5 100644 --- a/btcec/go.sum +++ b/btcec/go.sum @@ -1,8 +1,21 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/btcec/schnorr/musig2/data/nonce_agg_vectors.json b/btcec/schnorr/musig2/data/nonce_agg_vectors.json new file mode 100644 index 00000000..597246dd --- /dev/null +++ b/btcec/schnorr/musig2/data/nonce_agg_vectors.json @@ -0,0 +1,54 @@ +{ + "pnonces": [ + "020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833", + "020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30" + ], + "valid_test_cases": [ + { + "pnonce_indices": [0, 1], + "expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8" + }, + { + "pnonce_indices": [2, 3], + "expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000", + "comment": "Sum of second points encoded in the nonces is point at infinity which is serialized as 33 zero bytes" + } + ], + "error_test_cases": [ + { + "pnonce_indices": [0, 4], + "error": { + "type": "invalid_contribution", + "signer": 1, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 1 is invalid due wrong tag, 0x04, in the first half", + "btcec_err": "invalid public key: unsupported format: 4" + }, + { + "pnonce_indices": [5, 1], + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 0 is invalid because the second half does not correspond to an X coordinate", + "btcec_err": "invalid public key: x coordinate 48c264cdd57d3c24d79990b0f865674eb62a0f9018277a95011b41bfc193b831 is not on the secp256k1 curve" + }, + { + "pnonce_indices": [6, 1], + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 0 is invalid because second half exceeds field size", + "btcec_err": "invalid public key: x >= field prime" + } + ] +} diff --git a/btcec/schnorr/musig2/data/nonce_gen_vectors.json b/btcec/schnorr/musig2/data/nonce_gen_vectors.json new file mode 100644 index 00000000..9727cfe9 --- /dev/null +++ b/btcec/schnorr/musig2/data/nonce_gen_vectors.json @@ -0,0 +1,36 @@ +{ + "test_cases": [ + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": "0202020202020202020202020202020202020202020202020202020202020202", + "aggpk": "0707070707070707070707070707070707070707070707070707070707070707", + "msg": "0101010101010101010101010101010101010101010101010101010101010101", + "extra_in": "0808080808080808080808080808080808080808080808080808080808080808", + "expected": "BC6C683EBBCC39DCB3C29B3D010D2AAA7C86CFB562FC41ED9A460EE061013E75FB4AD2F0B816713269800D018803906D5481E00A940EAB4F4AC49B4A372EB0F4" + }, + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": "0202020202020202020202020202020202020202020202020202020202020202", + "aggpk": "0707070707070707070707070707070707070707070707070707070707070707", + "msg": "", + "extra_in": "0808080808080808080808080808080808080808080808080808080808080808", + "expected": "AAC4BFD707F4953B4063851D7E4AAD5C59D5D0BFB0E71012788A85698B5ACF8F11834D5051928424BA501C8CD064F3F942F8D4A07D8A2ED79F153E4ABD9EBBE9" + }, + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": "0202020202020202020202020202020202020202020202020202020202020202", + "aggpk": "0707070707070707070707070707070707070707070707070707070707070707", + "msg": "2626262626262626262626262626262626262626262626262626262626262626262626262626", + "extra_in": "0808080808080808080808080808080808080808080808080808080808080808", + "expected": "DF54500DD2B503DBA3753C48A9D6B67E6C11EC4325EDD1DC256C7F75D6A85DBECA6D9857A6F3F292FB3B50DBCBF69FADB67B1CDDB0EA6EB693F6455C4C9088E1" + }, + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": null, + "aggpk": null, + "msg": null, + "extra_in": null, + "expected": "7B3B5A002356471AF0E961DE2549C121BD0D48ABCEEDC6E034BDDF86AD3E0A187ECEE674CEF7364B0BC4BEEFB8B66CAD89F98DE2F8C5A5EAD5D1D1E4BD7D04CD" + } + ] +} diff --git a/btcec/schnorr/musig2/musig2_test.go b/btcec/schnorr/musig2/musig2_test.go index 8ab1725c..1b4a68fd 100644 --- a/btcec/schnorr/musig2/musig2_test.go +++ b/btcec/schnorr/musig2/musig2_test.go @@ -46,6 +46,8 @@ var ( "B65481B6BAAFB3C5810106717BEB") keyCombo4, _ = hex.DecodeString("2EB18851887E7BDC5E830E89B19DDBC28078" + "F1FA88AAD0AD01CA06FE4F80210B") + + testVectorBaseDir = "data" ) // getInfinityTweak returns a tweak that, when tweaking the Generator, triggers @@ -1340,210 +1342,6 @@ func TestMuSigEarlyNonce(t *testing.T) { } } -// TestMusig2NonceGenTestVectors tests the nonce generation function with -// the testvectors defined in the Musig2 BIP. -func TestMusig2NonceGenTestVectors(t *testing.T) { - t.Parallel() - - msg := bytes.Repeat([]byte{0x01}, 32) - sk := bytes.Repeat([]byte{0x02}, 32) - aggpk := bytes.Repeat([]byte{0x07}, 32) - extra_in := bytes.Repeat([]byte{0x08}, 32) - - testCases := []struct { - opts nonceGenOpts - expectedNonce string - }{ - { - opts: nonceGenOpts{ - randReader: &memsetRandReader{i: 0}, - secretKey: sk[:], - combinedKey: aggpk[:], - auxInput: extra_in[:], - msg: msg[:], - }, - expectedNonce: "BC6C683EBBCC39DCB3C29B3D010D2AAA7C86C" + - "FB562FC41ED9A460EE061013E75FB4AD2F0B81671326" + - "9800D018803906D5481E00A940EAB4F4AC49B4A372EB0F4", - }, - { - opts: nonceGenOpts{ - randReader: &memsetRandReader{i: 0}, - secretKey: sk[:], - combinedKey: aggpk[:], - auxInput: extra_in[:], - msg: nil, - }, - expectedNonce: "8A633F5EECBDB690A6BE4921426F41BE78D50" + - "9DC1CE894C1215844C0E4C6DE7ABC9A5BE0A3BF3FE31" + - "2CCB7E4817D2CB17A7CEA8382B73A99A583E323387B3C32", - }, - { - opts: nonceGenOpts{ - randReader: &memsetRandReader{i: 0}, - secretKey: nil, - combinedKey: nil, - auxInput: nil, - msg: nil, - }, - expectedNonce: "7B3B5A002356471AF0E961DE2549C121BD0D4" + - "8ABCEEDC6E034BDDF86AD3E0A187ECEE674CEF7364B0" + - "BC4BEEFB8B66CAD89F98DE2F8C5A5EAD5D1D1E4BD7D04CD", - }, - } - - for _, testCase := range testCases { - nonce, err := GenNonces(withCustomOptions(testCase.opts)) - if err != nil { - t.Fatalf("err gen nonce aux bytes %v", err) - } - - expectedBytes, _ := hex.DecodeString(testCase.expectedNonce) - if !bytes.Equal(nonce.SecNonce[:], expectedBytes) { - - t.Fatalf("nonces don't match: expected %x, got %x", - expectedBytes, nonce.SecNonce[:]) - } - } - -} - -var ( - pNonce1, _ = hex.DecodeString("020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E666" + - "03BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641") - pNonce2, _ = hex.DecodeString("03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A6" + - "0248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833") - - expectedNonce, _ = hex.DecodeString("035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B" + - "024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8") - - invalidNonce1, _ = hex.DecodeString("04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A6" + "0248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833") - invalidNonce2, _ = hex.DecodeString("03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A6" + "0248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831") - invalidNonce3, _ = hex.DecodeString("03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A6" + "02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30") -) - -type jsonNonceAggTestCase struct { - Nonces []string `json:"nonces"` - ExpectedNonce string `json:"expected_key"` - ExpectedError string `json:"expected_error"` -} - -func TestMusig2AggregateNoncesTestVectors(t *testing.T) { - t.Parallel() - - var jsonCases []jsonNonceAggTestCase - - testCases := []struct { - nonces [][]byte - expectedNonce []byte - expectedError error - }{ - // Vector 1: Valid. - { - nonces: [][]byte{pNonce1, pNonce2}, - expectedNonce: expectedNonce, - }, - - // Vector 2: Public nonce from signer 1 is invalid due wrong - // tag, 0x04, inthe first half. - { - nonces: [][]byte{pNonce1, invalidNonce1}, - expectedError: secp256k1.ErrPubKeyInvalidFormat, - }, - - // Vector 3: Public nonce from signer 0 is invalid because the - // second half does not correspond to an X coordinate. - { - nonces: [][]byte{invalidNonce2, pNonce2}, - expectedError: secp256k1.ErrPubKeyNotOnCurve, - }, - - // Vector 4: Public nonce from signer 0 is invalid because - // second half exceeds field size. - { - nonces: [][]byte{invalidNonce3, pNonce2}, - expectedError: secp256k1.ErrPubKeyXTooBig, - }, - - // Vector 5: Sum of second points encoded in the nonces would - // be point at infinity, therefore set sum to base point G. - { - nonces: [][]byte{ - append( - append([]byte{}, pNonce1[0:33]...), - getGBytes()..., - ), - append( - append([]byte{}, pNonce2[0:33]...), - getNegGBytes()..., - ), - }, - expectedNonce: append( - append([]byte{}, expectedNonce[0:33]...), - getInfinityBytes()..., - ), - }, - } - for i, testCase := range testCases { - testName := fmt.Sprintf("Vector %v", i+1) - t.Run(testName, func(t *testing.T) { - var ( - nonces [][66]byte - strNonces []string - jsonError string - ) - for _, nonce := range testCase.nonces { - nonces = append(nonces, toPubNonceSlice(nonce)) - strNonces = append(strNonces, hex.EncodeToString(nonce)) - } - - if testCase.expectedError != nil { - jsonError = testCase.expectedError.Error() - } - - jsonCases = append(jsonCases, jsonNonceAggTestCase{ - Nonces: strNonces, - ExpectedNonce: hex.EncodeToString(expectedNonce), - ExpectedError: jsonError, - }) - - aggregatedNonce, err := AggregateNonces(nonces) - - switch { - case testCase.expectedError != nil && - errors.Is(err, testCase.expectedError): - - return - case err != nil: - t.Fatalf("aggregating nonce error: %v", err) - } - - if !bytes.Equal(testCase.expectedNonce, aggregatedNonce[:]) { - t.Fatalf("case: #%v, invalid nonce aggregation: "+ - "expected %x, got %x", i, testCase.expectedNonce, - aggregatedNonce) - } - - }) - } - - if *dumpJson { - jsonBytes, err := json.Marshal(jsonCases) - if err != nil { - t.Fatalf("unable to encode json: %v", err) - } - - var formattedJson bytes.Buffer - json.Indent(&formattedJson, jsonBytes, "", "\t") - err = ioutil.WriteFile( - nonceAggTestVectorName, formattedJson.Bytes(), 0644, - ) - if err != nil { - t.Fatalf("unable to write file: %v", err) - } - } -} - type memsetRandReader struct { i int } diff --git a/btcec/schnorr/musig2/nonces.go b/btcec/schnorr/musig2/nonces.go index 201ff49d..4b2509a7 100644 --- a/btcec/schnorr/musig2/nonces.go +++ b/btcec/schnorr/musig2/nonces.go @@ -255,10 +255,10 @@ func genNonceAuxBytes(rand []byte, i int, return nil, err } - switch len(opts.msg) { + switch { // If the message isn't present, then we'll just write out a single // uint8 of a zero byte: m_prefixed = bytes(1, 0). - case 0: + case opts.msg == nil: if _, err := w.Write([]byte{0x00}); err != nil { return nil, err } @@ -266,6 +266,8 @@ func genNonceAuxBytes(rand []byte, i int, // Otherwise, we'll write a single byte of 0x01 with a 1 byte length // prefix, followed by the message itself with an 8 byte length prefix: // m_prefixed = bytes(1, 1) || bytes(8, len(m)) || m. + case len(opts.msg) == 0: + fallthrough default: if _, err := w.Write([]byte{0x01}); err != nil { return nil, err diff --git a/btcec/schnorr/musig2/nonces_test.go b/btcec/schnorr/musig2/nonces_test.go new file mode 100644 index 00000000..bdb76dbd --- /dev/null +++ b/btcec/schnorr/musig2/nonces_test.go @@ -0,0 +1,164 @@ +// Copyright 2013-2022 The btcsuite developers + +package musig2 + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "path" + "testing" + + "github.com/stretchr/testify/require" +) + +type nonceGenTestCase struct { + Rand string `json:"rand_"` + Sk string `json:"sk"` + AggPk string `json:"aggpk"` + Msg *string `json:"msg"` + ExtraIn string `json:"extra_in"` + + Expected string `json:"expected"` +} + +type nonceGenTestCases struct { + TestCases []nonceGenTestCase `json:"test_cases"` +} + +const ( + nonceGenTestVectorsFileName = "nonce_gen_vectors.json" + nonceAggTestVectorsFileName = "nonce_agg_vectors.json" +) + +// TestMusig2NonceGenTestVectors tests the nonce generation function with the +// testvectors defined in the Musig2 BIP. +func TestMusig2NonceGenTestVectors(t *testing.T) { + t.Parallel() + + testVectorPath := path.Join( + testVectorBaseDir, nonceGenTestVectorsFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + + var testCases nonceGenTestCases + require.NoError(t, json.Unmarshal(testVectorBytes, &testCases)) + + for i, testCase := range testCases.TestCases { + testCase := testCase + + customOpts := nonceGenOpts{ + randReader: &memsetRandReader{i: 0}, + secretKey: mustParseHex(testCase.Sk), + combinedKey: mustParseHex(testCase.AggPk), + auxInput: mustParseHex(testCase.ExtraIn), + } + if testCase.Msg != nil { + customOpts.msg = mustParseHex(*testCase.Msg) + } + + t.Run(fmt.Sprintf("test_case=%v", i), func(t *testing.T) { + nonce, err := GenNonces(withCustomOptions(customOpts)) + if err != nil { + t.Fatalf("err gen nonce aux bytes %v", err) + } + + expectedBytes, _ := hex.DecodeString(testCase.Expected) + if !bytes.Equal(nonce.SecNonce[:], expectedBytes) { + + t.Fatalf("nonces don't match: expected %x, got %x", + expectedBytes, nonce.SecNonce[:]) + } + }) + } +} + +type nonceAggError struct { + Type string `json:"type"` + Signer int `json:"signer"` + Contrib string `json:"contrib"` +} + +type nonceAggValidCase struct { + Indices []int `json:"pnonce_indices"` + + Expected string `json:"expected"` + + Comment string `json:"comment"` +} + +type nonceAggInvalidCase struct { + Indices []int `json:"pnonce_indices"` + + Error nonceAggError `json:"error"` + + Comment string `json:"comment"` + + ExpectedErr string `json:"btcec_err"` +} + +type nonceAggTestCases struct { + Nonces []string `json:"pnonces"` + + ValidCases []nonceAggValidCase `json:"valid_test_cases"` + + InvalidCases []nonceAggInvalidCase `json:"error_test_cases"` +} + +// TestMusig2AggregateNoncesTestVectors tests that the musig2 implementation +// passes the nonce aggregration test vectors for musig2 1.0. +func TestMusig2AggregateNoncesTestVectors(t *testing.T) { + t.Parallel() + + testVectorPath := path.Join( + testVectorBaseDir, nonceAggTestVectorsFileName, + ) + testVectorBytes, err := os.ReadFile(testVectorPath) + require.NoError(t, err) + + var testCases nonceAggTestCases + require.NoError(t, json.Unmarshal(testVectorBytes, &testCases)) + + nonces := make([][PubNonceSize]byte, len(testCases.Nonces)) + for i := range testCases.Nonces { + var nonce [PubNonceSize]byte + copy(nonce[:], mustParseHex(testCases.Nonces[i])) + + nonces[i] = nonce + } + + for i, testCase := range testCases.ValidCases { + testCase := testCase + + var testNonces [][PubNonceSize]byte + for _, idx := range testCase.Indices { + testNonces = append(testNonces, nonces[idx]) + } + + t.Run(fmt.Sprintf("valid_case=%v", i), func(t *testing.T) { + aggregatedNonce, err := AggregateNonces(testNonces) + require.NoError(t, err) + + var expectedNonce [PubNonceSize]byte + copy(expectedNonce[:], mustParseHex(testCase.Expected)) + + require.Equal(t, aggregatedNonce[:], expectedNonce[:]) + }) + } + + for i, testCase := range testCases.InvalidCases { + var testNonces [][PubNonceSize]byte + for _, idx := range testCase.Indices { + testNonces = append(testNonces, nonces[idx]) + } + + t.Run(fmt.Sprintf("invalid_case=%v", i), func(t *testing.T) { + _, err := AggregateNonces(testNonces) + require.True(t, err != nil) + require.Equal(t, testCase.ExpectedErr, err.Error()) + }) + } +}