// 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 = 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 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 }
// 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 > btcwire.MaxBlockPayload { return nil, fmt.Errorf("Read serialized transaction "+ "length of %d is larger max allowed %d", serializedTxLen, btcwire.MaxBlockPayload) } // Transaction. var msgTx btcwire.MsgTx err = msgTx.Deserialize(r) if err != nil { return nil, err } txD.Tx = btcutil.NewTx(&msgTx) // Transaction hash. txHash, err := msgTx.TxSha() if err != nil { return nil, err } txD.Hash = &txHash // Block height the transaction came from. err = binary.Read(r, binary.LittleEndian, &uintBuf) if err != nil { return nil, err } txD.BlockHeight = int64(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 }