コード例 #1
1
ファイル: example_test.go プロジェクト: stoiclabs/blockchainr
// 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
}
コード例 #2
0
ファイル: mining.go プロジェクト: stoiclabs/blockchainr
// 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
}
コード例 #3
0
ファイル: buildfanout.go プロジェクト: bclermont/btcbuilder
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
}
コード例 #4
0
ファイル: txutil.go プロジェクト: bclermont/btcbuilder
// 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
}
コード例 #5
0
ファイル: createtx_test.go プロジェクト: kingpro/btcwallet
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
	}
}
コード例 #6
0
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)
}
コード例 #7
0
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
}
コード例 #8
0
ファイル: builddust.go プロジェクト: bclermont/btcbuilder
// 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
}
コード例 #9
0
ファイル: bulletin.go プロジェクト: bclermont/protocol
// 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
}
コード例 #10
0
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
}
コード例 #11
0
ファイル: mining.go プロジェクト: nixoid/btcd
// 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
}
コード例 #12
0
ファイル: createtx.go プロジェクト: GeertJohan/btcwallet
// 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
}
コード例 #13
0
ファイル: rpcserver.go プロジェクト: Cryptoper/btcd
// 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
}
コード例 #14
0
ファイル: createtx.go プロジェクト: kingpro/btcwallet
// 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
}
コード例 #15
0
ファイル: createtx.go プロジェクト: kawalgrover/btcwallet
// 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
}