Skip to content

Commit 3d5b7db

Browse files
committed
sweepbatcher: factor out method musig2sign
It is long and is going to be even longer and hard to keep it inside a loop.
1 parent 822e474 commit 3d5b7db

File tree

1 file changed

+118
-110
lines changed

1 file changed

+118
-110
lines changed

sweepbatcher/sweep_batch.go

Lines changed: 118 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -896,11 +896,9 @@ func (b *batch) publishBatchCoop(ctx context.Context) (btcutil.Amount,
896896
return fee, err, false
897897
}
898898

899-
prevOutputFetcher := txscript.NewMultiPrevOutFetcher(prevOuts)
900-
901899
// Attempt to cooperatively sign the batch tx with the server.
902900
err = b.coopSignBatchTx(
903-
ctx, packet, sweeps, prevOutputFetcher, prevOuts, psbtBuf,
901+
ctx, packet, sweeps, prevOuts, psbtBuf.Bytes(),
904902
)
905903
if err != nil {
906904
return fee, err, false
@@ -942,138 +940,148 @@ func (b *batch) debugLogTx(msg string, tx *wire.MsgTx) {
942940
// coopSignBatchTx collects the necessary signatures from the server in order
943941
// to cooperatively sweep the funds.
944942
func (b *batch) coopSignBatchTx(ctx context.Context, packet *psbt.Packet,
945-
sweeps []sweep, prevOutputFetcher *txscript.MultiPrevOutFetcher,
946-
prevOuts map[wire.OutPoint]*wire.TxOut, psbtBuf bytes.Buffer) error {
943+
sweeps []sweep, prevOuts map[wire.OutPoint]*wire.TxOut,
944+
psbt []byte) error {
947945

948946
for i, sweep := range sweeps {
949947
sweep := sweep
950948

951-
sigHashes := txscript.NewTxSigHashes(
952-
packet.UnsignedTx, prevOutputFetcher,
953-
)
954-
955-
sigHash, err := txscript.CalcTaprootSignatureHash(
956-
sigHashes, txscript.SigHashDefault, packet.UnsignedTx,
957-
i, prevOutputFetcher,
949+
finalSig, err := b.musig2sign(
950+
ctx, i, sweep, packet.UnsignedTx, prevOuts, psbt,
958951
)
959952
if err != nil {
960953
return err
961954
}
962955

963-
var (
964-
signers [][]byte
965-
muSig2Version input.MuSig2Version
966-
)
967-
968-
// Depending on the MuSig2 version we either pass 32 byte
969-
// Schnorr public keys or normal 33 byte public keys.
970-
if sweep.protocolVersion >= loopdb.ProtocolVersionMuSig2 {
971-
muSig2Version = input.MuSig2Version100RC2
972-
signers = [][]byte{
973-
sweep.htlcKeys.SenderInternalPubKey[:],
974-
sweep.htlcKeys.ReceiverInternalPubKey[:],
975-
}
976-
} else {
977-
muSig2Version = input.MuSig2Version040
978-
signers = [][]byte{
979-
sweep.htlcKeys.SenderInternalPubKey[1:],
980-
sweep.htlcKeys.ReceiverInternalPubKey[1:],
981-
}
956+
packet.UnsignedTx.TxIn[i].Witness = wire.TxWitness{
957+
finalSig,
982958
}
959+
}
983960

984-
htlcScript, ok := sweep.htlc.HtlcScript.(*swap.HtlcScriptV3)
985-
if !ok {
986-
return fmt.Errorf("invalid htlc script version")
987-
}
961+
return nil
962+
}
988963

989-
// Now we're creating a local MuSig2 session using the receiver
990-
// key's key locator and the htlc's root hash.
991-
musig2SessionInfo, err := b.signerClient.MuSig2CreateSession(
992-
ctx, muSig2Version,
993-
&sweep.htlcKeys.ClientScriptKeyLocator, signers,
994-
lndclient.MuSig2TaprootTweakOpt(
995-
htlcScript.RootHash[:], false,
996-
),
997-
)
998-
if err != nil {
999-
return fmt.Errorf("signerClient.MuSig2CreateSession "+
1000-
"failed: %w", err)
1001-
}
964+
// musig2sign signs one sweep using musig2.
965+
func (b *batch) musig2sign(ctx context.Context, inputIndex int, sweep sweep,
966+
unsignedTx *wire.MsgTx, prevOuts map[wire.OutPoint]*wire.TxOut,
967+
psbt []byte) ([]byte, error) {
1002968

1003-
// With the session active, we can now send the server our
1004-
// public nonce and the sig hash, so that it can create it's own
1005-
// MuSig2 session and return the server side nonce and partial
1006-
// signature.
1007-
serverNonce, serverSig, err := b.muSig2SignSweep(
1008-
ctx, sweep.protocolVersion, sweep.swapHash,
1009-
sweep.swapInvoicePaymentAddr,
1010-
musig2SessionInfo.PublicNonce[:], psbtBuf.Bytes(),
1011-
prevOuts,
1012-
)
1013-
if err != nil {
1014-
return err
1015-
}
969+
prevOutputFetcher := txscript.NewMultiPrevOutFetcher(prevOuts)
1016970

1017-
var serverPublicNonce [musig2.PubNonceSize]byte
1018-
copy(serverPublicNonce[:], serverNonce)
971+
sigHashes := txscript.NewTxSigHashes(unsignedTx, prevOutputFetcher)
1019972

1020-
// Register the server's nonce before attempting to create our
1021-
// partial signature.
1022-
haveAllNonces, err := b.signerClient.MuSig2RegisterNonces(
1023-
ctx, musig2SessionInfo.SessionID,
1024-
[][musig2.PubNonceSize]byte{serverPublicNonce},
1025-
)
1026-
if err != nil {
1027-
return err
1028-
}
973+
sigHash, err := txscript.CalcTaprootSignatureHash(
974+
sigHashes, txscript.SigHashDefault, unsignedTx, inputIndex,
975+
prevOutputFetcher,
976+
)
977+
if err != nil {
978+
return nil, err
979+
}
1029980

1030-
// Sanity check that we have all the nonces.
1031-
if !haveAllNonces {
1032-
return fmt.Errorf("invalid MuSig2 session: " +
1033-
"nonces missing")
981+
var (
982+
signers [][]byte
983+
muSig2Version input.MuSig2Version
984+
)
985+
986+
// Depending on the MuSig2 version we either pass 32 byte
987+
// Schnorr public keys or normal 33 byte public keys.
988+
if sweep.protocolVersion >= loopdb.ProtocolVersionMuSig2 {
989+
muSig2Version = input.MuSig2Version100RC2
990+
signers = [][]byte{
991+
sweep.htlcKeys.SenderInternalPubKey[:],
992+
sweep.htlcKeys.ReceiverInternalPubKey[:],
993+
}
994+
} else {
995+
muSig2Version = input.MuSig2Version040
996+
signers = [][]byte{
997+
sweep.htlcKeys.SenderInternalPubKey[1:],
998+
sweep.htlcKeys.ReceiverInternalPubKey[1:],
1034999
}
1000+
}
10351001

1036-
var digest [32]byte
1037-
copy(digest[:], sigHash)
1002+
htlcScript, ok := sweep.htlc.HtlcScript.(*swap.HtlcScriptV3)
1003+
if !ok {
1004+
return nil, fmt.Errorf("invalid htlc script version")
1005+
}
10381006

1039-
// Since our MuSig2 session has all nonces, we can now create
1040-
// the local partial signature by signing the sig hash.
1041-
_, err = b.signerClient.MuSig2Sign(
1042-
ctx, musig2SessionInfo.SessionID, digest, false,
1043-
)
1044-
if err != nil {
1045-
return err
1046-
}
1007+
// Now we're creating a local MuSig2 session using the receiver key's
1008+
// key locator and the htlc's root hash.
1009+
keyLocator := &sweep.htlcKeys.ClientScriptKeyLocator
1010+
musig2SessionInfo, err := b.signerClient.MuSig2CreateSession(
1011+
ctx, muSig2Version, keyLocator, signers,
1012+
lndclient.MuSig2TaprootTweakOpt(htlcScript.RootHash[:], false),
1013+
)
1014+
if err != nil {
1015+
return nil, fmt.Errorf("signerClient.MuSig2CreateSession "+
1016+
"failed: %w", err)
1017+
}
10471018

1048-
// Now combine the partial signatures to use the final combined
1049-
// signature in the sweep transaction's witness.
1050-
haveAllSigs, finalSig, err := b.signerClient.MuSig2CombineSig(
1051-
ctx, musig2SessionInfo.SessionID, [][]byte{serverSig},
1052-
)
1053-
if err != nil {
1054-
return err
1055-
}
1019+
// With the session active, we can now send the server our
1020+
// public nonce and the sig hash, so that it can create it's own
1021+
// MuSig2 session and return the server side nonce and partial
1022+
// signature.
1023+
serverNonce, serverSig, err := b.muSig2SignSweep(
1024+
ctx, sweep.protocolVersion, sweep.swapHash,
1025+
sweep.swapInvoicePaymentAddr,
1026+
musig2SessionInfo.PublicNonce[:], psbt, prevOuts,
1027+
)
1028+
if err != nil {
1029+
return nil, err
1030+
}
10561031

1057-
if !haveAllSigs {
1058-
return fmt.Errorf("failed to combine signatures")
1059-
}
1032+
var serverPublicNonce [musig2.PubNonceSize]byte
1033+
copy(serverPublicNonce[:], serverNonce)
10601034

1061-
// To be sure that we're good, parse and validate that the
1062-
// combined signature is indeed valid for the sig hash and the
1063-
// internal pubkey.
1064-
err = b.verifySchnorrSig(
1065-
htlcScript.TaprootKey, sigHash, finalSig,
1066-
)
1067-
if err != nil {
1068-
return err
1069-
}
1035+
// Register the server's nonce before attempting to create our
1036+
// partial signature.
1037+
haveAllNonces, err := b.signerClient.MuSig2RegisterNonces(
1038+
ctx, musig2SessionInfo.SessionID,
1039+
[][musig2.PubNonceSize]byte{serverPublicNonce},
1040+
)
1041+
if err != nil {
1042+
return nil, err
1043+
}
10701044

1071-
packet.UnsignedTx.TxIn[i].Witness = wire.TxWitness{
1072-
finalSig,
1073-
}
1045+
// Sanity check that we have all the nonces.
1046+
if !haveAllNonces {
1047+
return nil, fmt.Errorf("invalid MuSig2 session: " +
1048+
"nonces missing")
10741049
}
10751050

1076-
return nil
1051+
var digest [32]byte
1052+
copy(digest[:], sigHash)
1053+
1054+
// Since our MuSig2 session has all nonces, we can now create
1055+
// the local partial signature by signing the sig hash.
1056+
_, err = b.signerClient.MuSig2Sign(
1057+
ctx, musig2SessionInfo.SessionID, digest, false,
1058+
)
1059+
if err != nil {
1060+
return nil, err
1061+
}
1062+
1063+
// Now combine the partial signatures to use the final combined
1064+
// signature in the sweep transaction's witness.
1065+
haveAllSigs, finalSig, err := b.signerClient.MuSig2CombineSig(
1066+
ctx, musig2SessionInfo.SessionID, [][]byte{serverSig},
1067+
)
1068+
if err != nil {
1069+
return nil, err
1070+
}
1071+
1072+
if !haveAllSigs {
1073+
return nil, fmt.Errorf("failed to combine signatures")
1074+
}
1075+
1076+
// To be sure that we're good, parse and validate that the
1077+
// combined signature is indeed valid for the sig hash and the
1078+
// internal pubkey.
1079+
err = b.verifySchnorrSig(htlcScript.TaprootKey, sigHash, finalSig)
1080+
if err != nil {
1081+
return nil, err
1082+
}
1083+
1084+
return finalSig, nil
10771085
}
10781086

10791087
// updateRbfRate updates the fee rate we should use for the new batch

0 commit comments

Comments
 (0)