// Receive waits for the response promised by the future and returns a // transaction given its hash. func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { reply, err := receiveFuture(r) if err != nil { return nil, err } // Ensure the returned data is the expected type. txHex, ok := reply.(string) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "getrawtransaction (verbose=0): %T\n", reply) } // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(txHex) if err != nil { return nil, err } // Deserialize the transaction and return it. var msgTx btcwire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, err } return btcutil.NewTx(&msgTx), nil }
// ProcessTransaction is the main workhorse for handling insertion of new // free-standing transactions into a memory pool. It includes functionality // such as rejecting duplicate transactions, ensuring transactions follow all // rules, orphan transaction handling, and insertion into the memory pool. func (mp *txMemPool) ProcessTransaction(tx *btcwire.MsgTx) error { txHash, err := tx.TxSha() if err != nil { return err } log.Tracef("[TXMP] Processing transaction %v", txHash) // Potentially accept the transaction to the memory pool. var isOrphan bool err = mp.maybeAcceptTransaction(tx, &isOrphan) if err != nil { return err } if !isOrphan { // Accept any orphan transactions that depend on this // transaction (they are no longer orphans) and repeat for those // accepted transactions until there are no more. err = mp.processOrphans(&txHash) if err != nil { return err } } else { // When the transaction is an orphan (has inputs missing), // potentially add it to the orphan pool. err := mp.maybeAddOrphan(tx, &txHash) if err != nil { return err } } return nil }
// minimumFee calculates the minimum fee required for a transaction. // If allowFree is true, a fee may be zero so long as the entire // transaction has a serialized length less than 1 kilobyte // and none of the outputs contain a value less than 1 bitcent. // Otherwise, the fee will be calculated using TxFeeIncrement, // incrementing the fee for each kilobyte of transaction. func minimumFee(tx *btcwire.MsgTx, allowFree bool) btcutil.Amount { txLen := tx.SerializeSize() TxFeeIncrement.Lock() incr := TxFeeIncrement.i TxFeeIncrement.Unlock() fee := btcutil.Amount(int64(1+txLen/1000) * int64(incr)) if allowFree && txLen < 1000 { fee = 0 } if fee < incr { for _, txOut := range tx.TxOut { if txOut.Value < btcutil.SatoshiPerBitcent { return incr } } } max := btcutil.Amount(btcutil.MaxSatoshi) if fee < 0 || fee > max { fee = max } return fee }
// Receive waits for the response promised by the future and returns the // signed transaction as well as whether or not all inputs are now signed. func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) { res, err := receiveFuture(r) if err != nil { return nil, false, err } // Unmarshal as a signrawtransaction result. var signRawTxResult btcjson.SignRawTransactionResult err = json.Unmarshal(res, &signRawTxResult) if err != nil { return nil, false, err } // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(signRawTxResult.Hex) if err != nil { return nil, false, err } // Deserialize the transaction and return it. var msgTx btcwire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, false, err } return &msgTx, signRawTxResult.Complete, nil }
// Receive waits for the response promised by the future and returns the // signed transaction as well as whether or not all inputs are now signed. func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) { reply, err := receiveFuture(r) if err != nil { return nil, false, err } // Ensure the returned data is the expected type. result, ok := reply.(*btcjson.SignRawTransactionResult) if !ok { return nil, false, fmt.Errorf("unexpected response type for "+ "signrawtransaction: %T\n", reply) } // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(result.Hex) if err != nil { return nil, false, err } // Deserialize the transaction and return it. var msgTx btcwire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, false, err } return &msgTx, result.Complete, nil }
// maybeAddOrphan potentially adds an orphan to the orphan pool. func (mp *txMemPool) maybeAddOrphan(tx *btcwire.MsgTx, txHash *btcwire.ShaHash) error { // Ignore orphan transactions that are too large. This helps avoid // a memory exhaustion attack based on sending a lot of really large // orphans. In the case there is a valid transaction larger than this, // it will ultimtely be rebroadcast after the parent transactions // have been mined or otherwise received. // // Note that the number of orphan transactions in the orphan pool is // also limited, so this equates to a maximum memory used of // maxOrphanTxSize * maxOrphanTransactions (which is 500MB as of the // time this comment was written). var serializedTxBuf bytes.Buffer err := tx.Serialize(&serializedTxBuf) if err != nil { return err } serializedLen := serializedTxBuf.Len() if serializedLen > maxOrphanTxSize { str := fmt.Sprintf("orphan transaction size of %d bytes is "+ "larger than max allowed size of %d bytes", serializedLen, maxOrphanTxSize) return TxRuleError(str) } // Add the orphan if the none of the above disqualified it. mp.addOrphan(tx, txHash) return nil }
// removeTransaction removes the passed transaction from the memory pool. func (mp *txMemPool) removeTransaction(tx *btcwire.MsgTx) { mp.lock.Lock() defer mp.lock.Unlock() // Remove any transactions which rely on this one. txHash, _ := tx.TxSha() for i := uint32(0); i < uint32(len(tx.TxOut)); i++ { outpoint := btcwire.NewOutPoint(&txHash, i) if txRedeemer, exists := mp.outpoints[*outpoint]; exists { mp.lock.Unlock() mp.removeTransaction(txRedeemer) mp.lock.Lock() } } // Remove the transaction and mark the referenced outpoints as unspent // by the pool. if tx, exists := mp.pool[txHash]; exists { for _, txIn := range tx.TxIn { delete(mp.outpoints, txIn.PreviousOutpoint) } delete(mp.pool, txHash) } }
// 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 } } }
// Receive waits for the response promised by the future and returns a // transaction given its hash. func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { res, err := receiveFuture(r) if err != nil { return nil, err } // Unmarshal result as a string. var txHex string err = json.Unmarshal(res, &txHex) if err != nil { return nil, err } // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(txHex) if err != nil { return nil, err } // Deserialize the transaction and return it. var msgTx btcwire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, err } return btcutil.NewTx(&msgTx), nil }
// BenchmarkDeserializeTx performs a benchmark on how long it takes to // deserialize a transaction. func BenchmarkDeserializeTx(b *testing.B) { buf := []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, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // 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, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c, 0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16, 0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c, 0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c, 0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4, 0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6, 0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e, 0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58, 0xee, // 65-byte signature 0xac, // OP_CHECKSIG 0x00, 0x00, 0x00, 0x00, // Lock time } var tx btcwire.MsgTx for i := 0; i < b.N; i++ { tx.Deserialize(bytes.NewBuffer(buf)) } }
// handleTxMsg is invoked when a peer receives a tx bitcoin message. It blocks // until the bitcoin transaction has been fully processed. Unlock the block // handler this does not serialize all transactions through a single thread // transactions don't rely on the previous one in a linear fashion like blocks. func (p *peer) handleTxMsg(msg *btcwire.MsgTx) { // Add the transaction to the known inventory for the peer. hash, err := msg.TxSha() if err != nil { log.Errorf("Unable to get transaction hash: %v", err) return } iv := btcwire.NewInvVect(btcwire.InvVect_Tx, &hash) p.addKnownInventory(iv) // Process the transaction. err = p.server.txMemPool.ProcessTransaction(msg) 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.Infof("Rejected transaction %v: %v", hash, err) } else { log.Errorf("Failed to process transaction %v: %v", hash, err) } return } }
// fetchTxDataByLoc returns several pieces of data regarding the given tx // located by the block/offset/size location func (db *LevelDb) fetchTxDataByLoc(blkHeight int64, txOff int, txLen int, txspent []byte) (rtx *btcwire.MsgTx, rblksha *btcwire.ShaHash, rheight int64, rtxspent []byte, err error) { var blksha *btcwire.ShaHash var blkbuf []byte blksha, blkbuf, err = db.getBlkByHeight(blkHeight) if err != nil { if err == leveldb.ErrNotFound { err = btcdb.TxShaMissing } return } //log.Trace("transaction %v is at block %v %v txoff %v, txlen %v\n", // txsha, blksha, blkHeight, txOff, txLen) if len(blkbuf) < txOff+txLen { err = btcdb.TxShaMissing return } rbuf := bytes.NewBuffer(blkbuf[txOff : txOff+txLen]) var tx btcwire.MsgTx err = tx.Deserialize(rbuf) if err != nil { log.Warnf("unable to decode tx block %v %v txoff %v txlen %v", blkHeight, blksha, txOff, txLen) return } return &tx, blksha, blkHeight, txspent, nil }
// FetchTxAllBySha returns several pieces of data regarding the given sha. func (db *SqliteDb) FetchTxAllBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rtxbuf []byte, rpver uint32, rblksha *btcwire.ShaHash, err error) { // Check Tx cache if txc, ok := db.fetchTxCache(txsha); ok { return txc.tx, txc.txbuf, txc.pver, &txc.blksha, nil } // If not cached load it bidx, toff, tlen, err := db.FetchLocationBySha(txsha) if err != nil { log.Warnf("unable to find location of origin tx %v", txsha) return } blksha, err := db.FetchBlockShaByHeight(bidx) if err != nil { log.Warnf("block idx lookup %v to %v", bidx, err) return } log.Tracef("transaction %v is at block %v %v tx %v", txsha, blksha, bidx, toff) blk, err := db.FetchBlockBySha(blksha) if err != nil { log.Warnf("unable to fetch block %v %v ", bidx, &blksha) return } blkbuf, pver, err := blk.Bytes() if err != nil { log.Warnf("unable to decode block %v %v", bidx, &blksha) return } txbuf := make([]byte, tlen) copy(txbuf[:], blkbuf[toff:toff+tlen]) rbuf := bytes.NewBuffer(txbuf) var tx btcwire.MsgTx err = tx.BtcDecode(rbuf, pver) if err != nil { log.Warnf("unable to decode tx block %v %v txoff %v txlen %v", bidx, &blksha, toff, tlen) return } // Shove data into TxCache // XXX - var txc txCacheObj txc.sha = *txsha txc.tx = &tx txc.txbuf = txbuf txc.pver = pver txc.blksha = *blksha db.insertTxCache(&txc) return &tx, txbuf, pver, blksha, nil }
// TestTxSerializeErrors performs negative tests against wire encode and decode // of MsgTx to confirm error paths work correctly. func TestTxSerializeErrors(t *testing.T) { tests := []struct { in *btcwire.MsgTx // Value to encode buf []byte // Serialized data max int // Max size of fixed buffer to induce errors writeErr error // Expected write error readErr error // Expected read error }{ // Force error in version. {multiTx, multiTxEncoded, 0, io.ErrShortWrite, io.EOF}, // Force error in number of transaction inputs. {multiTx, multiTxEncoded, 4, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block hash. {multiTx, multiTxEncoded, 5, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block hash. {multiTx, multiTxEncoded, 5, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block output index. {multiTx, multiTxEncoded, 37, io.ErrShortWrite, io.EOF}, // Force error in transaction input signature script length. {multiTx, multiTxEncoded, 41, io.ErrShortWrite, io.EOF}, // Force error in transaction input signature script. {multiTx, multiTxEncoded, 42, io.ErrShortWrite, io.EOF}, // Force error in transaction input sequence. {multiTx, multiTxEncoded, 49, io.ErrShortWrite, io.EOF}, // Force error in number of transaction outputs. {multiTx, multiTxEncoded, 53, io.ErrShortWrite, io.EOF}, // Force error in transaction output value. {multiTx, multiTxEncoded, 54, io.ErrShortWrite, io.EOF}, // Force error in transaction output pk script length. {multiTx, multiTxEncoded, 62, io.ErrShortWrite, io.EOF}, // Force error in transaction output pk script. {multiTx, multiTxEncoded, 63, io.ErrShortWrite, io.EOF}, // Force error in transaction output lock time. {multiTx, multiTxEncoded, 130, io.ErrShortWrite, io.EOF}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Serialize the transaction. w := newFixedWriter(test.max) err := test.in.Serialize(w) if err != test.writeErr { t.Errorf("Serialize #%d wrong error got: %v, want: %v", i, err, test.writeErr) continue } // Deserialize the transaction. var tx btcwire.MsgTx r := newFixedReader(test.max, test.buf) err = tx.Deserialize(r) if err != test.readErr { t.Errorf("Deserialize #%d wrong error got: %v, want: %v", i, err, test.readErr) continue } } }
// Creates a new bulletin from the containing Tx, supplied author and optional blockhash // by unpacking txOuts that are considered data. It ignores extra junk behind the protobuffer. // NewBulletin also asserts aspects of valid bulletins by throwing errors when msg len // is zero or board len is greater than MaxBoardLen. func NewBulletin(tx *btcwire.MsgTx, blkhash *btcwire.ShaHash, net *btcnet.Params) (*Bulletin, error) { wireBltn := &wirebulletin.WireBulletin{} author, err := getAuthor(tx, net) if err != nil { return nil, err } // TODO. scrutinize. // Bootleg solution, but if unmarshal fails slice txout and try again until we can try no more or it fails for j := len(tx.TxOut); j > 1; j-- { rel_txouts := tx.TxOut[:j] // slice off change txouts bytes, err := extractData(rel_txouts) if err != nil { continue } err = proto.Unmarshal(bytes, wireBltn) if err != nil { continue } else { // No errors, we found a good decode break } } if err != nil { return nil, err } board := wireBltn.GetBoard() // assert that the length of the board is within its max size! if len(board) > MaxBoardLen { return nil, ErrMaxBoardLen } msg := wireBltn.GetMessage() // assert that the bulletin has a non zero message length. if len(msg) < 1 { return nil, ErrNoMsg } // TODO assert that msg and board are valid UTF-8 strings. hash, _ := tx.TxSha() bltn := &Bulletin{ Txid: &hash, Block: blkhash, Author: author, Board: board, Message: msg, Timestamp: time.Unix(wireBltn.GetTimestamp(), 0), } return bltn, nil }
// NewTxFromReader returns a new instance of a bitcoin transaction given a // Reader to deserialize the transaction. See Tx. func NewTxFromReader(r io.Reader) (*Tx, error) { // Deserialize the bytes into a MsgTx. var msgTx btcwire.MsgTx err := msgTx.Deserialize(r) if err != nil { return nil, err } t := Tx{ msgTx: &msgTx, txIndex: TxIndexUnknown, } return &t, nil }
// NewTxFromBytes returns a new instance of a bitcoin transaction given the // serialized bytes. See Tx. func NewTxFromBytes(serializedTx []byte) (*Tx, error) { // Deserialize the bytes into a MsgTx. var msgTx btcwire.MsgTx br := bytes.NewBuffer(serializedTx) err := msgTx.Deserialize(br) if err != nil { return nil, err } t := Tx{ msgTx: &msgTx, serializedTx: serializedTx, txIndex: TxIndexUnknown, } return &t, nil }
// prevOutVal looks up all the values of the oupoints used in the current tx func PrevOutVal(tx *btcwire.MsgTx, client *btcrpcclient.Client) (int64, error) { // requires an rpc client and outpoints within wallets realm total := int64(0) for _, txin := range tx.TxIn { prevTxHash := txin.PreviousOutPoint.Hash var tx *btcutil.Tx tx, err := client.GetRawTransaction(&prevTxHash) if err != nil { return -1, err } vout := txin.PreviousOutPoint.Index txout := tx.MsgTx().TxOut[vout] total += txout.Value } return total, nil }
// handleDecodeRawTransaction handles decoderawtransaction commands. func handleDecodeRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) { c := cmd.(*btcjson.DecodeRawTransactionCmd) // Deserialize the transaction. hexStr := c.HexTx if len(hexStr)%2 != 0 { hexStr = "0" + hexStr } serializedTx, err := hex.DecodeString(hexStr) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInvalidParameter.Code, Message: fmt.Sprintf("argument must be hexadecimal "+ "string (not %q)", hexStr), } } var mtx btcwire.MsgTx err = mtx.Deserialize(bytes.NewBuffer(serializedTx)) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrDeserialization.Code, Message: "TX decode failed", } } txSha, _ := mtx.TxSha() vin, err := createVinList(&mtx) if err != nil { return nil, err } vout, err := createVoutList(&mtx, s.server.btcnet) if err != nil { return nil, err } // Create and return the result. txReply := btcjson.TxRawDecodeResult{ Txid: txSha.String(), Version: mtx.Version, Locktime: mtx.LockTime, Vin: vin, Vout: vout, } return txReply, nil }
// SignRawTransaction2Async returns an instance of a type that can be used to // get the result of the RPC at some future time by invoking the Receive // function on the returned instance. // // See SignRawTransaction2 for the blocking version and more details. func (c *Client) SignRawTransaction2Async(tx *btcwire.MsgTx, inputs []btcjson.RawTxInput) FutureSignRawTransactionResult { txHex := "" if tx != nil { // Serialize the transaction and convert to hex string. buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) if err := tx.Serialize(buf); err != nil { return newFutureError(err) } txHex = hex.EncodeToString(buf.Bytes()) } id := c.NextID() cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs) if err != nil { return newFutureError(err) } return c.sendCmd(cmd) }
// parseChainTxNtfnParams parses out the transaction and optional details about // the block it's mined in from the parameters of recvtx and redeemingtx // notifications. func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, *btcws.BlockDetails, error) { if len(params) == 0 || len(params) > 2 { return nil, nil, wrongNumParams(len(params)) } // Unmarshal first parameter as a string. var txHex string err := json.Unmarshal(params[0], &txHex) if err != nil { return nil, nil, err } // If present, unmarshal second optional parameter as the block details // JSON object. var block *btcws.BlockDetails if len(params) > 1 { err = json.Unmarshal(params[1], &block) if err != nil { return nil, nil, err } } // Hex decode and deserialize the transaction. serializedTx, err := hex.DecodeString(txHex) if err != nil { return nil, nil, err } var msgTx btcwire.MsgTx err = msgTx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { return nil, nil, err } // TODO: Change recvtx and redeemingtx callback signatures to use // nicer types for details about the block (block sha as a // btcwire.ShaHash, block time as a time.Time, etc.). return btcutil.NewTx(&msgTx), block, nil }
// 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 } } }
// TestTxOverflowErrors performs tests to ensure deserializing transactions // which are intentionally crafted to use large values for the variable number // of inputs and outputs are handled properly. This could otherwise potentially // be used as an attack vector. func TestTxOverflowErrors(t *testing.T) { // Use protocol version 70001 and transaction version 1 specifically // here instead of the latest values because the test data is using // bytes encoded with those versions. pver := uint32(70001) txVer := uint32(1) tests := []struct { buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding version uint32 // Transaction version err error // Expected error }{ // Transaction that claims to have ~uint64(0) inputs. { []byte{ 0x00, 0x00, 0x00, 0x01, // Version 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Varint for number of input transactions }, pver, txVer, &btcwire.MessageError{}, }, // Transaction that claims to have ~uint64(0) outputs. { []byte{ 0x00, 0x00, 0x00, 0x01, // Version 0x00, // Varint for number of input transactions 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Varint for number of output transactions }, pver, txVer, &btcwire.MessageError{}, }, // Transaction that has an input with a signature script that // claims to have ~uint64(0) length. { []byte{ 0x00, 0x00, 0x00, 0x01, // 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 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Varint for length of signature script }, pver, txVer, &btcwire.MessageError{}, }, // Transaction that has an output with a public key script // that claims to have ~uint64(0) length. { []byte{ 0x00, 0x00, 0x00, 0x01, // 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 0x00, // Varint for length of signature script 0xff, 0xff, 0xff, 0xff, // Sequence 0x01, // Varint for number of output transactions 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Transaction amount 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Varint for length of public key script }, pver, txVer, &btcwire.MessageError{}, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Decode from wire format. var msg btcwire.MsgTx r := bytes.NewReader(test.buf) err := msg.BtcDecode(r, test.pver) if reflect.TypeOf(err) != reflect.TypeOf(test.err) { t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", i, err, reflect.TypeOf(test.err)) continue } // Decode from wire format. r = bytes.NewReader(test.buf) err = msg.Deserialize(r) if reflect.TypeOf(err) != reflect.TypeOf(test.err) { t.Errorf("Deserialize #%d wrong error got: %v, want: %v", i, err, reflect.TypeOf(test.err)) continue } } }
func Test_dupTx(t *testing.T) { // Ignore db remove errors since it means we didn't have an old one. dbname := fmt.Sprintf("tstdbdup0") dbnamever := dbname + ".ver" _ = os.RemoveAll(dbname) _ = os.RemoveAll(dbnamever) db, err := btcdb.CreateDB("leveldb", dbname) if err != nil { t.Errorf("Failed to open test database %v", err) return } defer os.RemoveAll(dbname) defer os.RemoveAll(dbnamever) defer db.Close() testdatafile := filepath.Join("testdata", "blocks1-256.bz2") blocks, err := loadBlocks(t, testdatafile) if err != nil { t.Errorf("Unable to load blocks from test data for: %v", err) return } var lastSha *btcwire.ShaHash // Populate with the fisrt 256 blocks, so we have blocks to 'mess with' err = nil out: for height := int64(0); height < int64(len(blocks)); height++ { block := blocks[height] // except for NoVerify which does not allow lookups check inputs mblock := block.MsgBlock() var txneededList []*btcwire.ShaHash for _, tx := range mblock.Transactions { for _, txin := range tx.TxIn { if txin.PreviousOutpoint.Index == uint32(4294967295) { continue } origintxsha := &txin.PreviousOutpoint.Hash txneededList = append(txneededList, origintxsha) if !db.ExistsTxSha(origintxsha) { t.Errorf("referenced tx not found %v ", origintxsha) } _, err = db.FetchTxBySha(origintxsha) if err != nil { t.Errorf("referenced tx not found %v err %v ", origintxsha, err) } } } txlist := db.FetchUnSpentTxByShaList(txneededList) for _, txe := range txlist { if txe.Err != nil { t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err) break out } } newheight, err := db.InsertBlock(block) if err != nil { t.Errorf("failed to insert block %v err %v", height, err) break out } if newheight != height { t.Errorf("height mismatch expect %v returned %v", height, newheight) break out } newSha, blkid, err := db.NewestSha() if err != nil { t.Errorf("failed to obtain latest sha %v %v", height, err) } if blkid != height { t.Errorf("height doe not match latest block height %v %v %v", blkid, height, err) } blkSha, _ := block.Sha() if *newSha != *blkSha { t.Errorf("Newest block sha does not match freshly inserted one %v %v %v ", newSha, blkSha, err) } lastSha = blkSha } // genrate a new block based on the last sha // these block are not verified, so there are a bunch of garbage fields // in the 'generated' block. var bh btcwire.BlockHeader bh.Version = 2 bh.PrevBlock = *lastSha // Bits, Nonce are not filled in mblk := btcwire.NewMsgBlock(&bh) hash, _ := btcwire.NewShaHashFromStr("df2b060fa2e5e9c8ed5eaf6a45c13753ec8c63282b2688322eba40cd98ea067a") po := btcwire.NewOutPoint(hash, 0) txI := btcwire.NewTxIn(po, []byte("garbage")) txO := btcwire.NewTxOut(50000000, []byte("garbageout")) var tx btcwire.MsgTx tx.AddTxIn(txI) tx.AddTxOut(txO) mblk.AddTransaction(&tx) blk := btcutil.NewBlock(mblk) fetchList := []*btcwire.ShaHash{hash} listReply := db.FetchUnSpentTxByShaList(fetchList) for _, lr := range listReply { if lr.Err != nil { t.Errorf("sha %v spent %v err %v\n", lr.Sha, lr.TxSpent, lr.Err) } } _, err = db.InsertBlock(blk) if err != nil { t.Errorf("failed to insert phony block %v", err) } // ok, did it 'spend' the tx ? listReply = db.FetchUnSpentTxByShaList(fetchList) for _, lr := range listReply { if lr.Err != btcdb.TxShaMissing { t.Errorf("sha %v spent %v err %v\n", lr.Sha, lr.TxSpent, lr.Err) } } txshalist, _ := blk.TxShas() for _, txsha := range txshalist { txReply, err := db.FetchTxBySha(txsha) if err != nil { t.Errorf("fully spent lookup %v err %v\n", hash, err) } else { for _, lr := range txReply { if lr.Err != nil { fmt.Errorf("stx %v spent %v err %v\n", lr.Sha, lr.TxSpent, lr.Err) } } } } t.Logf("Dropping block") err = db.DropAfterBlockBySha(lastSha) if err != nil { t.Errorf("failed to drop spending block %v", err) } }
// TestTxWireErrors performs negative tests against wire encode and decode // of MsgTx to confirm error paths work correctly. func TestTxWireErrors(t *testing.T) { // Use protocol version 60002 specifically here instead of the latest // because the test data is using bytes encoded with that protocol // version. pver := uint32(60002) tests := []struct { in *btcwire.MsgTx // Value to encode buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding max int // Max size of fixed buffer to induce errors writeErr error // Expected write error readErr error // Expected read error }{ // Force error in version. {multiTx, multiTxEncoded, pver, 0, io.ErrShortWrite, io.EOF}, // Force error in number of transaction inputs. {multiTx, multiTxEncoded, pver, 4, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block hash. {multiTx, multiTxEncoded, pver, 5, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block hash. {multiTx, multiTxEncoded, pver, 5, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block output index. {multiTx, multiTxEncoded, pver, 37, io.ErrShortWrite, io.EOF}, // Force error in transaction input signature script length. {multiTx, multiTxEncoded, pver, 41, io.ErrShortWrite, io.EOF}, // Force error in transaction input signature script. {multiTx, multiTxEncoded, pver, 42, io.ErrShortWrite, io.EOF}, // Force error in transaction input sequence. {multiTx, multiTxEncoded, pver, 49, io.ErrShortWrite, io.EOF}, // Force error in number of transaction outputs. {multiTx, multiTxEncoded, pver, 53, io.ErrShortWrite, io.EOF}, // Force error in transaction output value. {multiTx, multiTxEncoded, pver, 54, io.ErrShortWrite, io.EOF}, // Force error in transaction output pk script length. {multiTx, multiTxEncoded, pver, 62, io.ErrShortWrite, io.EOF}, // Force error in transaction output pk script. {multiTx, multiTxEncoded, pver, 63, io.ErrShortWrite, io.EOF}, // Force error in transaction output lock time. {multiTx, multiTxEncoded, pver, 130, io.ErrShortWrite, io.EOF}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. w := newFixedWriter(test.max) err := test.in.BtcEncode(w, test.pver) if err != test.writeErr { t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", i, err, test.writeErr) continue } // Decode from wire format. var msg btcwire.MsgTx r := newFixedReader(test.max, test.buf) err = msg.BtcDecode(r, test.pver) if err != test.readErr { t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", i, err, test.readErr) continue } } }
// 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 } } }
// checkTransactionStandard performs a series of checks on a transaction to // ensure it is a "standard" transaction. A standard transaction is one that // conforms to several additional limiting cases over what is considered a // "sane" transaction such as having a version in the supported range, being // finalized, conforming to more stringent size constraints, having scripts // of recognized forms, and not containing "dust" outputs (those that are // so small it costs more to process them than they are worth). func checkTransactionStandard(tx *btcwire.MsgTx, height int64) error { // The transaction must be a currently supported version. if tx.Version > btcwire.TxVersion || tx.Version < 1 { str := fmt.Sprintf("transaction version %d is not in the "+ "valid range of %d-%d", tx.Version, 1, btcwire.TxVersion) return TxRuleError(str) } // The transaction must be finalized to be standard and therefore // considered for inclusion in a block. if !btcchain.IsFinalizedTransaction(tx, height, time.Now()) { str := fmt.Sprintf("transaction is not finalized") return TxRuleError(str) } // Since extremely large transactions with a lot of inputs can cost // almost as much to process as the sender fees, limit the maximum // size of a transaction. This also helps mitigate CPU exhaustion // attacks. var serializedTxBuf bytes.Buffer err := tx.Serialize(&serializedTxBuf) if err != nil { return err } serializedLen := serializedTxBuf.Len() if serializedLen > maxStandardTxSize { str := fmt.Sprintf("transaction size of %v is larger than max "+ "allowed size of %v", serializedLen, maxStandardTxSize) return TxRuleError(str) } for i, txIn := range tx.TxIn { // Each transaction input signature script must not exceed the // maximum size allowed for a standard transaction. See // the comment on maxStandardSigScriptSize for more details. sigScriptLen := len(txIn.SignatureScript) if sigScriptLen > maxStandardSigScriptSize { str := fmt.Sprintf("transaction input %d: signature "+ "script size of %d bytes is large than max "+ "allowed size of %d bytes", i, sigScriptLen, maxStandardSigScriptSize) return TxRuleError(str) } // Each transaction input signature script must only contain // opcodes which push data onto the stack. if !btcscript.IsPushOnlyScript(txIn.SignatureScript) { str := fmt.Sprintf("transaction input %d: signature "+ "script is not push only", i) return TxRuleError(str) } } // None of the output public key scripts can be a non-standard script or // be "dust". for i, txOut := range tx.TxOut { err := checkPkScriptStandard(txOut.PkScript) if err != nil { str := fmt.Sprintf("transaction output %d: %v", i, err) return TxRuleError(str) } if isDust(txOut) { str := fmt.Sprintf("transaction output %d: payment "+ "of %d is dust", i, txOut.Value) return TxRuleError(str) } } return nil }
// maybeAcceptTransaction is the main workhorse for handling insertion of new // free-standing transactions into a memory pool. It includes functionality // such as rejecting duplicate transactions, ensuring transactions follow all // rules, orphan transaction handling, and insertion into the memory pool. func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) error { *isOrphan = false txHash, err := tx.TxSha() if err != nil { return err } // Don't accept the transaction if it already exists in the pool. This // applies to orphan transactions as well. This check is intended to // be a quick check to weed out duplicates. It is more expensive to // detect a duplicate transaction in the main chain, so that is done // later. if mp.isTransactionInPool(&txHash) { str := fmt.Sprintf("already have transaction %v", txHash) return TxRuleError(str) } // Perform preliminary sanity checks on the transaction. This makes // use of btcchain which contains the invariant rules for what // transactions are allowed into blocks. err = btcchain.CheckTransactionSanity(tx) if err != nil { if _, ok := err.(btcchain.RuleError); ok { return TxRuleError(err.Error()) } return err } // A standalone transaction must not be a coinbase transaction. if btcchain.IsCoinBase(tx) { str := fmt.Sprintf("transaction %v is an individual coinbase", txHash) return TxRuleError(str) } // Don't accept transactions with a lock time after the maximum int32 // value for now. This is an artifact of older bitcoind clients which // treated this field as an int32 and would treat anything larger // incorrectly (as negative). if tx.LockTime > math.MaxInt32 { str := fmt.Sprintf("transaction %v is has a lock time after "+ "2038 which is not accepted yet", txHash) return TxRuleError(str) } // Get the current height of the main chain. A standalone transaction // will be mined into the next block at best, so _, curHeight, err := mp.server.db.NewestSha() if err != nil { return err } nextBlockHeight := curHeight + 1 // Don't allow non-standard transactions on the main network. if activeNetParams.btcnet == btcwire.MainNet { err := checkTransactionStandard(tx, nextBlockHeight) if err != nil { str := fmt.Sprintf("transaction %v is not a standard "+ "transaction: %v", txHash, err) return TxRuleError(str) } } // The transaction may not use any of the same outputs as other // transactions already in the pool as that would ultimately result in a // double spend. This check is intended to be quick and therefore only // detects double spends within the transaction pool itself. The // transaction could still be double spending coins from the main chain // at this point. There is a more in-depth check that happens later // after fetching the referenced transaction inputs from the main chain // which examines the actual spend data and prevents double spends. err = mp.checkPoolDoubleSpend(tx) if err != nil { return err } // Fetch all of the transactions referenced by the inputs to this // transaction. This function also attempts to fetch the transaction // itself to be used for detecting a duplicate transaction without // needing to do a separate lookup. txStore, err := mp.fetchInputTransactions(tx) if err != nil { return err } // Don't allow the transaction if it exists in the main chain and is not // not already fully spent. if txD, exists := txStore[txHash]; exists && txD.Err == nil { for _, isOutputSpent := range txD.Spent { if !isOutputSpent { str := fmt.Sprintf("transaction already exists") return TxRuleError(str) } } } delete(txStore, txHash) // Transaction is an orphan if any of the inputs don't exist. for _, txD := range txStore { if txD.Err == btcdb.TxShaMissing { *isOrphan = true return nil } } // Perform several checks on the transaction inputs using the invariant // rules in btcchain for what transactions are allowed into blocks. // Also returns the fees associated with the transaction which will be // used later. txFee, err := btcchain.CheckTransactionInputs(tx, nextBlockHeight, txStore) if err != nil { return err } // Don't allow transactions with non-standard inputs on the main // network. if activeNetParams.btcnet == btcwire.MainNet { err := checkInputsStandard(tx) if err != nil { str := fmt.Sprintf("transaction %v has a non-standard "+ "input: %v", txHash, err) return TxRuleError(str) } } // Note: if you modify this code to accept non-standard transactions, // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. // TODO(davec): Don't allow the transaction if the transation fee // would be too low to get into an empty block. _ = txFee // Verify crypto signatures for each input and reject the transaction if // any don't verify. err = btcchain.ValidateTransactionScripts(tx, &txHash, time.Now(), txStore) if err != nil { return err } // TODO(davec): Rate-limit free transactions // Add to transaction pool. mp.addTransaction(tx, &txHash) mp.lock.RLock() log.Debugf("[TXMP] Accepted transaction %v (pool size: %v)", txHash, len(mp.pool)) mp.lock.RUnlock() // TODO(davec): Notifications // Generate the inventory vector and relay it. iv := btcwire.NewInvVect(btcwire.InvVect_Tx, &txHash) mp.server.RelayInventory(iv) return nil }
// InsertBlock inserts raw block and transaction data from a block into the // database. The first block inserted into the database will be treated as the // genesis block. Every subsequent block insert requires the referenced parent // block to already exist. func (db *LevelDb) InsertBlock(block *btcutil.Block) (height int64, rerr error) { db.dbLock.Lock() defer db.dbLock.Unlock() defer func() { if rerr == nil { rerr = db.processBatches() } else { db.lBatch().Reset() } }() blocksha, err := block.Sha() if err != nil { log.Warnf("Failed to compute block sha %v", blocksha) return 0, err } mblock := block.MsgBlock() rawMsg, err := block.Bytes() if err != nil { log.Warnf("Failed to obtain raw block sha %v", blocksha) return 0, err } txloc, err := block.TxLoc() if err != nil { log.Warnf("Failed to obtain raw block sha %v", blocksha) return 0, err } // Insert block into database newheight, err := db.insertBlockData(blocksha, &mblock.Header.PrevBlock, rawMsg) if err != nil { log.Warnf("Failed to insert block %v %v %v", blocksha, &mblock.Header.PrevBlock, err) return 0, err } // At least two blocks in the long past were generated by faulty // miners, the sha of the transaction exists in a previous block, // detect this condition and 'accept' the block. for txidx, tx := range mblock.Transactions { txsha, err := block.TxSha(txidx) if err != nil { log.Warnf("failed to compute tx name block %v idx %v err %v", blocksha, txidx, err) return 0, err } spentbuflen := (len(tx.TxOut) + 7) / 8 spentbuf := make([]byte, spentbuflen, spentbuflen) if len(tx.TxOut)%8 != 0 { for i := uint(len(tx.TxOut) % 8); i < 8; i++ { spentbuf[spentbuflen-1] |= (byte(1) << i) } } err = db.insertTx(txsha, newheight, txloc[txidx].TxStart, txloc[txidx].TxLen, spentbuf) if err != nil { log.Warnf("block %v idx %v failed to insert tx %v %v err %v", blocksha, newheight, &txsha, txidx, err) return 0, err } // Some old blocks contain duplicate transactions // Attempt to cleanly bypass this problem by marking the // first as fully spent. // http://blockexplorer.com/b/91812 dup in 91842 // http://blockexplorer.com/b/91722 dup in 91880 if newheight == 91812 { dupsha, err := btcwire.NewShaHashFromStr("d5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599") if err != nil { panic("invalid sha string in source") } if txsha.IsEqual(dupsha) { // marking TxOut[0] as spent po := btcwire.NewOutPoint(dupsha, 0) txI := btcwire.NewTxIn(po, []byte("garbage")) var spendtx btcwire.MsgTx spendtx.AddTxIn(txI) err = db.doSpend(&spendtx) if err != nil { log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, &txsha, txidx, err) } } } if newheight == 91722 { dupsha, err := btcwire.NewShaHashFromStr("e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468") if err != nil { panic("invalid sha string in source") } if txsha.IsEqual(dupsha) { // marking TxOut[0] as spent po := btcwire.NewOutPoint(dupsha, 0) txI := btcwire.NewTxIn(po, []byte("garbage")) var spendtx btcwire.MsgTx spendtx.AddTxIn(txI) err = db.doSpend(&spendtx) if err != nil { log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, &txsha, txidx, err) } } } err = db.doSpend(tx) if err != nil { log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, txsha, txidx, err) return 0, err } } return newheight, nil }
// handleNotification examines the passed notification type, performs // conversions to get the raw notification types into higher level types and // delivers the notification to the appropriate On<X> handler registered with // the client. func (c *Client) handleNotification(cmd btcjson.Cmd) { // Ignore the notification if the client is not interested in any // notifications. if c.ntfnHandlers == nil { return } switch ntfn := cmd.(type) { // OnBlockConnected case *btcws.BlockConnectedNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnBlockConnected == nil { return } hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) if err != nil { log.Warnf("Received block connected notification with "+ "invalid hash string: %q", ntfn.Hash) return } c.ntfnHandlers.OnBlockConnected(hash, ntfn.Height) // OnBlockDisconnected case *btcws.BlockDisconnectedNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnBlockDisconnected == nil { return } hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) if err != nil { log.Warnf("Received block disconnected notification "+ "with invalid hash string: %q", ntfn.Hash) return } c.ntfnHandlers.OnBlockDisconnected(hash, ntfn.Height) // OnRecvTx case *btcws.RecvTxNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnRecvTx == nil { return } // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(ntfn.HexTx) if err != nil { log.Warnf("Received recvtx notification with invalid "+ "transaction hex '%q': %v", ntfn.HexTx, err) } // Deserialize the transaction. var msgTx btcwire.MsgTx err = msgTx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { log.Warnf("Received recvtx notification with "+ "transaction that failed to deserialize: %v", err) } c.ntfnHandlers.OnRecvTx(btcutil.NewTx(&msgTx), ntfn.Block) // OnRedeemingTx case *btcws.RedeemingTxNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnRedeemingTx == nil { return } // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(ntfn.HexTx) if err != nil { log.Warnf("Received redeemingtx notification with "+ "invalid transaction hex '%q': %v", ntfn.HexTx, err) } // Deserialize the transaction. var msgTx btcwire.MsgTx err = msgTx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { log.Warnf("Received redeemingtx notification with "+ "transaction that failed to deserialize: %v", err) } c.ntfnHandlers.OnRedeemingTx(btcutil.NewTx(&msgTx), ntfn.Block) // OnRescanProgress case *btcws.RescanProgressNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnRescanProgress == nil { return } c.ntfnHandlers.OnRescanProgress(ntfn.LastProcessed) // OnTxAccepted case *btcws.TxAcceptedNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnTxAccepted == nil { return } hash, err := btcwire.NewShaHashFromStr(ntfn.TxID) if err != nil { log.Warnf("Received tx accepted notification with "+ "invalid hash string: %q", ntfn.TxID) return } c.ntfnHandlers.OnTxAccepted(hash, btcutil.Amount(ntfn.Amount)) // OnTxAcceptedVerbose case *btcws.TxAcceptedVerboseNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnTxAcceptedVerbose == nil { return } c.ntfnHandlers.OnTxAcceptedVerbose(ntfn.RawTx) // OnBtcdConnected case *btcws.BtcdConnectedNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnBtcdConnected == nil { return } c.ntfnHandlers.OnBtcdConnected(ntfn.Connected) // OnAccountBalance case *btcws.AccountBalanceNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnAccountBalance == nil { return } balance, err := btcjson.JSONToAmount(ntfn.Balance) if err != nil { log.Warnf("Received account balance notification with "+ "an amount that does not parse: %v", ntfn.Balance) return } c.ntfnHandlers.OnAccountBalance(ntfn.Account, btcutil.Amount(balance), ntfn.Confirmed) // OnWalletLockState case *btcws.WalletLockStateNtfn: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnWalletLockState == nil { return } c.ntfnHandlers.OnWalletLockState(ntfn.Locked) // OnUnknownNotification default: if c.ntfnHandlers.OnUnknownNotification == nil { return } c.ntfnHandlers.OnUnknownNotification(ntfn) } }