// VerifyNewCommitmentSigs... 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 // 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. 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 } // 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 vm, err := txscript.NewEngine(c.lnChannel.fundingP2SH, commitTx, 0, txscript.StandardVerifyFlags, nil) if err != nil { return err } return vm.Execute() }
func main() { if len(os.Args) < 2 { log.Println("Not enough arguments") log.Println("Usage:", os.Args[0], " FILE") os.Exit(0) } log.SetFlags(log.LstdFlags | log.Lshortfile) file, err := os.Open(os.Args[1]) if err != nil { log.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) i := 0 lines := make([]string, 5) for scanner.Scan() { lines[i] = scanner.Text() i += 1 } if err := scanner.Err(); err != nil { log.Fatal(err) } scriptPubkey, err := hex.DecodeString(lines[0]) if err != nil { log.Fatal(err) } txToBytes, err := hex.DecodeString(lines[1]) if err != nil { log.Fatal(err) } txTo, err := btcutil.NewTxFromBytes(txToBytes) if err != nil { log.Fatal(err) } txToMsg := txTo.MsgTx() nIn, err := strconv.Atoi(lines[2]) if err != nil { log.Fatal(err) } script, err := txscript.NewEngine(scriptPubkey, txToMsg, int(nIn), 0) err = script.Execute() if err != nil { fmt.Println("0") fmt.Println(err.Error()) } else { fmt.Println("1") } }
// validateSigScripts executes the signature script of the tx input with the // given index, returning an error if it fails. func validateSigScript(msgtx *wire.MsgTx, idx int, pkScript []byte) error { vm, err := txscript.NewEngine(pkScript, msgtx, idx, txscript.StandardVerifyFlags, nil) if err != nil { return newError(ErrTxSigning, "cannot create script engine", err) } if err = vm.Execute(); err != nil { return newError(ErrTxSigning, "cannot validate tx signature", err) } return nil }
func validateMsgTx(msgtx *wire.MsgTx, prevOutputs []wtxmgr.Credit) error { for i := range msgtx.TxIn { vm, err := txscript.NewEngine(prevOutputs[i].PkScript, msgtx, i, txscript.StandardVerifyFlags, nil) if err != nil { return fmt.Errorf("cannot create script engine: %s", err) } if err = vm.Execute(); err != nil { return fmt.Errorf("cannot validate transaction: %s", err) } } return nil }
// validateMsgTx verifies transaction input scripts for tx. All previous output // scripts from outputs redeemed by the transaction, in the same order they are // spent, must be passed in the prevScripts slice. func validateMsgTx(tx *wire.MsgTx, prevScripts [][]byte) error { for i, prevScript := range prevScripts { vm, err := txscript.NewEngine(prevScript, tx, i, txscript.StandardVerifyFlags, nil) if err != nil { return fmt.Errorf("cannot create script engine: %s", err) } err = vm.Execute() if err != nil { return fmt.Errorf("cannot validate transaction: %s", err) } } return nil }
// TestInvalidFlagCombinations ensures the script engine returns the expected // error when disallowed flag combinations are specified. func TestInvalidFlagCombinations(t *testing.T) { t.Parallel() tests := []txscript.ScriptFlags{ txscript.ScriptVerifyCleanStack, } // tx with almost empty scripts. tx := &wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{ { PreviousOutPoint: wire.OutPoint{ Hash: wire.ShaHash([32]byte{ 0xc9, 0x97, 0xa5, 0xe5, 0x6e, 0x10, 0x41, 0x02, 0xfa, 0x20, 0x9c, 0x6a, 0x85, 0x2d, 0xd9, 0x06, 0x60, 0xa2, 0x0b, 0x2d, 0x9c, 0x35, 0x24, 0x23, 0xed, 0xce, 0x25, 0x85, 0x7f, 0xcd, 0x37, 0x04, }), Index: 0, }, SignatureScript: []uint8{txscript.OP_NOP}, Sequence: 4294967295, }, }, TxOut: []*wire.TxOut{ { Value: 1000000000, PkScript: nil, }, }, LockTime: 0, } pkScript := []byte{txscript.OP_NOP} for i, test := range tests { _, err := txscript.NewEngine(pkScript, tx, 0, test) if err != txscript.ErrInvalidFlags { t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+ "error: %v", i, err) } } }
func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byte) error { tx.TxIn[idx].SignatureScript = sigScript vm, err := txscript.NewEngine(pkScript, tx, idx, txscript.ScriptBip16|txscript.ScriptVerifyDERSignatures, nil) if err != nil { return fmt.Errorf("failed to make script engine for %s: %v", msg, err) } err = vm.Execute() if err != nil { return fmt.Errorf("invalid script signature for %s: %v", msg, err) } return nil }
func testBasicWalletReservationWorkFlow(lnwallet *LightningWallet, t *testing.T) { // Create our test wallet, will have a total of 20 BTC available for bobNode, err := newBobNode() if err != nil { t.Fatalf("unable to create bob node: %v", err) } // Bob initiates a channel funded with 5 BTC for each side, so 10 // BTC total. He also generates 2 BTC in change. fundingAmount := btcutil.Amount(5 * 1e8) chanReservation, err := lnwallet.InitChannelReservation(fundingAmount, SIGHASH, bobNode.id, 4) if err != nil { t.Fatalf("unable to initialize funding reservation: %v", err) } // The channel reservation should now be populated with a multi-sig key // from our HD chain, a change output with 3 BTC, and 2 outputs selected // of 4 BTC each. Additionally, the rest of the items needed to fufill a // funding contribution should also have been filled in. ourContribution := chanReservation.OurContribution() if len(ourContribution.Inputs) != 2 { t.Fatalf("outputs for funding tx not properly selected, have %v "+ "outputs should have 2", len(ourContribution.Inputs)) } if ourContribution.ChangeOutputs[0].Value != 3e8 { t.Fatalf("coin selection failed, change output should be 3e8 "+ "satoshis, is instead %v", ourContribution.ChangeOutputs[0].Value) } if ourContribution.MultiSigKey == nil { t.Fatalf("alice's key for multi-sig not found") } if ourContribution.CommitKey == nil { t.Fatalf("alice's key for commit not found") } if ourContribution.DeliveryAddress == nil { t.Fatalf("alice's final delivery address not found") } if bytes.Equal(ourContribution.RevocationHash[:], zeroHash) { t.Fatalf("alice's revocation hash not found") } if ourContribution.CsvDelay == 0 { t.Fatalf("csv delay not set") } // Bob sends over his output, change addr, pub keys, initial revocation, // final delivery address, and his accepted csv delay for the commitmen // t transactions. if err := chanReservation.ProcessContribution(bobNode.Contribution()); err != nil { t.Fatalf("unable to add bob's funds to the funding tx: %v", err) } // At this point, the reservation should have our signatures, and a // partial funding transaction (missing bob's sigs). theirContribution := chanReservation.TheirContribution() ourFundingSigs, ourCommitSig := chanReservation.OurSignatures() if len(ourFundingSigs) != 2 { t.Fatalf("only %v of our sigs present, should have 2", len(ourFundingSigs)) } if ourCommitSig == nil { t.Fatalf("commitment sig not found") } // Additionally, the funding tx should have been populated. if chanReservation.partialState.FundingTx == nil { t.Fatalf("funding transaction never created!") } // Their funds should also be filled in. if len(theirContribution.Inputs) != 1 { t.Fatalf("bob's outputs for funding tx not properly selected, have %v "+ "outputs should have 2", len(theirContribution.Inputs)) } if theirContribution.ChangeOutputs[0].Value != 2e8 { t.Fatalf("bob should have one change output with value 2e8"+ "satoshis, is instead %v", theirContribution.ChangeOutputs[0].Value) } if theirContribution.MultiSigKey == nil { t.Fatalf("bob's key for multi-sig not found") } if theirContribution.CommitKey == nil { t.Fatalf("bob's key for commit tx not found") } if theirContribution.DeliveryAddress == nil { t.Fatalf("bob's final delivery address not found") } if bytes.Equal(theirContribution.RevocationHash[:], zeroHash) { t.Fatalf("bob's revocaiton hash not found") } // Alice responds with her output, change addr, multi-sig key and signatures. // Bob then responds with his signatures. bobsSigs, err := bobNode.signFundingTx(chanReservation.partialState.FundingTx) if err != nil { t.Fatalf("unable to sign inputs for bob: %v", err) } commitSig, err := bobNode.signCommitTx( chanReservation.partialState.OurCommitTx, chanReservation.partialState.FundingRedeemScript) if err != nil { t.Fatalf("bob is unable to sign alice's commit tx: %v", err) } if err := chanReservation.CompleteReservation(bobsSigs, commitSig); err != nil { t.Fatalf("unable to complete funding tx: %v", err) } // At this point, the channel can be considered "open" when the funding // txn hits a "comfortable" depth. fundingTx := chanReservation.FinalFundingTx() // The resulting active channel state should have been persisted to the DB. channel, err := lnwallet.ChannelDB.FetchOpenChannel(bobNode.id) if err != nil { t.Fatalf("unable to retrieve channel from DB: %v", err) } if channel.FundingTx.TxSha() != fundingTx.TxSha() { t.Fatalf("channel state not properly saved") } // The funding tx should now be valid and complete. // Check each input and ensure all scripts are fully valid. // TODO(roasbeef): remove this loop after nodetest hooked up. var zeroHash wire.ShaHash for i, input := range fundingTx.TxIn { var pkscript []byte // Bob's txin if bytes.Equal(input.PreviousOutPoint.Hash.Bytes(), zeroHash.Bytes()) { pkscript = bobNode.changeOutputs[0].PkScript } else { // Does the wallet know about the txin? txDetail, err := lnwallet.TxStore.TxDetails(&input.PreviousOutPoint.Hash) if txDetail == nil || err != nil { t.Fatalf("txstore can't find tx detail, err: %v", err) } prevIndex := input.PreviousOutPoint.Index pkscript = txDetail.TxRecord.MsgTx.TxOut[prevIndex].PkScript } vm, err := txscript.NewEngine(pkscript, fundingTx, i, txscript.StandardVerifyFlags, nil) if err != nil { // TODO(roasbeef): cancel at this stage if invalid sigs? t.Fatalf("cannot create script engine: %s", err) } if err = vm.Execute(); err != nil { t.Fatalf("cannot validate transaction: %s", err) } } }
// validateHandler consumes items to validate from the internal validate channel // and returns the result of the validation on the internal result channel. It // must be run as a goroutine. func (v *txValidator) validateHandler() { out: for { select { case txVI := <-v.validateChan: // Ensure the referenced input transaction is available. txIn := txVI.txIn originTxHash := &txIn.PreviousOutPoint.Hash originTxIndex := txIn.PreviousOutPoint.Index txEntry := v.utxoView.LookupEntry(originTxHash) if txEntry == nil { str := fmt.Sprintf("unable to find input "+ "transaction %v referenced from "+ "transaction %v", originTxHash, txVI.tx.Hash()) err := ruleError(ErrMissingTx, str) v.sendResult(err) break out } // Ensure the referenced input transaction public key // script is available. pkScript := txEntry.PkScriptByIndex(originTxIndex) if pkScript == nil { str := fmt.Sprintf("unable to find unspent "+ "output %v script referenced from "+ "transaction %s:%d", txIn.PreviousOutPoint, txVI.tx.Hash(), txVI.txInIndex) err := ruleError(ErrBadTxInput, str) v.sendResult(err) break out } // Create a new script engine for the script pair. sigScript := txIn.SignatureScript vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(), txVI.txInIndex, v.flags, v.sigCache) if err != nil { str := fmt.Sprintf("failed to parse input "+ "%s:%d which references output %s:%d - "+ "%v (input script bytes %x, prev output "+ "script bytes %x)", txVI.tx.Hash(), txVI.txInIndex, originTxHash, originTxIndex, err, sigScript, pkScript) err := ruleError(ErrScriptMalformed, str) v.sendResult(err) break out } // Execute the script pair. if err := vm.Execute(); err != nil { str := fmt.Sprintf("failed to validate input "+ "%s:%d which references output %s:%d - "+ "%v (input script bytes %x, prev output "+ "script bytes %x)", txVI.tx.Hash(), txVI.txInIndex, originTxHash, originTxIndex, err, sigScript, pkScript) err := ruleError(ErrScriptValidation, str) v.sendResult(err) break out } // Validation succeeded. v.sendResult(nil) case <-v.quitChan: break out } } }
// Test the sigscript generation for valid and invalid inputs, all // hashTypes, and with and without compression. This test creates // sigscripts to spend fake coinbase inputs, as sigscripts cannot be // created for the MsgTxs in txTests, since they come from the blockchain // and we don't have the private keys. func TestSignatureScript(t *testing.T) { t.Parallel() privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyD) nexttest: for i := range sigScriptTests { tx := wire.NewMsgTx() output := wire.NewTxOut(500, []byte{txscript.OP_RETURN}) tx.AddTxOut(output) for range sigScriptTests[i].inputs { txin := wire.NewTxIn(coinbaseOutPoint, nil) tx.AddTxIn(txin) } var script []byte var err error for j := range tx.TxIn { var idx int if sigScriptTests[i].inputs[j].indexOutOfRange { t.Errorf("at test %v", sigScriptTests[i].name) idx = len(sigScriptTests[i].inputs) } else { idx = j } script, err = txscript.SignatureScript(tx, idx, sigScriptTests[i].inputs[j].txout.PkScript, sigScriptTests[i].hashType, privKey, sigScriptTests[i].compress) if (err == nil) != sigScriptTests[i].inputs[j].sigscriptGenerates { if err == nil { t.Errorf("passed test '%v' incorrectly", sigScriptTests[i].name) } else { t.Errorf("failed test '%v': %v", sigScriptTests[i].name, err) } continue nexttest } if !sigScriptTests[i].inputs[j].sigscriptGenerates { // done with this test continue nexttest } tx.TxIn[j].SignatureScript = script } // If testing using a correct sigscript but for an incorrect // index, use last input script for first input. Requires > 0 // inputs for test. if sigScriptTests[i].scriptAtWrongIndex { tx.TxIn[0].SignatureScript = script sigScriptTests[i].inputs[0].inputValidates = false } // Validate tx input scripts scriptFlags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures for j := range tx.TxIn { vm, err := txscript.NewEngine(sigScriptTests[i]. inputs[j].txout.PkScript, tx, j, scriptFlags, nil) if err != nil { t.Errorf("cannot create script vm for test %v: %v", sigScriptTests[i].name, err) continue nexttest } err = vm.Execute() if (err == nil) != sigScriptTests[i].inputs[j].inputValidates { if err == nil { t.Errorf("passed test '%v' validation incorrectly: %v", sigScriptTests[i].name, err) } else { t.Errorf("failed test '%v' validation: %v", sigScriptTests[i].name, err) } continue nexttest } } } }
// TestCheckErrorCondition tests the execute early test in CheckErrorCondition() // since most code paths are tested elsewhere. func TestCheckErrorCondition(t *testing.T) { t.Parallel() // tx with almost empty scripts. tx := &wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{ { PreviousOutPoint: wire.OutPoint{ Hash: wire.ShaHash([32]byte{ 0xc9, 0x97, 0xa5, 0xe5, 0x6e, 0x10, 0x41, 0x02, 0xfa, 0x20, 0x9c, 0x6a, 0x85, 0x2d, 0xd9, 0x06, 0x60, 0xa2, 0x0b, 0x2d, 0x9c, 0x35, 0x24, 0x23, 0xed, 0xce, 0x25, 0x85, 0x7f, 0xcd, 0x37, 0x04, }), Index: 0, }, SignatureScript: []uint8{}, Sequence: 4294967295, }, }, TxOut: []*wire.TxOut{ { Value: 1000000000, PkScript: nil, }, }, LockTime: 0, } pkScript := []byte{ txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_TRUE, } vm, err := txscript.NewEngine(pkScript, tx, 0, 0) if err != nil { t.Errorf("failed to create script: %v", err) } for i := 0; i < len(pkScript)-1; i++ { done, err := vm.Step() if err != nil { t.Errorf("failed to step %dth time: %v", i, err) return } if done { t.Errorf("finshed early on %dth time", i) return } err = vm.CheckErrorCondition(false) if err != txscript.ErrStackScriptUnfinished { t.Errorf("got unexepected error %v on %dth iteration", err, i) return } } done, err := vm.Step() if err != nil { t.Errorf("final step failed %v", err) return } if !done { t.Errorf("final step isn't done!") return } err = vm.CheckErrorCondition(false) if err != nil { t.Errorf("unexpected error %v on final check", err) } }
// TestBadPC sets the pc to a deliberately bad result then confirms that Step() // and Disasm fail correctly. func TestBadPC(t *testing.T) { t.Parallel() type pcTest struct { script, off int } pcTests := []pcTest{ { script: 2, off: 0, }, { script: 0, off: 2, }, } // tx with almost empty scripts. tx := &wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{ { PreviousOutPoint: wire.OutPoint{ Hash: wire.ShaHash([32]byte{ 0xc9, 0x97, 0xa5, 0xe5, 0x6e, 0x10, 0x41, 0x02, 0xfa, 0x20, 0x9c, 0x6a, 0x85, 0x2d, 0xd9, 0x06, 0x60, 0xa2, 0x0b, 0x2d, 0x9c, 0x35, 0x24, 0x23, 0xed, 0xce, 0x25, 0x85, 0x7f, 0xcd, 0x37, 0x04, }), Index: 0, }, SignatureScript: []uint8{txscript.OP_NOP}, Sequence: 4294967295, }, }, TxOut: []*wire.TxOut{ { Value: 1000000000, PkScript: nil, }, }, LockTime: 0, } pkScript := []byte{txscript.OP_NOP} for _, test := range pcTests { vm, err := txscript.NewEngine(pkScript, tx, 0, 0) if err != nil { t.Errorf("Failed to create script: %v", err) } // set to after all scripts vm.TstSetPC(test.script, test.off) _, err = vm.Step() if err == nil { t.Errorf("Step with invalid pc (%v) succeeds!", test) continue } _, err = vm.DisasmPC() if err == nil { t.Errorf("DisasmPC with invalid pc (%v) succeeds!", test) } } }
// handleFundingCounterPartySigs is the final step in the channel reservation // workflow. During this setp, we validate *all* the received signatures for // inputs to the funding transaction. If any of these are invalid, we bail, // and forcibly cancel this funding request. Additionally, we ensure that the // signature we received from the counterparty for our version of the commitment // transaction allows us to spend from the funding output with the addition of // our signature. func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigsMsg) { l.limboMtx.RLock() pendingReservation, ok := l.fundingLimbo[msg.pendingFundingID] l.limboMtx.RUnlock() if !ok { msg.err <- fmt.Errorf("attempted to update non-existant funding state") return } // Grab the mutex on the ChannelReservation to ensure thead-safety pendingReservation.Lock() defer pendingReservation.Unlock() // Now we can complete the funding transaction by adding their // signatures to their inputs. pendingReservation.theirFundingSigs = msg.theirFundingSigs fundingTx := pendingReservation.partialState.FundingTx for i, txin := range fundingTx.TxIn { if txin.SignatureScript == nil { // Fetch the alleged previous output along with the // pkscript referenced by this input. prevOut := txin.PreviousOutPoint output, err := l.rpc.GetTxOut(&prevOut.Hash, prevOut.Index, false) if output == nil { // TODO(roasbeef): do this at the start to avoid wasting out time? // 8 or a set of nodes "we" run with exposed unauthenticated RPC? msg.err <- fmt.Errorf("input to funding tx does not exist: %v", err) return } pkscript, err := hex.DecodeString(output.ScriptPubKey.Hex) if err != nil { msg.err <- err return } // Ensure that the signature is valid. vm, err := txscript.NewEngine(pkscript, fundingTx, i, txscript.StandardVerifyFlags, nil) if err != nil { // TODO(roasbeef): cancel at this stage if invalid sigs? msg.err <- fmt.Errorf("cannot create script engine: %s", err) return } if err = vm.Execute(); err != nil { msg.err <- fmt.Errorf("cannot validate transaction: %s", err) return } txin.SignatureScript = pendingReservation.theirFundingSigs[i] } } // At this point, we can also record and verify their signature for our // commitment transaction. pendingReservation.theirCommitmentSig = msg.theirCommitmentSig commitTx := pendingReservation.partialState.OurCommitTx theirKey := pendingReservation.theirContribution.MultiSigKey ourKey := pendingReservation.partialState.MultiSigKey // Re-generate both the redeemScript and p2sh output. We sign the // redeemScript script, but include the p2sh output as the subscript // for verification. redeemScript := pendingReservation.partialState.FundingRedeemScript p2sh, err := scriptHashPkScript(redeemScript) if err != nil { msg.err <- err return } // First, we sign our copy of the commitment transaction ourselves. ourCommitSig, err := txscript.RawTxInSignature(commitTx, 0, redeemScript, txscript.SigHashAll, ourKey) if err != nil { msg.err <- err return } // Next, create the spending scriptSig, and then verify that the script // is complete, allowing us to spend from the funding transaction. // // 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. var scriptSig []byte theirCommitSig := msg.theirCommitmentSig if bytes.Compare(ourKey.PubKey().SerializeCompressed(), theirKey.SerializeCompressed()) == -1 { scriptSig, err = spendMultiSig(redeemScript, theirCommitSig, ourCommitSig) } else { scriptSig, err = spendMultiSig(redeemScript, ourCommitSig, theirCommitSig) } if err != nil { msg.err <- err return } // Finally, create an instance of a Script VM, and ensure that the // Script executes succesfully. commitTx.TxIn[0].SignatureScript = scriptSig vm, err := txscript.NewEngine(p2sh, commitTx, 0, txscript.StandardVerifyFlags, nil) if err != nil { msg.err <- err return } if err := vm.Execute(); err != nil { msg.err <- fmt.Errorf("counterparty's commitment signature is invalid: %v", err) return } // Funding complete, this entry can be removed from limbo. l.limboMtx.Lock() delete(l.fundingLimbo, pendingReservation.reservationID) // TODO(roasbeef): unlock outputs here, Store.InsertTx will handle marking // input in unconfirmed tx, so future coin selects don't pick it up // * also record location of change address so can use AddCredit l.limboMtx.Unlock() // Add the complete funding transaction to the DB, in it's open bucket // which will be used for the lifetime of this channel. err = l.ChannelDB.PutOpenChannel(pendingReservation.partialState) // Create a goroutine to watch the chain so we can open the channel once // the funding tx has enough confirmations. // TODO(roasbeef): add number of confs to the confi go l.openChannelAfterConfirmations(pendingReservation, 3) msg.err <- err }
// This example demonstrates manually creating and signing a redeem transaction. func ExampleSignTxOutput() { // Ordinarily the private key would come from whatever storage mechanism // is being used, but for this example just hard code it. privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + "d4f8720ee63e502ee2869afab7de234b80c") if err != nil { fmt.Println(err) return } privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams) if err != nil { fmt.Println(err) return } // For this example, create a fake transaction that represents what // would ordinarily be the real transaction that is being spent. It // contains a single output that pays to address in the amount of 1 BTC. originTx := wire.NewMsgTx() prevOut := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0)) txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}) originTx.AddTxIn(txIn) pkScript, err := txscript.PayToAddrScript(addr) if err != nil { fmt.Println(err) return } txOut := wire.NewTxOut(100000000, pkScript) originTx.AddTxOut(txOut) originTxHash := originTx.TxSha() // Create the transaction to redeem the fake transaction. redeemTx := wire.NewMsgTx() // Add the input(s) the redeeming transaction will spend. There is no // signature script at this point since it hasn't been created or signed // yet, hence nil is provided for it. prevOut = wire.NewOutPoint(&originTxHash, 0) txIn = wire.NewTxIn(prevOut, nil) redeemTx.AddTxIn(txIn) // Ordinarily this would contain that actual destination of the funds, // but for this example don't bother. txOut = wire.NewTxOut(0, nil) redeemTx.AddTxOut(txOut) // Sign the redeeming transaction. lookupKey := func(a btcutil.Address) (*btcec.PrivateKey, bool, error) { // Ordinarily this function would involve looking up the private // key for the provided address, but since the only thing being // signed in this example uses the address associated with the // private key from above, simply return it with the compressed // flag set since the address is using the associated compressed // public key. // // NOTE: If you want to prove the code is actually signing the // transaction properly, uncomment the following line which // intentionally returns an invalid key to sign with, which in // turn will result in a failure during the script execution // when verifying the signature. // // privKey.D.SetInt64(12345) // return privKey, true, nil } // Notice that the script database parameter is nil here since it isn't // used. It must be specified when pay-to-script-hash transactions are // being signed. sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, txscript.KeyClosure(lookupKey), nil, nil) if err != nil { fmt.Println(err) return } redeemTx.TxIn[0].SignatureScript = sigScript // Prove that the transaction has been validly signed by executing the // script pair. flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptStrictMultiSig | txscript.ScriptDiscourageUpgradableNops vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags) if err != nil { fmt.Println(err) return } if err := vm.Execute(); err != nil { fmt.Println(err) return } fmt.Println("Transaction successfully signed") // Output: // Transaction successfully signed }
// validateHandler consumes items to validate from the internal validate channel // and returns the result of the validation on the internal result channel. It // must be run as a goroutine. func (v *txValidator) validateHandler() { out: for { select { case txVI := <-v.validateChan: // Ensure the referenced input transaction is available. txIn := txVI.txIn originTxHash := &txIn.PreviousOutPoint.Hash originTx, exists := v.txStore[*originTxHash] if !exists || originTx.Err != nil || originTx.Tx == nil { str := fmt.Sprintf("unable to find input "+ "transaction %v referenced from "+ "transaction %v", originTxHash, txVI.tx.Sha()) err := ruleError(ErrMissingTx, str) v.sendResult(err) break out } originMsgTx := originTx.Tx.MsgTx() // Ensure the output index in the referenced transaction // is available. originTxIndex := txIn.PreviousOutPoint.Index if originTxIndex >= uint32(len(originMsgTx.TxOut)) { str := fmt.Sprintf("out of bounds "+ "input index %d in transaction %v "+ "referenced from transaction %v", originTxIndex, originTxHash, txVI.tx.Sha()) err := ruleError(ErrBadTxInput, str) v.sendResult(err) break out } // Create a new script engine for the script pair. sigScript := txIn.SignatureScript pkScript := originMsgTx.TxOut[originTxIndex].PkScript vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(), txVI.txInIndex, v.flags, v.sigCache) if err != nil { str := fmt.Sprintf("failed to parse input "+ "%s:%d which references output %s:%d - "+ "%v (input script bytes %x, prev output "+ "script bytes %x)", txVI.tx.Sha(), txVI.txInIndex, originTxHash, originTxIndex, err, sigScript, pkScript) err := ruleError(ErrScriptMalformed, str) v.sendResult(err) break out } // Execute the script pair. if err := vm.Execute(); err != nil { str := fmt.Sprintf("failed to validate input "+ "%s:%d which references output %s:%d - "+ "%v (input script bytes %x, prev output "+ "script bytes %x)", txVI.tx.Sha(), txVI.txInIndex, originTxHash, originTxIndex, err, sigScript, pkScript) err := ruleError(ErrScriptValidation, str) v.sendResult(err) break out } // Validation succeeded. v.sendResult(nil) case <-v.quitChan: break out } } }