-
Notifications
You must be signed in to change notification settings - Fork 46
/
batch_signer.go
157 lines (137 loc) · 4.48 KB
/
batch_signer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package order
import (
"context"
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/txscript"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/pool/account"
"github.com/lightninglabs/pool/poolscript"
)
// batchSigner is a type that implements the BatchSigner interface and can sign
// for a trader's account inputs in a batch.
type batchSigner struct {
getAccount func(*btcec.PublicKey) (*account.Account, error)
signer lndclient.SignerClient
}
// Sign returns the witness stack of all account inputs in a batch that
// belong to the trader.
//
// NOTE: This method is part of the BatchSigner interface.
func (s *batchSigner) Sign(batch *Batch) (BatchSignature, AccountNonces, error) {
ourSigs := make(BatchSignature, len(batch.AccountDiffs))
ourNonces := make(AccountNonces, len(batch.AccountDiffs))
// At this point we know that the accounts charged are correct. So we
// can just go through them, find the corresponding input in the batch
// TX and sign it.
for _, acctDiff := range batch.AccountDiffs {
// Get account from DB and make sure we can create the output.
acct, err := s.getAccount(acctDiff.AccountKey)
if err != nil {
return nil, nil, fmt.Errorf("account not found: %v",
err)
}
acctOut, err := acct.Output()
if err != nil {
return nil, nil, fmt.Errorf("could not get account "+
"output: %v", err)
}
var acctKey [33]byte
copy(acctKey[:], acct.TraderKey.PubKey.SerializeCompressed())
// Find the input index we are going to sign.
inputIndex := -1
for idx, in := range batch.BatchTX.TxIn {
if in.PreviousOutPoint == acct.OutPoint {
inputIndex = idx
}
}
if inputIndex == -1 {
return nil, nil, fmt.Errorf("account input not found")
}
// MuSig2 signing works differently!
if acct.Version >= account.VersionTaprootEnabled {
partialSig, nonces, err := s.signInputMuSig2(
acctKey, acct, inputIndex, batch,
)
if err != nil {
return nil, nil, fmt.Errorf("error MuSig2 "+
"signing input %d: %v", inputIndex, err)
}
ourSigs[acctKey] = partialSig
ourNonces[acctKey] = nonces
// We're done signing for this account input.
continue
}
// Gather the remaining components required to sign the
// transaction and sign it.
traderKeyTweak := poolscript.TraderKeyTweak(
acct.BatchKey, acct.Secret, acct.TraderKey.PubKey,
)
witnessScript, err := poolscript.AccountWitnessScript(
acct.Expiry, acct.TraderKey.PubKey, acct.AuctioneerKey,
acct.BatchKey, acct.Secret,
)
if err != nil {
return nil, nil, err
}
signDesc := &lndclient.SignDescriptor{
KeyDesc: *acct.TraderKey,
SingleTweak: traderKeyTweak,
WitnessScript: witnessScript,
Output: acctOut,
HashType: txscript.SigHashAll,
InputIndex: inputIndex,
}
sigs, err := s.signer.SignOutputRaw(
context.Background(), batch.BatchTX,
[]*lndclient.SignDescriptor{signDesc}, nil,
)
if err != nil {
return nil, nil, err
}
// Make sure the signature is in the expected format (mostly a
// precaution).
_, err = ecdsa.ParseDERSignature(sigs[0])
if err != nil {
return nil, nil, err
}
ourSigs[acctKey] = sigs[0]
}
return ourSigs, ourNonces, nil
}
// signInputMuSig2 creates a MuSig2 partial signature for the given account's
// input.
func (s *batchSigner) signInputMuSig2(acctKey [33]byte,
account *account.Account, accountInputIdx int, batch *Batch) ([]byte,
poolscript.MuSig2Nonces, error) {
ctx := context.Background()
var emptyNonces poolscript.MuSig2Nonces
serverNonces, ok := batch.ServerNonces[acctKey]
if !ok {
return nil, emptyNonces, fmt.Errorf("server didn't include "+
"nonces for account %x", acctKey[:])
}
sessionInfo, cleanup, err := poolscript.TaprootMuSig2SigningSession(
ctx, account.Version.ScriptVersion(), account.Expiry,
account.TraderKey.PubKey, account.BatchKey, account.Secret,
account.AuctioneerKey, s.signer, &account.TraderKey.KeyLocator,
&serverNonces,
)
if err != nil {
return nil, emptyNonces, fmt.Errorf("error creating MuSig2 "+
"session: %v", err)
}
partialSig, err := poolscript.TaprootMuSig2Sign(
ctx, accountInputIdx, sessionInfo, s.signer, batch.BatchTX,
batch.PreviousOutputs, nil, nil,
)
if err != nil {
cleanup()
return nil, emptyNonces, fmt.Errorf("error signing batch TX: "+
"%v", err)
}
return partialSig, sessionInfo.PublicNonce, nil
}
// A compile-time constraint to ensure batchSigner implements BatchSigner.
var _ BatchSigner = (*batchSigner)(nil)