Extend parsing of block in coins

This commit is contained in:
Martin Boehm 2018-09-18 13:35:53 +02:00
parent 8c1ccda82e
commit 6c08d7c51c
8 changed files with 274 additions and 169 deletions

View File

@ -101,7 +101,10 @@ func (b *BCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash) return nil, errors.Annotatef(err, "hash %v", hash)
} }
// size is not returned by GetBlockHeader and would be overwritten
size := block.Size
block.BlockHeader = *header block.BlockHeader = *header
block.Size = size
return block, nil return block, nil
} }

View File

@ -5,6 +5,7 @@ import (
"blockbook/bchain/coins/btc" "blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils" "blockbook/bchain/coins/utils"
"bytes" "bytes"
"encoding/binary"
"io" "io"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
@ -75,11 +76,13 @@ func GetChainParams(chain string) *chaincfg.Params {
// headerFixedLength is the length of fixed fields of a block (i.e. without solution) // headerFixedLength is the length of fixed fields of a block (i.e. without solution)
// see https://github.com/BTCGPU/BTCGPU/wiki/Technical-Spec#block-header // see https://github.com/BTCGPU/BTCGPU/wiki/Technical-Spec#block-header
const headerFixedLength = 44 + (chainhash.HashSize * 3) const headerFixedLength = 44 + (chainhash.HashSize * 3)
const timestampOffset = 100
const timestampLength = 4
// ParseBlock parses raw block to our Block struct // ParseBlock parses raw block to our Block struct
func (p *BGoldParser) ParseBlock(b []byte) (*bchain.Block, error) { func (p *BGoldParser) ParseBlock(b []byte) (*bchain.Block, error) {
r := bytes.NewReader(b) r := bytes.NewReader(b)
err := skipHeader(r, 0) time, err := getTimestampAndSkipHeader(r, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -95,24 +98,41 @@ func (p *BGoldParser) 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: time,
},
Txs: txs,
}, nil
} }
func skipHeader(r io.ReadSeeker, pver uint32) error { func getTimestampAndSkipHeader(r io.ReadSeeker, pver uint32) (int64, error) {
_, err := r.Seek(headerFixedLength, io.SeekStart) _, err := r.Seek(timestampOffset, io.SeekStart)
if err != nil { if err != nil {
return err return 0, err
}
buf := make([]byte, timestampLength)
if _, err = io.ReadFull(r, buf); err != nil {
return 0, err
}
time := binary.LittleEndian.Uint32(buf)
_, err = r.Seek(headerFixedLength-timestampOffset-timestampLength, io.SeekCurrent)
if err != nil {
return 0, err
} }
size, err := wire.ReadVarInt(r, pver) size, err := wire.ReadVarInt(r, pver)
if err != nil { if err != nil {
return err return 0, err
} }
_, err = r.Seek(int64(size), io.SeekCurrent) _, err = r.Seek(int64(size), io.SeekCurrent)
if err != nil { if err != nil {
return err return 0, err
} }
return nil return int64(time), nil
} }

View File

@ -12,8 +12,17 @@ import (
"testing" "testing"
) )
var testParseBlockTxs = map[int][]string{ type testBlock struct {
104000: []string{ size int
time int64
txs []string
}
var testParseBlockTxs = map[int]testBlock{
104000: testBlock{
size: 15776,
time: 1295705889,
txs: []string{
"331d4ef64118e9e5be75f0f51f1a4c5057550c3320e22ff7206f3e1101f113d0", "331d4ef64118e9e5be75f0f51f1a4c5057550c3320e22ff7206f3e1101f113d0",
"1f4817d8e91c21d8c8d163dabccdd1875f760fd2dc34a1c2b7b8fa204e103597", "1f4817d8e91c21d8c8d163dabccdd1875f760fd2dc34a1c2b7b8fa204e103597",
"268163b1a1092aa0996d118a6027b0b6f1076627e02addc4e66ae30239936818", "268163b1a1092aa0996d118a6027b0b6f1076627e02addc4e66ae30239936818",
@ -62,7 +71,11 @@ var testParseBlockTxs = map[int][]string{
"3393bdcc3a7232b37d0fb6b56d603a2b9b0419e461bf989f1c375859a5d0156a", "3393bdcc3a7232b37d0fb6b56d603a2b9b0419e461bf989f1c375859a5d0156a",
"33ad36d79d63b575c7532c516f16b19541f5c637caf7073beb7ddf604c3f39cc", "33ad36d79d63b575c7532c516f16b19541f5c637caf7073beb7ddf604c3f39cc",
}, },
532144: []string{ },
532144: testBlock{
size: 12198,
time: 1528372417,
txs: []string{
"574348e23301cc89535408b6927bf75f2ac88fadf8fdfb181c17941a5de02fe0", "574348e23301cc89535408b6927bf75f2ac88fadf8fdfb181c17941a5de02fe0",
"9f048446401e7fac84963964df045b1f3992eda330a87b02871e422ff0a3fd28", "9f048446401e7fac84963964df045b1f3992eda330a87b02871e422ff0a3fd28",
"9516c320745a227edb07c98087b1febea01c3ba85122a34387896fc82ba965e4", "9516c320745a227edb07c98087b1febea01c3ba85122a34387896fc82ba965e4",
@ -79,6 +92,7 @@ var testParseBlockTxs = map[int][]string{
"84ad99dc0884e03fc71f163eebf515a1eb79d00b1aad7a1126b22747960a8275", "84ad99dc0884e03fc71f163eebf515a1eb79d00b1aad7a1126b22747960a8275",
"728c8d0858e506d4a1a9b506f7b974b335e6c54047af9f40d4cb1a0561f783e1", "728c8d0858e506d4a1a9b506f7b974b335e6c54047af9f40d4cb1a0561f783e1",
}, },
},
} }
func helperLoadBlock(t *testing.T, height int) []byte { func helperLoadBlock(t *testing.T, height int) []byte {
@ -104,7 +118,7 @@ func helperLoadBlock(t *testing.T, height int) []byte {
func TestParseBlock(t *testing.T) { func TestParseBlock(t *testing.T) {
p := NewBGoldParser(GetChainParams("main"), &btc.Configuration{}) p := NewBGoldParser(GetChainParams("main"), &btc.Configuration{})
for height, txs := range testParseBlockTxs { for height, tb := range testParseBlockTxs {
b := helperLoadBlock(t, height) b := helperLoadBlock(t, height)
blk, err := p.ParseBlock(b) blk, err := p.ParseBlock(b)
@ -112,11 +126,19 @@ func TestParseBlock(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if len(blk.Txs) != len(txs) { if blk.Size != tb.size {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(txs)) t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size)
} }
for ti, tx := range txs { if blk.Time != tb.time {
t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time)
}
if len(blk.Txs) != len(tb.txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.txs))
}
for ti, tx := range tb.txs {
if blk.Txs[ti].Txid != tx { if blk.Txs[ti].Txid != tx {
t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx) t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
} }

View File

@ -75,5 +75,11 @@ func (p *DogecoinParser) 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: h.Timestamp.Unix(),
},
Txs: txs,
}, nil
} }

View File

@ -259,9 +259,18 @@ func Test_UnpackTx(t *testing.T) {
} }
} }
var testParseBlockTxs = map[int][]string{ type testBlock struct {
size int
time int64
txs []string
}
var testParseBlockTxs = map[int]testBlock{
// block without auxpow // block without auxpow
12345: []string{ 12345: testBlock{
size: 8582,
time: 1387104223,
txs: []string{
"9d1662dcc1443af9999c4fd1d6921b91027b5e2d0d3ebfaa41d84163cb99cad5", "9d1662dcc1443af9999c4fd1d6921b91027b5e2d0d3ebfaa41d84163cb99cad5",
"8284292cedeb0c9c509f9baa235802d52a546e1e9990040d35d018b97ad11cfa", "8284292cedeb0c9c509f9baa235802d52a546e1e9990040d35d018b97ad11cfa",
"3299d93aae5c3d37c795c07150ceaf008aefa5aad3205ea2519f94a35adbbe10", "3299d93aae5c3d37c795c07150ceaf008aefa5aad3205ea2519f94a35adbbe10",
@ -294,8 +303,12 @@ var testParseBlockTxs = map[int][]string{
"1c396493a8dfd59557052b6e8643123405894b64f48b2eb6eb7a003159034077", "1c396493a8dfd59557052b6e8643123405894b64f48b2eb6eb7a003159034077",
"2e2816ffb7bf1378f11acf5ba30d498efc8fd219d4b67a725e8254ce61b1b7ee", "2e2816ffb7bf1378f11acf5ba30d498efc8fd219d4b67a725e8254ce61b1b7ee",
}, },
},
// 1st block with auxpow // 1st block with auxpow
371337: []string{ 371337: testBlock{
size: 1704,
time: 1410464577,
txs: []string{
"4547b14bc16db4184fa9f141d645627430dd3dfa662d0e6f418fba497091da75", "4547b14bc16db4184fa9f141d645627430dd3dfa662d0e6f418fba497091da75",
"a965dba2ed06827ed9a24f0568ec05b73c431bc7f0fb6913b144e62db7faa519", "a965dba2ed06827ed9a24f0568ec05b73c431bc7f0fb6913b144e62db7faa519",
"5e3ab18cb7ba3abc44e62fb3a43d4c8168d00cf0a2e0f8dbeb2636bb9a212d12", "5e3ab18cb7ba3abc44e62fb3a43d4c8168d00cf0a2e0f8dbeb2636bb9a212d12",
@ -303,8 +316,12 @@ var testParseBlockTxs = map[int][]string{
"ec063cc8025f9f30a6ed40fc8b1fe63b0cbd2ea2c62664eb26b365e6243828ca", "ec063cc8025f9f30a6ed40fc8b1fe63b0cbd2ea2c62664eb26b365e6243828ca",
"02c16e3389320da3e77686d39773dda65a1ecdf98a2ef9cfb938c9f4b58f7a40", "02c16e3389320da3e77686d39773dda65a1ecdf98a2ef9cfb938c9f4b58f7a40",
}, },
},
// block with auxpow // block with auxpow
567890: []string{ 567890: testBlock{
size: 3833,
time: 1422855443,
txs: []string{
"db20feea53be1f60848a66604d5bca63df62de4f6c66220f9c84436d788625a8", "db20feea53be1f60848a66604d5bca63df62de4f6c66220f9c84436d788625a8",
"cf7e9e27c0f56f0b100eaf5c776ce106025e3412bd5927c6e1ce575500e24eaa", "cf7e9e27c0f56f0b100eaf5c776ce106025e3412bd5927c6e1ce575500e24eaa",
"af84e010c1cf0bd927740d08e5e8163db45397b70f00df07aea5339c14d5f3aa", "af84e010c1cf0bd927740d08e5e8163db45397b70f00df07aea5339c14d5f3aa",
@ -315,8 +332,12 @@ var testParseBlockTxs = map[int][]string{
"3211ccc66d05b10959fa6e56d1955c12368ea52b40303558b254d7dc22570382", "3211ccc66d05b10959fa6e56d1955c12368ea52b40303558b254d7dc22570382",
"54c1b279e78b924dfa15857c80131c3ddf835ab02f513dc03aa514f87b680493", "54c1b279e78b924dfa15857c80131c3ddf835ab02f513dc03aa514f87b680493",
}, },
},
// recent block // recent block
2264125: []string{ 2264125: testBlock{
size: 8531,
time: 1529099968,
txs: []string{
"76f0126562c99e020b5fba41b68dd8141a4f21eef62012b76a1e0635092045e9", "76f0126562c99e020b5fba41b68dd8141a4f21eef62012b76a1e0635092045e9",
"7bb6688bec16de94014574e3e1d3f6f5fb956530d6b179b28db367f1fd8ae099", "7bb6688bec16de94014574e3e1d3f6f5fb956530d6b179b28db367f1fd8ae099",
"d7e2ee30c3d179ac896651fc09c1396333f41d952d008af8d5d6665cbea377bf", "d7e2ee30c3d179ac896651fc09c1396333f41d952d008af8d5d6665cbea377bf",
@ -343,6 +364,7 @@ var testParseBlockTxs = map[int][]string{
"d515d271849717b091a9c46bf11c47efb9d975e72b668c137786a208cf0a9739", "d515d271849717b091a9c46bf11c47efb9d975e72b668c137786a208cf0a9739",
"a800da44e6eed944043561fe22ee0a6e11341e6bc1a8ec2789b83930cc9b170e", "a800da44e6eed944043561fe22ee0a6e11341e6bc1a8ec2789b83930cc9b170e",
}, },
},
} }
func helperLoadBlock(t *testing.T, height int) []byte { func helperLoadBlock(t *testing.T, height int) []byte {
@ -368,7 +390,7 @@ func helperLoadBlock(t *testing.T, height int) []byte {
func TestParseBlock(t *testing.T) { func TestParseBlock(t *testing.T) {
p := NewDogecoinParser(GetChainParams("main"), &btc.Configuration{}) p := NewDogecoinParser(GetChainParams("main"), &btc.Configuration{})
for height, txs := range testParseBlockTxs { for height, tb := range testParseBlockTxs {
b := helperLoadBlock(t, height) b := helperLoadBlock(t, height)
blk, err := p.ParseBlock(b) blk, err := p.ParseBlock(b)
@ -376,11 +398,19 @@ func TestParseBlock(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if len(blk.Txs) != len(txs) { if blk.Size != tb.size {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(txs)) t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size)
} }
for ti, tx := range txs { if blk.Time != tb.time {
t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time)
}
if len(blk.Txs) != len(tb.txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.txs))
}
for ti, tx := range tb.txs {
if blk.Txs[ti].Txid != tx { if blk.Txs[ti].Txid != tx {
t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx) t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
} }

View File

@ -75,5 +75,11 @@ func (p *NamecoinParser) 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: h.Timestamp.Unix(),
},
Txs: txs,
}, nil
} }

View File

@ -53,12 +53,22 @@ func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
} }
} }
var testParseBlockTxs = map[int][]string{ type testBlock struct {
40000: []string{ size int
time int64
txs []string
}
var testParseBlockTxs = map[int]testBlock{
40000: testBlock{
size: 1385,
time: 1327728573,
txs: []string{
"e193a821393b20b99f4a4e05a481368ef8a8cfd43d0c45bdad7f53bc9535e844", "e193a821393b20b99f4a4e05a481368ef8a8cfd43d0c45bdad7f53bc9535e844",
"ddcbf95797e81dd04127885bd001e96695b717e11c52721f6e8ee53f6dea8a6f", "ddcbf95797e81dd04127885bd001e96695b717e11c52721f6e8ee53f6dea8a6f",
"31ff728f24200f59fa4958e6c26de03d172b320e6eef2b8abecf6f94d01dd4ae", "31ff728f24200f59fa4958e6c26de03d172b320e6eef2b8abecf6f94d01dd4ae",
}, },
},
} }
func helperLoadBlock(t *testing.T, height int) []byte { func helperLoadBlock(t *testing.T, height int) []byte {
@ -84,7 +94,7 @@ func helperLoadBlock(t *testing.T, height int) []byte {
func TestParseBlock(t *testing.T) { func TestParseBlock(t *testing.T) {
p := NewNamecoinParser(GetChainParams("main"), &btc.Configuration{}) p := NewNamecoinParser(GetChainParams("main"), &btc.Configuration{})
for height, txs := range testParseBlockTxs { for height, tb := range testParseBlockTxs {
b := helperLoadBlock(t, height) b := helperLoadBlock(t, height)
blk, err := p.ParseBlock(b) blk, err := p.ParseBlock(b)
@ -92,11 +102,19 @@ func TestParseBlock(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if len(blk.Txs) != len(txs) { if blk.Size != tb.size {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(txs)) t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size)
} }
for ti, tx := range txs { if blk.Time != tb.time {
t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time)
}
if len(blk.Txs) != len(tb.txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.txs))
}
for ti, tx := range tb.txs {
if blk.Txs[ti].Txid != tx { if blk.Txs[ti].Txid != tx {
t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx) t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
} }

View File

@ -33,7 +33,7 @@ func DecodeTransactions(r io.Reader, pver uint32, enc wire.MessageEncoding, blk
if txCount > maxTxPerBlock { if txCount > maxTxPerBlock {
str := fmt.Sprintf("too many transactions to fit into a block "+ str := fmt.Sprintf("too many transactions to fit into a block "+
"[count %d, max %d]", txCount, maxTxPerBlock) "[count %d, max %d]", txCount, maxTxPerBlock)
return &wire.MessageError{Func: "btg.decodeTransactions", Description: str} return &wire.MessageError{Func: "utils.decodeTransactions", Description: str}
} }
blk.Transactions = make([]*wire.MsgTx, 0, txCount) blk.Transactions = make([]*wire.MsgTx, 0, txCount)