// CreateTransaction returns a fully signed transaction paying to the specified // outputs while observing the desired fee rate. The passed fee rate should be // expressed in satoshis-per-byte. // // This function is safe for concurrent access. func (m *memWallet) CreateTransaction(outputs []*wire.TxOut, feeRate btcutil.Amount) (*wire.MsgTx, error) { m.Lock() defer m.Unlock() tx := wire.NewMsgTx(wire.TxVersion) // Tally up the total amount to be sent in order to perform coin // selection shortly below. var outputAmt btcutil.Amount for _, output := range outputs { outputAmt += btcutil.Amount(output.Value) tx.AddTxOut(output) } // Attempt to fund the transaction with spendable utxos. if err := m.fundTx(tx, outputAmt, feeRate); err != nil { return nil, err } // Populate all the selected inputs with valid sigScript for spending. // Along the way record all outputs being spent in order to avoid a // potential double spend. spentOutputs := make([]*utxo, 0, len(tx.TxIn)) for i, txIn := range tx.TxIn { outPoint := txIn.PreviousOutPoint utxo := m.utxos[outPoint] extendedKey, err := m.hdRoot.Child(utxo.keyIndex) if err != nil { return nil, err } privKey, err := extendedKey.ECPrivKey() if err != nil { return nil, err } sigScript, err := txscript.SignatureScript(tx, i, utxo.pkScript, txscript.SigHashAll, privKey, true) if err != nil { return nil, err } txIn.SignatureScript = sigScript spentOutputs = append(spentOutputs, utxo) } // As these outputs are now being spent by this newly created // transaction, mark the outputs are "locked". This action ensures // these outputs won't be double spent by any subsequent transactions. // These locked outputs can be freed via a call to UnlockOutputs. for _, utxo := range spentOutputs { utxo.isLocked = true } return tx, nil }
// SignThis isn't used anymore... func (t *TxStore) SignThis(tx *wire.MsgTx) error { fmt.Printf("-= SignThis =-\n") // sort tx before signing. txsort.InPlaceSort(tx) sigs := make([][]byte, len(tx.TxIn)) // first iterate over each input for j, in := range tx.TxIn { for k := uint32(0); k < uint32(len(t.Adrs)); k++ { child, err := t.rootPrivKey.Child(k + hdkeychain.HardenedKeyStart) if err != nil { return err } myadr, err := child.Address(t.Param) if err != nil { return err } adrScript, err := txscript.PayToAddrScript(myadr) if err != nil { return err } if bytes.Equal(adrScript, in.SignatureScript) { fmt.Printf("Hit; key %d matches input %d. Signing.\n", k, j) priv, err := child.ECPrivKey() if err != nil { return err } sigs[j], err = txscript.SignatureScript( tx, j, in.SignatureScript, txscript.SigHashAll, priv, true) if err != nil { return err } break } } } for i, s := range sigs { if s != nil { tx.TxIn[i].SignatureScript = s } } return nil }
func testSpendNotification(miner *rpctest.Harness, notifier chainntnfs.ChainNotifier, t *testing.T) { // We'd like to test the spend notifiations for all // ChainNotifier concrete implemenations. // // To do so, we first create a new output to our test target // address. txid, err := getTestTxId(miner) if err != nil { t.Fatalf("unable to create test addr: %v", err) } // Mine a single block which should include that txid above. if _, err := miner.Node.Generate(1); err != nil { t.Fatalf("unable to generate single block: %v", err) } // Now that we have the txid, fetch the transaction itself. wrappedTx, err := miner.Node.GetRawTransaction(txid) if err != nil { t.Fatalf("unable to get new tx: %v", err) } tx := wrappedTx.MsgTx() // Locate the output index sent to us. We need this so we can // construct a spending txn below. outIndex := -1 var pkScript []byte for i, txOut := range tx.TxOut { if bytes.Contains(txOut.PkScript, testAddr.ScriptAddress()) { pkScript = txOut.PkScript outIndex = i break } } if outIndex == -1 { t.Fatalf("unable to locate new output") } // Now that we've found the output index, register for a spentness // notification for the newly created output. outpoint := wire.NewOutPoint(txid, uint32(outIndex)) spentIntent, err := notifier.RegisterSpendNtfn(outpoint) if err != nil { t.Fatalf("unable to register for spend ntfn: %v", err) } // Next, create a new transaction spending that output. spendingTx := wire.NewMsgTx() spendingTx.AddTxIn(&wire.TxIn{ PreviousOutPoint: *outpoint, }) spendingTx.AddTxOut(&wire.TxOut{ Value: 1e8, PkScript: pkScript, }) sigScript, err := txscript.SignatureScript(spendingTx, 0, pkScript, txscript.SigHashAll, privKey, true) if err != nil { t.Fatalf("unable to sign tx: %v", err) } spendingTx.TxIn[0].SignatureScript = sigScript // Broadcast our spending transaction. spenderSha, err := miner.Node.SendRawTransaction(spendingTx, true) if err != nil { t.Fatalf("unable to brodacst tx: %v", err) } // Now we mine a single block, which should include our spend. The // notification should also be sent off. if _, err := miner.Node.Generate(1); err != nil { t.Fatalf("unable to generate single block: %v", err) } spentNtfn := make(chan *chainntnfs.SpendDetail) go func() { spentNtfn <- <-spentIntent.Spend }() select { case ntfn := <-spentNtfn: // We've received the spend nftn. So now verify all the fields // have been set properly. if ntfn.SpentOutPoint != outpoint { t.Fatalf("ntfn includes wrong output, reports %v instead of %v", ntfn.SpentOutPoint, outpoint) } if !bytes.Equal(ntfn.SpenderTxHash.Bytes(), spenderSha.Bytes()) { t.Fatalf("ntfn includes wrong spender tx sha, reports %v intead of %v", ntfn.SpenderTxHash.Bytes(), spenderSha.Bytes()) } if ntfn.SpenderInputIndex != 0 { t.Fatalf("ntfn includes wrong spending input index, reports %v, should be %v", ntfn.SpenderInputIndex, 0) } case <-time.After(2 * time.Second): t.Fatalf("spend ntfn never received") } }
// SendCoins does send coins, but it's very rudimentary // wit makes it into p2wpkh. Which is not yet spendable. func (s *SPVCon) SendCoins(adrs []btcutil.Address, sendAmts []int64) error { if len(adrs) != len(sendAmts) { return fmt.Errorf("%d addresses and %d amounts", len(adrs), len(sendAmts)) } var err error var score, totalSend, fee int64 dustCutoff := int64(20000) // below this amount, just give to miners satPerByte := int64(80) // satoshis per byte fee; have as arg later rawUtxos, err := s.TS.GetAllUtxos() if err != nil { return err } var allUtxos SortableUtxoSlice // start with utxos sorted by value. for _, utxo := range rawUtxos { score += utxo.Value allUtxos = append(allUtxos, *utxo) } // smallest and unconfirmed last (because it's reversed) sort.Sort(sort.Reverse(allUtxos)) // sort.Reverse(allUtxos) for _, amt := range sendAmts { totalSend += amt } // important rule in bitcoin, output total > input total is invalid. if totalSend > score { return fmt.Errorf("trying to send %d but %d available.", totalSend, score) } tx := wire.NewMsgTx() // make new tx // add non-change (arg) outputs for i, adr := range adrs { // make address script 76a914...88ac or 0014... outAdrScript, err := txscript.PayToAddrScript(adr) if err != nil { return err } // make user specified txout and add to tx txout := wire.NewTxOut(sendAmts[i], outAdrScript) tx.AddTxOut(txout) } // generate a utxo slice for your inputs var ins utxoSlice // add utxos until we've had enough nokori := totalSend // nokori is how much is needed on input side for _, utxo := range allUtxos { // skip unconfirmed. Or de-prioritize? // if utxo.AtHeight == 0 { // continue // } // yeah, lets add this utxo! ins = append(ins, utxo) // as we add utxos, fill in sigscripts // generate previous pkscripts (subscritpt?) for all utxos // then make txins with the utxo and prevpk, and insert them into the tx // these are all zeroed out during signing but it's an easy way to keep track var prevPKs []byte if utxo.IsWit { //tx.Flags = 0x01 wa, err := btcutil.NewAddressWitnessPubKeyHash( s.TS.Adrs[utxo.KeyIdx].PkhAdr.ScriptAddress(), s.TS.Param) prevPKs, err = txscript.PayToAddrScript(wa) if err != nil { return err } } else { // otherwise generate directly prevPKs, err = txscript.PayToAddrScript( s.TS.Adrs[utxo.KeyIdx].PkhAdr) if err != nil { return err } } tx.AddTxIn(wire.NewTxIn(&utxo.Op, prevPKs, nil)) nokori -= utxo.Value // if nokori is positive, don't bother checking fee yet. if nokori < 0 { fee = EstFee(tx, satPerByte) if nokori < -fee { // done adding utxos: nokori below negative est. fee break } } } // see if there's enough left to also add a change output changeOld, err := s.TS.NewAdr() // change is witnessy if err != nil { return err } changeAdr, err := btcutil.NewAddressWitnessPubKeyHash( changeOld.ScriptAddress(), s.TS.Param) if err != nil { return err } changeScript, err := txscript.PayToAddrScript(changeAdr) if err != nil { return err } changeOut := wire.NewTxOut(0, changeScript) tx.AddTxOut(changeOut) fee = EstFee(tx, satPerByte) changeOut.Value = -(nokori + fee) if changeOut.Value < dustCutoff { // remove last output (change) : not worth it tx.TxOut = tx.TxOut[:len(tx.TxOut)-1] } // sort utxos on the input side. use this instead of txsort // because we want to remember which keys are associated with which inputs sort.Sort(ins) // sort tx -- this only will change txouts since inputs are already sorted txsort.InPlaceSort(tx) // tx is ready for signing, sigStash := make([][]byte, len(ins)) witStash := make([][][]byte, len(ins)) // generate tx-wide hashCache for segwit stuff // middle index number doesn't matter for sighashAll. hCache := txscript.NewTxSigHashes(tx) for i, txin := range tx.TxIn { // pick key child, err := s.TS.rootPrivKey.Child( ins[i].KeyIdx + hdkeychain.HardenedKeyStart) if err != nil { return err } priv, err := child.ECPrivKey() if err != nil { return err } // This is where witness based sighash types need to happen // sign into stash if ins[i].IsWit { witStash[i], err = txscript.WitnessScript( tx, hCache, i, ins[i].Value, txin.SignatureScript, txscript.SigHashAll, priv, true) if err != nil { return err } } else { sigStash[i], err = txscript.SignatureScript( tx, i, txin.SignatureScript, txscript.SigHashAll, priv, true) if err != nil { return err } } } // swap sigs into sigScripts in txins for i, txin := range tx.TxIn { if sigStash[i] != nil { txin.SignatureScript = sigStash[i] } if witStash[i] != nil { txin.Witness = witStash[i] txin.SignatureScript = nil } } // fmt.Printf("tx: %s", TxToString(tx)) // buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) // send it out on the wire. hope it gets there. // we should deal with rejects. Don't yet. err = s.NewOutgoingTx(tx) if err != nil { return err } return nil }
func (s *SPVCon) SendOne(u Utxo, adr btcutil.Address) error { // fixed fee fee := int64(5000) sendAmt := u.Value - fee tx := wire.NewMsgTx() // make new tx // add single output outAdrScript, err := txscript.PayToAddrScript(adr) if err != nil { return err } // make user specified txout and add to tx txout := wire.NewTxOut(sendAmt, outAdrScript) tx.AddTxOut(txout) var prevPKs []byte if u.IsWit { //tx.Flags = 0x01 wa, err := btcutil.NewAddressWitnessPubKeyHash( s.TS.Adrs[u.KeyIdx].PkhAdr.ScriptAddress(), s.TS.Param) prevPKs, err = txscript.PayToAddrScript(wa) if err != nil { return err } } else { // otherwise generate directly prevPKs, err = txscript.PayToAddrScript( s.TS.Adrs[u.KeyIdx].PkhAdr) if err != nil { return err } } tx.AddTxIn(wire.NewTxIn(&u.Op, prevPKs, nil)) var sig []byte var wit [][]byte hCache := txscript.NewTxSigHashes(tx) child, err := s.TS.rootPrivKey.Child(u.KeyIdx + hdkeychain.HardenedKeyStart) if err != nil { return err } priv, err := child.ECPrivKey() if err != nil { return err } // This is where witness based sighash types need to happen // sign into stash if u.IsWit { wit, err = txscript.WitnessScript( tx, hCache, 0, u.Value, tx.TxIn[0].SignatureScript, txscript.SigHashAll, priv, true) if err != nil { return err } } else { sig, err = txscript.SignatureScript( tx, 0, tx.TxIn[0].SignatureScript, txscript.SigHashAll, priv, true) if err != nil { return err } } // swap sigs into sigScripts in txins if sig != nil { tx.TxIn[0].SignatureScript = sig } if wit != nil { tx.TxIn[0].Witness = wit tx.TxIn[0].SignatureScript = nil } return s.NewOutgoingTx(tx) }
// TestBIP0113Activation tests for proper adherance of the BIP 113 rule // constraint which requires all transaction finality tests to use the MTP of // the last 11 blocks, rather than the timestamp of the block which includes // them. // // Overview: // - Pre soft-fork: // - Transactions with non-final lock-times from the PoV of MTP should be // rejected from the mempool. // - Transactions within non-final MTP based lock-times should be accepted // in valid blocks. // // - Post soft-fork: // - Transactions with non-final lock-times from the PoV of MTP should be // rejected from the mempool and when found within otherwise valid blocks. // - Transactions with final lock-times from the PoV of MTP should be // accepted to the mempool and mined in future block. func TestBIP0113Activation(t *testing.T) { t.Parallel() btcdCfg := []string{"--rejectnonstd"} r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg) if err != nil { t.Fatal("unable to create primary harness: ", err) } if err := r.SetUp(true, 1); err != nil { t.Fatalf("unable to setup test chain: %v", err) } defer r.TearDown() // Create a fresh output for usage within the test below. const outputValue = btcutil.SatoshiPerBitcoin outputKey, testOutput, testPkScript, err := makeTestOutput(r, t, outputValue) if err != nil { t.Fatalf("unable to create test output: %v", err) } // Fetch a fresh address from the harness, we'll use this address to // send funds back into the Harness. addr, err := r.NewAddress() if err != nil { t.Fatalf("unable to generate address: %v", err) } addrScript, err := txscript.PayToAddrScript(addr) if err != nil { t.Fatalf("unable to generate addr script: %v", err) } // Now create a transaction with a lock time which is "final" according // to the latest block, but not according to the current median time // past. tx := wire.NewMsgTx(1) tx.AddTxIn(&wire.TxIn{ PreviousOutPoint: *testOutput, }) tx.AddTxOut(&wire.TxOut{ PkScript: addrScript, Value: outputValue - 1000, }) // We set the lock-time of the transaction to just one minute after the // current MTP of the chain. chainInfo, err := r.Node.GetBlockChainInfo() if err != nil { t.Fatalf("unable to query for chain info: %v", err) } tx.LockTime = uint32(chainInfo.MedianTime) + 1 sigScript, err := txscript.SignatureScript(tx, 0, testPkScript, txscript.SigHashAll, outputKey, true) if err != nil { t.Fatalf("unable to generate sig: %v", err) } tx.TxIn[0].SignatureScript = sigScript // This transaction should be rejected from the mempool as using MTP // for transactions finality is now a policy rule. Additionally, the // exact error should be the rejection of a non-final transaction. _, err = r.Node.SendRawTransaction(tx, true) if err == nil { t.Fatalf("transaction accepted, but should be non-final") } else if !strings.Contains(err.Error(), "not finalized") { t.Fatalf("transaction should be rejected due to being "+ "non-final, instead: %v", err) } // However, since the block validation consensus rules haven't yet // activated, a block including the transaction should be accepted. txns := []*btcutil.Tx{btcutil.NewTx(tx)} block, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{}) if err != nil { t.Fatalf("unable to submit block: %v", err) } txid := tx.TxHash() assertTxInBlock(r, t, block.Hash(), &txid) // At this point, the block height should be 103: we mined 101 blocks // to create a single mature output, then an additional block to create // a new output, and then mined a single block above to include our // transation. assertChainHeight(r, t, 103) // Next, mine enough blocks to ensure that the soft-fork becomes // activated. Assert that the block version of the second-to-last block // in the final range is active. // Next, mine ensure blocks to ensure that the soft-fork becomes // active. We're at height 103 and we need 200 blocks to be mined after // the genesis target period, so we mine 196 blocks. This'll put us at // height 299. The getblockchaininfo call checks the state for the // block AFTER the current height. numBlocks := (r.ActiveNet.MinerConfirmationWindow * 2) - 4 if _, err := r.Node.Generate(numBlocks); err != nil { t.Fatalf("unable to generate blocks: %v", err) } assertChainHeight(r, t, 299) assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdActive) // The timeLockDeltas slice represents a series of deviations from the // current MTP which will be used to test border conditions w.r.t // transaction finality. -1 indicates 1 second prior to the MTP, 0 // indicates the current MTP, and 1 indicates 1 second after the // current MTP. // // This time, all transactions which are final according to the MTP // *should* be accepted to both the mempool and within a valid block. // While transactions with lock-times *after* the current MTP should be // rejected. timeLockDeltas := []int64{-1, 0, 1} for _, timeLockDelta := range timeLockDeltas { chainInfo, err = r.Node.GetBlockChainInfo() if err != nil { t.Fatalf("unable to query for chain info: %v", err) } medianTimePast := chainInfo.MedianTime // Create another test output to be spent shortly below. outputKey, testOutput, testPkScript, err = makeTestOutput(r, t, outputValue) if err != nil { t.Fatalf("unable to create test output: %v", err) } // Create a new transaction with a lock-time past the current known // MTP. tx = wire.NewMsgTx(1) tx.AddTxIn(&wire.TxIn{ PreviousOutPoint: *testOutput, }) tx.AddTxOut(&wire.TxOut{ PkScript: addrScript, Value: outputValue - 1000, }) tx.LockTime = uint32(medianTimePast + timeLockDelta) sigScript, err = txscript.SignatureScript(tx, 0, testPkScript, txscript.SigHashAll, outputKey, true) if err != nil { t.Fatalf("unable to generate sig: %v", err) } tx.TxIn[0].SignatureScript = sigScript // If the time-lock delta is greater than -1, then the // transaction should be rejected from the mempool and when // included within a block. A time-lock delta of -1 should be // accepted as it has a lock-time of one // second _before_ the current MTP. _, err = r.Node.SendRawTransaction(tx, true) if err == nil && timeLockDelta >= 0 { t.Fatal("transaction was accepted into the mempool " + "but should be rejected!") } else if err != nil && !strings.Contains(err.Error(), "not finalized") { t.Fatalf("transaction should be rejected from mempool "+ "due to being non-final, instead: %v", err) } txns = []*btcutil.Tx{btcutil.NewTx(tx)} _, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{}) if err == nil && timeLockDelta >= 0 { t.Fatal("block should be rejected due to non-final " + "txn, but was accepted") } else if err != nil && !strings.Contains(err.Error(), "unfinalized") { t.Fatalf("block should be rejected due to non-final "+ "tx, instead: %v", err) } } }