Store extended info about block in heigth column
This commit is contained in:
parent
78f6162d5c
commit
c9471bf867
@ -184,7 +184,13 @@ func (p *BitcoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
|
|||||||
txs[ti] = p.TxFromMsgTx(t, false)
|
txs[ti] = p.TxFromMsgTx(t, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &bchain.Block{Txs: txs}, nil
|
return &bchain.Block{
|
||||||
|
BlockHeader: bchain.BlockHeader{
|
||||||
|
Size: len(b),
|
||||||
|
Time: w.Header.Timestamp.Unix(),
|
||||||
|
},
|
||||||
|
Txs: txs,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackTx packs transaction to byte array
|
// PackTx packs transaction to byte array
|
||||||
|
|||||||
@ -321,9 +321,9 @@ func (b *EthereumRPC) ethHeaderToBlockHeader(h *ethtypes.Header) (*bchain.BlockH
|
|||||||
Hash: ethHashToHash(h.Hash()),
|
Hash: ethHashToHash(h.Hash()),
|
||||||
Height: uint32(hn),
|
Height: uint32(hn),
|
||||||
Confirmations: int(c),
|
Confirmations: int(c),
|
||||||
|
Time: int64(h.Time.Uint64()),
|
||||||
// Next
|
// Next
|
||||||
// Prev
|
// Prev
|
||||||
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,6 +393,8 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
|
|||||||
return nil, errors.Annotatef(fmt.Errorf("server returned empty transaction list but block header indicates transactions"), "hash %v, height %v", hash, height)
|
return nil, errors.Annotatef(fmt.Errorf("server returned empty transaction list but block header indicates transactions"), "hash %v, height %v", hash, height)
|
||||||
}
|
}
|
||||||
bbh, err := b.ethHeaderToBlockHeader(head)
|
bbh, err := b.ethHeaderToBlockHeader(head)
|
||||||
|
// TODO - this is probably not the correct size
|
||||||
|
bbh.Size = len(raw)
|
||||||
btxs := make([]bchain.Tx, len(body.Transactions))
|
btxs := make([]bchain.Tx, len(body.Transactions))
|
||||||
for i, tx := range body.Transactions {
|
for i, tx := range body.Transactions {
|
||||||
btx, err := b.Parser.ethTxToTx(&tx, int64(head.Time.Uint64()), uint32(bbh.Confirmations))
|
btx, err := b.Parser.ethTxToTx(&tx, int64(head.Time.Uint64()), uint32(bbh.Confirmations))
|
||||||
|
|||||||
@ -88,6 +88,8 @@ type BlockHeader struct {
|
|||||||
Next string `json:"nextblockhash"`
|
Next string `json:"nextblockhash"`
|
||||||
Height uint32 `json:"height"`
|
Height uint32 `json:"height"`
|
||||||
Confirmations int `json:"confirmations"`
|
Confirmations int `json:"confirmations"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Time int64 `json:"time,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MempoolEntry struct {
|
type MempoolEntry struct {
|
||||||
|
|||||||
@ -1091,17 +1091,63 @@ func (d *RocksDB) writeAddressesNonUTXO(wb *gorocksdb.WriteBatch, block *bchain.
|
|||||||
|
|
||||||
// Block index
|
// Block index
|
||||||
|
|
||||||
|
type BlockInfo struct {
|
||||||
|
Txid string
|
||||||
|
Time time.Time
|
||||||
|
Txs uint32
|
||||||
|
Size uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *RocksDB) packBlockInfo(block *bchain.Block) ([]byte, error) {
|
||||||
|
packed := make([]byte, 0, 64)
|
||||||
|
varBuf := make([]byte, vlq.MaxLen64)
|
||||||
|
b, err := d.chainParser.PackBlockHash(block.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
packed = append(packed, b...)
|
||||||
|
packed = append(packed, packUint(uint32(block.Time))...)
|
||||||
|
l := packVaruint(uint(len(block.Txs)), varBuf)
|
||||||
|
packed = append(packed, varBuf[:l]...)
|
||||||
|
l = packVaruint(uint(block.Size), varBuf)
|
||||||
|
packed = append(packed, varBuf[:l]...)
|
||||||
|
return packed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *RocksDB) unpackBlockInfo(buf []byte) (*BlockInfo, error) {
|
||||||
|
pl := d.chainParser.PackedTxidLen()
|
||||||
|
// minimum length is PackedTxidLen+4 bytes time + 1 byte txs + 1 byte size
|
||||||
|
if len(buf) < pl+4+2 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
txid, err := d.chainParser.UnpackBlockHash(buf[:pl])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := unpackUint(buf[pl:])
|
||||||
|
txs, l := unpackVaruint(buf[pl+4:])
|
||||||
|
size, _ := unpackVaruint(buf[pl+4+l:])
|
||||||
|
return &BlockInfo{
|
||||||
|
Txid: txid,
|
||||||
|
Time: time.Unix(int64(t), 0),
|
||||||
|
Txs: uint32(txs),
|
||||||
|
Size: uint32(size),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetBestBlock returns the block hash of the block with highest height in the db
|
// GetBestBlock returns the block hash of the block with highest height in the db
|
||||||
func (d *RocksDB) GetBestBlock() (uint32, string, error) {
|
func (d *RocksDB) GetBestBlock() (uint32, string, error) {
|
||||||
it := d.db.NewIteratorCF(d.ro, d.cfh[cfHeight])
|
it := d.db.NewIteratorCF(d.ro, d.cfh[cfHeight])
|
||||||
defer it.Close()
|
defer it.Close()
|
||||||
if it.SeekToLast(); it.Valid() {
|
if it.SeekToLast(); it.Valid() {
|
||||||
bestHeight := unpackUint(it.Key().Data())
|
bestHeight := unpackUint(it.Key().Data())
|
||||||
val, err := d.chainParser.UnpackBlockHash(it.Value().Data())
|
info, err := d.unpackBlockInfo(it.Value().Data())
|
||||||
if glog.V(1) {
|
if info != nil {
|
||||||
glog.Infof("rocksdb: bestblock %d %s", bestHeight, val)
|
if glog.V(1) {
|
||||||
|
glog.Infof("rocksdb: bestblock %d %+v", bestHeight, info)
|
||||||
|
}
|
||||||
|
return bestHeight, info.Txid, err
|
||||||
}
|
}
|
||||||
return bestHeight, val, err
|
|
||||||
}
|
}
|
||||||
return 0, "", nil
|
return 0, "", nil
|
||||||
}
|
}
|
||||||
@ -1114,7 +1160,22 @@ func (d *RocksDB) GetBlockHash(height uint32) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer val.Free()
|
defer val.Free()
|
||||||
return d.chainParser.UnpackBlockHash(val.Data())
|
info, err := d.unpackBlockInfo(val.Data())
|
||||||
|
if info == nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return info.Txid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockInfo returns block info stored in db
|
||||||
|
func (d *RocksDB) GetBlockInfo(height uint32) (*BlockInfo, error) {
|
||||||
|
key := packUint(height)
|
||||||
|
val, err := d.db.GetCF(d.ro, d.cfh[cfHeight], key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer val.Free()
|
||||||
|
return d.unpackBlockInfo(val.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *RocksDB) writeHeight(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error {
|
func (d *RocksDB) writeHeight(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error {
|
||||||
@ -1122,7 +1183,7 @@ func (d *RocksDB) writeHeight(wb *gorocksdb.WriteBatch, block *bchain.Block, op
|
|||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case opInsert:
|
case opInsert:
|
||||||
val, err := d.chainParser.PackBlockHash(block.Hash)
|
val, err := d.packBlockInfo(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"blockbook/bchain"
|
"blockbook/bchain"
|
||||||
"blockbook/bchain/coins/btc"
|
"blockbook/bchain/coins/btc"
|
||||||
"blockbook/common"
|
"blockbook/common"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
vlq "github.com/bsm/go-vlq"
|
vlq "github.com/bsm/go-vlq"
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
@ -92,6 +94,12 @@ func varuintToHex(i uint) string {
|
|||||||
return hex.EncodeToString(b[:l])
|
return hex.EncodeToString(b[:l])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func uintToHex(i uint32) string {
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(buf, i)
|
||||||
|
return hex.EncodeToString(buf)
|
||||||
|
}
|
||||||
|
|
||||||
// keyPair is used to compare given key value in DB with expected
|
// keyPair is used to compare given key value in DB with expected
|
||||||
// for more complicated compares it is possible to specify CompareFunc
|
// for more complicated compares it is possible to specify CompareFunc
|
||||||
type keyPair struct {
|
type keyPair struct {
|
||||||
@ -187,6 +195,8 @@ func getTestUTXOBlock1(t *testing.T, d *RocksDB) *bchain.Block {
|
|||||||
BlockHeader: bchain.BlockHeader{
|
BlockHeader: bchain.BlockHeader{
|
||||||
Height: 225493,
|
Height: 225493,
|
||||||
Hash: "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997",
|
Hash: "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997",
|
||||||
|
Size: 1234567,
|
||||||
|
Time: 1534858021,
|
||||||
},
|
},
|
||||||
Txs: []bchain.Tx{
|
Txs: []bchain.Tx{
|
||||||
bchain.Tx{
|
bchain.Tx{
|
||||||
@ -247,6 +257,8 @@ func getTestUTXOBlock2(t *testing.T, d *RocksDB) *bchain.Block {
|
|||||||
BlockHeader: bchain.BlockHeader{
|
BlockHeader: bchain.BlockHeader{
|
||||||
Height: 225494,
|
Height: 225494,
|
||||||
Hash: "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6",
|
Hash: "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6",
|
||||||
|
Size: 2345678,
|
||||||
|
Time: 1534859123,
|
||||||
},
|
},
|
||||||
Txs: []bchain.Tx{
|
Txs: []bchain.Tx{
|
||||||
bchain.Tx{
|
bchain.Tx{
|
||||||
@ -368,7 +380,11 @@ func getTestUTXOBlock2(t *testing.T, d *RocksDB) *bchain.Block {
|
|||||||
|
|
||||||
func verifyAfterUTXOBlock1(t *testing.T, d *RocksDB, afterDisconnect bool) {
|
func verifyAfterUTXOBlock1(t *testing.T, d *RocksDB, afterDisconnect bool) {
|
||||||
if err := checkColumn(d, cfHeight, []keyPair{
|
if err := checkColumn(d, cfHeight, []keyPair{
|
||||||
keyPair{"000370d5", "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997", nil},
|
keyPair{
|
||||||
|
"000370d5",
|
||||||
|
"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1534858021) + varuintToHex(2) + varuintToHex(1234567),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
{
|
{
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -445,8 +461,16 @@ func verifyAfterUTXOBlock1(t *testing.T, d *RocksDB, afterDisconnect bool) {
|
|||||||
|
|
||||||
func verifyAfterUTXOBlock2(t *testing.T, d *RocksDB) {
|
func verifyAfterUTXOBlock2(t *testing.T, d *RocksDB) {
|
||||||
if err := checkColumn(d, cfHeight, []keyPair{
|
if err := checkColumn(d, cfHeight, []keyPair{
|
||||||
keyPair{"000370d5", "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997", nil},
|
keyPair{
|
||||||
keyPair{"000370d6", "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6", nil},
|
"000370d5",
|
||||||
|
"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1534858021) + varuintToHex(2) + varuintToHex(1234567),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
keyPair{
|
||||||
|
"000370d6",
|
||||||
|
"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6" + uintToHex(1534859123) + varuintToHex(4) + varuintToHex(2345678),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
{
|
{
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -692,6 +716,30 @@ func TestRocksDB_Index_UTXO(t *testing.T) {
|
|||||||
t.Fatalf("GetBlockHash: got hash %v, expected %v", hash, "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997")
|
t.Fatalf("GetBlockHash: got hash %v, expected %v", hash, "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not connected block
|
||||||
|
hash, err = d.GetBlockHash(225495)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if hash != "" {
|
||||||
|
t.Fatalf("GetBlockHash: got hash '%v', expected ''", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockHash
|
||||||
|
info, err := d.GetBlockInfo(225494)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
iw := &BlockInfo{
|
||||||
|
Txid: "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6",
|
||||||
|
Txs: 4,
|
||||||
|
Size: 2345678,
|
||||||
|
Time: time.Unix(1534859123, 0),
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(info, iw) {
|
||||||
|
t.Errorf("GetAddressBalance() = %+v, want %+v", info, iw)
|
||||||
|
}
|
||||||
|
|
||||||
// Test tx caching functionality, leave one tx in db to test cleanup in DisconnectBlock
|
// Test tx caching functionality, leave one tx in db to test cleanup in DisconnectBlock
|
||||||
testTxCache(t, d, block1, &block1.Txs[0])
|
testTxCache(t, d, block1, &block1.Txs[0])
|
||||||
testTxCache(t, d, block2, &block2.Txs[0])
|
testTxCache(t, d, block2, &block2.Txs[0])
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user