input: pass input.Signature to multisig spend

Modifies SpendMultiSig to accept input.Signature, so that we can
ultimately assert the size of multisig witnesses.
This commit is contained in:
Conner Fromknecht 2020-04-05 17:07:01 -07:00
parent 0f94b8dc62
commit f2b6e2af04
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
6 changed files with 87 additions and 50 deletions

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/davecgh/go-spew/spew"
@ -510,11 +509,15 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b
// transaction! We'll craft the final closing transaction so
// we can broadcast it to the network.
matchingSig := c.priorFeeOffers[remoteProposedFee].Signature
localSigBytes := matchingSig.ToSignatureBytes()
localSig := append(localSigBytes, byte(txscript.SigHashAll))
localSig, err := matchingSig.ToSignature()
if err != nil {
return nil, false, err
}
remoteSigBytes := closeSignedMsg.Signature.ToSignatureBytes()
remoteSig := append(remoteSigBytes, byte(txscript.SigHashAll))
remoteSig, err := closeSignedMsg.Signature.ToSignature()
if err != nil {
return nil, false, err
}
closeTx, _, err := c.cfg.channel.CompleteCooperativeClose(
localSig, remoteSig, c.localDeliveryScript,

@ -96,7 +96,9 @@ func GenFundingPkScript(aPub, bPub []byte, amt int64) ([]byte, *wire.TxOut, erro
// SpendMultiSig generates the witness stack required to redeem the 2-of-2 p2wsh
// multi-sig output.
func SpendMultiSig(witnessScript, pubA, sigA, pubB, sigB []byte) [][]byte {
func SpendMultiSig(witnessScript, pubA []byte, sigA Signature,
pubB []byte, sigB Signature) [][]byte {
witness := make([][]byte, 4)
// When spending a p2wsh multi-sig script, rather than an OP_0, we add
@ -108,11 +110,11 @@ func SpendMultiSig(witnessScript, pubA, sigA, pubB, sigB []byte) [][]byte {
// ensure the signatures appear on the Script Virtual Machine stack in
// the correct order.
if bytes.Compare(pubA, pubB) == 1 {
witness[1] = sigB
witness[2] = sigA
witness[1] = append(sigB.Serialize(), byte(txscript.SigHashAll))
witness[2] = append(sigA.Serialize(), byte(txscript.SigHashAll))
} else {
witness[1] = sigA
witness[2] = sigB
witness[1] = append(sigA.Serialize(), byte(txscript.SigHashAll))
witness[2] = append(sigB.Serialize(), byte(txscript.SigHashAll))
}
// Finally, add the preimage as the last witness element.

@ -5065,17 +5065,21 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
// for the transaction.
localCommit := lc.channelState.LocalCommitment
commitTx := localCommit.CommitTx.Copy()
theirSig := append(localCommit.CommitSig, byte(txscript.SigHashAll))
// With this, we then generate the full witness so the caller can
// broadcast a fully signed transaction.
lc.signDesc.SigHashes = txscript.NewTxSigHashes(commitTx)
ourSigRaw, err := lc.Signer.SignOutputRaw(commitTx, lc.signDesc)
theirSig, err := btcec.ParseDERSignature(
localCommit.CommitSig, btcec.S256(),
)
if err != nil {
return nil, err
}
ourSig := append(ourSigRaw.Serialize(), byte(txscript.SigHashAll))
// With this, we then generate the full witness so the caller can
// broadcast a fully signed transaction.
lc.signDesc.SigHashes = txscript.NewTxSigHashes(commitTx)
ourSig, err := lc.Signer.SignOutputRaw(commitTx, lc.signDesc)
if err != nil {
return nil, err
}
// With the final signature generated, create the witness stack
// required to spend from the multi-sig output.
@ -6017,7 +6021,8 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount,
//
// NOTE: The passed local and remote sigs are expected to be fully complete
// signatures including the proper sighash byte.
func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte,
func (lc *LightningChannel) CompleteCooperativeClose(
localSig, remoteSig input.Signature,
localDeliveryScript, remoteDeliveryScript []byte,
proposedFee btcutil.Amount) (*wire.MsgTx, btcutil.Amount, error) {
@ -6070,8 +6075,10 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte,
SerializeCompressed()
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
witness := input.SpendMultiSig(lc.signDesc.WitnessScript, ourKey,
localSig, theirKey, remoteSig)
witness := input.SpendMultiSig(
lc.signDesc.WitnessScript, ourKey, localSig, theirKey,
remoteSig,
)
closeTx.TxIn[0].Witness = witness
// Validate the finalized transaction to ensure the output script is

@ -616,7 +616,6 @@ func TestCooperativeChannelClosure(t *testing.T) {
if err != nil {
t.Fatalf("unable to create alice coop close proposal: %v", err)
}
aliceCloseSig := append(aliceSig.Serialize(), byte(txscript.SigHashAll))
bobFee := bobChannel.CalcFee(bobFeeRate)
bobSig, _, _, err := bobChannel.CreateCloseProposal(
@ -625,14 +624,13 @@ func TestCooperativeChannelClosure(t *testing.T) {
if err != nil {
t.Fatalf("unable to create bob coop close proposal: %v", err)
}
bobCloseSig := append(bobSig.Serialize(), byte(txscript.SigHashAll))
// With the proposals created, both sides should be able to properly
// process the other party's signature. This indicates that the
// transaction is well formed, and the signatures verify.
aliceCloseTx, _, err := bobChannel.CompleteCooperativeClose(
bobCloseSig, aliceCloseSig, bobDeliveryScript,
aliceDeliveryScript, bobFee,
bobSig, aliceSig, bobDeliveryScript, aliceDeliveryScript,
bobFee,
)
if err != nil {
t.Fatalf("unable to complete alice cooperative close: %v", err)
@ -640,8 +638,8 @@ func TestCooperativeChannelClosure(t *testing.T) {
bobCloseSha := aliceCloseTx.TxHash()
bobCloseTx, _, err := aliceChannel.CompleteCooperativeClose(
aliceCloseSig, bobCloseSig, aliceDeliveryScript,
bobDeliveryScript, aliceFee,
aliceSig, bobSig, aliceDeliveryScript, bobDeliveryScript,
aliceFee,
)
if err != nil {
t.Fatalf("unable to complete bob cooperative close: %v", err)
@ -2054,24 +2052,25 @@ func TestCooperativeCloseDustAdherence(t *testing.T) {
// balances. As a result, performing a cooperative closure now result
// in both sides having an output within the closure transaction.
aliceFee := btcutil.Amount(aliceChannel.CalcFee(aliceFeeRate)) + 1000
aliceSig, _, _, err := aliceChannel.CreateCloseProposal(aliceFee,
aliceDeliveryScript, bobDeliveryScript)
aliceSig, _, _, err := aliceChannel.CreateCloseProposal(
aliceFee, aliceDeliveryScript, bobDeliveryScript,
)
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
aliceCloseSig := append(aliceSig.Serialize(), byte(txscript.SigHashAll))
bobFee := btcutil.Amount(bobChannel.CalcFee(bobFeeRate)) + 1000
bobSig, _, _, err := bobChannel.CreateCloseProposal(bobFee,
bobDeliveryScript, aliceDeliveryScript)
bobSig, _, _, err := bobChannel.CreateCloseProposal(
bobFee, bobDeliveryScript, aliceDeliveryScript,
)
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
bobCloseSig := append(bobSig.Serialize(), byte(txscript.SigHashAll))
closeTx, _, err := bobChannel.CompleteCooperativeClose(
bobCloseSig, aliceCloseSig,
bobDeliveryScript, aliceDeliveryScript, bobFee)
bobSig, aliceSig, bobDeliveryScript, aliceDeliveryScript,
bobFee,
)
if err != nil {
t.Fatalf("unable to accept channel close: %v", err)
}
@ -2093,23 +2092,24 @@ func TestCooperativeCloseDustAdherence(t *testing.T) {
// Attempt another cooperative channel closure. It should succeed
// without any issues.
aliceSig, _, _, err = aliceChannel.CreateCloseProposal(aliceFee,
aliceDeliveryScript, bobDeliveryScript)
aliceSig, _, _, err = aliceChannel.CreateCloseProposal(
aliceFee, aliceDeliveryScript, bobDeliveryScript,
)
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
aliceCloseSig = append(aliceSig.Serialize(), byte(txscript.SigHashAll))
bobSig, _, _, err = bobChannel.CreateCloseProposal(bobFee,
bobDeliveryScript, aliceDeliveryScript)
bobSig, _, _, err = bobChannel.CreateCloseProposal(
bobFee, bobDeliveryScript, aliceDeliveryScript,
)
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
bobCloseSig = append(bobSig.Serialize(), byte(txscript.SigHashAll))
closeTx, _, err = bobChannel.CompleteCooperativeClose(
bobCloseSig, aliceCloseSig,
bobDeliveryScript, aliceDeliveryScript, bobFee)
bobSig, aliceSig, bobDeliveryScript, aliceDeliveryScript,
bobFee,
)
if err != nil {
t.Fatalf("unable to accept channel close: %v", err)
}
@ -2141,7 +2141,6 @@ func TestCooperativeCloseDustAdherence(t *testing.T) {
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
aliceCloseSig = append(aliceSig.Serialize(), byte(txscript.SigHashAll))
bobSig, _, _, err = bobChannel.CreateCloseProposal(
bobFee, bobDeliveryScript, aliceDeliveryScript,
@ -2149,11 +2148,11 @@ func TestCooperativeCloseDustAdherence(t *testing.T) {
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
bobCloseSig = append(bobSig.Serialize(), byte(txscript.SigHashAll))
closeTx, _, err = bobChannel.CompleteCooperativeClose(
bobCloseSig, aliceCloseSig,
bobDeliveryScript, aliceDeliveryScript, bobFee)
bobSig, aliceSig, bobDeliveryScript, aliceDeliveryScript,
bobFee,
)
if err != nil {
t.Fatalf("unable to accept channel close: %v", err)
}

@ -98,7 +98,7 @@ func newChannelTestCtx(chanSize int64) (*channelTestCtx, error) {
}
sigHashes := txscript.NewTxSigHashes(commitTx)
aliceSig, err := txscript.RawTxInWitnessSignature(
aliceSigRaw, err := txscript.RawTxInWitnessSignature(
commitTx, sigHashes, 0, chanSize,
multiSigScript, txscript.SigHashAll, alicePriv,
)
@ -106,7 +106,14 @@ func newChannelTestCtx(chanSize int64) (*channelTestCtx, error) {
return nil, err
}
bobSig, err := txscript.RawTxInWitnessSignature(
aliceSig, err := btcec.ParseDERSignature(
aliceSigRaw, btcec.S256(),
)
if err != nil {
return nil, err
}
bobSigRaw, err := txscript.RawTxInWitnessSignature(
commitTx, sigHashes, 0, chanSize,
multiSigScript, txscript.SigHashAll, bobPriv,
)
@ -114,6 +121,13 @@ func newChannelTestCtx(chanSize int64) (*channelTestCtx, error) {
return nil, err
}
bobSig, err := btcec.ParseDERSignature(
bobSigRaw, btcec.S256(),
)
if err != nil {
return nil, err
}
commitTx.TxIn[0].Witness = input.SpendMultiSig(
multiSigScript, alicePub.SerializeCompressed(), aliceSig,
bobPub.SerializeCompressed(), bobSig,

@ -1,7 +1,6 @@
package lnwallet
import (
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/hex"
@ -81,6 +80,19 @@ var (
},
LockTime: 5,
}
// A valid, DER-encoded signature (taken from btcec unit tests).
testSigBytes = []byte{
0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69,
0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3,
0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32,
0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab,
0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15,
0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48, 0x60,
0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c,
0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22,
0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
}
)
// CreateTestChannels creates to fully populated channels to be used within
@ -257,7 +269,7 @@ func CreateTestChannels(chanType channeldb.ChannelType) (
CommitFee: commitFee,
FeePerKw: btcutil.Amount(feePerKw),
CommitTx: aliceCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
CommitSig: testSigBytes,
}
bobCommit := channeldb.ChannelCommitment{
CommitHeight: 0,
@ -266,7 +278,7 @@ func CreateTestChannels(chanType channeldb.ChannelType) (
CommitFee: commitFee,
FeePerKw: btcutil.Amount(feePerKw),
CommitTx: bobCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
CommitSig: testSigBytes,
}
var chanIDBytes [8]byte