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

     1  package block
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     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/encoding/address"
    12  	"github.com/nspcc-dev/neo-go/pkg/io"
    13  	"github.com/nspcc-dev/neo-go/pkg/util"
    14  )
    15  
    16  // VersionInitial is the default Neo block version.
    17  const VersionInitial uint32 = 0
    18  
    19  // Header holds the base info of a block.
    20  type Header struct {
    21  	// Version of the block.
    22  	Version uint32
    23  
    24  	// hash of the previous block.
    25  	PrevHash util.Uint256
    26  
    27  	// Root hash of a transaction list.
    28  	MerkleRoot util.Uint256
    29  
    30  	// Timestamp is a millisecond-precision timestamp.
    31  	// The time stamp of each block must be later than the previous block's time stamp.
    32  	// Generally, the difference between two block's time stamps is about 15 seconds and imprecision is allowed.
    33  	// The height of the block must be exactly equal to the height of the previous block plus 1.
    34  	Timestamp uint64
    35  
    36  	// Nonce is block random number.
    37  	Nonce uint64
    38  
    39  	// index/height of the block
    40  	Index uint32
    41  
    42  	// Contract address of the next miner
    43  	NextConsensus util.Uint160
    44  
    45  	// Script used to validate the block
    46  	Script transaction.Witness
    47  
    48  	// StateRootEnabled specifies if the header contains state root.
    49  	StateRootEnabled bool
    50  	// PrevStateRoot is the state root of the previous block.
    51  	PrevStateRoot util.Uint256
    52  	// PrimaryIndex is the index of the primary consensus node for this block.
    53  	PrimaryIndex byte
    54  
    55  	// Hash of this block, created when binary encoded (double SHA256).
    56  	hash util.Uint256
    57  }
    58  
    59  // baseAux is used to marshal/unmarshal to/from JSON, it's almost the same
    60  // as original Base, but with Nonce and NextConsensus fields differing and
    61  // Hash added.
    62  type baseAux struct {
    63  	Hash          util.Uint256          `json:"hash"`
    64  	Version       uint32                `json:"version"`
    65  	PrevHash      util.Uint256          `json:"previousblockhash"`
    66  	MerkleRoot    util.Uint256          `json:"merkleroot"`
    67  	Timestamp     uint64                `json:"time"`
    68  	Nonce         string                `json:"nonce"`
    69  	Index         uint32                `json:"index"`
    70  	NextConsensus string                `json:"nextconsensus"`
    71  	PrimaryIndex  byte                  `json:"primary"`
    72  	PrevStateRoot *util.Uint256         `json:"previousstateroot,omitempty"`
    73  	Witnesses     []transaction.Witness `json:"witnesses"`
    74  }
    75  
    76  // Hash returns the hash of the block.
    77  func (b *Header) Hash() util.Uint256 {
    78  	if b.hash.Equals(util.Uint256{}) {
    79  		b.createHash()
    80  	}
    81  	return b.hash
    82  }
    83  
    84  // DecodeBinary implements the Serializable interface.
    85  func (b *Header) DecodeBinary(br *io.BinReader) {
    86  	b.decodeHashableFields(br)
    87  	witnessCount := br.ReadVarUint()
    88  	if br.Err == nil && witnessCount != 1 {
    89  		br.Err = errors.New("wrong witness count")
    90  		return
    91  	}
    92  
    93  	b.Script.DecodeBinary(br)
    94  }
    95  
    96  // EncodeBinary implements the Serializable interface.
    97  func (b *Header) EncodeBinary(bw *io.BinWriter) {
    98  	b.encodeHashableFields(bw)
    99  	bw.WriteVarUint(1)
   100  	b.Script.EncodeBinary(bw)
   101  }
   102  
   103  // createHash creates the hash of the block.
   104  // When calculating the hash value of the block, instead of processing the entire block,
   105  // only the header (without the signatures) is added as an input for the hash. It differs
   106  // from the complete block only in that it doesn't contain transactions, but their hashes
   107  // are used for MerkleRoot hash calculation. Therefore, adding/removing/changing any
   108  // transaction affects the header hash and there is no need to use the complete block for
   109  // hash calculation.
   110  func (b *Header) createHash() {
   111  	buf := io.NewBufBinWriter()
   112  	// No error can occur while encoding hashable fields.
   113  	b.encodeHashableFields(buf.BinWriter)
   114  
   115  	b.hash = hash.Sha256(buf.Bytes())
   116  }
   117  
   118  // encodeHashableFields will only encode the fields used for hashing.
   119  // see Hash() for more information about the fields.
   120  func (b *Header) encodeHashableFields(bw *io.BinWriter) {
   121  	bw.WriteU32LE(b.Version)
   122  	bw.WriteBytes(b.PrevHash[:])
   123  	bw.WriteBytes(b.MerkleRoot[:])
   124  	bw.WriteU64LE(b.Timestamp)
   125  	bw.WriteU64LE(b.Nonce)
   126  	bw.WriteU32LE(b.Index)
   127  	bw.WriteB(b.PrimaryIndex)
   128  	bw.WriteBytes(b.NextConsensus[:])
   129  	if b.StateRootEnabled {
   130  		bw.WriteBytes(b.PrevStateRoot[:])
   131  	}
   132  }
   133  
   134  // decodeHashableFields decodes the fields used for hashing.
   135  // see Hash() for more information about the fields.
   136  func (b *Header) decodeHashableFields(br *io.BinReader) {
   137  	b.Version = br.ReadU32LE()
   138  	br.ReadBytes(b.PrevHash[:])
   139  	br.ReadBytes(b.MerkleRoot[:])
   140  	b.Timestamp = br.ReadU64LE()
   141  	b.Nonce = br.ReadU64LE()
   142  	b.Index = br.ReadU32LE()
   143  	b.PrimaryIndex = br.ReadB()
   144  	br.ReadBytes(b.NextConsensus[:])
   145  	if b.StateRootEnabled {
   146  		br.ReadBytes(b.PrevStateRoot[:])
   147  	}
   148  
   149  	// Make the hash of the block here so we dont need to do this
   150  	// again.
   151  	if br.Err == nil {
   152  		b.createHash()
   153  	}
   154  }
   155  
   156  // MarshalJSON implements the json.Marshaler interface.
   157  func (b Header) MarshalJSON() ([]byte, error) {
   158  	aux := baseAux{
   159  		Hash:          b.Hash(),
   160  		Version:       b.Version,
   161  		PrevHash:      b.PrevHash,
   162  		MerkleRoot:    b.MerkleRoot,
   163  		Timestamp:     b.Timestamp,
   164  		Nonce:         fmt.Sprintf("%016X", b.Nonce),
   165  		Index:         b.Index,
   166  		PrimaryIndex:  b.PrimaryIndex,
   167  		NextConsensus: address.Uint160ToString(b.NextConsensus),
   168  		Witnesses:     []transaction.Witness{b.Script},
   169  	}
   170  	if b.StateRootEnabled {
   171  		aux.PrevStateRoot = &b.PrevStateRoot
   172  	}
   173  	return json.Marshal(aux)
   174  }
   175  
   176  // UnmarshalJSON implements the json.Unmarshaler interface.
   177  func (b *Header) UnmarshalJSON(data []byte) error {
   178  	var aux = new(baseAux)
   179  	var nextC util.Uint160
   180  
   181  	err := json.Unmarshal(data, aux)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	var nonce uint64
   187  	if len(aux.Nonce) != 0 {
   188  		nonce, err = strconv.ParseUint(aux.Nonce, 16, 64)
   189  		if err != nil {
   190  			return err
   191  		}
   192  	}
   193  	nextC, err = address.StringToUint160(aux.NextConsensus)
   194  	if err != nil {
   195  		return err
   196  	}
   197  	if len(aux.Witnesses) != 1 {
   198  		return errors.New("wrong number of witnesses")
   199  	}
   200  	b.Version = aux.Version
   201  	b.PrevHash = aux.PrevHash
   202  	b.MerkleRoot = aux.MerkleRoot
   203  	b.Timestamp = aux.Timestamp
   204  	b.Nonce = nonce
   205  	b.Index = aux.Index
   206  	b.PrimaryIndex = aux.PrimaryIndex
   207  	b.NextConsensus = nextC
   208  	b.Script = aux.Witnesses[0]
   209  	if b.StateRootEnabled {
   210  		if aux.PrevStateRoot == nil {
   211  			return errors.New("'previousstateroot' is empty")
   212  		}
   213  		b.PrevStateRoot = *aux.PrevStateRoot
   214  	}
   215  	if !aux.Hash.Equals(b.Hash()) {
   216  		return errors.New("json 'hash' doesn't match block hash")
   217  	}
   218  	return nil
   219  }