// This example demonstrates creating a script which pays to a bitcoin address. // It also prints the created script hex and uses the DisasmString function to // display the disassembled script. func ExamplePayToAddrScript() { // Parse the address to send the coins to into a btcutil.Address // which is useful to ensure the accuracy of the address and determine // the address type. It is also required for the upcoming call to // PayToAddrScript. addressStr := "12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV" address, err := btcutil.DecodeAddress(addressStr, &btcnet.MainNetParams) if err != nil { fmt.Println(err) return } // Create a public key script that pays to the address. script, err := btcscript.PayToAddrScript(address) if err != nil { fmt.Println(err) return } fmt.Printf("Script Hex: %x\n", script) disasm, err := btcscript.DisasmString(script) if err != nil { fmt.Println(err) return } fmt.Println("Script Disassembly:", disasm) // Output: // Script Hex: 76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac // Script Disassembly: OP_DUP OP_HASH160 128004ff2fcaf13b2b91eb654b1dc2b674f7ec61 OP_EQUALVERIFY OP_CHECKSIG }
// 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(coinbaseScript []byte, nextBlockHeight int64, 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 = btcscript.PayToAddrScript(addr) if err != nil { return nil, err } } else { scriptBuilder := btcscript.NewScriptBuilder() pkScript = scriptBuilder.AddOp(btcscript.OP_TRUE).Script() } tx := btcwire.NewMsgTx() tx.AddTxIn(&btcwire.TxIn{ // Coinbase transactions have no inputs, so previous outpoint is // zero hash and max index. PreviousOutPoint: *btcwire.NewOutPoint(&btcwire.ShaHash{}, btcwire.MaxPrevOutIndex), SignatureScript: coinbaseScript, Sequence: btcwire.MaxTxInSequenceNum, }) tx.AddTxOut(&btcwire.TxOut{ Value: btcchain.CalcBlockSubsidy(nextBlockHeight, activeNetParams.Params), PkScript: pkScript, }) return btcutil.NewTx(tx), nil }
func (fanB *FanOutBuilder) Build() (*btcwire.MsgTx, error) { totalSpent := fanB.SatNeeded() // Compose a set of Txins with enough to fund this transactions needs inParamSet, totalIn, err := composeUnspents( totalSpent, fanB.Params) if err != nil { return nil, err } msgtx := btcwire.NewMsgTx() // funding inputs speced out with blank for _, inpParam := range inParamSet { txin := btcwire.NewTxIn(inpParam.OutPoint, []byte{}) msgtx.AddTxIn(txin) } for i := range fanB.Builders { builder := fanB.Builders[i] amnt := builder.SatNeeded() for j := int64(0); j < fanB.Copies; j++ { addr, err := newAddr(fanB.Params.Client) if err != nil { return nil, err } script, _ := btcscript.PayToAddrScript(addr) txout := btcwire.NewTxOut(amnt, script) msgtx.AddTxOut(txout) } } changeAddr, err := newAddr(fanB.Params.Client) if err != nil { return nil, err } // change to solve unevenness change, ok := changeOutput(totalIn-totalSpent, fanB.Params.DustAmnt, changeAddr) if ok { msgtx.AddTxOut(change) } // sign msgtx for each input for i, inpParam := range inParamSet { privkey := inpParam.Wif.PrivKey subscript := inpParam.TxOut.PkScript sigflag := btcscript.SigHashAll scriptSig, err := btcscript.SignatureScript(msgtx, i, subscript, sigflag, privkey, true) if err != nil { return nil, err } msgtx.TxIn[i].SignatureScript = scriptSig } fanB.Log(fmt.Sprintf("InVal: %d\n", sumInputs(inParamSet))) fanB.Log(fmt.Sprintf("OutVal: %d\n", sumOutputs(msgtx))) return msgtx, nil }
// generates a change output funding provided addr func changeOutput(change, dustAmnt int64, addr btcutil.Address) (*btcwire.TxOut, bool) { if change < dustAmnt { return nil, false } script, _ := btcscript.PayToAddrScript(addr) txout := btcwire.NewTxOut(change, script) return txout, true }
func TestFakeTxs(t *testing.T) { // First we need a wallet. w, err := wallet.NewWallet("banana wallet", "", []byte("banana"), btcwire.MainNet, &wallet.BlockStamp{}, 100) if err != nil { t.Errorf("Can not create encrypted wallet: %s", err) return } a := &Account{ Wallet: w, lockedOutpoints: map[btcwire.OutPoint]struct{}{}, } w.Unlock([]byte("banana")) // Create and add a fake Utxo so we have some funds to spend. // // This will pass validation because btcscript is unaware of invalid // tx inputs, however, this example would fail in btcd. utxo := &tx.Utxo{} addr, err := w.NextChainedAddress(&wallet.BlockStamp{}, 100) if err != nil { t.Errorf("Cannot get next address: %s", err) return } copy(utxo.AddrHash[:], addr.ScriptAddress()) ophash := (btcwire.ShaHash)([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}) out := btcwire.NewOutPoint(&ophash, 0) utxo.Out = tx.OutPoint(*out) ss, err := btcscript.PayToAddrScript(addr) if err != nil { t.Errorf("Could not create utxo PkScript: %s", err) return } utxo.Subscript = tx.PkScript(ss) utxo.Amt = 1000000 utxo.Height = 12345 a.UtxoStore = append(a.UtxoStore, utxo) // Fake our current block height so btcd doesn't need to be queried. curBlock.BlockStamp.Height = 12346 // Create the transaction. pairs := map[string]int64{ "17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH": 5000, } _, err = a.txToPairs(pairs, 1) if err != nil { t.Errorf("Tx creation failed: %s", err) return } }
func PayToAddrScript(addressStr string) { // Parse the address to send the coins to into a btcutil.Address // which is useful to ensure the accuracy of the address and determine // the address type. It is also required for the upcoming call to // PayToAddrScript. address, err := btcutil.DecodeAddress(addressStr, &btcnet.MainNetParams) handle(err) // Create a public key script that pays to the address. script, err := btcscript.PayToAddrScript(address) handle(err) fmt.Printf("Script Hex: %x\n", script) disasm, err := btcscript.DisasmString(script) handle(err) fmt.Println("Script Disassembly:", disasm) }
func (builder *ToAddrBuilder) Build() (*btcwire.MsgTx, error) { utxo, err := selectUnspent(builder.SatNeeded(), builder.Params) if err != nil { return nil, err } txin := btcwire.NewTxIn(utxo.OutPoint, []byte{}) msgtx := btcwire.NewMsgTx() msgtx.AddTxIn(txin) // add send to addr valout := builder.Params.InTarget - builder.Params.Fee outscript, _ := btcscript.PayToAddrScript(builder.Addr) txout := btcwire.NewTxOut(valout, outscript) msgtx.AddTxOut(txout) // add send to change addr total := utxo.TxOut.Value changeval := total - builder.SatNeeded() if changeval > builder.Params.DustAmnt { // Change needed changeAddr, err := builder.Params.Client.GetNewAddress() if err != nil { return nil, err } change, ok := changeOutput(changeval, builder.Params.DustAmnt, changeAddr) if ok { msgtx.AddTxOut(change) } } subscript := utxo.TxOut.PkScript privkey := utxo.Wif.PrivKey scriptSig, err := btcscript.SignatureScript(msgtx, 0, subscript, btcscript.SigHashAll, privkey, true) if err != nil { return nil, err } txin.SignatureScript = scriptSig return msgtx, nil }
// A transaction that contains only dust ouputs and obeys the TxBuilder interface func (builder *DustBuilder) Build() (*btcwire.MsgTx, error) { var inparams *TxInParams var err error inparams, err = specificUnspent( builder.SatNeeded(), builder.Params) if err != nil { return nil, err } oldTxOut := inparams.TxOut outpoint := inparams.OutPoint wifkey := inparams.Wif msgtx := btcwire.NewMsgTx() txin := btcwire.NewTxIn(outpoint, []byte{}) msgtx.AddTxIn(txin) for i := int64(0); i < builder.NumOuts; i++ { dumb := bytes.Repeat([]byte{66}, 20) addr := dataAddr(dumb, builder.Params.NetParams) addrScript, err := btcscript.PayToAddrScript(addr) if err != nil { return nil, err } txOut := btcwire.NewTxOut(builder.Params.DustAmnt, addrScript) msgtx.AddTxOut(txOut) } // sign as usual privkey := wifkey.PrivKey sig, err := btcscript.SignatureScript(msgtx, 0, oldTxOut.PkScript, btcscript.SigHashAll, privkey, true) if err != nil { return nil, err } txin.SignatureScript = sig return msgtx, nil }
// Converts a bulletin into public key scripts for encoding func (bltn *Bulletin) TxOuts(toBurn int64, net *btcnet.Params) ([]*btcwire.TxOut, error) { rawbytes, err := bltn.Bytes() if err != nil { return []*btcwire.TxOut{}, err } numcuts, _ := bltn.NumOuts() cuts := make([][]byte, numcuts, numcuts) for i := 0; i < numcuts; i++ { sliceb := make([]byte, 20, 20) copy(sliceb, rawbytes) cuts[i] = sliceb if len(rawbytes) >= 20 { rawbytes = rawbytes[20:] } } // Convert raw data into txouts txouts := make([]*btcwire.TxOut, 0) for _, cut := range cuts { fakeaddr, err := btcutil.NewAddressPubKeyHash(cut, net) if err != nil { return []*btcwire.TxOut{}, err } pkscript, err := btcscript.PayToAddrScript(fakeaddr) if err != nil { return []*btcwire.TxOut{}, err } txout := &btcwire.TxOut{ PkScript: pkscript, Value: toBurn, } txouts = append(txouts, txout) } return txouts, nil }
func (pkhB *PubKeyHashBuilder) Build() (*btcwire.MsgTx, error) { inparams, err := specificUnspent(pkhB.SatNeeded(), pkhB.Params) if err != nil { return nil, err } msgtx := btcwire.NewMsgTx() txin := btcwire.NewTxIn(inparams.OutPoint, []byte{}) msgtx.AddTxIn(txin) for i := int64(0); i < pkhB.NumOuts; i++ { addr, err := newAddr(pkhB.Params.Client) if err != nil { return nil, err } addrScript, err := btcscript.PayToAddrScript(addr) amntSend := pkhB.eachOutVal() if amntSend < pkhB.Params.DustAmnt { return nil, errors.New("Output would be under the dust limit") } txout := btcwire.NewTxOut(pkhB.eachOutVal(), addrScript) msgtx.AddTxOut(txout) } privkey := inparams.Wif.PrivKey sig, err := btcscript.SignatureScript(msgtx, 0, inparams.TxOut.PkScript, btcscript.SigHashAll, privkey, true) if err != nil { return nil, err } txin.SignatureScript = sig return msgtx, nil }
// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy // based on the passed block height to the passed public key. It also accepts // an extra nonce value for the signature script. This extra nonce helps ensure // the transaction is not a duplicate transaction (paying the same value to the // same public key address would otherwise be an identical transaction for // block version 1). func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int64, addr btcutil.Address) (*btcutil.Tx, error) { // Create a script to pay to the specific address. pkScript, err := btcscript.PayToAddrScript(addr) if err != nil { return nil, err } tx := btcwire.NewMsgTx() tx.AddTxIn(&btcwire.TxIn{ // Coinbase transactions have no inputs, so previous outpoint is // zero hash and max index. PreviousOutpoint: *btcwire.NewOutPoint(&btcwire.ShaHash{}, btcwire.MaxPrevOutIndex), SignatureScript: coinbaseScript, Sequence: btcwire.MaxTxInSequenceNum, }) tx.AddTxOut(&btcwire.TxOut{ Value: btcchain.CalcBlockSubsidy(nextBlockHeight, activeNetParams.Params), PkScript: pkScript, }) return btcutil.NewTx(tx), nil }
// txToPairs creates a raw transaction sending the amounts for each // address/amount pair and fee to each address and the miner. minconf // specifies the minimum number of confirmations required before an // unspent output is eligible for spending. Leftover input funds not sent // to addr or as a fee for the miner are sent to a newly generated // address. If change is needed to return funds back to an owned // address, changeUtxo will point to a unconfirmed (height = -1, zeroed // block hash) Utxo. ErrInsufficientFunds is returned if there are not // enough eligible unspent outputs to create the transaction. func (a *Account) txToPairs(pairs map[string]btcutil.Amount, minconf int) (*CreatedTx, error) { // Wallet must be unlocked to compose transaction. if a.IsLocked() { return nil, wallet.ErrWalletLocked } // Create a new transaction which will include all input scripts. msgtx := btcwire.NewMsgTx() // Calculate minimum amount needed for inputs. var amt btcutil.Amount for _, v := range pairs { // Error out if any amount is negative. if v <= 0 { return nil, ErrNonPositiveAmount } amt += v } // Add outputs to new tx. for addrStr, amt := range pairs { addr, err := btcutil.DecodeAddress(addrStr, cfg.Net()) if err != nil { return nil, fmt.Errorf("cannot decode address: %s", err) } // Add output to spend amt to addr. pkScript, err := btcscript.PayToAddrScript(addr) if err != nil { return nil, fmt.Errorf("cannot create txout script: %s", err) } txout := btcwire.NewTxOut(int64(amt), pkScript) msgtx.AddTxOut(txout) } // Get current block's height and hash. bs, err := GetCurBlock() if err != nil { return nil, err } // Make a copy of msgtx before any inputs are added. This will be // used as a starting point when trying a fee and starting over with // a higher fee if not enough was originally chosen. txNoInputs := msgtx.Copy() unspent, err := a.TxStore.UnspentOutputs() if err != nil { return nil, err } var selectedInputs []*txstore.Credit // These are nil/zeroed until a change address is needed, and reused // again in case a change utxo has already been chosen. var changeAddr btcutil.Address // Get the number of satoshis to increment fee by when searching for // the minimum tx fee needed. fee := btcutil.Amount(0) for { msgtx = txNoInputs.Copy() // Select unspent outputs to be used in transaction based on the amount // neededing to sent, and the current fee estimation. inputs, btcin, err := selectInputs(unspent, amt+fee, minconf) if err != nil { return nil, err } // Check if there are leftover unspent outputs, and return coins back to // a new address we own. // // TODO: change needs to be inserted into a random txout index, or else // this is a privacy risk. change := btcin - amt - fee if change > 0 { // Get a new change address if one has not already been found. if changeAddr == nil { changeAddr, err = a.ChangeAddress(&bs, cfg.KeypoolSize) if err != nil { return nil, fmt.Errorf("failed to get next address: %s", err) } // Mark change address as belonging to this account. AcctMgr.MarkAddressForAccount(changeAddr, a) } // Spend change. pkScript, err := btcscript.PayToAddrScript(changeAddr) if err != nil { return nil, fmt.Errorf("cannot create txout script: %s", err) } msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript)) } // Selected unspent outputs become new transaction's inputs. for _, ip := range inputs { msgtx.AddTxIn(btcwire.NewTxIn(ip.OutPoint(), nil)) } for i, input := range inputs { _, addrs, _, _ := input.Addresses(cfg.Net()) if len(addrs) != 1 { continue } apkh, ok := addrs[0].(*btcutil.AddressPubKeyHash) if !ok { continue // don't handle inputs to this yes } ai, err := a.Address(apkh) if err != nil { return nil, fmt.Errorf("cannot get address info: %v", err) } pka := ai.(wallet.PubKeyAddress) privkey, err := pka.PrivKey() if err == wallet.ErrWalletLocked { return nil, wallet.ErrWalletLocked } else if err != nil { return nil, fmt.Errorf("cannot get address key: %v", err) } sigscript, err := btcscript.SignatureScript(msgtx, i, input.TxOut().PkScript, btcscript.SigHashAll, privkey, ai.Compressed()) if err != nil { return nil, fmt.Errorf("cannot create sigscript: %s", err) } msgtx.TxIn[i].SignatureScript = sigscript } noFeeAllowed := false if !cfg.DisallowFree { noFeeAllowed = allowFree(bs.Height, inputs, msgtx.SerializeSize()) } if minFee := minimumFee(msgtx, noFeeAllowed); fee < minFee { fee = minFee } else { selectedInputs = inputs break } } // Validate msgtx before returning the raw transaction. flags := btcscript.ScriptCanonicalSignatures bip16 := time.Now().After(btcscript.Bip16Activation) if bip16 { flags |= btcscript.ScriptBip16 } for i, txin := range msgtx.TxIn { engine, err := btcscript.NewScript(txin.SignatureScript, selectedInputs[i].TxOut().PkScript, i, msgtx, flags) if err != nil { return nil, fmt.Errorf("cannot create script engine: %s", err) } if err = engine.Execute(); err != nil { return nil, fmt.Errorf("cannot validate transaction: %s", err) } } buf := bytes.NewBuffer(nil) buf.Grow(msgtx.SerializeSize()) msgtx.BtcEncode(buf, btcwire.ProtocolVersion) info := &CreatedTx{ tx: btcutil.NewTx(msgtx), inputs: selectedInputs, changeAddr: changeAddr, } return info, nil }
// handleCreateRawTransaction handles createrawtransaction commands. func handleCreateRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) { c := cmd.(*btcjson.CreateRawTransactionCmd) // Add all transaction inputs to a new transaction after performing // some validity checks. mtx := btcwire.NewMsgTx() for _, input := range c.Inputs { txHash, err := btcwire.NewShaHashFromStr(input.Txid) if err != nil { return nil, btcjson.ErrDecodeHexString } if input.Vout < 0 { return nil, btcjson.Error{ Code: btcjson.ErrInvalidParameter.Code, Message: "Invalid parameter, vout must be positive", } } prevOut := btcwire.NewOutPoint(txHash, uint32(input.Vout)) txIn := btcwire.NewTxIn(prevOut, []byte{}) mtx.AddTxIn(txIn) } // Add all transaction outputs to the transaction after performing // some validity checks. for encodedAddr, amount := range c.Amounts { // Ensure amount is in the valid range for monetary amounts. if amount <= 0 || amount > btcutil.MaxSatoshi { return nil, btcjson.Error{ Code: btcjson.ErrType.Code, Message: "Invalid amount", } } // Decode the provided address. addr, err := btcutil.DecodeAddress(encodedAddr, activeNetParams.btcnet) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInvalidAddressOrKey.Code, Message: btcjson.ErrInvalidAddressOrKey.Message + ": " + err.Error(), } } // Ensure the address is one of the supported types and that // the network encoded with the address matches the network the // server is currently on. switch addr.(type) { case *btcutil.AddressPubKeyHash: case *btcutil.AddressScriptHash: default: return nil, btcjson.ErrInvalidAddressOrKey } if !addr.IsForNet(s.server.btcnet) { return nil, btcjson.Error{ Code: btcjson.ErrInvalidAddressOrKey.Code, Message: fmt.Sprintf("%s: %q", btcjson.ErrInvalidAddressOrKey.Message, encodedAddr), } } // Create a new script which pays to the provided address. pkScript, err := btcscript.PayToAddrScript(addr) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } txOut := btcwire.NewTxOut(amount, pkScript) mtx.AddTxOut(txOut) } // Return the serialized and hex-encoded transaction. mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } return mtxHex, nil }
// txToPairs creates a raw transaction sending the amounts for each // address/amount pair and fee to each address and the miner. minconf // specifies the minimum number of confirmations required before an // unspent output is eligible for spending. Leftover input funds not sent // to addr or as a fee for the miner are sent to a newly generated // address. If change is needed to return funds back to an owned // address, changeUtxo will point to a unconfirmed (height = -1, zeroed // block hash) Utxo. ErrInsufficientFunds is returned if there are not // enough eligible unspent outputs to create the transaction. func (a *Account) txToPairs(pairs map[string]btcutil.Amount, minconf int) (*CreatedTx, error) { // Wallet must be unlocked to compose transaction. if a.IsLocked() { return nil, wallet.ErrWalletLocked } // Create a new transaction which will include all input scripts. msgtx := btcwire.NewMsgTx() // Calculate minimum amount needed for inputs. var amt btcutil.Amount for _, v := range pairs { // Error out if any amount is negative. if v <= 0 { return nil, ErrNonPositiveAmount } amt += v } // Add outputs to new tx. for addrStr, amt := range pairs { addr, err := btcutil.DecodeAddress(addrStr, activeNet.Params) if err != nil { return nil, fmt.Errorf("cannot decode address: %s", err) } // Add output to spend amt to addr. pkScript, err := btcscript.PayToAddrScript(addr) if err != nil { return nil, fmt.Errorf("cannot create txout script: %s", err) } txout := btcwire.NewTxOut(int64(amt), pkScript) msgtx.AddTxOut(txout) } // Get current block's height and hash. bs, err := GetCurBlock() if err != nil { return nil, err } // Make a copy of msgtx before any inputs are added. This will be // used as a starting point when trying a fee and starting over with // a higher fee if not enough was originally chosen. txNoInputs := msgtx.Copy() unspent, err := a.TxStore.UnspentOutputs() if err != nil { return nil, err } // Filter out unspendable outputs, that is, remove those that (at this // time) are not P2PKH outputs. Other inputs must be manually included // in transactions and sent (for example, using createrawtransaction, // signrawtransaction, and sendrawtransaction). eligible := make([]txstore.Credit, 0, len(unspent)) for i := range unspent { switch btcscript.GetScriptClass(unspent[i].TxOut().PkScript) { case btcscript.PubKeyHashTy: if !unspent[i].Confirmed(minconf, bs.Height) { continue } // Coinbase transactions must have have reached maturity // before their outputs may be spent. if unspent[i].IsCoinbase() { target := btcchain.CoinbaseMaturity if !unspent[i].Confirmed(target, bs.Height) { continue } } // Locked unspent outputs are skipped. if a.LockedOutpoint(*unspent[i].OutPoint()) { continue } eligible = append(eligible, unspent[i]) } } // Sort eligible inputs, as selectInputs expects these to be sorted // by amount in reverse order. sort.Sort(sort.Reverse(ByAmount(eligible))) var selectedInputs []txstore.Credit // changeAddr is nil/zeroed until a change address is needed, and reused // again in case a change utxo has already been chosen. var changeAddr btcutil.Address // Get the number of satoshis to increment fee by when searching for // the minimum tx fee needed. fee := btcutil.Amount(0) for { msgtx = txNoInputs.Copy() // Select eligible outputs to be used in transaction based on the amount // neededing to sent, and the current fee estimation. inputs, btcin, err := selectInputs(eligible, amt, fee, minconf) if err != nil { return nil, err } // Check if there are leftover unspent outputs, and return coins back to // a new address we own. change := btcin - amt - fee if change > 0 { // Get a new change address if one has not already been found. if changeAddr == nil { changeAddr, err = a.ChangeAddress(&bs, cfg.KeypoolSize) if err != nil { return nil, fmt.Errorf("failed to get next address: %s", err) } // Mark change address as belonging to this account. AcctMgr.MarkAddressForAccount(changeAddr, a) } // Spend change. pkScript, err := btcscript.PayToAddrScript(changeAddr) if err != nil { return nil, fmt.Errorf("cannot create txout script: %s", err) } msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript)) // Randomize index of the change output. rng := badrand.New(badrand.NewSource(time.Now().UnixNano())) r := rng.Int31n(int32(len(msgtx.TxOut))) // random index c := len(msgtx.TxOut) - 1 // change index msgtx.TxOut[r], msgtx.TxOut[c] = msgtx.TxOut[c], msgtx.TxOut[r] } // Selected unspent outputs become new transaction's inputs. for _, ip := range inputs { msgtx.AddTxIn(btcwire.NewTxIn(ip.OutPoint(), nil)) } for i, input := range inputs { // Errors don't matter here, as we only consider the // case where len(addrs) == 1. _, addrs, _, _ := input.Addresses(activeNet.Params) if len(addrs) != 1 { continue } apkh, ok := addrs[0].(*btcutil.AddressPubKeyHash) if !ok { continue // don't handle inputs to this yes } ai, err := a.Address(apkh) if err != nil { return nil, fmt.Errorf("cannot get address info: %v", err) } pka := ai.(wallet.PubKeyAddress) privkey, err := pka.PrivKey() if err == wallet.ErrWalletLocked { return nil, wallet.ErrWalletLocked } else if err != nil { return nil, fmt.Errorf("cannot get address key: %v", err) } sigscript, err := btcscript.SignatureScript(msgtx, i, input.TxOut().PkScript, btcscript.SigHashAll, privkey, ai.Compressed()) if err != nil { return nil, fmt.Errorf("cannot create sigscript: %s", err) } msgtx.TxIn[i].SignatureScript = sigscript } noFeeAllowed := false if !cfg.DisallowFree { noFeeAllowed = allowFree(bs.Height, inputs, msgtx.SerializeSize()) } if minFee := minimumFee(msgtx, noFeeAllowed); fee < minFee { fee = minFee } else { selectedInputs = inputs break } } // Validate msgtx before returning the raw transaction. flags := btcscript.ScriptCanonicalSignatures bip16 := time.Now().After(btcscript.Bip16Activation) if bip16 { flags |= btcscript.ScriptBip16 } for i, txin := range msgtx.TxIn { engine, err := btcscript.NewScript(txin.SignatureScript, selectedInputs[i].TxOut().PkScript, i, msgtx, flags) if err != nil { return nil, fmt.Errorf("cannot create script engine: %s", err) } if err = engine.Execute(); err != nil { return nil, fmt.Errorf("cannot validate transaction: %s", err) } } buf := bytes.Buffer{} buf.Grow(msgtx.SerializeSize()) if err := msgtx.BtcEncode(&buf, btcwire.ProtocolVersion); err != nil { // Hitting OOM by growing or writing to a bytes.Buffer already // panics, and all returned errors are unexpected. panic(err) } info := &CreatedTx{ tx: btcutil.NewTx(msgtx), inputs: selectedInputs, changeAddr: changeAddr, } return info, nil }
// txToPairs creates a raw transaction sending the amounts for each // address/amount pair and fee to each address and the miner. minconf // specifies the minimum number of confirmations required before an // unspent output is eligible for spending. Leftover input funds not sent // to addr or as a fee for the miner are sent to a newly generated // address. If change is needed to return funds back to an owned // address, changeUtxo will point to a unconfirmed (height = -1, zeroed // block hash) Utxo. ErrInsufficientFunds is returned if there are not // enough eligible unspent outputs to create the transaction. func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, error) { // Create a new transaction which will include all input scripts. msgtx := btcwire.NewMsgTx() // Calculate minimum amount needed for inputs. var amt int64 for _, v := range pairs { // Error out if any amount is negative. if v <= 0 { return nil, ErrNonPositiveAmount } amt += v } // outputs is a tx.Pair slice representing each output that is created // by the transaction. outputs := make([]tx.Pair, 0, len(pairs)+1) // Add outputs to new tx. for addrStr, amt := range pairs { addr, err := btcutil.DecodeAddr(addrStr) if err != nil { return nil, fmt.Errorf("cannot decode address: %s", err) } // Add output to spend amt to addr. pkScript, err := btcscript.PayToAddrScript(addr) if err != nil { return nil, fmt.Errorf("cannot create txout script: %s", err) } txout := btcwire.NewTxOut(int64(amt), pkScript) msgtx.AddTxOut(txout) // Create amount, address pair and add to outputs. out := tx.Pair{ Amount: amt, PubkeyHash: addr.ScriptAddress(), } outputs = append(outputs, out) } // Get current block's height and hash. bs, err := GetCurBlock() if err != nil { return nil, err } // Make a copy of msgtx before any inputs are added. This will be // used as a starting point when trying a fee and starting over with // a higher fee if not enough was originally chosen. txNoInputs := msgtx.Copy() // These are nil/zeroed until a change address is needed, and reused // again in case a change utxo has already been chosen. var changeAddr *btcutil.AddressPubKeyHash var btcspent int64 var selectedInputs []*tx.Utxo var finalChangeUtxo *tx.Utxo // Get the number of satoshis to increment fee by when searching for // the minimum tx fee needed. fee := int64(0) for { msgtx = txNoInputs.Copy() // Select unspent outputs to be used in transaction based on the amount // neededing to sent, and the current fee estimation. inputs, btcin, err := selectInputs(a.UtxoStore, uint64(amt+fee), minconf) if err != nil { return nil, err } // Check if there are leftover unspent outputs, and return coins back to // a new address we own. var changeUtxo *tx.Utxo change := btcin - uint64(amt+fee) if change > 0 { // Create a new address to spend leftover outputs to. // Get a new change address if one has not already been found. if changeAddr == nil { changeAddr, err = a.ChangeAddress(&bs, cfg.KeypoolSize) if err != nil { return nil, fmt.Errorf("failed to get next address: %s", err) } // Mark change address as belonging to this account. MarkAddressForAccount(changeAddr.EncodeAddress(), a.Name()) } // Spend change. pkScript, err := btcscript.PayToAddrScript(changeAddr) if err != nil { return nil, fmt.Errorf("cannot create txout script: %s", err) } msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript)) changeUtxo = &tx.Utxo{ Amt: change, Out: tx.OutPoint{ // Hash is unset (zeroed) here and must be filled in // with the transaction hash of the complete // transaction. Index: uint32(len(pairs)), }, Height: -1, Subscript: pkScript, } copy(changeUtxo.AddrHash[:], changeAddr.ScriptAddress()) } // Selected unspent outputs become new transaction's inputs. for _, ip := range inputs { msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&ip.Out), nil)) } for i, ip := range inputs { // Error is ignored as the length and network checks can never fail // for these inputs. addr, _ := btcutil.NewAddressPubKeyHash(ip.AddrHash[:], a.Wallet.Net()) privkey, err := a.AddressKey(addr) if err == wallet.ErrWalletLocked { return nil, wallet.ErrWalletLocked } else if err != nil { return nil, fmt.Errorf("cannot get address key: %v", err) } ai, err := a.AddressInfo(addr) if err != nil { return nil, fmt.Errorf("cannot get address info: %v", err) } sigscript, err := btcscript.SignatureScript(msgtx, i, ip.Subscript, btcscript.SigHashAll, privkey, ai.Compressed) if err != nil { return nil, fmt.Errorf("cannot create sigscript: %s", err) } msgtx.TxIn[i].SignatureScript = sigscript } noFeeAllowed := false if !cfg.DisallowFree { noFeeAllowed = allowFree(bs.Height, inputs, msgtx.SerializeSize()) } if minFee := minimumFee(msgtx, noFeeAllowed); fee < minFee { fee = minFee } else { // Fill Tx hash of change outpoint with transaction hash. if changeUtxo != nil { txHash, err := msgtx.TxSha() if err != nil { return nil, fmt.Errorf("cannot create transaction hash: %s", err) } copy(changeUtxo.Out.Hash[:], txHash[:]) // Add change to outputs. out := tx.Pair{ Amount: int64(change), PubkeyHash: changeAddr.ScriptAddress(), Change: true, } outputs = append(outputs, out) finalChangeUtxo = changeUtxo } selectedInputs = inputs btcspent = int64(btcin) break } } // Validate msgtx before returning the raw transaction. flags := btcscript.ScriptCanonicalSignatures bip16 := time.Now().After(btcscript.Bip16Activation) if bip16 { flags |= btcscript.ScriptBip16 } for i, txin := range msgtx.TxIn { engine, err := btcscript.NewScript(txin.SignatureScript, selectedInputs[i].Subscript, i, msgtx, flags) if err != nil { return nil, fmt.Errorf("cannot create script engine: %s", err) } if err = engine.Execute(); err != nil { return nil, fmt.Errorf("cannot validate transaction: %s", err) } } txid, err := msgtx.TxSha() if err != nil { return nil, fmt.Errorf("cannot create txid for created tx: %v", err) } buf := new(bytes.Buffer) msgtx.BtcEncode(buf, btcwire.ProtocolVersion) info := &CreatedTx{ rawTx: buf.Bytes(), txid: txid, time: time.Now(), inputs: selectedInputs, outputs: outputs, btcspent: btcspent, fee: fee, changeAddr: changeAddr, changeUtxo: finalChangeUtxo, } return info, nil }