github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/block/block.go (about)

     1  package block
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"math"
     7  	"math/big"
     8  
     9  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    10  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    11  	"github.com/nspcc-dev/neo-go/pkg/io"
    12  	"github.com/nspcc-dev/neo-go/pkg/util"
    13  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    14  )
    15  
    16  const (
    17  	// MaxTransactionsPerBlock is the maximum number of transactions per block.
    18  	MaxTransactionsPerBlock = math.MaxUint16
    19  )
    20  
    21  // ErrMaxContentsPerBlock is returned when the maximum number of contents per block is reached.
    22  var ErrMaxContentsPerBlock = errors.New("the number of contents exceeds the maximum number of contents per block")
    23  
    24  var expectedHeaderSizeWithEmptyWitness int
    25  
    26  func init() {
    27  	expectedHeaderSizeWithEmptyWitness = io.GetVarSize(new(Header))
    28  }
    29  
    30  // Block represents one block in the chain.
    31  type Block struct {
    32  	// The base of the block.
    33  	Header
    34  
    35  	// Transaction list.
    36  	Transactions []*transaction.Transaction
    37  
    38  	// True if this block is created from trimmed data.
    39  	Trimmed bool
    40  }
    41  
    42  // auxBlockOut is used for JSON i/o.
    43  type auxBlockOut struct {
    44  	Transactions []*transaction.Transaction `json:"tx"`
    45  }
    46  
    47  // auxBlockIn is used for JSON i/o.
    48  type auxBlockIn struct {
    49  	Transactions []json.RawMessage `json:"tx"`
    50  }
    51  
    52  // ComputeMerkleRoot computes Merkle tree root hash based on actual block's data.
    53  func (b *Block) ComputeMerkleRoot() util.Uint256 {
    54  	hashes := make([]util.Uint256, len(b.Transactions))
    55  	for i, tx := range b.Transactions {
    56  		hashes[i] = tx.Hash()
    57  	}
    58  
    59  	return hash.CalcMerkleRoot(hashes)
    60  }
    61  
    62  // RebuildMerkleRoot rebuilds the merkleroot of the block.
    63  func (b *Block) RebuildMerkleRoot() {
    64  	b.MerkleRoot = b.ComputeMerkleRoot()
    65  }
    66  
    67  // NewTrimmedFromReader returns a new block from trimmed data.
    68  // This is commonly used to create a block from stored data.
    69  // Blocks created from trimmed data will have their Trimmed field
    70  // set to true.
    71  func NewTrimmedFromReader(stateRootEnabled bool, br *io.BinReader) (*Block, error) {
    72  	block := &Block{
    73  		Header: Header{
    74  			StateRootEnabled: stateRootEnabled,
    75  		},
    76  		Trimmed: true,
    77  	}
    78  
    79  	block.Header.DecodeBinary(br)
    80  	lenHashes := br.ReadVarUint()
    81  	if lenHashes > MaxTransactionsPerBlock {
    82  		return nil, ErrMaxContentsPerBlock
    83  	}
    84  	if lenHashes > 0 {
    85  		block.Transactions = make([]*transaction.Transaction, lenHashes)
    86  		for i := 0; i < int(lenHashes); i++ {
    87  			var hash util.Uint256
    88  			hash.DecodeBinary(br)
    89  			block.Transactions[i] = transaction.NewTrimmedTX(hash)
    90  		}
    91  	}
    92  
    93  	return block, br.Err
    94  }
    95  
    96  // New creates a new blank block with proper state root setting.
    97  func New(stateRootEnabled bool) *Block {
    98  	return &Block{
    99  		Header: Header{
   100  			StateRootEnabled: stateRootEnabled,
   101  		},
   102  	}
   103  }
   104  
   105  // EncodeTrimmed writes trimmed representation of the block data into w. Trimmed blocks
   106  // do not store complete transactions, instead they only store their hashes.
   107  func (b *Block) EncodeTrimmed(w *io.BinWriter) {
   108  	b.Header.EncodeBinary(w)
   109  
   110  	w.WriteVarUint(uint64(len(b.Transactions)))
   111  	for _, tx := range b.Transactions {
   112  		h := tx.Hash()
   113  		h.EncodeBinary(w)
   114  	}
   115  }
   116  
   117  // DecodeBinary decodes the block from the given BinReader, implementing
   118  // Serializable interface.
   119  func (b *Block) DecodeBinary(br *io.BinReader) {
   120  	b.Header.DecodeBinary(br)
   121  	contentsCount := br.ReadVarUint()
   122  	if contentsCount > MaxTransactionsPerBlock {
   123  		br.Err = ErrMaxContentsPerBlock
   124  		return
   125  	}
   126  	txes := make([]*transaction.Transaction, contentsCount)
   127  	for i := 0; i < int(contentsCount); i++ {
   128  		tx := &transaction.Transaction{}
   129  		tx.DecodeBinary(br)
   130  		txes[i] = tx
   131  	}
   132  	b.Transactions = txes
   133  	if br.Err != nil {
   134  		return
   135  	}
   136  }
   137  
   138  // EncodeBinary encodes the block to the given BinWriter, implementing
   139  // Serializable interface.
   140  func (b *Block) EncodeBinary(bw *io.BinWriter) {
   141  	b.Header.EncodeBinary(bw)
   142  	bw.WriteVarUint(uint64(len(b.Transactions)))
   143  	for i := 0; i < len(b.Transactions); i++ {
   144  		b.Transactions[i].EncodeBinary(bw)
   145  	}
   146  }
   147  
   148  // MarshalJSON implements the json.Marshaler interface.
   149  func (b Block) MarshalJSON() ([]byte, error) {
   150  	abo := auxBlockOut{
   151  		Transactions: b.Transactions,
   152  	}
   153  	// `"tx": []` (C#) vs `"tx": null` (default Go when missing any transactions)
   154  	if abo.Transactions == nil {
   155  		abo.Transactions = []*transaction.Transaction{}
   156  	}
   157  	auxb, err := json.Marshal(abo)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	baseBytes, err := json.Marshal(b.Header)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	// Stitch them together.
   167  	if baseBytes[len(baseBytes)-1] != '}' || auxb[0] != '{' {
   168  		return nil, errors.New("can't merge internal jsons")
   169  	}
   170  	baseBytes[len(baseBytes)-1] = ','
   171  	baseBytes = append(baseBytes, auxb[1:]...)
   172  	return baseBytes, nil
   173  }
   174  
   175  // UnmarshalJSON implements the json.Unmarshaler interface.
   176  func (b *Block) UnmarshalJSON(data []byte) error {
   177  	// As Base and auxb are at the same level in json,
   178  	// do unmarshalling separately for both structs.
   179  	auxb := new(auxBlockIn)
   180  	err := json.Unmarshal(data, auxb)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	err = json.Unmarshal(data, &b.Header)
   185  	if err != nil {
   186  		return err
   187  	}
   188  	if len(auxb.Transactions) != 0 {
   189  		b.Transactions = make([]*transaction.Transaction, 0, len(auxb.Transactions))
   190  		for _, txBytes := range auxb.Transactions {
   191  			tx := &transaction.Transaction{}
   192  			err = tx.UnmarshalJSON(txBytes)
   193  			if err != nil {
   194  				return err
   195  			}
   196  			b.Transactions = append(b.Transactions, tx)
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  // GetExpectedBlockSize returns the expected block size which should be equal to io.GetVarSize(b).
   203  func (b *Block) GetExpectedBlockSize() int {
   204  	var transactionsSize int
   205  	for _, tx := range b.Transactions {
   206  		transactionsSize += tx.Size()
   207  	}
   208  	return b.GetExpectedBlockSizeWithoutTransactions(len(b.Transactions)) + transactionsSize
   209  }
   210  
   211  // GetExpectedBlockSizeWithoutTransactions returns the expected block size without transactions size.
   212  func (b *Block) GetExpectedBlockSizeWithoutTransactions(txCount int) int {
   213  	size := expectedHeaderSizeWithEmptyWitness - 1 - 1 + // 1 is for the zero-length (new(Header)).Script.Invocation/Verification
   214  		io.GetVarSize(&b.Script) +
   215  		io.GetVarSize(txCount)
   216  	if b.StateRootEnabled {
   217  		size += util.Uint256Size
   218  	}
   219  	return size
   220  }
   221  
   222  // ToStackItem converts Block to stackitem.Item.
   223  func (b *Block) ToStackItem() stackitem.Item {
   224  	items := []stackitem.Item{
   225  		stackitem.NewByteArray(b.Hash().BytesBE()),
   226  		stackitem.NewBigInteger(big.NewInt(int64(b.Version))),
   227  		stackitem.NewByteArray(b.PrevHash.BytesBE()),
   228  		stackitem.NewByteArray(b.MerkleRoot.BytesBE()),
   229  		stackitem.NewBigInteger(big.NewInt(int64(b.Timestamp))),
   230  		stackitem.NewBigInteger(new(big.Int).SetUint64(b.Nonce)),
   231  		stackitem.NewBigInteger(big.NewInt(int64(b.Index))),
   232  		stackitem.NewByteArray(b.NextConsensus.BytesBE()),
   233  		stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))),
   234  	}
   235  	if b.StateRootEnabled {
   236  		items = append(items, stackitem.NewByteArray(b.PrevStateRoot.BytesBE()))
   237  	}
   238  
   239  	return stackitem.NewArray(items)
   240  }