kaspad/util/block.go
stasatdaglabs f46dec449d [NOD-510] Change all references to Bitcoin to Kaspa (#531)
* [NOD-510] Change coinbase flags to kaspad.

* [NOD-510] Removed superfluous spaces after periods in comments.

* [NOD-510] Rename btcd -> kaspad in the root folder.

* [NOD-510] Rename BtcEncode -> KaspaEncode and BtcDecode -> KaspaDecode.

* [NOD-510] Rename BtcEncode -> KaspaEncode and BtcDecode -> KaspaDecode.

* [NOD-510] Continue renaming btcd -> kaspad.

* [NOD-510] Rename btcjson -> kaspajson.

* [NOD-510] Rename file names inside kaspajson.

* [NOD-510] Rename kaspajson -> jsonrpc.

* [NOD-510] Finish renaming in addrmgr.

* [NOD-510] Rename package btcec to ecc.

* [NOD-510] Finish renaming stuff in blockdag.

* [NOD-510] Rename stuff in cmd.

* [NOD-510] Rename stuff in config.

* [NOD-510] Rename stuff in connmgr.

* [NOD-510] Rename stuff in dagconfig.

* [NOD-510] Rename stuff in database.

* [NOD-510] Rename stuff in docker.

* [NOD-510] Rename stuff in integration.

* [NOD-510] Rename jsonrpc to rpcmodel.

* [NOD-510] Rename stuff in limits.

* [NOD-510] Rename stuff in logger.

* [NOD-510] Rename stuff in mempool.

* [NOD-510] Rename stuff in mining.

* [NOD-510] Rename stuff in netsync.

* [NOD-510] Rename stuff in peer.

* [NOD-510] Rename stuff in release.

* [NOD-510] Rename stuff in rpcclient.

* [NOD-510] Rename stuff in server.

* [NOD-510] Rename stuff in signal.

* [NOD-510] Rename stuff in txscript.

* [NOD-510] Rename stuff in util.

* [NOD-510] Rename stuff in wire.

* [NOD-510] Fix failing tests.

* [NOD-510] Fix merge errors.

* [NOD-510] Fix go vet errors.

* [NOD-510] Remove merged file that's no longer relevant.

* [NOD-510] Add a comment above Op0.

* [NOD-510] Fix some comments referencing Bitcoin Core.

* [NOD-510] Fix some more comments referencing Bitcoin Core.

* [NOD-510] Fix bitcoin -> kaspa.

* [NOD-510] Fix more bitcoin -> kaspa.

* [NOD-510] Fix comments, remove DisconnectBlock in addrindex.

* [NOD-510] Rename KSPD to KASD.

* [NOD-510] Fix comments and user agent.
2019-12-12 15:21:41 +02:00

263 lines
8.1 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package util
import (
"bytes"
"fmt"
"io"
"math"
"time"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
)
// OutOfRangeError describes an error due to accessing an element that is out
// of range.
type OutOfRangeError string
const (
// BlockHeightUnknown is the value returned for a block height that is unknown.
// This is typically because the block has not been inserted into the DAG yet.
BlockHeightUnknown = math.MaxUint64
// CoinbaseTransactionIndex is the index of the coinbase transaction in every block
CoinbaseTransactionIndex = 0
)
// Error satisfies the error interface and prints human-readable errors.
func (e OutOfRangeError) Error() string {
return string(e)
}
// Block defines a kaspa block that provides easier and more efficient
// manipulation of raw blocks. It also memoizes hashes for the block and its
// transactions on their first access so subsequent accesses don't have to
// repeat the relatively expensive hashing operations.
type Block struct {
msgBlock *wire.MsgBlock // Underlying MsgBlock
serializedBlock []byte // Serialized bytes for the block
blockHash *daghash.Hash // Cached block hash
chainHeight uint64 // Selected-chain height
transactions []*Tx // Transactions
txnsGenerated bool // ALL wrapped transactions generated
}
// MsgBlock returns the underlying wire.MsgBlock for the Block.
func (b *Block) MsgBlock() *wire.MsgBlock {
// Return the cached block.
return b.msgBlock
}
// Bytes returns the serialized bytes for the Block. This is equivalent to
// calling Serialize on the underlying wire.MsgBlock, however it caches the
// result so subsequent calls are more efficient.
func (b *Block) Bytes() ([]byte, error) {
// Return the cached serialized bytes if it has already been generated.
if len(b.serializedBlock) != 0 {
return b.serializedBlock, nil
}
// Serialize the MsgBlock.
w := bytes.NewBuffer(make([]byte, 0, b.msgBlock.SerializeSize()))
err := b.msgBlock.Serialize(w)
if err != nil {
return nil, err
}
serializedBlock := w.Bytes()
// Cache the serialized bytes and return them.
b.serializedBlock = serializedBlock
return serializedBlock, nil
}
// Hash returns the block identifier hash for the Block. This is equivalent to
// calling BlockHash on the underlying wire.MsgBlock, however it caches the
// result so subsequent calls are more efficient.
func (b *Block) Hash() *daghash.Hash {
// Return the cached block hash if it has already been generated.
if b.blockHash != nil {
return b.blockHash
}
// Cache the block hash and return it.
hash := b.msgBlock.BlockHash()
b.blockHash = hash
return hash
}
// Tx returns a wrapped transaction (util.Tx) for the transaction at the
// specified index in the Block. The supplied index is 0 based. That is to
// say, the first transaction in the block is txNum 0. This is nearly
// equivalent to accessing the raw transaction (wire.MsgTx) from the
// underlying wire.MsgBlock, however the wrapped transaction has some helpful
// properties such as caching the hash so subsequent calls are more efficient.
func (b *Block) Tx(txNum int) (*Tx, error) {
// Ensure the requested transaction is in range.
numTx := uint64(len(b.msgBlock.Transactions))
if txNum < 0 || uint64(txNum) > numTx {
str := fmt.Sprintf("transaction index %d is out of range - max %d",
txNum, numTx-1)
return nil, OutOfRangeError(str)
}
// Generate slice to hold all of the wrapped transactions if needed.
if len(b.transactions) == 0 {
b.transactions = make([]*Tx, numTx)
}
// Return the wrapped transaction if it has already been generated.
if b.transactions[txNum] != nil {
return b.transactions[txNum], nil
}
// Generate and cache the wrapped transaction and return it.
newTx := NewTx(b.msgBlock.Transactions[txNum])
newTx.SetIndex(txNum)
b.transactions[txNum] = newTx
return newTx, nil
}
// Transactions returns a slice of wrapped transactions (util.Tx) for all
// transactions in the Block. This is nearly equivalent to accessing the raw
// transactions (wire.MsgTx) in the underlying wire.MsgBlock, however it
// instead provides easy access to wrapped versions (util.Tx) of them.
func (b *Block) Transactions() []*Tx {
// Return transactions if they have ALL already been generated. This
// flag is necessary because the wrapped transactions are lazily
// generated in a sparse fashion.
if b.txnsGenerated {
return b.transactions
}
// Generate slice to hold all of the wrapped transactions if needed.
if len(b.transactions) == 0 {
b.transactions = make([]*Tx, len(b.msgBlock.Transactions))
}
// Generate and cache the wrapped transactions for all that haven't
// already been done.
for i, tx := range b.transactions {
if tx == nil {
newTx := NewTx(b.msgBlock.Transactions[i])
newTx.SetIndex(i)
b.transactions[i] = newTx
}
}
b.txnsGenerated = true
return b.transactions
}
// TxHash returns the hash for the requested transaction number in the Block.
// The supplied index is 0 based. That is to say, the first transaction in the
// block is txNum 0. This is equivalent to calling TxHash on the underlying
// wire.MsgTx, however it caches the result so subsequent calls are more
// efficient.
func (b *Block) TxHash(txNum int) (*daghash.Hash, error) {
// Attempt to get a wrapped transaction for the specified index. It
// will be created lazily if needed or simply return the cached version
// if it has already been generated.
tx, err := b.Tx(txNum)
if err != nil {
return nil, err
}
// Defer to the wrapped transaction which will return the cached hash if
// it has already been generated.
return tx.Hash(), nil
}
// TxLoc returns the offsets and lengths of each transaction in a raw block.
// It is used to allow fast indexing into transactions within the raw byte
// stream.
func (b *Block) TxLoc() ([]wire.TxLoc, error) {
rawMsg, err := b.Bytes()
if err != nil {
return nil, err
}
rbuf := bytes.NewBuffer(rawMsg)
var mblock wire.MsgBlock
txLocs, err := mblock.DeserializeTxLoc(rbuf)
if err != nil {
return nil, err
}
return txLocs, err
}
// ChainHeight returns the saved chan height of the block . This value
// will be BlockHeightUnknown if it hasn't already explicitly been set.
func (b *Block) ChainHeight() uint64 {
return b.chainHeight
}
// SetChainHeight sets the chain height of the block.
func (b *Block) SetChainHeight(chainHeight uint64) {
b.chainHeight = chainHeight
}
// IsGenesis returns whether or not this block is the genesis block.
func (b *Block) IsGenesis() bool {
return b.MsgBlock().Header.IsGenesis()
}
// CoinbaseTransaction returns this block's coinbase transaction
func (b *Block) CoinbaseTransaction() *Tx {
return b.Transactions()[CoinbaseTransactionIndex]
}
// Timestamp returns this block's timestamp
func (b *Block) Timestamp() time.Time {
return b.msgBlock.Header.Timestamp
}
// NewBlock returns a new instance of a kaspa block given an underlying
// wire.MsgBlock. See Block.
func NewBlock(msgBlock *wire.MsgBlock) *Block {
return &Block{
msgBlock: msgBlock,
chainHeight: BlockHeightUnknown,
}
}
// NewBlockFromBytes returns a new instance of a kaspa block given the
// serialized bytes. See Block.
func NewBlockFromBytes(serializedBlock []byte) (*Block, error) {
br := bytes.NewReader(serializedBlock)
b, err := NewBlockFromReader(br)
if err != nil {
return nil, err
}
b.serializedBlock = serializedBlock
return b, nil
}
// NewBlockFromReader returns a new instance of a kaspa block given a
// Reader to deserialize the block. See Block.
func NewBlockFromReader(r io.Reader) (*Block, error) {
// Deserialize the bytes into a MsgBlock.
var msgBlock wire.MsgBlock
err := msgBlock.Deserialize(r)
if err != nil {
return nil, err
}
b := Block{
msgBlock: &msgBlock,
}
return &b, nil
}
// NewBlockFromBlockAndBytes returns a new instance of a kaspa block given
// an underlying wire.MsgBlock and the serialized bytes for it. See Block.
func NewBlockFromBlockAndBytes(msgBlock *wire.MsgBlock, serializedBlock []byte) *Block {
return &Block{
msgBlock: msgBlock,
serializedBlock: serializedBlock,
}
}