@@ -10,10 +10,8 @@ import (
10
10
"github.com/ark-network/ark/common/bitcointree"
11
11
"github.com/ark-network/ark/common/tree"
12
12
"github.com/btcsuite/btcd/btcec/v2"
13
- "github.com/btcsuite/btcd/chaincfg/chainhash"
14
13
"github.com/btcsuite/btcd/txscript"
15
14
"github.com/btcsuite/btcd/wire"
16
- "github.com/decred/dcrd/dcrec/secp256k1/v4"
17
15
"github.com/stretchr/testify/require"
18
16
)
19
17
@@ -24,10 +22,10 @@ const (
24
22
25
23
var (
26
24
vtxoTreeExpiry = common.RelativeLocktime {Type : common .LocktimeTypeBlock , Value : 144 }
27
- testTxid , _ = chainhash . NewHashFromStr ("49f8664acc899be91902f8ade781b7eeb9cbe22bdd9efbc36e56195de21bcd12" )
28
- serverPrivKey , _ = secp256k1 . GeneratePrivateKey ()
25
+ rootInput , _ = wire . NewOutPointFromString ("49f8664acc899be91902f8ade781b7eeb9cbe22bdd9efbc36e56195de21bcd12:0 " )
26
+ serverPrivKey , _ = btcec . NewPrivateKey ()
29
27
sweepScript , _ = (& tree.CSVMultisigClosure {
30
- MultisigClosure : tree.MultisigClosure {PubKeys : []* secp256k1 .PublicKey {serverPrivKey .PubKey ()}},
28
+ MultisigClosure : tree.MultisigClosure {PubKeys : []* btcec .PublicKey {serverPrivKey .PubKey ()}},
31
29
Locktime : vtxoTreeExpiry ,
32
30
}).Script ()
33
31
sweepRoot = txscript .NewBaseTapLeaf (sweepScript ).TapHash ()
@@ -37,125 +35,214 @@ var (
37
35
func TestBuildAndSignVtxoTree (t * testing.T ) {
38
36
t .Parallel ()
39
37
40
- for _ , tc := range generateTestCases (t ) {
41
- t .Run (tc .name , func (t * testing.T ) {
42
- sharedOutputScript , sharedOutputAmount , err := bitcointree .CraftSharedOutput (
43
- tc .receivers ,
44
- minRelayFee ,
45
- sweepRoot [:],
38
+ testVectors , err := makeTestVectors ()
39
+ require .NoError (t , err )
40
+ require .NotEmpty (t , testVectors )
41
+
42
+ for _ , v := range testVectors {
43
+ t .Run (v .name , func (t * testing.T ) {
44
+ sharedOutScript , sharedOutAmount , err := bitcointree .CraftSharedOutput (
45
+ v .receivers , minRelayFee , sweepRoot [:],
46
46
)
47
47
require .NoError (t , err )
48
- require .NotNil (t , sharedOutputScript )
48
+ require .NotNil (t , sharedOutScript )
49
+ require .NotZero (t , sharedOutAmount )
49
50
50
51
vtxoTree , err := bitcointree .BuildVtxoTree (
51
- & wire.OutPoint {
52
- Hash : * testTxid ,
53
- Index : 0 ,
54
- },
55
- tc .receivers ,
56
- minRelayFee ,
57
- sweepRoot [:],
58
- vtxoTreeExpiry ,
52
+ rootInput , v .receivers , minRelayFee , sweepRoot [:], vtxoTreeExpiry ,
59
53
)
60
54
require .NoError (t , err )
55
+ require .NotNil (t , vtxoTree )
61
56
62
- serverCoordinator , err := bitcointree .NewTreeCoordinatorSession (
63
- sharedOutputAmount ,
64
- vtxoTree ,
65
- sweepRoot [:],
57
+ coordinator , err := bitcointree .NewTreeCoordinatorSession (
58
+ sharedOutAmount , vtxoTree , sweepRoot [:],
66
59
)
67
60
require .NoError (t , err )
61
+ require .NotNil (t , coordinator )
68
62
69
- // Cceate signer sessions for each receivers
70
- signerSessions := make (map [* btcec.PublicKey ]bitcointree.SignerSession )
71
- for _ , prvkey := range tc .privKeys {
72
- session := bitcointree .NewTreeSignerSession (prvkey )
73
- err := session .Init (sweepRoot [:], sharedOutputAmount , vtxoTree )
74
- require .NoError (t , err )
75
- signerSessions [prvkey .PubKey ()] = session
76
- }
63
+ signers , err := makeCosigners (v .privKeys , sharedOutAmount , vtxoTree )
64
+ require .NoError (t , err )
65
+ require .NotNil (t , signers )
77
66
78
- // Create server's signer session
79
- serverSession := bitcointree .NewTreeSignerSession (serverPrivKey )
80
- err = serverSession .Init (sweepRoot [:], sharedOutputAmount , vtxoTree )
67
+ err = makeAggregatedNonces (signers , coordinator , checkNoncesRoundtrip (t ))
81
68
require .NoError (t , err )
82
- signerSessions [serverPrivKey .PubKey ()] = serverSession
83
-
84
- // generate nonces from all signers
85
- for pubkey , session := range signerSessions {
86
- nonces , err := session .GetNonces ()
87
- require .NoError (t , err )
88
- var encodedNonces bytes.Buffer
89
- err = nonces .Encode (& encodedNonces )
90
- require .NoError (t , err )
91
- decodedNonces , err := bitcointree .DecodeNonces (& encodedNonces )
92
- require .NoError (t , err )
93
- for i , nonceRow := range nonces {
94
- for j , nonce := range nonceRow {
95
- require .Equal (t , nonce , decodedNonces [i ][j ])
96
- }
97
- }
98
69
99
- serverCoordinator .AddNonce (pubkey , nonces )
100
- }
70
+ signedTree , err := makeAggregatedSignatures (signers , coordinator , checkSigsRoundtrip (t ))
71
+ require .NoError (t , err )
72
+ require .NotNil (t , signedTree )
101
73
102
- aggregatedNonce , err := serverCoordinator .AggregateNonces ()
74
+ // validate signatures
75
+ err = bitcointree .ValidateTreeSigs (sweepRoot [:], sharedOutAmount , signedTree )
103
76
require .NoError (t , err )
77
+ })
78
+ }
79
+ }
80
+
81
+ func checkNoncesRoundtrip (t * testing.T ) func (nonces bitcointree.TreeNonces ) {
82
+ return func (nonces bitcointree.TreeNonces ) {
83
+ var encodedNonces bytes.Buffer
84
+ err := nonces .Encode (& encodedNonces )
85
+ require .NoError (t , err )
104
86
105
- // set the aggregated nonces for all signers sessions
106
- for _ , session := range signerSessions {
107
- session .SetAggregatedNonces (aggregatedNonce )
87
+ decodedNonces , err := bitcointree .DecodeNonces (& encodedNonces )
88
+ require .NoError (t , err )
89
+ for i , nonceRow := range nonces {
90
+ for j , nonce := range nonceRow {
91
+ require .Equal (t , nonce , decodedNonces [i ][j ])
108
92
}
93
+ }
94
+ }
95
+ }
109
96
110
- // get signatures from all signers sessions
111
- for pubkey , session := range signerSessions {
112
- sig , err := session .Sign ()
113
- require .NoError (t , err )
114
- require .NotNil (t , sig )
115
- var encodedSig bytes.Buffer
116
- err = sig .Encode (& encodedSig )
117
- require .NoError (t , err )
118
- decodedSig , err := bitcointree .DecodeSignatures (& encodedSig )
119
- require .NoError (t , err )
120
- for i , sigRow := range sig {
121
- for j , sig := range sigRow {
122
- if sig == nil {
123
- require .Nil (t , decodedSig [i ][j ])
124
- } else {
125
- require .Equal (t , sig .S , decodedSig [i ][j ].S )
126
- }
127
- }
97
+ func checkSigsRoundtrip (t * testing.T ) func (sigs bitcointree.TreePartialSigs ) {
98
+ return func (sigs bitcointree.TreePartialSigs ) {
99
+ var encodedSig bytes.Buffer
100
+ err := sigs .Encode (& encodedSig )
101
+ require .NoError (t , err )
102
+ decodedSig , err := bitcointree .DecodeSignatures (& encodedSig )
103
+ require .NoError (t , err )
104
+ for i , sigRow := range sigs {
105
+ for j , sig := range sigRow {
106
+ if sig == nil {
107
+ require .Nil (t , decodedSig [i ][j ])
108
+ } else {
109
+ require .Equal (t , sig .S , decodedSig [i ][j ].S )
128
110
}
129
-
130
- serverCoordinator .AddSig (pubkey , sig )
131
111
}
112
+ }
113
+ }
114
+ }
132
115
133
- // aggregate signatures
134
- signedTree , err := serverCoordinator .SignTree ()
135
- require .NoError (t , err )
136
- require .NotNil (t , signedTree )
137
- // validate signatures
138
- err = bitcointree .ValidateTreeSigs (
139
- sweepRoot [:],
140
- sharedOutputAmount ,
141
- signedTree ,
142
- )
143
- require .NoError (t , err )
144
- })
116
+ func makeCosigners (
117
+ keys []* btcec.PrivateKey , sharedOutAmount int64 , vtxoTree tree.VtxoTree ,
118
+ ) (map [string ]bitcointree.SignerSession , error ) {
119
+ signers := make (map [string ]bitcointree.SignerSession )
120
+ for _ , prvkey := range keys {
121
+ session := bitcointree .NewTreeSignerSession (prvkey )
122
+ if err := session .Init (sweepRoot [:], sharedOutAmount , vtxoTree ); err != nil {
123
+ return nil , err
124
+ }
125
+ signers [keyToStr (prvkey )] = session
126
+ }
127
+
128
+ // create signer session for the server itself
129
+ serverSession := bitcointree .NewTreeSignerSession (serverPrivKey )
130
+ if err := serverSession .Init (sweepRoot [:], sharedOutAmount , vtxoTree ); err != nil {
131
+ return nil , err
132
+ }
133
+ signers [keyToStr (serverPrivKey )] = serverSession
134
+ return signers , nil
135
+ }
136
+
137
+ func makeAggregatedNonces (
138
+ signers map [string ]bitcointree.SignerSession , coordinator bitcointree.CoordinatorSession ,
139
+ checkNoncesRoundtrip func (bitcointree.TreeNonces ),
140
+ ) error {
141
+ for pk , session := range signers {
142
+ buf , err := hex .DecodeString (pk )
143
+ if err != nil {
144
+ return err
145
+ }
146
+ pubkey , err := btcec .ParsePubKey (buf )
147
+ if err != nil {
148
+ return err
149
+ }
150
+
151
+ nonces , err := session .GetNonces ()
152
+ if err != nil {
153
+ return err
154
+ }
155
+ checkNoncesRoundtrip (nonces )
156
+
157
+ coordinator .AddNonce (pubkey , nonces )
158
+ }
159
+
160
+ aggregatedNonce , err := coordinator .AggregateNonces ()
161
+ if err != nil {
162
+ return err
163
+ }
164
+
165
+ // set the aggregated nonces for all signers sessions
166
+ for _ , session := range signers {
167
+ session .SetAggregatedNonces (aggregatedNonce )
168
+ }
169
+ return nil
170
+ }
171
+
172
+ func makeAggregatedSignatures (
173
+ signers map [string ]bitcointree.SignerSession , coordinator bitcointree.CoordinatorSession ,
174
+ checkSigsRoundtrip func (bitcointree.TreePartialSigs ),
175
+ ) (tree.VtxoTree , error ) {
176
+ for pk , session := range signers {
177
+ buf , err := hex .DecodeString (pk )
178
+ if err != nil {
179
+ return nil , err
180
+ }
181
+ pubkey , err := btcec .ParsePubKey (buf )
182
+ if err != nil {
183
+ return nil , err
184
+ }
185
+
186
+ sigs , err := session .Sign ()
187
+ if err != nil {
188
+ return nil , err
189
+ }
190
+ checkSigsRoundtrip (sigs )
191
+
192
+ coordinator .AddSignatures (pubkey , sigs )
145
193
}
194
+
195
+ // aggregate signatures
196
+ return coordinator .SignTree ()
146
197
}
147
198
148
199
type testCase struct {
149
200
name string
150
201
receivers []tree.VtxoLeaf
151
- privKeys []* secp256k1.PrivateKey
202
+ privKeys []* btcec.PrivateKey
203
+ }
204
+
205
+ func makeTestVectors () ([]testCase , error ) {
206
+ vectors := make ([]testCase , 0 , len (receiverCounts ))
207
+ for _ , count := range receiverCounts {
208
+ receivers , privKeys , err := generateMockedReceivers (count )
209
+ if err != nil {
210
+ return nil , err
211
+ }
212
+
213
+ // add mixed types test case if count is between 2 and 32
214
+ if count > 1 && count < 32 {
215
+ vectors = append (vectors , testCase {
216
+ name : fmt .Sprintf ("%d receivers Mixed Signing Types" , len (receivers )),
217
+ receivers : withMixedSigningTypes (receivers ),
218
+ privKeys : privKeys ,
219
+ })
220
+ }
221
+
222
+ // add SignAll test case if count is less than 32
223
+ if count < 32 {
224
+ vectors = append (vectors , testCase {
225
+ name : fmt .Sprintf ("%d receivers SignAll" , len (receivers )),
226
+ receivers : withSigningType (tree .SignAll , receivers ),
227
+ privKeys : privKeys ,
228
+ })
229
+ }
230
+
231
+ // always add SignBranch test case
232
+ vectors = append (vectors , testCase {
233
+ name : fmt .Sprintf ("%d receivers SignBranch" , len (receivers )),
234
+ receivers : withSigningType (tree .SignBranch , receivers ),
235
+ privKeys : privKeys ,
236
+ })
237
+ }
238
+ return vectors , nil
152
239
}
153
240
154
- func generateReceiversFixture ( count int ) ([]tree.VtxoLeaf , []* secp256k1 .PrivateKey , error ) {
155
- receivers := make ([]tree.VtxoLeaf , 0 , count )
156
- privKeys := make ([]* secp256k1 .PrivateKey , 0 , count )
157
- for i := 0 ; i < count ; i ++ {
158
- prvkey , err := secp256k1 . GeneratePrivateKey ()
241
+ func generateMockedReceivers ( num int ) ([]tree.VtxoLeaf , []* btcec .PrivateKey , error ) {
242
+ receivers := make ([]tree.VtxoLeaf , 0 , num )
243
+ privKeys := make ([]* btcec .PrivateKey , 0 , num )
244
+ for i := 0 ; i < num ; i ++ {
245
+ prvkey , err := btcec . NewPrivateKey ()
159
246
if err != nil {
160
247
return nil , nil , err
161
248
}
@@ -196,35 +283,6 @@ func withMixedSigningTypes(receivers []tree.VtxoLeaf) []tree.VtxoLeaf {
196
283
return append (first , second ... )
197
284
}
198
285
199
- func generateTestCases (t * testing.T ) []testCase {
200
- testCases := make ([]testCase , 0 )
201
- for _ , count := range receiverCounts {
202
- receivers , privKeys , err := generateReceiversFixture (count )
203
- require .NoError (t , err )
204
- // add mixed types test case if count is between 2 and 32
205
- if count > 1 && count < 32 {
206
- testCases = append (testCases , testCase {
207
- name : fmt .Sprintf ("%d receivers Mixed Signing Types" , len (receivers )),
208
- receivers : withMixedSigningTypes (receivers ),
209
- privKeys : privKeys ,
210
- })
211
- }
212
-
213
- // add SignAll test case if count is less than 32
214
- if count < 32 {
215
- testCases = append (testCases , testCase {
216
- name : fmt .Sprintf ("%d receivers SignAll" , len (receivers )),
217
- receivers : withSigningType (tree .SignAll , receivers ),
218
- privKeys : privKeys ,
219
- })
220
- }
221
-
222
- // always add SignBranch test case
223
- testCases = append (testCases , testCase {
224
- name : fmt .Sprintf ("%d receivers SignBranch" , len (receivers )),
225
- receivers : withSigningType (tree .SignBranch , receivers ),
226
- privKeys : privKeys ,
227
- })
228
- }
229
- return testCases
286
+ func keyToStr (key * btcec.PrivateKey ) string {
287
+ return hex .EncodeToString (key .PubKey ().SerializeCompressed ())
230
288
}
0 commit comments