// fetchTxDataByLoc returns several pieces of data regarding the given tx // located by the block/offset/size location func (db *LevelDb) fetchTxDataByLoc(blkHeight int32, txOff int, txLen int, txspent []byte) (rtx *wire.MsgTx, rblksha *wire.ShaHash, rheight int32, rtxspent []byte, err error) { var blksha *wire.ShaHash var blkbuf []byte blksha, blkbuf, err = db.getBlkByHeight(blkHeight) if err != nil { if err == leveldb.ErrNotFound { err = database.ErrTxShaMissing } 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 = database.ErrTxShaMissing return } rbuf := bytes.NewReader(blkbuf[txOff : txOff+txLen]) var tx wire.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 }
// calcPriority returns a transaction priority given a transaction and the sum // of each of its input values multiplied by their age (# of confirmations). // Thus, the final formula for the priority is: // sum(inputValue * inputAge) / adjustedTxSize func calcPriority(tx *wire.MsgTx, txStore blockchain.TxStore, nextBlockHeight int32) float64 { // In order to encourage spending multiple old unspent transaction // outputs thereby reducing the total set, don't count the constant // overhead for each input as well as enough bytes of the signature // script to cover a pay-to-script-hash redemption with a compressed // pubkey. This makes additional inputs free by boosting the priority // of the transaction accordingly. No more incentive is given to avoid // encouraging gaming future transactions through the use of junk // outputs. This is the same logic used in the reference // implementation. // // The constant overhead for a txin is 41 bytes since the previous // outpoint is 36 bytes + 4 bytes for the sequence + 1 byte the // signature script length. // // A compressed pubkey pay-to-script-hash redemption with a maximum len // signature is of the form: // [OP_DATA_73 <73-byte sig> + OP_DATA_35 + {OP_DATA_33 // <33 byte compresed pubkey> + OP_CHECKSIG}] // // Thus 1 + 73 + 1 + 1 + 33 + 1 = 110 overhead := 0 for _, txIn := range tx.TxIn { // Max inputs + size can't possibly overflow here. overhead += 41 + minInt(110, len(txIn.SignatureScript)) } serializedTxSize := tx.SerializeSize() if overhead >= serializedTxSize { return 0.0 } inputValueAge := calcInputValueAge(tx, txStore, nextBlockHeight) return inputValueAge / float64(serializedTxSize-overhead) }
// 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 *wire.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 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, 206, 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 wire.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 } } }
func newCoinBase(outputValues ...int64) *wire.MsgTx { tx := wire.MsgTx{ TxIn: []*wire.TxIn{ &wire.TxIn{ PreviousOutPoint: wire.OutPoint{Index: ^uint32(0)}, }, }, } for _, val := range outputValues { tx.TxOut = append(tx.TxOut, &wire.TxOut{Value: val}) } return &tx }
func spendOutput(txHash *wire.ShaHash, index uint32, outputValues ...int64) *wire.MsgTx { tx := wire.MsgTx{ TxIn: []*wire.TxIn{ &wire.TxIn{ PreviousOutPoint: wire.OutPoint{Hash: *txHash, Index: index}, }, }, } for _, val := range outputValues { tx.TxOut = append(tx.TxOut, &wire.TxOut{Value: val}) } return &tx }
// addChange adds a new output with the given amount and address, and // randomizes the index (and returns it) of the newly added output. func addChange(msgtx *wire.MsgTx, change coinutil.Amount, changeAddr coinutil.Address) (int, error) { pkScript, err := txscript.PayToAddrScript(changeAddr) if err != nil { return 0, fmt.Errorf("cannot create txout script: %s", err) } msgtx.AddTxOut(wire.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] return int(r), 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 wire.MsgTx err := msgTx.Deserialize(r) if err != nil { return nil, err } t := Tx{ msgTx: &msgTx, txIndex: TxIndexUnknown, } return &t, nil }
// NewTxRecordFromMsgTx creates a new transaction record that may be inserted // into the store. func NewTxRecordFromMsgTx(msgTx *wire.MsgTx, received time.Time) (*TxRecord, error) { buf := bytes.NewBuffer(make([]byte, 0, msgTx.SerializeSize())) err := msgTx.Serialize(buf) if err != nil { str := "failed to serialize transaction" return nil, storeError(ErrInput, str, err) } rec := &TxRecord{ MsgTx: *msgTx, Received: received, SerializedTx: buf.Bytes(), } copy(rec.Hash[:], wire.DoubleSha256(rec.SerializedTx)) return rec, nil }
func equalTxs(t *testing.T, got, exp *wire.MsgTx) { var bufGot, bufExp bytes.Buffer err := got.Serialize(&bufGot) if err != nil { t.Fatal(err) } err = exp.Serialize(&bufExp) if err != nil { t.Fatal(err) } if !bytes.Equal(bufGot.Bytes(), bufExp.Bytes()) { t.Errorf("Found unexpected wire.MsgTx:") t.Errorf("Got: %v", got) t.Errorf("Expected: %v", exp) } }
// addOutputs adds the given address/amount pairs as outputs to msgtx, // returning their total amount. func addOutputs(msgtx *wire.MsgTx, pairs map[string]coinutil.Amount, chainParams *chaincfg.Params) (coinutil.Amount, error) { var minAmount coinutil.Amount for addrStr, amt := range pairs { if amt <= 0 { return minAmount, ErrNonPositiveAmount } minAmount += amt addr, err := coinutil.DecodeAddress(addrStr, chainParams) if err != nil { return minAmount, fmt.Errorf("cannot decode address: %s", err) } // Add output to spend amt to addr. pkScript, err := txscript.PayToAddrScript(addr) if err != nil { return minAmount, fmt.Errorf("cannot create txout script: %s", err) } txout := wire.NewTxOut(int64(amt), pkScript) msgtx.AddTxOut(txout) } return minAmount, nil }
// TestTxSerialize tests MsgTx serialize and deserialize. func TestTxSerialize(t *testing.T) { noTx := wire.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 *wire.MsgTx // Message to encode out *wire.MsgTx // Expected decoded message buf []byte // Serialized data pkScriptLocs []int // Expected output script locations }{ // No transactions. { noTx, noTx, noTxEncoded, nil, }, // Multiple transactions. { multiTx, multiTx, multiTxEncoded, multiTxPkScriptLocs, }, } 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 wire.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 } // Ensure the public key script locations are accurate. pkScriptLocs := test.in.PkScriptLocs() if !reflect.DeepEqual(pkScriptLocs, test.pkScriptLocs) { t.Errorf("PkScriptLocs #%d\n got: %s want: %s", i, spew.Sdump(pkScriptLocs), spew.Sdump(test.pkScriptLocs)) continue } for j, loc := range pkScriptLocs { wantPkScript := test.in.TxOut[j].PkScript gotPkScript := test.buf[loc : loc+len(wantPkScript)] if !bytes.Equal(gotPkScript, wantPkScript) { t.Errorf("PkScriptLocs #%d:%d\n unexpected "+ "script got: %s want: %s", i, j, spew.Sdump(gotPkScript), spew.Sdump(wantPkScript)) } } } }
// 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 *wire.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 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, 206, 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 wire.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 := wire.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 *wire.MsgTx // Message to encode out *wire.MsgTx // Expected decoded message buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding }{ // Latest protocol version with no transactions. { noTx, noTx, noTxEncoded, wire.ProtocolVersion, }, // Latest protocol version with multiple transactions. { multiTx, multiTx, multiTxEncoded, wire.ProtocolVersion, }, // Protocol version BIP0035Version with no transactions. { noTx, noTx, noTxEncoded, wire.BIP0035Version, }, // Protocol version BIP0035Version with multiple transactions. { multiTx, multiTx, multiTxEncoded, wire.BIP0035Version, }, // Protocol version BIP0031Version with no transactions. { noTx, noTx, noTxEncoded, wire.BIP0031Version, }, // Protocol version BIP0031Version with multiple transactions. { multiTx, multiTx, multiTxEncoded, wire.BIP0031Version, }, // Protocol version NetAddressTimeVersion with no transactions. { noTx, noTx, noTxEncoded, wire.NetAddressTimeVersion, }, // Protocol version NetAddressTimeVersion with multiple transactions. { multiTx, multiTx, multiTxEncoded, wire.NetAddressTimeVersion, }, // Protocol version MultipleAddressVersion with no transactions. { noTx, noTx, noTxEncoded, wire.MultipleAddressVersion, }, // Protocol version MultipleAddressVersion with multiple transactions. { multiTx, multiTx, multiTxEncoded, wire.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 wire.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 } } }
// 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 *coinutil.Block) (height int32, rerr error) { db.dbLock.Lock() defer db.dbLock.Unlock() defer func() { if rerr == nil { rerr = db.processBatches() } else { db.lBatch().Reset() } }() blocksha := block.Sha() 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 := wire.NewShaHashFromStr("d5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599") if err != nil { panic("invalid sha string in source") } if txsha.IsEqual(dupsha) { // marking TxOut[0] as spent po := wire.NewOutPoint(dupsha, 0) txI := wire.NewTxIn(po, []byte("garbage")) var spendtx wire.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 := wire.NewShaHashFromStr("e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468") if err != nil { panic("invalid sha string in source") } if txsha.IsEqual(dupsha) { // marking TxOut[0] as spent po := wire.NewOutPoint(dupsha, 0) txI := wire.NewTxIn(po, []byte("garbage")) var spendtx wire.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 }
// loadTxStore returns a transaction store loaded from a file. func loadTxStore(filename string) (blockchain.TxStore, error) { // The txstore file format is: // <num tx data entries> <tx length> <serialized tx> <blk height> // <num spent bits> <spent bits> // // All num and length fields are little-endian uint32s. The spent bits // field is padded to a byte boundary. filename = filepath.Join("testdata/", filename) fi, err := os.Open(filename) if err != nil { return nil, err } // Choose read based on whether the file is compressed or not. var r io.Reader if strings.HasSuffix(filename, ".bz2") { r = bzip2.NewReader(fi) } else { r = fi } defer fi.Close() // Num of transaction store objects. var numItems uint32 if err := binary.Read(r, binary.LittleEndian, &numItems); err != nil { return nil, err } txStore := make(blockchain.TxStore) var uintBuf uint32 for height := uint32(0); height < numItems; height++ { txD := blockchain.TxData{} // Serialized transaction length. err = binary.Read(r, binary.LittleEndian, &uintBuf) if err != nil { return nil, err } serializedTxLen := uintBuf if serializedTxLen > wire.MaxBlockPayload { return nil, fmt.Errorf("Read serialized transaction "+ "length of %d is larger max allowed %d", serializedTxLen, wire.MaxBlockPayload) } // Transaction. var msgTx wire.MsgTx err = msgTx.Deserialize(r) if err != nil { return nil, err } txD.Tx = coinutil.NewTx(&msgTx) // Transaction hash. txHash := msgTx.TxSha() txD.Hash = &txHash // Block height the transaction came from. err = binary.Read(r, binary.LittleEndian, &uintBuf) if err != nil { return nil, err } txD.BlockHeight = int32(uintBuf) // Num spent bits. err = binary.Read(r, binary.LittleEndian, &uintBuf) if err != nil { return nil, err } numSpentBits := uintBuf numSpentBytes := numSpentBits / 8 if numSpentBits%8 != 0 { numSpentBytes++ } // Packed spent bytes. spentBytes := make([]byte, numSpentBytes) _, err = io.ReadFull(r, spentBytes) if err != nil { return nil, err } // Populate spent data based on spent bits. txD.Spent = make([]bool, numSpentBits) for byteNum, spentByte := range spentBytes { for bit := 0; bit < 8; bit++ { if uint32((byteNum*8)+bit) < numSpentBits { if spentByte&(1<<uint(bit)) != 0 { txD.Spent[(byteNum*8)+bit] = true } } } } txStore[*txD.Hash] = &txD } return txStore, nil }
// 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, &wire.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, &wire.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, &wire.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, &wire.MessageError{}, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Decode from wire format. var msg wire.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 } } }
// TestSort ensures the transaction sorting works according to the BIP. func TestSort(t *testing.T) { tests := []struct { name string hexFile string isSorted bool unsortedHash string sortedHash string }{ { name: "first test case from BIPLI01 - sorts inputs only, based on hash", hexFile: "li01-1.hex", isSorted: false, unsortedHash: "0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3", sortedHash: "839503cb611a3e3734bd521c608f881be2293ff77b7384057ab994c794fce623", }, { name: "second test case from BIPLI01 - already sorted", hexFile: "li01-2.hex", isSorted: true, unsortedHash: "28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f", sortedHash: "28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f", }, { name: "block 100001 tx[1] - sorts outputs only, based on amount", hexFile: "li01-3.hex", isSorted: false, unsortedHash: "fbde5d03b027d2b9ba4cf5d4fecab9a99864df2637b25ea4cbcb1796ff6550ca", sortedHash: "0a8c246c55f6b82f094d211f4f57167bf2ea4898741d218b09bdb2536fd8d13f", }, { name: "block 100001 tx[2] - sorts both inputs and outputs", hexFile: "li01-4.hex", isSorted: false, unsortedHash: "8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb", sortedHash: "a3196553b928b0b6154b002fa9a1ce875adabc486fedaaaf4c17430fd4486329", }, { name: "block 100998 tx[6] - sorts outputs only, based on output script", hexFile: "li01-5.hex", isSorted: false, unsortedHash: "ff85e8fc92e71bbc217e3ea9a3bacb86b435e52b6df0b089d67302c293a2b81d", sortedHash: "9a6c24746de024f77cac9b2138694f11101d1c66289261224ca52a25155a7c94", }, } for _, test := range tests { // Load and deserialize the test transaction. filePath := filepath.Join("testdata", test.hexFile) txHexBytes, err := ioutil.ReadFile(filePath) if err != nil { t.Errorf("ReadFile (%s): failed to read test file: %v", test.name, err) continue } txBytes, err := hex.DecodeString(string(txHexBytes)) if err != nil { t.Errorf("DecodeString (%s): failed to decode tx: %v", test.name, err) continue } var tx wire.MsgTx err = tx.Deserialize(bytes.NewReader(txBytes)) if err != nil { t.Errorf("Deserialize (%s): unexpected error %v", test.name, err) continue } // Ensure the sort order of the original transaction matches the // expected value. if got := txsort.IsSorted(&tx); got != test.isSorted { t.Errorf("IsSorted (%s): sort does not match "+ "expected - got %v, want %v", test.name, got, test.isSorted) continue } // Sort the transaction and ensure the resulting hash is the // expected value. sortedTx := txsort.Sort(&tx) if got := sortedTx.TxSha().String(); got != test.sortedHash { t.Errorf("Sort (%s): sorted hash does not match "+ "expected - got %v, want %v", test.name, got, test.sortedHash) continue } // Ensure the original transaction is not modified. if got := tx.TxSha().String(); got != test.unsortedHash { t.Errorf("Sort (%s): unsorted hash does not match "+ "expected - got %v, want %v", test.name, got, test.unsortedHash) continue } // Now sort the transaction using the mutable version and ensure // the resulting hash is the expected value. txsort.InPlaceSort(&tx) if got := tx.TxSha().String(); got != test.sortedHash { t.Errorf("SortMutate (%s): sorted hash does not match "+ "expected - got %v, want %v", test.name, got, test.sortedHash) continue } } }
// Sort returns a new transaction with the inputs and outputs sorted based on // BIP LI01. The passed transaction is not modified and the new transaction // might have a different hash if any sorting was done. func Sort(tx *wire.MsgTx) *wire.MsgTx { txCopy := tx.Copy() sort.Sort(sortableInputSlice(txCopy.TxIn)) sort.Sort(sortableOutputSlice(txCopy.TxOut)) return txCopy }
// calcSignatureHash will, given a script and hash type for the current script // engine instance, calculate the signature hash to be used for signing and // verification. func calcSignatureHash(script []parsedOpcode, hashType SigHashType, tx *wire.MsgTx, idx int) []byte { // The SigHashSingle signature type signs only the corresponding input // and output (the output with the same index number as the input). // // Since transactions can have more inputs than outputs, this means it // is improper to use SigHashSingle on input indices that don't have a // corresponding output. // // A bug in the original Satoshi client implementation means specifying // an index that is out of range results in a signature hash of 1 (as a // uint256 little endian). The original intent appeared to be to // indicate failure, but unfortunately, it was never checked and thus is // treated as the actual signature hash. This buggy behavior is now // part of the consensus and a hard fork would be required to fix it. // // Due to this, care must be taken by software that creates transactions // which make use of SigHashSingle because it can lead to an extremely // dangerous situation where the invalid inputs will end up signing a // hash of 1. This in turn presents an opportunity for attackers to // cleverly construct transactions which can steal those coins provided // they can reuse signatures. if hashType&sigHashMask == SigHashSingle && idx >= len(tx.TxOut) { var hash wire.ShaHash hash[0] = 0x01 return hash[:] } // Remove all instances of OP_CODESEPARATOR from the script. script = removeOpcode(script, OP_CODESEPARATOR) // Make a deep copy of the transaction, zeroing out the script for all // inputs that are not currently being processed. txCopy := tx.Copy() for i := range txCopy.TxIn { if i == idx { // UnparseScript cannot fail here because removeOpcode // above only returns a valid script. sigScript, _ := unparseScript(script) txCopy.TxIn[idx].SignatureScript = sigScript } else { txCopy.TxIn[i].SignatureScript = nil } } switch hashType & sigHashMask { case SigHashNone: txCopy.TxOut = txCopy.TxOut[0:0] // Empty slice. for i := range txCopy.TxIn { if i != idx { txCopy.TxIn[i].Sequence = 0 } } case SigHashSingle: // Resize output array to up to and including requested index. txCopy.TxOut = txCopy.TxOut[:idx+1] // All but current output get zeroed out. for i := 0; i < idx; i++ { txCopy.TxOut[i].Value = -1 txCopy.TxOut[i].PkScript = nil } // Sequence on all other inputs is 0, too. for i := range txCopy.TxIn { if i != idx { txCopy.TxIn[i].Sequence = 0 } } default: // Consensus treats undefined hashtypes like normal SigHashAll // for purposes of hash generation. fallthrough case SigHashOld: fallthrough case SigHashAll: // Nothing special here. } if hashType&SigHashAnyOneCanPay != 0 { txCopy.TxIn = txCopy.TxIn[idx : idx+1] idx = 0 } // The final hash is the double sha256 of both the serialized modified // transaction and the hash type (encoded as a 4-byte little-endian // value) appended. var wbuf bytes.Buffer txCopy.Serialize(&wbuf) binary.Write(&wbuf, binary.LittleEndian, hashType) return wire.DoubleSha256(wbuf.Bytes()) }
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 := database.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 func() { if err := db.Close(); err != nil { t.Errorf("Close: unexpected error: %v", err) } }() 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 *wire.ShaHash // Populate with the fisrt 256 blocks, so we have blocks to 'mess with' err = nil out: for height := int32(0); height < int32(len(blocks)); height++ { block := blocks[height] // except for NoVerify which does not allow lookups check inputs mblock := block.MsgBlock() var txneededList []*wire.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) exists, err := db.ExistsTxSha(origintxsha) if err != nil { t.Errorf("ExistsTxSha: unexpected error %v ", err) } if !exists { 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 } // generate 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 wire.BlockHeader bh.Version = 2 bh.PrevBlock = *lastSha // Bits, Nonce are not filled in mblk := wire.NewMsgBlock(&bh) hash, _ := wire.NewShaHashFromStr("df2b060fa2e5e9c8ed5eaf6a45c13753ec8c63282b2688322eba40cd98ea067a") po := wire.NewOutPoint(hash, 0) txI := wire.NewTxIn(po, []byte("garbage")) txO := wire.NewTxOut(50000000, []byte("garbageout")) var tx wire.MsgTx tx.AddTxIn(txI) tx.AddTxOut(txO) mblk.AddTransaction(&tx) blk := coinutil.NewBlock(mblk) fetchList := []*wire.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 != database.ErrTxShaMissing { t.Errorf("sha %v spent %v err %v\n", lr.Sha, lr.TxSpent, lr.Err) } } txlist := blk.Transactions() for _, tx := range txlist { txsha := tx.Sha() 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 { t.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) } }