diff --git a/bchain/coins/btg/bgoldparser.go b/bchain/coins/btg/bgoldparser.go index 7c20a005..67d2c7ef 100644 --- a/bchain/coins/btg/bgoldparser.go +++ b/bchain/coins/btg/bgoldparser.go @@ -3,8 +3,8 @@ package btg import ( "blockbook/bchain" "blockbook/bchain/coins/btc" + "blockbook/bchain/coins/utils" "bytes" - "fmt" "io" "github.com/btcsuite/btcd/chaincfg" @@ -72,19 +72,6 @@ func GetChainParams(chain string) *chaincfg.Params { } } -// minTxPayload is the minimum payload size for a transaction. Note -// that any realistically usable transaction must have at least one -// input or output, but that is a rule enforced at a higher layer, so -// it is intentionally not included here. -// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint -// number of transaction outputs 1 byte + LockTime 4 bytes + min input -// payload + min output payload. -const minTxPayload = 10 - -// maxTxPerBlock is the maximum number of transactions that could -// possibly fit into a block. -const maxTxPerBlock = (wire.MaxBlockPayload / minTxPayload) + 1 - // 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 const headerFixedLength = 44 + (chainhash.HashSize * 3) @@ -98,7 +85,7 @@ func (p *BGoldParser) ParseBlock(b []byte) (*bchain.Block, error) { } w := wire.MsgBlock{} - err = decodeTransactions(r, 0, wire.WitnessEncoding, &w) + err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w) if err != nil { return nil, err } @@ -129,31 +116,3 @@ func skipHeader(r io.ReadSeeker, pver uint32) error { return nil } - -func decodeTransactions(r io.Reader, pver uint32, enc wire.MessageEncoding, blk *wire.MsgBlock) error { - txCount, err := wire.ReadVarInt(r, pver) - if err != nil { - return err - } - - // Prevent more transactions than could possibly fit into a block. - // It would be possible to cause memory exhaustion and panics without - // a sane upper bound on this count. - if txCount > maxTxPerBlock { - str := fmt.Sprintf("too many transactions to fit into a block "+ - "[count %d, max %d]", txCount, maxTxPerBlock) - return &wire.MessageError{Func: "btg.decodeTransactions", Description: str} - } - - blk.Transactions = make([]*wire.MsgTx, 0, txCount) - for i := uint64(0); i < txCount; i++ { - tx := wire.MsgTx{} - err := tx.BtcDecode(r, pver, enc) - if err != nil { - return err - } - blk.Transactions = append(blk.Transactions, &tx) - } - - return nil -} diff --git a/bchain/coins/dogecoin/dogecoinparser.go b/bchain/coins/dogecoin/dogecoinparser.go index 075cdfa9..ac7ea94b 100644 --- a/bchain/coins/dogecoin/dogecoinparser.go +++ b/bchain/coins/dogecoin/dogecoinparser.go @@ -3,9 +3,8 @@ package dogecoin import ( "blockbook/bchain" "blockbook/bchain/coins/btc" + "blockbook/bchain/coins/utils" "bytes" - "fmt" - "io" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" @@ -50,21 +49,6 @@ func GetChainParams(chain string) *chaincfg.Params { } } -// minTxPayload is the minimum payload size for a transaction. Note -// that any realistically usable transaction must have at least one -// input or output, but that is a rule enforced at a higher layer, so -// it is intentionally not included here. -// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint -// number of transaction outputs 1 byte + LockTime 4 bytes + min input -// payload + min output payload. -const minTxPayload = 10 - -// maxTxPerBlock is the maximum number of transactions that could -// possibly fit into a block. -const maxTxPerBlock = (wire.MaxBlockPayload / minTxPayload) + 1 - -const versionAuxpow = (1 << 8) - // ParseBlock parses raw block to our Block struct // it has special handling for Auxpow blocks that cannot be parsed by standard btc wire parser func (p *DogecoinParser) ParseBlock(b []byte) (*bchain.Block, error) { @@ -75,57 +59,13 @@ func (p *DogecoinParser) ParseBlock(b []byte) (*bchain.Block, error) { if err != nil { return nil, err } - if (h.Version & versionAuxpow) != 0 { - // skip Auxpow part of the block - // https://github.com/dogecoin/dogecoin/blob/master/src/auxpow.h#L130 - // CMerkleTx CTransaction - tx := wire.MsgTx{} - err = tx.BtcDecode(r, 0, wire.WitnessEncoding) - if err != nil { - return nil, err - } - // CMerkleTx uint256 hashBlock - _, err = r.Seek(32, io.SeekCurrent) - if err != nil { - return nil, err - } - // CMerkleTx std::vector vMerkleBranch - size, err := wire.ReadVarInt(r, 0) - if err != nil { - return nil, err - } - _, err = r.Seek(int64(size)*32, io.SeekCurrent) - if err != nil { - return nil, err - } - // CMerkleTx int nIndex - _, err = r.Seek(4, io.SeekCurrent) - if err != nil { - return nil, err - } - // CAuxPow std::vector vChainMerkleBranch; - size, err = wire.ReadVarInt(r, 0) - if err != nil { - return nil, err - } - _, err = r.Seek(int64(size)*32, io.SeekCurrent) - if err != nil { - return nil, err - } - // CAuxPow int nChainIndex; - _, err = r.Seek(4, io.SeekCurrent) - if err != nil { - return nil, err - } - // CAuxPow CPureBlockHeader parentBlock; - ph := wire.BlockHeader{} - err = ph.Deserialize(r) - if err != nil { + if (h.Version & utils.VersionAuxpow) != 0 { + if err = utils.SkipAuxpow(r); err != nil { return nil, err } } - err = decodeTransactions(r, 0, wire.WitnessEncoding, &w) + err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w) if err != nil { return nil, err } @@ -137,31 +77,3 @@ func (p *DogecoinParser) ParseBlock(b []byte) (*bchain.Block, error) { return &bchain.Block{Txs: txs}, nil } - -func decodeTransactions(r io.Reader, pver uint32, enc wire.MessageEncoding, blk *wire.MsgBlock) error { - txCount, err := wire.ReadVarInt(r, pver) - if err != nil { - return err - } - - // Prevent more transactions than could possibly fit into a block. - // It would be possible to cause memory exhaustion and panics without - // a sane upper bound on this count. - if txCount > maxTxPerBlock { - str := fmt.Sprintf("too many transactions to fit into a block "+ - "[count %d, max %d]", txCount, maxTxPerBlock) - return &wire.MessageError{Func: "btg.decodeTransactions", Description: str} - } - - blk.Transactions = make([]*wire.MsgTx, 0, txCount) - for i := uint64(0); i < txCount; i++ { - tx := wire.MsgTx{} - err := tx.BtcDecode(r, pver, enc) - if err != nil { - return err - } - blk.Transactions = append(blk.Transactions, &tx) - } - - return nil -} diff --git a/bchain/coins/utils/parserutils.go b/bchain/coins/utils/parserutils.go new file mode 100644 index 00000000..4fbe422c --- /dev/null +++ b/bchain/coins/utils/parserutils.go @@ -0,0 +1,105 @@ +package utils + +import ( + "fmt" + "io" + + "github.com/btcsuite/btcd/wire" +) + +// minTxPayload is the minimum payload size for a transaction. Note +// that any realistically usable transaction must have at least one +// input or output, but that is a rule enforced at a higher layer, so +// it is intentionally not included here. +// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint +// number of transaction outputs 1 byte + LockTime 4 bytes + min input +// payload + min output payload. +const minTxPayload = 10 + +// maxTxPerBlock is the maximum number of transactions that could +// possibly fit into a block. +const maxTxPerBlock = (wire.MaxBlockPayload / minTxPayload) + 1 + +// DecodeTransactions decodes transactions from input stream using wire +func DecodeTransactions(r io.Reader, pver uint32, enc wire.MessageEncoding, blk *wire.MsgBlock) error { + txCount, err := wire.ReadVarInt(r, pver) + if err != nil { + return err + } + + // Prevent more transactions than could possibly fit into a block. + // It would be possible to cause memory exhaustion and panics without + // a sane upper bound on this count. + if txCount > maxTxPerBlock { + str := fmt.Sprintf("too many transactions to fit into a block "+ + "[count %d, max %d]", txCount, maxTxPerBlock) + return &wire.MessageError{Func: "btg.decodeTransactions", Description: str} + } + + blk.Transactions = make([]*wire.MsgTx, 0, txCount) + for i := uint64(0); i < txCount; i++ { + tx := wire.MsgTx{} + err := tx.BtcDecode(r, pver, enc) + if err != nil { + return err + } + blk.Transactions = append(blk.Transactions, &tx) + } + + return nil +} + +// VersionAuxpow marks that block contains Auxpow +const VersionAuxpow = (1 << 8) + +// SkipAuxpow skips Auxpow data in block +func SkipAuxpow(r io.ReadSeeker) error { + // skip Auxpow part of the block + // https://github.com/dogecoin/dogecoin/blob/master/src/auxpow.h#L130 + // CMerkleTx CTransaction + tx := wire.MsgTx{} + err := tx.BtcDecode(r, 0, wire.WitnessEncoding) + if err != nil { + return err + } + // CMerkleTx uint256 hashBlock + _, err = r.Seek(32, io.SeekCurrent) + if err != nil { + return err + } + // CMerkleTx std::vector vMerkleBranch + size, err := wire.ReadVarInt(r, 0) + if err != nil { + return err + } + _, err = r.Seek(int64(size)*32, io.SeekCurrent) + if err != nil { + return err + } + // CMerkleTx int nIndex + _, err = r.Seek(4, io.SeekCurrent) + if err != nil { + return err + } + // CAuxPow std::vector vChainMerkleBranch; + size, err = wire.ReadVarInt(r, 0) + if err != nil { + return err + } + _, err = r.Seek(int64(size)*32, io.SeekCurrent) + if err != nil { + return err + } + // CAuxPow int nChainIndex; + _, err = r.Seek(4, io.SeekCurrent) + if err != nil { + return err + } + // CAuxPow CPureBlockHeader parentBlock; + ph := wire.BlockHeader{} + err = ph.Deserialize(r) + if err != nil { + return err + } + return nil +}