From eeb2887fe90638034600871669b92cc9a57aa2f8 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 3 May 2016 19:45:32 -0700 Subject: [PATCH] lnwallet: segwit-ify all scripts and signing utils --- lnwallet/channel.go | 21 +++++-------- lnwallet/script_utils.go | 65 +++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index da48024f..3bf83eca 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -81,7 +81,7 @@ func newLightningChannel(wallet *LightningWallet, events chainntnfs.ChainNotifie lc.updateTotem <- struct{}{} fundingTxId := state.FundingTx.TxSha() - fundingPkScript, err := scriptHashPkScript(state.FundingRedeemScript) + fundingPkScript, err := witnessScriptHash(state.FundingRedeemScript) if err != nil { return nil, err } @@ -181,8 +181,6 @@ func (c *ChannelUpdate) VerifyNewCommitmentSigs(ourSig, theirSig []byte) error { c.lnChannel.stateMtx.RLock() defer c.lnChannel.stateMtx.RUnlock() - var err error - var scriptSig []byte channelState := c.lnChannel.channelState // When initially generating the redeemScript, we sorted the serialized @@ -192,15 +190,12 @@ func (c *ChannelUpdate) VerifyNewCommitmentSigs(ourSig, theirSig []byte) error { redeemScript := channelState.FundingRedeemScript ourKey := channelState.OurCommitKey.PubKey().SerializeCompressed() theirKey := channelState.TheirCommitKey.SerializeCompressed() - scriptSig, err = spendMultiSig(redeemScript, ourKey, ourSig, theirKey, theirSig) - if err != nil { - return err - } + witness := spendMultiSig(redeemScript, ourKey, ourSig, theirKey, theirSig) // Attach the scriptSig to our commitment transaction's only input, // then validate that the scriptSig executes correctly. commitTx := c.ourPendingCommitTx - commitTx.TxIn[0].SignatureScript = scriptSig + commitTx.TxIn[0].Witness = witness vm, err := txscript.NewEngine(c.lnChannel.fundingP2SH, commitTx, 0, txscript.StandardVerifyFlags, nil, nil, 0) if err != nil { @@ -387,13 +382,13 @@ func (lc *LightningChannel) addHTLC(ourCommitTx, theirCommitTx *wire.MsgTx, return nil } - // Now that we have the redeem scripts, create the P2SH public key + // Now that we have the redeem scripts, create the P2WSH public key // script for each. - senderP2SH, err := scriptHashPkScript(senderPKScript) + senderP2SH, err := witnessScriptHash(senderPKScript) if err != nil { return nil } - receiverP2SH, err := scriptHashPkScript(receiverPKScript) + receiverP2SH, err := witnessScriptHash(receiverPKScript) if err != nil { return nil } @@ -566,7 +561,7 @@ func createCommitTx(fundingOutput *wire.TxIn, selfKey, theirKey *btcec.PublicKey if err != nil { return nil, err } - payToUsScriptHash, err := scriptHashPkScript(ourRedeemScript) + payToUsScriptHash, err := witnessScriptHash(ourRedeemScript) if err != nil { return nil, err } @@ -578,7 +573,7 @@ func createCommitTx(fundingOutput *wire.TxIn, selfKey, theirKey *btcec.PublicKey if err != nil { return nil, err } - payToThemScriptHash, err := scriptHashPkScript(theirRedeemScript) + payToThemScriptHash, err := witnessScriptHash(theirRedeemScript) if err != nil { return nil, err } diff --git a/lnwallet/script_utils.go b/lnwallet/script_utils.go index 6cd65f6f..3adac3c0 100644 --- a/lnwallet/script_utils.go +++ b/lnwallet/script_utils.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/btcsuite/fastsha256" ) var ( @@ -18,19 +19,20 @@ var ( OP_CHECKSEQUENCEVERIFY byte = txscript.OP_NOP3 ) -// scriptHashPkScript generates a pay-to-script-hash public key script paying -// to the hash160 of the passed redeem script. -func scriptHashPkScript(redeemScript []byte) ([]byte, error) { +// witnessScriptHash generates a pay-to-witness-script-hash public key script +// paying to a version 0 witness program paying to the passed redeem script. +func witnessScriptHash(redeemScript []byte) ([]byte, error) { bldr := txscript.NewScriptBuilder() - bldr.AddOp(txscript.OP_HASH160) - bldr.AddData(btcutil.Hash160(redeemScript)) - bldr.AddOp(txscript.OP_EQUAL) + + bldr.AddOp(txscript.OP_0) + scriptHash := fastsha256.Sum256(redeemScript) + bldr.AddData(scriptHash[:]) return bldr.Script() } -// getFundingPkScript generates the non-p2sh'd multisig script for 2 of 2 +// genMultiSigScript generates the non-p2sh'd multisig script for 2 of 2 // pubkeys. -func genFundingPkScript(aPub, bPub []byte) ([]byte, error) { +func genMultiSigScript(aPub, bPub []byte) ([]byte, error) { if len(aPub) != 33 || len(bPub) != 33 { return nil, fmt.Errorf("Pubkey size error. Compressed pubkeys only") } @@ -52,23 +54,24 @@ func genFundingPkScript(aPub, bPub []byte) ([]byte, error) { return bldr.Script() } -// fundMultiSigOut create the redeemScript for the funding transaction, and -// also a TxOut paying to the p2sh of the multi-sig redeemScript. Give it the -// two pubkeys and it'll give you the p2sh'd txout. You don't have to remember -// the p2sh preimage, as long as you remember the pubkeys involved. -func fundMultiSigOut(aPub, bPub []byte, amt int64) ([]byte, *wire.TxOut, error) { - if amt < 0 { +// genFundingPkScript creates a redeem script, and its matching p2wsh +// output for the funding transaction. +func genFundingPkScript(aPub, bPub []byte, amt int64) ([]byte, *wire.TxOut, error) { + // As a sanity check, ensure that the passed amount is above zero. + if amt <= 0 { return nil, nil, fmt.Errorf("can't create FundTx script with " + - "negative coins") + "zero, or negative coins") } - // p2shify - redeemScript, err := genFundingPkScript(aPub, bPub) + // First, create the 2-of-2 multi-sig script itself. + redeemScript, err := genMultiSigScript(aPub, bPub) if err != nil { return nil, nil, err } - pkScript, err := scriptHashPkScript(redeemScript) + // With the 2-of-2 script in had, generate a p2wsh script which pays + // to the funding script. + pkScript, err := witnessScriptHash(redeemScript) if err != nil { return nil, nil, err } @@ -76,31 +79,31 @@ func fundMultiSigOut(aPub, bPub []byte, amt int64) ([]byte, *wire.TxOut, error) return redeemScript, wire.NewTxOut(amt, pkScript), nil } -// spendMultiSig generates the scriptSig required to redeem the 2-of-2 p2sh +// spendMultiSig generates the witness stack required to redeem the 2-of-2 p2wsh // multi-sig output. -func spendMultiSig(redeemScript, pubA, sigA, pubB, sigB []byte) ([]byte, error) { - bldr := txscript.NewScriptBuilder() +func spendMultiSig(redeemScript, pubA, sigA, pubB, sigB []byte) [][]byte { + witness := make([][]byte, 4) - // add a 0 for some multisig fun - bldr.AddOp(txscript.OP_0) + // When spending a p2wsh multi-sig script, rather than an OP_0, we add + // a nil stack element to eat the extra pop. + witness[0] = nil // When initially generating the redeemScript, we sorted the serialized // public keys in descending order. So we do a quick comparison in order // ensure the signatures appear on the Script Virual Machine stack in // the correct order. if bytes.Compare(pubA, pubB) == -1 { - bldr.AddData(sigB) - bldr.AddData(sigA) + witness[1] = sigB + witness[2] = sigA } else { - bldr.AddData(sigA) - bldr.AddData(sigB) + witness[1] = sigA + witness[2] = sigB } - // preimage goes on AT THE ENDDDD - bldr.AddData(redeemScript) + // Finally, add the pre-image as the last witness element. + witness[3] = redeemScript - // that's all, get bytes - return bldr.Script() + return witness } // findScriptOutputIndex finds the index of the public key script output