neoneo-go/pkg/core/block.go
Roman Khimov 5bf00db2c9 io: move BinReader/BinWriter there, redo Serializable with it
The logic here is that we'll have all binary encoding/decoding done via our io
package, which simplifies error handling. This functionality doesn't belong to
util, so it's moved.

This also expands BufBinWriter with Reset() method to fit the needs of core
package.
2019-09-16 23:39:51 +03:00

165 lines
3.8 KiB
Go

package core
import (
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util"
log "github.com/sirupsen/logrus"
)
// Block represents one block in the chain.
type Block struct {
// The base of the block.
BlockBase
// Transaction list.
Transactions []*transaction.Transaction `json:"tx"`
// True if this block is created from trimmed data.
Trimmed bool `json:"-"`
}
// Header returns the Header of the Block.
func (b *Block) Header() *Header {
return &Header{
BlockBase: b.BlockBase,
}
}
// rebuildMerkleRoot rebuild the merkleroot of the block.
func (b *Block) rebuildMerkleRoot() error {
hashes := make([]util.Uint256, len(b.Transactions))
for i, tx := range b.Transactions {
hashes[i] = tx.Hash()
}
merkle, err := crypto.NewMerkleTree(hashes)
if err != nil {
return err
}
b.MerkleRoot = merkle.Root()
return nil
}
// Verify the integrity of the block.
func (b *Block) Verify(full bool) bool {
// The first TX has to be a miner transaction.
if b.Transactions[0].Type != transaction.MinerType {
return false
}
// If the first TX is a minerTX then all others cant.
for _, tx := range b.Transactions[1:] {
if tx.Type == transaction.MinerType {
return false
}
}
// TODO: When full is true, do a full verification.
if full {
log.Warn("full verification of blocks is not yet implemented")
}
return true
}
// NewBlockFromTrimmedBytes returns a new block from trimmed data.
// This is commonly used to create a block from stored data.
// Blocks created from trimmed data will have their Trimmed field
// set to true.
func NewBlockFromTrimmedBytes(b []byte) (*Block, error) {
block := &Block{
Trimmed: true,
}
br := io.NewBinReaderFromBuf(b)
if err := block.decodeHashableFields(br); err != nil {
return block, err
}
var padding uint8
br.ReadLE(&padding)
if br.Err != nil {
return block, br.Err
}
block.Script = &transaction.Witness{}
if err := block.Script.DecodeBinary(br); err != nil {
return block, err
}
lenTX := br.ReadVarUint()
block.Transactions = make([]*transaction.Transaction, lenTX)
for i := 0; i < int(lenTX); i++ {
var hash util.Uint256
br.ReadLE(&hash)
block.Transactions[i] = transaction.NewTrimmedTX(hash)
}
return block, br.Err
}
// Trim returns a subset of the block data to save up space
// in storage.
// Notice that only the hashes of the transactions are stored.
func (b *Block) Trim() ([]byte, error) {
buf := io.NewBufBinWriter()
if err := b.encodeHashableFields(buf.BinWriter); err != nil {
return nil, err
}
buf.WriteLE(uint8(1))
if buf.Err != nil {
return nil, buf.Err
}
if err := b.Script.EncodeBinary(buf.BinWriter); err != nil {
return nil, err
}
buf.WriteVarUint(uint64(len(b.Transactions)))
for _, tx := range b.Transactions {
buf.WriteLE(tx.Hash())
}
if buf.Err != nil {
return nil, buf.Err
}
return buf.Bytes(), nil
}
// DecodeBinary decodes the block from the given reader.
func (b *Block) DecodeBinary(br *io.BinReader) error {
if err := b.BlockBase.DecodeBinary(br); err != nil {
return err
}
lentx := br.ReadVarUint()
if br.Err != nil {
return br.Err
}
b.Transactions = make([]*transaction.Transaction, lentx)
for i := 0; i < int(lentx); i++ {
b.Transactions[i] = &transaction.Transaction{}
if err := b.Transactions[i].DecodeBinary(br); err != nil {
return err
}
}
return nil
}
// EncodeBinary encodes the block to the given writer.
func (b *Block) EncodeBinary(bw *io.BinWriter) error {
err := b.BlockBase.EncodeBinary(bw)
if err != nil {
return err
}
bw.WriteVarUint(uint64(len(b.Transactions)))
if bw.Err != nil {
return err
}
for _, tx := range b.Transactions {
err := tx.EncodeBinary(bw)
if err != nil {
return err
}
}
return nil
}