예제 #1
0
파일: sign.go 프로젝트: shevilangle/sports
func makeTx(outputs []output, amount, value int64, toAddr, changeAddr string) (*btcwire.MsgTx, error) {
	msgTx := btcwire.NewMsgTx()

	for _, op := range outputs {
		hash, err := btcwire.NewShaHashFromStr(op.TxHash)
		if err != nil {
			return nil, err
		}
		b, err := hex.DecodeString(op.Script)
		if err != nil {
			return nil, err
		}
		txIn := btcwire.NewTxIn(btcwire.NewOutPoint(hash, op.TxN), b)
		msgTx.AddTxIn(txIn)
	}

	script, err := makeScriptPubKey(toAddr)
	if err != nil {
		return nil, err
	}
	txOut := btcwire.NewTxOut(value, script)
	msgTx.AddTxOut(txOut)

	if amount > value {
		script, err = makeScriptPubKey(changeAddr)
		if err != nil {
			return nil, err
		}
		txOut := btcwire.NewTxOut(amount-value, script)
		msgTx.AddTxOut(txOut)
	}
	return msgTx, nil
}
예제 #2
0
// 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
파일: msgtx_test.go 프로젝트: kac-/btcwire
// TestTxSerialize tests MsgTx serialize and deserialize.
func TestTxSerialize(t *testing.T) {
	noTx := btcwire.NewMsgTx()
	noTx.Version = 1
	noTxEncoded := []byte{
		0x01, 0x00, 0x00, 0x00, // Version
		0x00,                   // Varint for number of input transactions
		0x00,                   // Varint for number of output transactions
		0x00, 0x00, 0x00, 0x00, // Lock time
	}

	tests := []struct {
		in  *btcwire.MsgTx // Message to encode
		out *btcwire.MsgTx // Expected decoded message
		buf []byte         // Serialized data
	}{
		// No transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
		},

		// Multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
		},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		// Serialize the transaction.
		var buf bytes.Buffer
		err := test.in.Serialize(&buf)
		if err != nil {
			t.Errorf("Serialize #%d error %v", i, err)
			continue
		}
		if !bytes.Equal(buf.Bytes(), test.buf) {
			t.Errorf("Serialize #%d\n got: %s want: %s", i,
				spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
			continue
		}

		// Deserialize the transaction.
		var tx btcwire.MsgTx
		rbuf := bytes.NewReader(test.buf)
		err = tx.Deserialize(rbuf)
		if err != nil {
			t.Errorf("Deserialize #%d error %v", i, err)
			continue
		}
		if !reflect.DeepEqual(&tx, test.out) {
			t.Errorf("Deserialize #%d\n got: %s want: %s", i,
				spew.Sdump(&tx), spew.Sdump(test.out))
			continue
		}
	}
}
예제 #4
0
파일: msgtx_test.go 프로젝트: kac-/btcwire
// TestTxSerializeSize performs tests to ensure the serialize size for various
// transactions is accurate.
func TestTxSerializeSize(t *testing.T) {
	// Empty tx message.
	noTx := btcwire.NewMsgTx()
	noTx.Version = 1

	tests := []struct {
		in   *btcwire.MsgTx // Tx to encode
		size int            // Expected serialized size
	}{
		// No inputs or outpus.
		{noTx, 10},

		// Transcaction with an input and an output.
		{multiTx, 134},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		serializedSize := test.in.SerializeSize()
		if serializedSize != test.size {
			t.Errorf("MsgTx.SerializeSize: #%d got: %d, want: %d", i,
				serializedSize, test.size)
			continue
		}
	}
}
예제 #5
0
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
}
예제 #6
0
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
}
예제 #7
0
파일: msgtx_test.go 프로젝트: h00gs/btcwire
func TestTxSha(t *testing.T) {
	pver := btcwire.ProtocolVersion

	// Hash of first transaction from block 113875.
	hashStr := "f051e59b5e2503ac626d03aaeac8ab7be2d72ba4b7e97119c5852d70d52dcb86"
	wantHash, err := btcwire.NewShaHashFromStr(hashStr)
	if err != nil {
		t.Errorf("NewShaHashFromStr: %v", err)
		return
	}

	// First transaction from block 113875.
	msgTx := btcwire.NewMsgTx()
	txIn := btcwire.TxIn{
		PreviousOutpoint: btcwire.OutPoint{
			Hash:  btcwire.ShaHash{0x00},
			Index: 0xffffffff,
		},
		SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62},
		Sequence:        0xffffffff,
	}
	txOut := btcwire.TxOut{
		Value: 5000000000,
		PkScript: []byte{
			0x41, // OP_DATA_65
			0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
			0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
			0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
			0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
			0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
			0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
			0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
			0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
			0xa6, // 65-byte signature
			0xac, // OP_CHECKSIG
		},
	}
	msgTx.AddTxIn(&txIn)
	msgTx.AddTxOut(&txOut)
	msgTx.LockTime = 0

	// Ensure the hash produced is expected.
	txHash, err := msgTx.TxSha(pver)
	if err != nil {
		t.Errorf("TxSha: %v", err)
	}
	if !txHash.IsEqual(wantHash) {
		t.Errorf("TxSha: wrong hash - got %v, want %v",
			spew.Sprint(txHash), spew.Sprint(wantHash))
	}
}
예제 #8
0
// NewMsgTxWithInputCoins takes the coins in the CoinSet and makes them
// the inputs to a new btcwire.MsgTx which is returned.
func NewMsgTxWithInputCoins(inputCoins Coins) *btcwire.MsgTx {
	msgTx := btcwire.NewMsgTx()
	coins := inputCoins.Coins()
	msgTx.TxIn = make([]*btcwire.TxIn, len(coins))
	for i, coin := range coins {
		msgTx.TxIn[i] = &btcwire.TxIn{
			PreviousOutPoint: btcwire.OutPoint{
				Hash:  *coin.Hash(),
				Index: coin.Index(),
			},
			SignatureScript: nil,
			Sequence:        btcwire.MaxTxInSequenceNum,
		}
	}
	return msgTx
}
예제 #9
0
파일: rpcserver.go 프로젝트: shea256/btcd
// handleSendRawTransaction implements the sendrawtransaction command.
func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
	c := cmd.(*btcjson.SendRawTransactionCmd)
	// Deserialize and send off to tx relay
	serializedTx, err := hex.DecodeString(c.HexTx)
	if err != nil {
		return nil, btcjson.ErrDecodeHexString
	}
	msgtx := btcwire.NewMsgTx()
	err = msgtx.Deserialize(bytes.NewBuffer(serializedTx))
	if err != nil {
		err := btcjson.Error{
			Code:    btcjson.ErrDeserialization.Code,
			Message: "TX decode failed",
		}
		return nil, err
	}

	tx := btcutil.NewTx(msgtx)
	err = s.server.txMemPool.ProcessTransaction(tx, false)
	if err != nil {
		// When the error is a rule error, it means the transaction was
		// simply rejected as opposed to something actually going wrong,
		// so log it as such.  Otherwise, something really did go wrong,
		// so log it as an actual error.  In both cases, a JSON-RPC
		// error is returned to the client with the deserialization
		// error code (to match bitcoind behavior).
		if _, ok := err.(TxRuleError); ok {
			rpcsLog.Debugf("Rejected transaction %v: %v", tx.Sha(),
				err)
		} else {
			rpcsLog.Errorf("Failed to process transaction %v: %v",
				tx.Sha(), err)
		}
		err = btcjson.Error{
			Code:    btcjson.ErrDeserialization.Code,
			Message: fmt.Sprintf("TX rejected: %v", err),
		}
		return nil, err
	}

	// We keep track of all the sendrawtransaction request txs so that we
	// can rebroadcast them if they don't make their way into a block.
	iv := btcwire.NewInvVect(btcwire.InvTypeTx, tx.Sha())
	s.server.AddRebroadcastInventory(iv)

	return tx.Sha().String(), nil
}
예제 #10
0
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
}
예제 #11
0
// handleSendRawTransaction implements the sendrawtransaction command.
func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) {
	c := cmd.(*btcjson.SendRawTransactionCmd)
	// Deserialize and send off to tx relay
	serializedTx, err := hex.DecodeString(c.HexTx)
	if err != nil {
		return nil, btcjson.ErrDecodeHexString
	}
	msgtx := btcwire.NewMsgTx()
	err = msgtx.Deserialize(bytes.NewBuffer(serializedTx))
	if err != nil {
		err := btcjson.Error{
			Code:    btcjson.ErrDeserialization.Code,
			Message: "Unable to deserialize raw tx",
		}
		return nil, err
	}

	tx := btcutil.NewTx(msgtx)
	err = s.server.txMemPool.ProcessTransaction(tx)
	if err != nil {
		// When the error is a rule error, it means the transaction was
		// simply rejected as opposed to something actually going wrong,
		// so log it as such.  Otherwise, something really did go wrong,
		// so log it as an actual error.
		if _, ok := err.(TxRuleError); ok {
			log.Debugf("RPCS: Rejected transaction %v: %v", tx.Sha(),
				err)
		} else {
			log.Errorf("RPCS: Failed to process transaction %v: %v",
				tx.Sha(), err)
		}
		err = btcjson.Error{
			Code:    btcjson.ErrDeserialization.Code,
			Message: "Failed to process transaction",
		}
		return nil, err
	}

	// If called from websocket code, add a mined tx hashes
	// request.
	if walletNotification != nil {
		s.ws.requests.AddMinedTxRequest(walletNotification, tx.Sha())
	}

	return tx.Sha().String(), nil
}
예제 #12
0
// TestCheckSerializedHeight tests the checkSerializedHeight function with
// various serialized heights and also does negative tests to ensure errors
// and handled properly.
func TestCheckSerializedHeight(t *testing.T) {
	// Create an empty coinbase template to be used in the tests below.
	coinbaseOutpoint := btcwire.NewOutPoint(&btcwire.ShaHash{}, math.MaxUint32)
	coinbaseTx := btcwire.NewMsgTx()
	coinbaseTx.Version = 2
	coinbaseTx.AddTxIn(btcwire.NewTxIn(coinbaseOutpoint, nil))

	//
	tests := []struct {
		sigScript  []byte // Serialized data
		wantHeight int64  // Expected height
		err        error  // Expected error type
	}{
		// No serialized height length.
		{[]byte{}, 0, btcchain.RuleError("")},
		// Serialized height length with no height bytes.
		{[]byte{0x02}, 0, btcchain.RuleError("")},
		// Serialized height length with too few height bytes.
		{[]byte{0x02, 0x4a}, 0, btcchain.RuleError("")},
		// Serialized height that needs 2 bytes to encode.
		{[]byte{0x02, 0x4a, 0x52}, 21066, nil},
		// Serialized height that needs 2 bytes to encode, but backwards
		// endianness.
		{[]byte{0x02, 0x4a, 0x52}, 19026, btcchain.RuleError("")},
		// Serialized height that needs 3 bytes to encode.
		{[]byte{0x03, 0x40, 0x0d, 0x03}, 200000, nil},
		// Serialized height that needs 3 bytes to encode, but backwards
		// endianness.
		{[]byte{0x03, 0x40, 0x0d, 0x03}, 1074594560, btcchain.RuleError("")},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		msgTx := coinbaseTx.Copy()
		msgTx.TxIn[0].SignatureScript = test.sigScript
		tx := btcutil.NewTx(msgTx)

		err := btcchain.TstCheckSerializedHeight(tx, test.wantHeight)
		if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
			t.Errorf("checkSerializedHeight #%d wrong error type "+
				"got: %v <%T>, want: %T", i, err, err, test.err)
			continue
		}
	}
}
예제 #13
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
}
예제 #14
0
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
}
예제 #15
0
// 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
}
예제 #16
0
파일: rpcserver.go 프로젝트: hsk81/btcd
// handleSendRawTransaction implements the sendrawtransaction command.
func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
	c := cmd.(*btcjson.SendRawTransactionCmd)
	// Deserialize and send off to tx relay
	serializedTx, err := hex.DecodeString(c.HexTx)
	if err != nil {
		return nil, btcjson.ErrDecodeHexString
	}
	msgtx := btcwire.NewMsgTx()
	err = msgtx.Deserialize(bytes.NewBuffer(serializedTx))
	if err != nil {
		err := btcjson.Error{
			Code:    btcjson.ErrDeserialization.Code,
			Message: "TX decode failed",
		}
		return nil, err
	}

	tx := btcutil.NewTx(msgtx)
	err = s.server.txMemPool.ProcessTransaction(tx)
	if err != nil {
		// When the error is a rule error, it means the transaction was
		// simply rejected as opposed to something actually going wrong,
		// so log it as such.  Otherwise, something really did go wrong,
		// so log it as an actual error.
		if _, ok := err.(TxRuleError); ok {
			rpcsLog.Debugf("Rejected transaction %v: %v", tx.Sha(),
				err)
		} else {
			rpcsLog.Errorf("Failed to process transaction %v: %v",
				tx.Sha(), err)
			err = btcjson.Error{
				Code:    btcjson.ErrDeserialization.Code,
				Message: "TX rejected",
			}
			return nil, err
		}
	}

	return tx.Sha().String(), nil
}
예제 #17
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
}
예제 #18
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
}
예제 #19
0
// Returns the "Author" who signed the first txin of the transaction
func getAuthor(tx *btcwire.MsgTx, net *btcnet.Params) (string, error) {
	sigScript := tx.TxIn[0].SignatureScript

	dummyTx := btcwire.NewMsgTx()

	// Setup a script executor to parse the raw bytes of the signature script.
	script, err := btcscript.NewScript(sigScript, make([]byte, 0), 0, dummyTx, 0)
	if err != nil {
		return "", err
	}
	// Step twice due to <sig> <pubkey> format of pay 2pubkeyhash
	script.Step()
	script.Step()
	// Pull off the <pubkey>
	pkBytes := script.GetStack()[1]

	addrPubKey, err := btcutil.NewAddressPubKey(pkBytes, net)
	if err != nil {
		return "", err
	}

	return addrPubKey.EncodeAddress(), nil
}
예제 #20
0
// 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
}
예제 #21
0
// 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
}
예제 #22
0
파일: msgtx_test.go 프로젝트: kac-/btcwire
// TestTxWire tests the MsgTx wire encode and decode for various numbers
// of transaction inputs and outputs and protocol versions.
func TestTxWire(t *testing.T) {
	// Empty tx message.
	noTx := btcwire.NewMsgTx()
	noTx.Version = 1
	noTxEncoded := []byte{
		0x01, 0x00, 0x00, 0x00, // Version
		0x00,                   // Varint for number of input transactions
		0x00,                   // Varint for number of output transactions
		0x00, 0x00, 0x00, 0x00, // Lock time
	}

	tests := []struct {
		in   *btcwire.MsgTx // Message to encode
		out  *btcwire.MsgTx // Expected decoded message
		buf  []byte         // Wire encoding
		pver uint32         // Protocol version for wire encoding
	}{
		// Latest protocol version with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.ProtocolVersion,
		},

		// Latest protocol version with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.ProtocolVersion,
		},

		// Protocol version BIP0035Version with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.BIP0035Version,
		},

		// Protocol version BIP0035Version with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.BIP0035Version,
		},

		// Protocol version BIP0031Version with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.BIP0031Version,
		},

		// Protocol version BIP0031Version with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.BIP0031Version,
		},

		// Protocol version NetAddressTimeVersion with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.NetAddressTimeVersion,
		},

		// Protocol version NetAddressTimeVersion with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.NetAddressTimeVersion,
		},

		// Protocol version MultipleAddressVersion with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.MultipleAddressVersion,
		},

		// Protocol version MultipleAddressVersion with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.MultipleAddressVersion,
		},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		// Encode the message to wire format.
		var buf bytes.Buffer
		err := test.in.BtcEncode(&buf, test.pver)
		if err != nil {
			t.Errorf("BtcEncode #%d error %v", i, err)
			continue
		}
		if !bytes.Equal(buf.Bytes(), test.buf) {
			t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
				spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
			continue
		}

		// Decode the message from wire format.
		var msg btcwire.MsgTx
		rbuf := bytes.NewReader(test.buf)
		err = msg.BtcDecode(rbuf, test.pver)
		if err != nil {
			t.Errorf("BtcDecode #%d error %v", i, err)
			continue
		}
		if !reflect.DeepEqual(&msg, test.out) {
			t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
				spew.Sdump(&msg), spew.Sdump(test.out))
			continue
		}
	}
}
예제 #23
0
파일: msgtx_test.go 프로젝트: kac-/btcwire
// TestTx tests the MsgTx API.
func TestTx(t *testing.T) {
	pver := btcwire.ProtocolVersion

	// Block 100000 hash.
	hashStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
	hash, err := btcwire.NewShaHashFromStr(hashStr)
	if err != nil {
		t.Errorf("NewShaHashFromStr: %v", err)
	}

	// Ensure the command is expected value.
	wantCmd := "tx"
	msg := btcwire.NewMsgTx()
	if cmd := msg.Command(); cmd != wantCmd {
		t.Errorf("NewMsgAddr: wrong command - got %v want %v",
			cmd, wantCmd)
	}

	// Ensure max payload is expected value for latest protocol version.
	// Num addresses (varInt) + max allowed addresses.
	wantPayload := uint32(1000 * 1000)
	maxPayload := msg.MaxPayloadLength(pver)
	if maxPayload != wantPayload {
		t.Errorf("MaxPayloadLength: wrong max payload length for "+
			"protocol version %d - got %v, want %v", pver,
			maxPayload, wantPayload)
	}

	// Ensure we get the same transaction output point data back out.
	prevOutIndex := uint32(1)
	prevOut := btcwire.NewOutPoint(hash, prevOutIndex)
	if !prevOut.Hash.IsEqual(hash) {
		t.Errorf("NewOutPoint: wrong hash - got %v, want %v",
			spew.Sprint(&prevOut.Hash), spew.Sprint(hash))
	}
	if prevOut.Index != prevOutIndex {
		t.Errorf("NewOutPoint: wrong index - got %v, want %v",
			prevOut.Index, prevOutIndex)
	}

	// Ensure we get the same transaction input back out.
	sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}
	txIn := btcwire.NewTxIn(prevOut, sigScript)
	if !reflect.DeepEqual(&txIn.PreviousOutpoint, prevOut) {
		t.Errorf("NewTxIn: wrong prev outpoint - got %v, want %v",
			spew.Sprint(&txIn.PreviousOutpoint),
			spew.Sprint(prevOut))
	}
	if !bytes.Equal(txIn.SignatureScript, sigScript) {
		t.Errorf("NewTxIn: wrong signature script - got %v, want %v",
			spew.Sdump(txIn.SignatureScript),
			spew.Sdump(sigScript))
	}

	// Ensure we get the same transaction output back out.
	txValue := int64(5000000000)
	pkScript := []byte{
		0x41, // OP_DATA_65
		0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
		0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
		0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
		0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
		0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
		0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
		0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
		0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
		0xa6, // 65-byte signature
		0xac, // OP_CHECKSIG
	}
	txOut := btcwire.NewTxOut(txValue, pkScript)
	if txOut.Value != txValue {
		t.Errorf("NewTxOut: wrong pk script - got %v, want %v",
			txOut.Value, txValue)

	}
	if !bytes.Equal(txOut.PkScript, pkScript) {
		t.Errorf("NewTxOut: wrong pk script - got %v, want %v",
			spew.Sdump(txOut.PkScript),
			spew.Sdump(pkScript))
	}

	// Ensure transaction inputs are added properly.
	msg.AddTxIn(txIn)
	if !reflect.DeepEqual(msg.TxIn[0], txIn) {
		t.Errorf("AddTxIn: wrong transaction input added - got %v, want %v",
			spew.Sprint(msg.TxIn[0]), spew.Sprint(txIn))
	}

	// Ensure transaction outputs are added properly.
	msg.AddTxOut(txOut)
	if !reflect.DeepEqual(msg.TxOut[0], txOut) {
		t.Errorf("AddTxIn: wrong transaction output added - got %v, want %v",
			spew.Sprint(msg.TxOut[0]), spew.Sprint(txOut))
	}

	// Ensure the copy produced an identical transaction message.
	newMsg := msg.Copy()
	if !reflect.DeepEqual(newMsg, msg) {
		t.Errorf("Copy: mismatched tx messages - got %v, want %v",
			spew.Sdump(newMsg), spew.Sdump(msg))
	}

	return
}
예제 #24
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
}
예제 #25
0
파일: msgtx_test.go 프로젝트: h00gs/btcwire
// TestTxWire tests the MsgTx wire encode and decode for various numbers
// of transaction inputs and outputs and protocol versions.
func TestTxWire(t *testing.T) {
	// Previous transaction output point for coinbase to test.
	prevOutIndex := uint32(0xffffffff)
	prevOut := btcwire.NewOutPoint(&btcwire.ShaHash{}, prevOutIndex)

	// Transaction input to test.
	sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}
	txIn := btcwire.NewTxIn(prevOut, sigScript)
	txIn.Sequence = 0xffffffff

	// Transaction output to test.
	txValue := int64(5000000000)
	pkScript := []byte{
		0x41, // OP_DATA_65
		0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
		0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
		0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
		0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
		0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
		0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
		0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
		0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
		0xa6, // 65-byte signature
		0xac, // OP_CHECKSIG
	}
	txOut := btcwire.NewTxOut(txValue, pkScript)

	// Empty tx message.
	noTx := btcwire.NewMsgTx()
	noTx.Version = 1
	noTxEncoded := []byte{
		0x01, 0x00, 0x00, 0x00, // Version
		0x00,                   // Varint for number of input transactions
		0x00,                   // Varint for number of output transactions
		0x00, 0x00, 0x00, 0x00, // Lock time
	}

	multiTx := btcwire.NewMsgTx()
	multiTx.Version = 1
	multiTx.AddTxIn(txIn)
	multiTx.AddTxOut(txOut)
	multiTx.LockTime = 0
	multiTxEncoded := []byte{
		0x01, 0x00, 0x00, 0x00, // Version
		0x01, // Varint for number of input transactions
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
		0xff, 0xff, 0xff, 0xff, // Prevous output index
		0x07,                                     // Varint for length of signature script
		0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62, // Signature script
		0xff, 0xff, 0xff, 0xff, // Sequence
		0x01,                                           // Varint for number of output transactions
		0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
		0x43, // Varint for length of pk script
		0x41, // OP_DATA_65
		0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
		0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
		0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
		0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
		0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
		0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
		0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
		0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
		0xa6,                   // 65-byte signature
		0xac,                   // OP_CHECKSIG
		0x00, 0x00, 0x00, 0x00, // Lock time
	}

	tests := []struct {
		in   *btcwire.MsgTx // Message to encode
		out  *btcwire.MsgTx // Expected decoded message
		buf  []byte         // Wire encoding
		pver uint32         // Protocol version for wire encoding
	}{
		// Latest protocol version with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.ProtocolVersion,
		},

		// Latest protocol version with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.ProtocolVersion,
		},

		// Protocol version BIP0035Version with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.BIP0035Version,
		},

		// Protocol version BIP0035Version with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.BIP0035Version,
		},

		// Protocol version BIP0031Version with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.BIP0031Version,
		},

		// Protocol version BIP0031Version with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.BIP0031Version,
		},

		// Protocol version NetAddressTimeVersion with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.NetAddressTimeVersion,
		},

		// Protocol version NetAddressTimeVersion with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.NetAddressTimeVersion,
		},

		// Protocol version MultipleAddressVersion with no transactions.
		{
			noTx,
			noTx,
			noTxEncoded,
			btcwire.MultipleAddressVersion,
		},

		// Protocol version MultipleAddressVersion with multiple transactions.
		{
			multiTx,
			multiTx,
			multiTxEncoded,
			btcwire.MultipleAddressVersion,
		},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		// Encode the message to wire format.
		var buf bytes.Buffer
		err := test.in.BtcEncode(&buf, test.pver)
		if err != nil {
			t.Errorf("BtcEncode #%d error %v", i, err)
			continue
		}
		if !bytes.Equal(buf.Bytes(), test.buf) {
			t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
				spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
			continue
		}

		// Decode the message from wire format.
		var msg btcwire.MsgTx
		rbuf := bytes.NewBuffer(test.buf)
		err = msg.BtcDecode(rbuf, test.pver)
		if err != nil {
			t.Errorf("BtcDecode #%d error %v", i, err)
			continue
		}
		if !reflect.DeepEqual(&msg, test.out) {
			t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
				spew.Sdump(&msg), spew.Sdump(test.out))
			continue
		}
	}
}
예제 #26
0
func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
	// Create a double spend of the received blockchain transaction.
	dupRecvTx, _ := btcutil.NewTxFromBytes(TstRecvSerializedTx)
	// Switch txout amount to 1 BTC.  Transaction store doesn't
	// validate txs, so this is fine for testing a double spend
	// removal.
	TstDupRecvAmount := int64(1e8)
	newDupMsgTx := dupRecvTx.MsgTx()
	newDupMsgTx.TxOut[0].Value = TstDupRecvAmount
	TstDoubleSpendTx := btcutil.NewTx(newDupMsgTx)

	// Create a "signed" (with invalid sigs) tx that spends output 0 of
	// the double spend.
	spendingTx := btcwire.NewMsgTx()
	spendingTxIn := btcwire.NewTxIn(btcwire.NewOutPoint(TstDoubleSpendTx.Sha(), 0), []byte{0, 1, 2, 3, 4})
	spendingTx.AddTxIn(spendingTxIn)
	spendingTxOut1 := btcwire.NewTxOut(1e7, []byte{5, 6, 7, 8, 9})
	spendingTxOut2 := btcwire.NewTxOut(9e7, []byte{10, 11, 12, 13, 14})
	spendingTx.AddTxOut(spendingTxOut1)
	spendingTx.AddTxOut(spendingTxOut2)
	TstSpendingTx := btcutil.NewTx(spendingTx)
	var _ = TstSpendingTx

	tests := []struct {
		name     string
		f        func(*Store) (*Store, error)
		bal, unc btcutil.Amount
		unspents map[btcwire.OutPoint]struct{}
		unmined  map[btcwire.ShaHash]struct{}
	}{
		{
			name: "new store",
			f: func(_ *Store) (*Store, error) {
				return New(), nil
			},
			bal:      0,
			unc:      0,
			unspents: map[btcwire.OutPoint]struct{}{},
			unmined:  map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "txout insert",
			f: func(s *Store) (*Store, error) {
				r, err := s.InsertTx(TstRecvTx, nil)
				if err != nil {
					return nil, err
				}

				_, err = r.AddCredit(0, false)
				if err != nil {
					return nil, err
				}
				// Verify that we can create the JSON output without any
				// errors.
				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: 0,
			unc: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstRecvTx.Sha(), 0): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "insert duplicate unconfirmed",
			f: func(s *Store) (*Store, error) {
				r, err := s.InsertTx(TstRecvTx, nil)
				if err != nil {
					return nil, err
				}

				_, err = r.AddCredit(0, false)
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: 0,
			unc: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstRecvTx.Sha(), 0): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "confirmed txout insert",
			f: func(s *Store) (*Store, error) {
				TstRecvTx.SetIndex(TstRecvIndex)
				r, err := s.InsertTx(TstRecvTx, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				_, err = r.AddCredit(0, false)
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unc: 0,
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstRecvTx.Sha(), 0): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "insert duplicate confirmed",
			f: func(s *Store) (*Store, error) {
				TstRecvTx.SetIndex(TstRecvIndex)
				r, err := s.InsertTx(TstRecvTx, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				_, err = r.AddCredit(0, false)
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unc: 0,
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstRecvTx.Sha(), 0): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "rollback confirmed credit",
			f: func(s *Store) (*Store, error) {
				err := s.Rollback(TstRecvTxBlockDetails.Height)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: 0,
			unc: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstRecvTx.Sha(), 0): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "insert confirmed double spend",
			f: func(s *Store) (*Store, error) {
				TstDoubleSpendTx.SetIndex(TstRecvIndex)
				r, err := s.InsertTx(TstDoubleSpendTx, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				_, err = r.AddCredit(0, false)
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)

				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: btcutil.Amount(TstDoubleSpendTx.MsgTx().TxOut[0].Value),
			unc: 0,
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstDoubleSpendTx.Sha(), 0): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "insert unconfirmed debit",
			f: func(s *Store) (*Store, error) {
				prev, err := s.InsertTx(TstDoubleSpendTx, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				r, err := s.InsertTx(TstSpendingTx, nil)
				if err != nil {
					return nil, err
				}

				_, err = r.AddDebits(prev.Credits())
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal:      0,
			unc:      0,
			unspents: map[btcwire.OutPoint]struct{}{},
			unmined: map[btcwire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "insert unconfirmed debit again",
			f: func(s *Store) (*Store, error) {
				prev, err := s.InsertTx(TstDoubleSpendTx, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				r, err := s.InsertTx(TstSpendingTx, nil)
				if err != nil {
					return nil, err
				}

				_, err = r.AddDebits(prev.Credits())
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal:      0,
			unc:      0,
			unspents: map[btcwire.OutPoint]struct{}{},
			unmined: map[btcwire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "insert change (index 0)",
			f: func(s *Store) (*Store, error) {
				r, err := s.InsertTx(TstSpendingTx, nil)
				if err != nil {
					return nil, err
				}

				_, err = r.AddCredit(0, true)
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: 0,
			unc: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value),
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 0): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "insert output back to this own wallet (index 1)",
			f: func(s *Store) (*Store, error) {
				r, err := s.InsertTx(TstSpendingTx, nil)
				if err != nil {
					return nil, err
				}

				_, err = r.AddCredit(1, true)
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: 0,
			unc: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 0): {},
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 1): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "confirm signed tx",
			f: func(s *Store) (*Store, error) {
				TstSpendingTx.SetIndex(TstSignedTxIndex)
				r, err := s.InsertTx(TstSpendingTx, TstSignedTxBlockDetails)
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unc: 0,
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 0): {},
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 1): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "rollback after spending tx",
			f: func(s *Store) (*Store, error) {
				err := s.Rollback(TstSignedTxBlockDetails.Height + 1)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unc: 0,
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 0): {},
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 1): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
		{
			name: "rollback spending tx block",
			f: func(s *Store) (*Store, error) {
				err := s.Rollback(TstSignedTxBlockDetails.Height)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: 0,
			unc: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 0): {},
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 1): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "rollback double spend tx block",
			f: func(s *Store) (*Store, error) {
				err := s.Rollback(TstRecvTxBlockDetails.Height)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: 0,
			unc: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 0): {},
				*btcwire.NewOutPoint(TstSpendingTx.Sha(), 1): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "insert original recv txout",
			f: func(s *Store) (*Store, error) {
				TstRecvTx.SetIndex(TstRecvIndex)
				r, err := s.InsertTx(TstRecvTx, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				_, err = r.AddCredit(0, false)
				if err != nil {
					return nil, err
				}

				_, err = r.ToJSON("", 100, &btcnet.MainNetParams)
				if err != nil {
					return nil, err
				}
				return s, nil
			},
			bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unc: 0,
			unspents: map[btcwire.OutPoint]struct{}{
				*btcwire.NewOutPoint(TstRecvTx.Sha(), 0): {},
			},
			unmined: map[btcwire.ShaHash]struct{}{},
		},
	}

	var s *Store
	for _, test := range tests {
		tmpStore, err := test.f(s)
		if err != nil {
			t.Fatalf("%s: got error: %v", test.name, err)
		}
		s = tmpStore
		bal, err := s.Balance(1, TstRecvCurrentHeight)
		if err != nil {
			t.Fatalf("%s: Confirmed Balance() failed: %v", test.name, err)
		}
		if bal != test.bal {
			t.Fatalf("%s: balance mismatch: expected: %d, got: %d", test.name, test.bal, bal)
		}
		unc, err := s.Balance(0, TstRecvCurrentHeight)
		if err != nil {
			t.Fatalf("%s: Unconfirmed Balance() failed: %v", test.name, err)
		}
		unc -= bal
		if unc != test.unc {
			t.Errorf("%s: unconfirmed balance mismatch: expected %d, got %d", test.name, test.unc, unc)
		}

		// Check that unspent outputs match expected.
		unspent, err := s.UnspentOutputs()
		if err != nil {
			t.Fatal(err)
		}
		for _, r := range unspent {
			if r.Spent() {
				t.Errorf("%s: unspent record marked as spent", test.name)
			}

			op := *r.OutPoint()
			if _, ok := test.unspents[op]; !ok {
				t.Errorf("%s: unexpected unspent output: %v", test.name, op)
			}
			delete(test.unspents, op)
		}
		if len(test.unspents) != 0 {
			t.Errorf("%s: missing expected unspent output(s)", test.name)
		}

		// Check that unmined sent txs match expected.
		for _, tx := range s.UnminedDebitTxs() {
			if _, ok := test.unmined[*tx.Sha()]; !ok {
				t.Fatalf("%s: unexpected unmined signed tx: %v", test.name, *tx.Sha())
			}
			delete(test.unmined, *tx.Sha())
		}
		if len(test.unmined) != 0 {
			t.Errorf("%s: missing expected unmined signed tx(s)", test.name)
		}

		// Pass a re-serialized version of the store to each next test.
		buf := new(bytes.Buffer)
		nWritten, err := s.WriteTo(buf)
		if err != nil {
			t.Fatalf("%v: serialization failed: %v (wrote %v bytes)", test.name, err, nWritten)
		}
		if nWritten != int64(buf.Len()) {
			t.Errorf("%v: wrote %v bytes but buffer has %v", test.name, nWritten, buf.Len())
		}
		nRead, err := s.ReadFrom(buf)
		if err != nil {
			t.Fatalf("%v: deserialization failed: %v (read %v bytes after writing %v)",
				test.name, err, nRead, nWritten)
		}
		if nWritten != nRead {
			t.Errorf("%v: number of bytes written (%v) does not match those read (%v)",
				test.name, nWritten, nRead)
		}
	}
}
예제 #27
0
// 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
}
예제 #28
0
// TestMessage tests the Read/WriteMessage API.
func TestMessage(t *testing.T) {
	pver := btcwire.ProtocolVersion

	// Create the various types of messages to test.

	// MsgVersion.
	addrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}
	you, err := btcwire.NewNetAddress(addrYou, btcwire.SFNodeNetwork)
	if err != nil {
		t.Errorf("NewNetAddress: %v", err)
	}
	you.Timestamp = time.Time{} // Version message has zero value timestamp.
	addrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
	me, err := btcwire.NewNetAddress(addrMe, btcwire.SFNodeNetwork)
	if err != nil {
		t.Errorf("NewNetAddress: %v", err)
	}
	me.Timestamp = time.Time{} // Version message has zero value timestamp.
	msgVersion := btcwire.NewMsgVersion(me, you, 123123, "/test:0.0.1/", 0)

	msgVerack := btcwire.NewMsgVerAck()
	msgGetAddr := btcwire.NewMsgGetAddr()
	msgAddr := btcwire.NewMsgAddr()
	msgGetBlocks := btcwire.NewMsgGetBlocks(&btcwire.ShaHash{})
	msgBlock := &blockOne
	msgInv := btcwire.NewMsgInv()
	msgGetData := btcwire.NewMsgGetData()
	msgNotFound := btcwire.NewMsgNotFound()
	msgTx := btcwire.NewMsgTx()
	msgPing := btcwire.NewMsgPing(123123)
	msgPong := btcwire.NewMsgPong(123123)
	msgGetHeaders := btcwire.NewMsgGetHeaders()
	msgHeaders := btcwire.NewMsgHeaders()
	msgAlert := btcwire.NewMsgAlert("payload", "signature")
	msgMemPool := btcwire.NewMsgMemPool()

	tests := []struct {
		in     btcwire.Message    // Value to encode
		out    btcwire.Message    // Expected decoded value
		pver   uint32             // Protocol version for wire encoding
		btcnet btcwire.BitcoinNet // Network to use for wire encoding
	}{
		{msgVersion, msgVersion, pver, btcwire.MainNet},
		{msgVerack, msgVerack, pver, btcwire.MainNet},
		{msgGetAddr, msgGetAddr, pver, btcwire.MainNet},
		{msgAddr, msgAddr, pver, btcwire.MainNet},
		{msgGetBlocks, msgGetBlocks, pver, btcwire.MainNet},
		{msgBlock, msgBlock, pver, btcwire.MainNet},
		{msgInv, msgInv, pver, btcwire.MainNet},
		{msgGetData, msgGetData, pver, btcwire.MainNet},
		{msgNotFound, msgNotFound, pver, btcwire.MainNet},
		{msgTx, msgTx, pver, btcwire.MainNet},
		{msgPing, msgPing, pver, btcwire.MainNet},
		{msgPong, msgPong, pver, btcwire.MainNet},
		{msgGetHeaders, msgGetHeaders, pver, btcwire.MainNet},
		{msgHeaders, msgHeaders, pver, btcwire.MainNet},
		{msgAlert, msgAlert, pver, btcwire.MainNet},
		{msgMemPool, msgMemPool, pver, btcwire.MainNet},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		// Encode to wire format.
		var buf bytes.Buffer
		err := btcwire.WriteMessage(&buf, test.in, test.pver, test.btcnet)
		if err != nil {
			t.Errorf("WriteMessage #%d error %v", i, err)
			continue
		}

		// Decode from wire format.
		rbuf := bytes.NewBuffer(buf.Bytes())
		msg, _, err := btcwire.ReadMessage(rbuf, test.pver, test.btcnet)
		if err != nil {
			t.Errorf("ReadMessage #%d error %v, msg %v", i, err,
				spew.Sdump(msg))
			continue
		}
		if !reflect.DeepEqual(msg, test.out) {
			t.Errorf("ReadMessage #%d\n got: %v want: %v", i,
				spew.Sdump(msg), spew.Sdump(test.out))
			continue
		}
	}
}
예제 #29
0
// 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
}