func (bltnB *BulletinBuilder) Build() (*btcwire.MsgTx, error) { utxo, err := selectUnspent(bltnB.SatNeeded(), bltnB.Params) if err != nil { return nil, err } msgtx := btcwire.NewMsgTx() // Add data storing txouts. txouts, err := bltnB.Bulletin.TxOuts(bltnB.BurnAmnt, bltnB.Params.NetParams) if err != nil { return nil, err } msgtx.TxOut = txouts txin := btcwire.NewTxIn(utxo.OutPoint, []byte{}) msgtx.AddTxIn(txin) // Deal with change changeAmnt := utxo.TxOut.Value - bltnB.SatNeeded() if changeAmnt > bltnB.Params.DustAmnt { changeOut, err := makeChange(changeAmnt, bltnB.Params) if err != nil { return nil, err } msgtx.AddTxOut(changeOut) } // Sign the Bulletin privkey := utxo.Wif.PrivKey scriptSig, err := btcscript.SignatureScript(msgtx, 0, utxo.TxOut.PkScript, btcscript.SigHashAll, privkey, true) if err != nil { return nil, err } txin.SignatureScript = scriptSig return msgtx, 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 }
func signScript(tx *btcwire.MsgTx, idx int, subscript []byte, privKey string) ([]byte, error) { wif, err := btcutil.DecodeWIF(privKey) if err != nil { return nil, err } return btcscript.SignatureScript(tx, idx, subscript, SIGHASH_ALL, wif.PrivKey.ToECDSA(), wif.CompressPubKey) }
func (shsB *SigHashSingleBuilder) Build() (*btcwire.MsgTx, error) { // RPC to setup previous TX utxo, err := selectUnspent(shsB.SatNeeded()+shsB.Params.DustAmnt, shsB.Params) if err != nil { return nil, err } oldTxOut := utxo.TxOut outpoint := utxo.OutPoint wifkey := utxo.Wif // Transaction building txin := btcwire.NewTxIn(outpoint, []byte{}) // notice amount in total := oldTxOut.Value changeval := total - (shsB.SatNeeded()) change, ok := changeOutput(changeval, shsB.Params.DustAmnt, wifToAddr(wifkey, shsB.Params.NetParams)) if !ok { return nil, errors.New("Not enough for change.") } // Blank permutable txout for users to play with blankval := shsB.Params.InTarget - shsB.Params.Fee blank := btcwire.NewTxOut(blankval, change.PkScript) //[]byte{}) msgtx := btcwire.NewMsgTx() msgtx.AddTxIn(txin) msgtx.AddTxOut(change) msgtx.AddTxOut(blank) subscript := oldTxOut.PkScript privkey := wifkey.PrivKey scriptSig, err := btcscript.SignatureScript(msgtx, 0, subscript, btcscript.SigHashSingle, privkey, true) if err != nil { return nil, err } msgtx.TxIn[0].SignatureScript = scriptSig // This demonstrates that we can sign and then permute a txout //msgtx.TxOut[1].PkScript = oldTxOut.PkScript blank.Value = blankval + 1 return msgtx, nil }
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 }
func (ndB *NullDataBuilder) Build() (*btcwire.MsgTx, error) { utxo, err := specificUnspent(ndB.SatNeeded(), ndB.Params) if err != nil { return nil, err } msgtx := btcwire.NewMsgTx() if len(ndB.Data) > 40 { return nil, errors.New("Data is too long to make this a standard tx.") } // OP Return output retbuilder := btcscript.NewScriptBuilder().AddOp(btcscript.OP_RETURN).AddData(ndB.Data) op_return := btcwire.NewTxOut(0, retbuilder.Script()) msgtx.AddTxOut(op_return) if ndB.Change { // change ouput addr, _ := newAddr(ndB.Params.Client) change, ok := changeOutput(ndB.SatNeeded()-ndB.Params.Fee, ndB.Params.DustAmnt, addr) if !ok { return nil, errors.New("Not enough for change") } msgtx.AddTxOut(change) } // funding input txin := btcwire.NewTxIn(utxo.OutPoint, []byte{}) msgtx.AddTxIn(txin) // sign msgtx privkey := utxo.Wif.PrivKey scriptSig, err := btcscript.SignatureScript(msgtx, 0, utxo.TxOut.PkScript, 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 }
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 }
// TODO This will add multisig Txouts to the unspent set be AWARE func (msB *MultiSigBuilder) Build() (*btcwire.MsgTx, error) { utxo, err := specificUnspent(msB.SatNeeded(), msB.Params) if err != nil { return nil, err } msgtx := btcwire.NewMsgTx() txin := btcwire.NewTxIn(utxo.OutPoint, []byte{}) msgtx.AddTxIn(txin) for _, pubkeys := range msB.PubKeyList { // M pubkey pubkey pubkey N OP_CHECKMULTISIG scriptBuilder := btcscript.NewScriptBuilder().AddInt64(msB.M) for _, pk := range pubkeys { scriptBuilder = scriptBuilder.AddData(pk) } scriptBuilder = scriptBuilder.AddInt64(msB.N).AddOp(btcscript.OP_CHECKMULTISIG) PkScript := scriptBuilder.Script() txout := btcwire.NewTxOut(msB.eachOutVal(), PkScript) msgtx.AddTxOut(txout) } // Sign this puppy privkey := utxo.Wif.PrivKey subscript := utxo.TxOut.PkScript sigflag := btcscript.SigHashAll scriptSig, err := btcscript.SignatureScript(msgtx, 0, subscript, sigflag, privkey, true) if err != nil { return nil, err } msgtx.TxIn[0].SignatureScript = scriptSig return msgtx, 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 }
// 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 }