// spendNestedWitnessPubKey generates both a sigScript, and valid witness for // spending the passed pkScript with the specified input amount. The generated // sigScript is the version 0 p2wkh witness program corresponding to the queried // key. The witness stack is identical to that of one which spends a regular // p2wkh output. The input amount *must* correspond to the output value of the // previous pkScript, or else verification will fail since the new sighash // digest algorithm defined in BIP0143 includes the input value in the sighash. func spendNestedWitnessPubKeyHash(txIn *wire.TxIn, pkScript []byte, inputValue int64, chainParams *chaincfg.Params, secrets SecretsSource, tx *wire.MsgTx, hashCache *txscript.TxSigHashes, idx int) error { // First we need to obtain the key pair related to this p2sh output. _, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript, chainParams) if err != nil { return err } privKey, compressed, err := secrets.GetKey(addrs[0]) if err != nil { return err } pubKey := privKey.PubKey() var pubKeyHash []byte if compressed { pubKeyHash = btcutil.Hash160(pubKey.SerializeCompressed()) } else { pubKeyHash = btcutil.Hash160(pubKey.SerializeUncompressed()) } // Next, we'll generate a valid sigScript that'll allow us to spend // the p2sh output. The sigScript will contain only a single push of // the p2wkh witness program corresponding to the matching public key // of this address. p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, chainParams) if err != nil { return err } witnessProgram, err := txscript.PayToAddrScript(p2wkhAddr) if err != nil { return err } bldr := txscript.NewScriptBuilder() bldr.AddData(witnessProgram) sigScript, err := bldr.Script() if err != nil { return err } txIn.SignatureScript = sigScript // With the sigScript in place, we'll next generate the proper witness // that'll allow us to spend the p2wkh output. witnessScript, err := txscript.WitnessScript(tx, hashCache, idx, inputValue, witnessProgram, txscript.SigHashAll, privKey, compressed) if err != nil { return err } txIn.Witness = witnessScript return nil }
// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy // based on the passed block height to the provided address. When the address // is nil, the coinbase transaction will instead be redeemable by anyone. // // See the comment for NewBlockTemplate for more information about why the nil // address handling is useful. func createCoinbaseTx(params *chaincfg.Params, coinbaseScript []byte, nextBlockHeight int32, addr btcutil.Address) (*btcutil.Tx, error) { // Create the script to pay to the provided payment address if one was // specified. Otherwise create a script that allows the coinbase to be // redeemable by anyone. var pkScript []byte if addr != nil { var err error pkScript, err = txscript.PayToAddrScript(addr) if err != nil { return nil, err } } else { var err error scriptBuilder := txscript.NewScriptBuilder() pkScript, err = scriptBuilder.AddOp(txscript.OP_TRUE).Script() if err != nil { return nil, err } } tx := wire.NewMsgTx(wire.TxVersion) tx.AddTxIn(&wire.TxIn{ // Coinbase transactions have no inputs, so previous outpoint is // zero hash and max index. PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{}, wire.MaxPrevOutIndex), SignatureScript: coinbaseScript, Sequence: wire.MaxTxInSequenceNum, }) tx.AddTxOut(&wire.TxOut{ Value: blockchain.CalcBlockSubsidy(nextBlockHeight, params), PkScript: pkScript, }) return btcutil.NewTx(tx), nil }
// commitScriptToSelf constructs the public key script for the output on the // commitment transaction paying to the "owner" of said commitment transaction. // If the other party learns of the pre-image to the revocation hash, then they // can claim all the settled funds in the channel, plus the unsettled funds. // // Possible Input Scripts: // REVOKE: <sig> 1 // SENDRSWEEP: <sig> 0 // // Output Script: // OP_IF // <revokeKey> OP_CHECKSIG // OP_ELSE // <timeKey> OP_CHECKSIGVERIFY // <numRelativeBlocks> OP_CHECKSEQUENCEVERIFY // OP_ENDIF func commitScriptToSelf(csvTimeout uint32, selfKey, revokeKey *btcec.PublicKey) ([]byte, error) { // This script is spendable under two conditions: either the 'csvTimeout' // has passed and we can redeem our funds, or they can produce a valid // signature with the revocation public key. The revocation public key // will *only* be known to the other party if we have divulged the // revocation hash, allowing them to homomorphically derive the proper // private key which corresponds to the revoke public key. builder := txscript.NewScriptBuilder() builder.AddOp(txscript.OP_IF) // If a valid signature using the revocation key is presented, then // allow an immediate spend provided the proper signature. builder.AddData(revokeKey.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIG) builder.AddOp(txscript.OP_ELSE) // Otherwise, we can re-claim our funds after a CSV delay of // 'csvTimeout' timeout blocks, and a valid signature. builder.AddData(selfKey.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIGVERIFY) builder.AddInt64(int64(csvTimeout)) builder.AddOp(OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_ENDIF) return builder.Script() }
// commitScriptUnencumbered constructs the public key script on the commitment // transaction paying to the "other" party. The constructed output is a normal // p2wkh output spendable immediately, requiring no contestation period. func commitScriptUnencumbered(key *btcec.PublicKey) ([]byte, error) { // This script goes to the "other" party, and it spendable immediately. builder := txscript.NewScriptBuilder() builder.AddOp(txscript.OP_0) builder.AddData(btcutil.Hash160(key.SerializeCompressed())) return builder.Script() }
// 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(witnessScript []byte) ([]byte, error) { bldr := txscript.NewScriptBuilder() bldr.AddOp(txscript.OP_0) scriptHash := fastsha256.Sum256(witnessScript) bldr.AddData(scriptHash[:]) return bldr.Script() }
// createCSVOutput creates an output paying to a trivially redeemable CSV // pkScript with the specified time-lock. func createCSVOutput(r *rpctest.Harness, t *testing.T, numSatoshis btcutil.Amount, timeLock int32, isSeconds bool) ([]byte, *wire.OutPoint, *wire.MsgTx, error) { // Convert the time-lock to the proper sequence lock based according to // if the lock is seconds or time based. sequenceLock := blockchain.LockTimeToSequence(isSeconds, uint32(timeLock)) // Our CSV script is simply: <sequenceLock> OP_CSV OP_DROP b := txscript.NewScriptBuilder(). AddInt64(int64(sequenceLock)). AddOp(txscript.OP_CHECKSEQUENCEVERIFY). AddOp(txscript.OP_DROP) csvScript, err := b.Script() if err != nil { return nil, nil, nil, err } // Using the script generated above, create a P2SH output which will be // accepted into the mempool. p2shAddr, err := btcutil.NewAddressScriptHash(csvScript, r.ActiveNet) if err != nil { return nil, nil, nil, err } p2shScript, err := txscript.PayToAddrScript(p2shAddr) if err != nil { return nil, nil, nil, err } output := &wire.TxOut{ PkScript: p2shScript, Value: int64(numSatoshis), } // Finally create a valid transaction which creates the output crafted // above. tx, err := r.CreateTransaction([]*wire.TxOut{output}, 10) if err != nil { return nil, nil, nil, err } var outputIndex uint32 if !bytes.Equal(tx.TxOut[0].PkScript, p2shScript) { outputIndex = 1 } utxo := &wire.OutPoint{ Hash: tx.TxHash(), Index: outputIndex, } return csvScript, utxo, tx, nil }
// signMultiSigUTXO signs the P2SH UTXO with the given index by constructing a // script containing all given signatures plus the redeem (multi-sig) script. The // redeem script is obtained by looking up the address of the given P2SH pkScript // on the address manager. // The order of the signatures must match that of the public keys in the multi-sig // script as OP_CHECKMULTISIG expects that. // This function must be called with the manager unlocked. func signMultiSigUTXO(mgr *waddrmgr.Manager, tx *wire.MsgTx, idx int, pkScript []byte, sigs []RawSig) error { class, addresses, _, err := txscript.ExtractPkScriptAddrs(pkScript, mgr.ChainParams()) if err != nil { return newError(ErrTxSigning, "unparseable pkScript", err) } if class != txscript.ScriptHashTy { return newError(ErrTxSigning, fmt.Sprintf("pkScript is not P2SH: %s", class), nil) } redeemScript, err := getRedeemScript(mgr, addresses[0].(*btcutil.AddressScriptHash)) if err != nil { return newError(ErrTxSigning, "unable to retrieve redeem script", err) } class, _, nRequired, err := txscript.ExtractPkScriptAddrs(redeemScript, mgr.ChainParams()) if err != nil { return newError(ErrTxSigning, "unparseable redeem script", err) } if class != txscript.MultiSigTy { return newError(ErrTxSigning, fmt.Sprintf("redeem script is not multi-sig: %v", class), nil) } if len(sigs) < nRequired { errStr := fmt.Sprintf("not enough signatures; need %d but got only %d", nRequired, len(sigs)) return newError(ErrTxSigning, errStr, nil) } // Construct the unlocking script. // Start with an OP_0 because of the bug in bitcoind, then add nRequired signatures. unlockingScript := txscript.NewScriptBuilder().AddOp(txscript.OP_FALSE) for _, sig := range sigs[:nRequired] { unlockingScript.AddData(sig) } // Combine the redeem script and the unlocking script to get the actual signature script. sigScript := unlockingScript.AddData(redeemScript) script, err := sigScript.Script() if err != nil { return newError(ErrTxSigning, "error building sigscript", err) } tx.TxIn[idx].SignatureScript = script if err := validateSigScript(tx, idx, pkScript); err != nil { return err } return nil }
// genMultiSigScript generates the non-p2sh'd multisig script for 2 of 2 // pubkeys. func genMultiSigScript(aPub, bPub []byte) ([]byte, error) { if len(aPub) != 33 || len(bPub) != 33 { return nil, fmt.Errorf("Pubkey size error. Compressed pubkeys only") } // Swap to sort pubkeys if needed. Keys are sorted in lexicographical // order. The signatures within the scriptSig must also adhere to the // order, ensuring that the signatures for each public key appears // in the proper order on the stack. if bytes.Compare(aPub, bPub) == -1 { aPub, bPub = bPub, aPub } bldr := txscript.NewScriptBuilder() bldr.AddOp(txscript.OP_2) bldr.AddData(aPub) // Add both pubkeys (sorted). bldr.AddData(bPub) bldr.AddOp(txscript.OP_2) bldr.AddOp(txscript.OP_CHECKMULTISIG) return bldr.Script() }
// spendCSVOutput spends an output previously created by the createCSVOutput // function. The sigScript is a trivial push of OP_TRUE followed by the // redeemScript to pass P2SH evaluation. func spendCSVOutput(redeemScript []byte, csvUTXO *wire.OutPoint, sequence uint32, targetOutput *wire.TxOut, txVersion int32) (*wire.MsgTx, error) { tx := wire.NewMsgTx(txVersion) tx.AddTxIn(&wire.TxIn{ PreviousOutPoint: *csvUTXO, Sequence: sequence, }) tx.AddTxOut(targetOutput) b := txscript.NewScriptBuilder(). AddOp(txscript.OP_TRUE). AddData(redeemScript) sigScript, err := b.Script() if err != nil { return nil, err } tx.TxIn[0].SignatureScript = sigScript return tx, nil }
func testListTransactionDetails(miner *rpctest.Harness, wallet *lnwallet.LightningWallet, t *testing.T) { t.Log("Running list transaction details test") // Create 5 new outputs spendable by the wallet. const numTxns = 5 const outputAmt = btcutil.SatoshiPerBitcoin txids := make(map[wire.ShaHash]struct{}) for i := 0; i < numTxns; i++ { addr, err := wallet.NewAddress(lnwallet.WitnessPubKey, false) if err != nil { t.Fatalf("unable to create new address: %v", err) } script, err := txscript.PayToAddrScript(addr) if err != nil { t.Fatalf("unable to create output script: %v", err) } output := &wire.TxOut{outputAmt, script} txid, err := miner.CoinbaseSpend([]*wire.TxOut{output}) if err != nil { t.Fatalf("unable to send coinbase: %v", err) } txids[*txid] = struct{}{} } // Generate 10 blocks to mine all the transactions created above. const numBlocksMined = 10 blocks, err := miner.Node.Generate(numBlocksMined) if err != nil { t.Fatalf("unable to mine blocks: %v", err) } // Next, fetch all the current transaction details. // TODO(roasbeef): use ntfn client here instead? time.Sleep(time.Second * 2) txDetails, err := wallet.ListTransactionDetails() if err != nil { t.Fatalf("unable to fetch tx details: %v", err) } // Each of the transactions created above should be found with the // proper details populated. for _, txDetail := range txDetails { if _, ok := txids[txDetail.Hash]; !ok { continue } if txDetail.NumConfirmations != numBlocksMined { t.Fatalf("num confs incorrect, got %v expected %v", txDetail.NumConfirmations, numBlocksMined) } if txDetail.Value != outputAmt { t.Fatalf("tx value incorrect, got %v expected %v", txDetail.Value, outputAmt) } if !bytes.Equal(txDetail.BlockHash[:], blocks[0][:]) { t.Fatalf("block hash mismatch, got %v expected %v", txDetail.BlockHash, blocks[0]) } delete(txids, txDetail.Hash) } if len(txids) != 0 { t.Fatalf("all transactions not found in details!") } // Next create a transaction paying to an output which isn't under the // wallet's control. b := txscript.NewScriptBuilder() b.AddOp(txscript.OP_0) outputScript, err := b.Script() if err != nil { t.Fatalf("unable to make output script: %v", err) } burnOutput := wire.NewTxOut(outputAmt, outputScript) burnTXID, err := wallet.SendOutputs([]*wire.TxOut{burnOutput}) if err != nil { t.Fatalf("unable to create burn tx: %v", err) } burnBlock, err := miner.Node.Generate(1) if err != nil { t.Fatalf("unable to mine block: %v", err) } // Fetch the transaction details again, the new transaction should be // shown as debiting from the wallet's balance. time.Sleep(time.Second * 2) txDetails, err = wallet.ListTransactionDetails() if err != nil { t.Fatalf("unable to fetch tx details: %v", err) } var burnTxFound bool for _, txDetail := range txDetails { if !bytes.Equal(txDetail.Hash[:], burnTXID[:]) { continue } burnTxFound = true if txDetail.NumConfirmations != 1 { t.Fatalf("num confs incorrect, got %v expected %v", txDetail.NumConfirmations, 1) } if txDetail.Value >= -outputAmt { t.Fatalf("tx value incorrect, got %v expected %v", txDetail.Value, -outputAmt) } if !bytes.Equal(txDetail.BlockHash[:], burnBlock[0][:]) { t.Fatalf("block hash mismatch, got %v expected %v", txDetail.BlockHash, burnBlock[0]) } } if !burnTxFound { t.Fatalf("tx burning btc not found") } }
// standardCoinbaseScript returns a standard script suitable for use as the // signature script of the coinbase transaction of a new block. In particular, // it starts with the block height that is required by version 2 blocks and adds // the extra nonce as well as additional coinbase flags. func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, error) { return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)). AddInt64(int64(extraNonce)).AddData([]byte(CoinbaseFlags)). Script() }
// TestCheckPkScriptStandard tests the checkPkScriptStandard API. func TestCheckPkScriptStandard(t *testing.T) { var pubKeys [][]byte for i := 0; i < 4; i++ { pk, err := btcec.NewPrivateKey(btcec.S256()) if err != nil { t.Fatalf("TestCheckPkScriptStandard NewPrivateKey failed: %v", err) return } pubKeys = append(pubKeys, pk.PubKey().SerializeCompressed()) } tests := []struct { name string // test description. script *txscript.ScriptBuilder isStandard bool }{ { "key1 and key2", txscript.NewScriptBuilder().AddOp(txscript.OP_2). AddData(pubKeys[0]).AddData(pubKeys[1]). AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG), true, }, { "key1 or key2", txscript.NewScriptBuilder().AddOp(txscript.OP_1). AddData(pubKeys[0]).AddData(pubKeys[1]). AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG), true, }, { "escrow", txscript.NewScriptBuilder().AddOp(txscript.OP_2). AddData(pubKeys[0]).AddData(pubKeys[1]). AddData(pubKeys[2]). AddOp(txscript.OP_3).AddOp(txscript.OP_CHECKMULTISIG), true, }, { "one of four", txscript.NewScriptBuilder().AddOp(txscript.OP_1). AddData(pubKeys[0]).AddData(pubKeys[1]). AddData(pubKeys[2]).AddData(pubKeys[3]). AddOp(txscript.OP_4).AddOp(txscript.OP_CHECKMULTISIG), false, }, { "malformed1", txscript.NewScriptBuilder().AddOp(txscript.OP_3). AddData(pubKeys[0]).AddData(pubKeys[1]). AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG), false, }, { "malformed2", txscript.NewScriptBuilder().AddOp(txscript.OP_2). AddData(pubKeys[0]).AddData(pubKeys[1]). AddOp(txscript.OP_3).AddOp(txscript.OP_CHECKMULTISIG), false, }, { "malformed3", txscript.NewScriptBuilder().AddOp(txscript.OP_0). AddData(pubKeys[0]).AddData(pubKeys[1]). AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG), false, }, { "malformed4", txscript.NewScriptBuilder().AddOp(txscript.OP_1). AddData(pubKeys[0]).AddData(pubKeys[1]). AddOp(txscript.OP_0).AddOp(txscript.OP_CHECKMULTISIG), false, }, { "malformed5", txscript.NewScriptBuilder().AddOp(txscript.OP_1). AddData(pubKeys[0]).AddData(pubKeys[1]). AddOp(txscript.OP_CHECKMULTISIG), false, }, { "malformed6", txscript.NewScriptBuilder().AddOp(txscript.OP_1). AddData(pubKeys[0]).AddData(pubKeys[1]), false, }, } for _, test := range tests { script, err := test.script.Script() if err != nil { t.Fatalf("TestCheckPkScriptStandard test '%s' "+ "failed: %v", test.name, err) continue } scriptClass := txscript.GetScriptClass(script) got := checkPkScriptStandard(script, scriptClass) if (test.isStandard && got != nil) || (!test.isStandard && got == nil) { t.Fatalf("TestCheckPkScriptStandard test '%s' failed", test.name) return } } }
// receiverHTLCScript constructs the public key script for an incoming HTLC // output payment for the receiver's version of the commitment transaction: // // Possible Input Scripts: // RECVR: <sig> <preimage> 1 // REVOK: <sig> <preimage> 1 0 // SENDR: <sig> 0 0 // // OP_IF // //Receiver // OP_SIZE 32 OP_EQUALVERIFY // OP_SHA256 // <payment hash> OP_EQUALVERIFY // <relative blockheight> OP_CHECKSEQUENCEVERIFY OP_DROP // <receiver key> OP_CHECKSIG // OP_ELSE // //Sender // OP_IF // //Revocation // OP_SHA256 // <revoke hash> OP_EQUALVERIFY // OP_ELSE // //Refund // <absolute blockehight> OP_CHECKLOCKTIMEVERIFY OP_DROP // OP_ENDIF // <sender key> OP_CHECKSIG // OP_ENDIF // TODO(roasbeef): go back to revocation keys in the HTLC outputs? // * also could combine pre-image with their key? func receiverHTLCScript(absoluteTimeout, relativeTimeout uint32, senderKey, receiverKey *btcec.PublicKey, revokeHash, paymentHash []byte) ([]byte, error) { builder := txscript.NewScriptBuilder() // The receiver of the script will place a 1 as the first item of the // witness stack forcing Script execution to enter the "if" clause of // the main body of the script. builder.AddOp(txscript.OP_IF) // In this clause, the receiver can redeem the HTLC after a relative timeout. // This added delay gives the sender (at this time) an opportunity to // re-claim the pending HTLC in the event that the receiver // (at this time) broadcasts this old commitment transaction after it // has been revoked. Additionally, we require that the pre-image is // exactly 32-bytes in order to avoid undesirable redemption // asymmerties in the multi-hop scenario. builder.AddOp(txscript.OP_SIZE) builder.AddInt64(32) builder.AddOp(txscript.OP_EQUALVERIFY) builder.AddOp(txscript.OP_SHA256) builder.AddData(paymentHash) builder.AddOp(txscript.OP_EQUALVERIFY) builder.AddInt64(int64(relativeTimeout)) builder.AddOp(OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) builder.AddData(receiverKey.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIG) // Otherwise, the sender will place a 0 as the first item of the // witness stack forcing exeuction to enter the "else" clause of the // main body of the script. builder.AddOp(txscript.OP_ELSE) // The sender will place a 1 as the second item of the witness stack // in the scenario that the receiver broadcasts an invalidated // commitment transaction, allowing the sender to sweep all the // receiver's funds. builder.AddOp(txscript.OP_IF) builder.AddOp(txscript.OP_SHA256) builder.AddData(revokeHash) builder.AddOp(txscript.OP_EQUALVERIFY) // If not, then the sender needs to wait for the HTLC timeout. This // clause may be executed if the receiver fails to present the r-value // in time. This prevents the pending funds from being locked up // indefinately. // The sender will place a 0 as the second item of the witness stack if // they wish to sweep the HTLC after an absolute refund timeout. This // time out clause prevents the pending funds from being locked up // indefinately. builder.AddOp(txscript.OP_ELSE) builder.AddInt64(int64(absoluteTimeout)) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) builder.AddOp(txscript.OP_DROP) builder.AddOp(txscript.OP_ENDIF) // In either case, we also require a valid signature with the sender's // commitment private key. builder.AddData(senderKey.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIG) builder.AddOp(txscript.OP_ENDIF) return builder.Script() }
// senderHTLCScript constructs the public key script for an outgoing HTLC // output payment for the sender's version of the commitment transaction: // // Possible Input Scripts: // SENDR: <sig> 0 // RECVR: <sig> <preimage> 0 1 // REVOK: <sig <preimage> 1 1 // * receiver revoke // // OP_IF // //Receiver // OP_IF // //Revoke // <revocation hash> // OP_ELSE // //Receive // OP_SIZE 32 OP_EQUALVERIFY // <payment hash> // OP_ENDIF // OP_SWAP // OP_SHA256 OP_EQUALVERIFY // <recv key> OP_CHECKSIG // OP_ELSE // //Sender // <absolute blockheight> OP_CHECKLOCKTIMEVERIFY // <relative blockheight> OP_CHECKSEQUENCEVERIFY // OP_2DROP // <sendr key> OP_CHECKSIG // OP_ENDIF func senderHTLCScript(absoluteTimeout, relativeTimeout uint32, senderKey, receiverKey *btcec.PublicKey, revokeHash, paymentHash []byte) ([]byte, error) { builder := txscript.NewScriptBuilder() // The receiver of the HTLC places a 1 as the first item in the witness // stack, forcing Script execution to enter the "if" clause within the // main body of the script. builder.AddOp(txscript.OP_IF) // The receiver will place a 1 as the second item of the witness stack // in the case the sender broadcasts a revoked commitment transaction. // Executing this branch allows the receiver to claim the sender's // funds as a result of their contract violation. builder.AddOp(txscript.OP_IF) builder.AddData(revokeHash) // Alternatively, the receiver can place a 0 as the second item of the // witness stack if they wish to claim the HTLC with the proper // pre-image as normal. In order to prevent an over-sized pre-image // attack (which can create undesirable redemption asymmerties), we // strongly require that all HTLC pre-images are exactly 32 bytes. builder.AddOp(txscript.OP_ELSE) builder.AddOp(txscript.OP_SIZE) builder.AddInt64(32) builder.AddOp(txscript.OP_EQUALVERIFY) builder.AddData(paymentHash) builder.AddOp(txscript.OP_ENDIF) builder.AddOp(txscript.OP_SWAP) builder.AddOp(txscript.OP_SHA256) builder.AddOp(txscript.OP_EQUALVERIFY) // In either case, we require a valid signature by the receiver. builder.AddData(receiverKey.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIG) // Otherwise, the sender of the HTLC will place a 0 as the first item // of the witness stack in order to sweep the funds back after the HTLC // times out. builder.AddOp(txscript.OP_ELSE) // In this case, the sender will need to wait for an absolute HTLC // timeout, then afterwards a relative timeout before we claim re-claim // the unsettled funds. This delay gives the other party a chance to // present the pre-image to the revocation hash in the event that the // sender (at this time) broadcasts this commitment transaction after // it has been revoked. builder.AddInt64(int64(absoluteTimeout)) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) builder.AddInt64(int64(relativeTimeout)) builder.AddOp(OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_2DROP) builder.AddData(senderKey.SerializeCompressed()) builder.AddOp(txscript.OP_CHECKSIG) builder.AddOp(txscript.OP_ENDIF) return builder.Script() }
// ComputeInputScript generates a complete InputIndex for the passed // transaction with the signature as defined within the passed SignDescriptor. // This method is capable of generating the proper input script for both // regular p2wkh output and p2wkh outputs nested within a regular p2sh output. // // This is a part of the WalletController interface. func (b *BtcWallet) ComputeInputScript(tx *wire.MsgTx, signDesc *lnwallet.SignDescriptor) (*lnwallet.InputScript, error) { outputScript := signDesc.Output.PkScript walletAddr, err := b.fetchOutputAddr(outputScript) if err != nil { return nil, nil } pka := walletAddr.(waddrmgr.ManagedPubKeyAddress) privKey, err := pka.PrivKey() if err != nil { return nil, err } var witnessProgram []byte inputScript := &lnwallet.InputScript{} // If we're spending p2wkh output nested within a p2sh output, then // we'll need to attach a sigScript in addition to witness data. switch { case pka.IsNestedWitness(): pubKey := privKey.PubKey() pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) // Next, we'll generate a valid sigScript that'll allow us to // spend the p2sh output. The sigScript will contain only a // single push of the p2wkh witness program corresponding to // the matching public key of this address. p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, b.netParams) if err != nil { return nil, err } witnessProgram, err = txscript.PayToAddrScript(p2wkhAddr) if err != nil { return nil, err } bldr := txscript.NewScriptBuilder() bldr.AddData(witnessProgram) sigScript, err := bldr.Script() if err != nil { return nil, err } inputScript.ScriptSig = sigScript // Otherwise, this is a regular p2wkh output, so we include the // witness program itself as the subscript to generate the proper // sighash digest. As part of the new sighash digest algorithm, the // p2wkh witness program will be expanded into a regular p2kh // script. default: witnessProgram = outputScript } // Generate a valid witness stack for the input. // TODO(roasbeef): adhere to passed HashType witnessScript, err := txscript.WitnessScript(tx, signDesc.SigHashes, signDesc.InputIndex, signDesc.Output.Value, witnessProgram, txscript.SigHashAll, privKey, true) if err != nil { return nil, err } inputScript.Witness = witnessScript return inputScript, nil }