Skip to content

Commit 17564f8

Browse files
authored
Improve readability of musig2 test (#8)
1 parent ed81573 commit 17564f8

File tree

3 files changed

+188
-130
lines changed

3 files changed

+188
-130
lines changed

common/bitcointree/musig2.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ type SignerSession interface {
9191

9292
type CoordinatorSession interface {
9393
AddNonce(*btcec.PublicKey, TreeNonces)
94-
AddSig(*btcec.PublicKey, TreePartialSigs)
94+
AddSignatures(*btcec.PublicKey, TreePartialSigs)
9595
AggregateNonces() (TreeNonces, error)
9696
// SignTree combines the signatures and add them to the tree's psbts
9797
SignTree() (tree.VtxoTree, error)
@@ -420,7 +420,7 @@ func (t *treeCoordinatorSession) AddNonce(pubkey *btcec.PublicKey, nonce TreeNon
420420
t.nonces[hex.EncodeToString(schnorr.SerializePubKey(pubkey))] = nonce
421421
}
422422

423-
func (t *treeCoordinatorSession) AddSig(pubkey *btcec.PublicKey, sig TreePartialSigs) {
423+
func (t *treeCoordinatorSession) AddSignatures(pubkey *btcec.PublicKey, sig TreePartialSigs) {
424424
t.sigs[hex.EncodeToString(schnorr.SerializePubKey(pubkey))] = sig
425425
}
426426

common/bitcointree/musig2_test.go

Lines changed: 184 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import (
1010
"github.com/ark-network/ark/common/bitcointree"
1111
"github.com/ark-network/ark/common/tree"
1212
"github.com/btcsuite/btcd/btcec/v2"
13-
"github.com/btcsuite/btcd/chaincfg/chainhash"
1413
"github.com/btcsuite/btcd/txscript"
1514
"github.com/btcsuite/btcd/wire"
16-
"github.com/decred/dcrd/dcrec/secp256k1/v4"
1715
"github.com/stretchr/testify/require"
1816
)
1917

@@ -24,10 +22,10 @@ const (
2422

2523
var (
2624
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()
2927
sweepScript, _ = (&tree.CSVMultisigClosure{
30-
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{serverPrivKey.PubKey()}},
28+
MultisigClosure: tree.MultisigClosure{PubKeys: []*btcec.PublicKey{serverPrivKey.PubKey()}},
3129
Locktime: vtxoTreeExpiry,
3230
}).Script()
3331
sweepRoot = txscript.NewBaseTapLeaf(sweepScript).TapHash()
@@ -37,125 +35,214 @@ var (
3735
func TestBuildAndSignVtxoTree(t *testing.T) {
3836
t.Parallel()
3937

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[:],
4646
)
4747
require.NoError(t, err)
48-
require.NotNil(t, sharedOutputScript)
48+
require.NotNil(t, sharedOutScript)
49+
require.NotZero(t, sharedOutAmount)
4950

5051
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,
5953
)
6054
require.NoError(t, err)
55+
require.NotNil(t, vtxoTree)
6156

62-
serverCoordinator, err := bitcointree.NewTreeCoordinatorSession(
63-
sharedOutputAmount,
64-
vtxoTree,
65-
sweepRoot[:],
57+
coordinator, err := bitcointree.NewTreeCoordinatorSession(
58+
sharedOutAmount, vtxoTree, sweepRoot[:],
6659
)
6760
require.NoError(t, err)
61+
require.NotNil(t, coordinator)
6862

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)
7766

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))
8168
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-
}
9869

99-
serverCoordinator.AddNonce(pubkey, nonces)
100-
}
70+
signedTree, err := makeAggregatedSignatures(signers, coordinator, checkSigsRoundtrip(t))
71+
require.NoError(t, err)
72+
require.NotNil(t, signedTree)
10173

102-
aggregatedNonce, err := serverCoordinator.AggregateNonces()
74+
// validate signatures
75+
err = bitcointree.ValidateTreeSigs(sweepRoot[:], sharedOutAmount, signedTree)
10376
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)
10486

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])
10892
}
93+
}
94+
}
95+
}
10996

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)
128110
}
129-
130-
serverCoordinator.AddSig(pubkey, sig)
131111
}
112+
}
113+
}
114+
}
132115

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)
145193
}
194+
195+
// aggregate signatures
196+
return coordinator.SignTree()
146197
}
147198

148199
type testCase struct {
149200
name string
150201
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
152239
}
153240

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()
159246
if err != nil {
160247
return nil, nil, err
161248
}
@@ -196,35 +283,6 @@ func withMixedSigningTypes(receivers []tree.VtxoLeaf) []tree.VtxoLeaf {
196283
return append(first, second...)
197284
}
198285

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())
230288
}

0 commit comments

Comments
 (0)