sweepbatcher: factor out method musig2sign

It is long and is going to be even longer and hard to keep it inside a loop.
pull/784/head
Boris Nagaev 3 months ago
parent 822e4746c1
commit 3d5b7db1f7
No known key found for this signature in database

@ -896,11 +896,9 @@ func (b *batch) publishBatchCoop(ctx context.Context) (btcutil.Amount,
return fee, err, false return fee, err, false
} }
prevOutputFetcher := txscript.NewMultiPrevOutFetcher(prevOuts)
// Attempt to cooperatively sign the batch tx with the server. // Attempt to cooperatively sign the batch tx with the server.
err = b.coopSignBatchTx( err = b.coopSignBatchTx(
ctx, packet, sweeps, prevOutputFetcher, prevOuts, psbtBuf, ctx, packet, sweeps, prevOuts, psbtBuf.Bytes(),
) )
if err != nil { if err != nil {
return fee, err, false return fee, err, false
@ -942,138 +940,148 @@ func (b *batch) debugLogTx(msg string, tx *wire.MsgTx) {
// coopSignBatchTx collects the necessary signatures from the server in order // coopSignBatchTx collects the necessary signatures from the server in order
// to cooperatively sweep the funds. // to cooperatively sweep the funds.
func (b *batch) coopSignBatchTx(ctx context.Context, packet *psbt.Packet, func (b *batch) coopSignBatchTx(ctx context.Context, packet *psbt.Packet,
sweeps []sweep, prevOutputFetcher *txscript.MultiPrevOutFetcher, sweeps []sweep, prevOuts map[wire.OutPoint]*wire.TxOut,
prevOuts map[wire.OutPoint]*wire.TxOut, psbtBuf bytes.Buffer) error { psbt []byte) error {
for i, sweep := range sweeps { for i, sweep := range sweeps {
sweep := sweep sweep := sweep
sigHashes := txscript.NewTxSigHashes( finalSig, err := b.musig2sign(
packet.UnsignedTx, prevOutputFetcher, ctx, i, sweep, packet.UnsignedTx, prevOuts, psbt,
)
sigHash, err := txscript.CalcTaprootSignatureHash(
sigHashes, txscript.SigHashDefault, packet.UnsignedTx,
i, prevOutputFetcher,
) )
if err != nil { if err != nil {
return err return err
} }
var ( packet.UnsignedTx.TxIn[i].Witness = wire.TxWitness{
signers [][]byte finalSig,
muSig2Version input.MuSig2Version
)
// Depending on the MuSig2 version we either pass 32 byte
// Schnorr public keys or normal 33 byte public keys.
if sweep.protocolVersion >= loopdb.ProtocolVersionMuSig2 {
muSig2Version = input.MuSig2Version100RC2
signers = [][]byte{
sweep.htlcKeys.SenderInternalPubKey[:],
sweep.htlcKeys.ReceiverInternalPubKey[:],
}
} else {
muSig2Version = input.MuSig2Version040
signers = [][]byte{
sweep.htlcKeys.SenderInternalPubKey[1:],
sweep.htlcKeys.ReceiverInternalPubKey[1:],
}
} }
}
htlcScript, ok := sweep.htlc.HtlcScript.(*swap.HtlcScriptV3) return nil
if !ok { }
return fmt.Errorf("invalid htlc script version")
}
// Now we're creating a local MuSig2 session using the receiver // musig2sign signs one sweep using musig2.
// key's key locator and the htlc's root hash. func (b *batch) musig2sign(ctx context.Context, inputIndex int, sweep sweep,
musig2SessionInfo, err := b.signerClient.MuSig2CreateSession( unsignedTx *wire.MsgTx, prevOuts map[wire.OutPoint]*wire.TxOut,
ctx, muSig2Version, psbt []byte) ([]byte, error) {
&sweep.htlcKeys.ClientScriptKeyLocator, signers,
lndclient.MuSig2TaprootTweakOpt(
htlcScript.RootHash[:], false,
),
)
if err != nil {
return fmt.Errorf("signerClient.MuSig2CreateSession "+
"failed: %w", err)
}
// With the session active, we can now send the server our prevOutputFetcher := txscript.NewMultiPrevOutFetcher(prevOuts)
// public nonce and the sig hash, so that it can create it's own
// MuSig2 session and return the server side nonce and partial
// signature.
serverNonce, serverSig, err := b.muSig2SignSweep(
ctx, sweep.protocolVersion, sweep.swapHash,
sweep.swapInvoicePaymentAddr,
musig2SessionInfo.PublicNonce[:], psbtBuf.Bytes(),
prevOuts,
)
if err != nil {
return err
}
var serverPublicNonce [musig2.PubNonceSize]byte sigHashes := txscript.NewTxSigHashes(unsignedTx, prevOutputFetcher)
copy(serverPublicNonce[:], serverNonce)
// Register the server's nonce before attempting to create our sigHash, err := txscript.CalcTaprootSignatureHash(
// partial signature. sigHashes, txscript.SigHashDefault, unsignedTx, inputIndex,
haveAllNonces, err := b.signerClient.MuSig2RegisterNonces( prevOutputFetcher,
ctx, musig2SessionInfo.SessionID, )
[][musig2.PubNonceSize]byte{serverPublicNonce}, if err != nil {
) return nil, err
if err != nil { }
return err
}
// Sanity check that we have all the nonces. var (
if !haveAllNonces { signers [][]byte
return fmt.Errorf("invalid MuSig2 session: " + muSig2Version input.MuSig2Version
"nonces missing") )
// Depending on the MuSig2 version we either pass 32 byte
// Schnorr public keys or normal 33 byte public keys.
if sweep.protocolVersion >= loopdb.ProtocolVersionMuSig2 {
muSig2Version = input.MuSig2Version100RC2
signers = [][]byte{
sweep.htlcKeys.SenderInternalPubKey[:],
sweep.htlcKeys.ReceiverInternalPubKey[:],
}
} else {
muSig2Version = input.MuSig2Version040
signers = [][]byte{
sweep.htlcKeys.SenderInternalPubKey[1:],
sweep.htlcKeys.ReceiverInternalPubKey[1:],
} }
}
var digest [32]byte htlcScript, ok := sweep.htlc.HtlcScript.(*swap.HtlcScriptV3)
copy(digest[:], sigHash) if !ok {
return nil, fmt.Errorf("invalid htlc script version")
}
// Since our MuSig2 session has all nonces, we can now create // Now we're creating a local MuSig2 session using the receiver key's
// the local partial signature by signing the sig hash. // key locator and the htlc's root hash.
_, err = b.signerClient.MuSig2Sign( keyLocator := &sweep.htlcKeys.ClientScriptKeyLocator
ctx, musig2SessionInfo.SessionID, digest, false, musig2SessionInfo, err := b.signerClient.MuSig2CreateSession(
) ctx, muSig2Version, keyLocator, signers,
if err != nil { lndclient.MuSig2TaprootTweakOpt(htlcScript.RootHash[:], false),
return err )
} if err != nil {
return nil, fmt.Errorf("signerClient.MuSig2CreateSession "+
"failed: %w", err)
}
// Now combine the partial signatures to use the final combined // With the session active, we can now send the server our
// signature in the sweep transaction's witness. // public nonce and the sig hash, so that it can create it's own
haveAllSigs, finalSig, err := b.signerClient.MuSig2CombineSig( // MuSig2 session and return the server side nonce and partial
ctx, musig2SessionInfo.SessionID, [][]byte{serverSig}, // signature.
) serverNonce, serverSig, err := b.muSig2SignSweep(
if err != nil { ctx, sweep.protocolVersion, sweep.swapHash,
return err sweep.swapInvoicePaymentAddr,
} musig2SessionInfo.PublicNonce[:], psbt, prevOuts,
)
if err != nil {
return nil, err
}
if !haveAllSigs { var serverPublicNonce [musig2.PubNonceSize]byte
return fmt.Errorf("failed to combine signatures") copy(serverPublicNonce[:], serverNonce)
}
// To be sure that we're good, parse and validate that the // Register the server's nonce before attempting to create our
// combined signature is indeed valid for the sig hash and the // partial signature.
// internal pubkey. haveAllNonces, err := b.signerClient.MuSig2RegisterNonces(
err = b.verifySchnorrSig( ctx, musig2SessionInfo.SessionID,
htlcScript.TaprootKey, sigHash, finalSig, [][musig2.PubNonceSize]byte{serverPublicNonce},
) )
if err != nil { if err != nil {
return err return nil, err
} }
packet.UnsignedTx.TxIn[i].Witness = wire.TxWitness{ // Sanity check that we have all the nonces.
finalSig, if !haveAllNonces {
} return nil, fmt.Errorf("invalid MuSig2 session: " +
"nonces missing")
} }
return nil var digest [32]byte
copy(digest[:], sigHash)
// Since our MuSig2 session has all nonces, we can now create
// the local partial signature by signing the sig hash.
_, err = b.signerClient.MuSig2Sign(
ctx, musig2SessionInfo.SessionID, digest, false,
)
if err != nil {
return nil, err
}
// Now combine the partial signatures to use the final combined
// signature in the sweep transaction's witness.
haveAllSigs, finalSig, err := b.signerClient.MuSig2CombineSig(
ctx, musig2SessionInfo.SessionID, [][]byte{serverSig},
)
if err != nil {
return nil, err
}
if !haveAllSigs {
return nil, fmt.Errorf("failed to combine signatures")
}
// To be sure that we're good, parse and validate that the
// combined signature is indeed valid for the sig hash and the
// internal pubkey.
err = b.verifySchnorrSig(htlcScript.TaprootKey, sigHash, finalSig)
if err != nil {
return nil, err
}
return finalSig, nil
} }
// updateRbfRate updates the fee rate we should use for the new batch // updateRbfRate updates the fee rate we should use for the new batch

Loading…
Cancel
Save