github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/types/block.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/gnolang/gno/tm2/pkg/amino"
    11  	typesver "github.com/gnolang/gno/tm2/pkg/bft/types/version"
    12  	"github.com/gnolang/gno/tm2/pkg/bitarray"
    13  	"github.com/gnolang/gno/tm2/pkg/crypto"
    14  	"github.com/gnolang/gno/tm2/pkg/crypto/merkle"
    15  	"github.com/gnolang/gno/tm2/pkg/crypto/tmhash"
    16  	"github.com/gnolang/gno/tm2/pkg/errors"
    17  )
    18  
    19  // Block defines the atomic unit of a Tendermint blockchain.
    20  type Block struct {
    21  	mtx        sync.Mutex
    22  	Header     `json:"header"`
    23  	Data       `json:"data"`
    24  	LastCommit *Commit `json:"last_commit"`
    25  }
    26  
    27  // ValidateBasic performs basic validation that doesn't involve state data.
    28  // It checks the internal consistency of the block.
    29  // Further validation is done using state#ValidateBlock.
    30  func (b *Block) ValidateBasic() error {
    31  	if b == nil {
    32  		return errors.New("nil block")
    33  	}
    34  	b.mtx.Lock()
    35  	defer b.mtx.Unlock()
    36  
    37  	if len(b.ChainID) > MaxChainIDLen {
    38  		return fmt.Errorf("ChainID is too long. Max is %d, got %d", MaxChainIDLen, len(b.ChainID))
    39  	}
    40  
    41  	if b.Height < 0 {
    42  		return errors.New("Negative Header.Height")
    43  	} else if b.Height == 0 {
    44  		return errors.New("Zero Header.Height")
    45  	}
    46  
    47  	// NOTE: Timestamp validation is subtle and handled elsewhere.
    48  
    49  	newTxs := int64(len(b.Data.Txs))
    50  	if b.NumTxs != newTxs {
    51  		return fmt.Errorf("wrong Header.NumTxs. Expected %v, got %v",
    52  			newTxs,
    53  			b.NumTxs,
    54  		)
    55  	}
    56  
    57  	// TODO: fix tests so we can do this
    58  	/*if b.TotalTxs < b.NumTxs {
    59  		return fmt.Errorf("Header.TotalTxs (%d) is less than Header.NumTxs (%d)", b.TotalTxs, b.NumTxs)
    60  	}*/
    61  	if b.TotalTxs < 0 {
    62  		return errors.New("Negative Header.TotalTxs")
    63  	}
    64  
    65  	if err := b.LastBlockID.ValidateBasic(); err != nil {
    66  		return fmt.Errorf("wrong Header.LastBlockID: %w", err)
    67  	}
    68  
    69  	// Validate the last commit and its hash.
    70  	if b.Header.Height > 1 {
    71  		if b.LastCommit == nil {
    72  			return errors.New("nil LastCommit")
    73  		}
    74  		if err := b.LastCommit.ValidateBasic(); err != nil {
    75  			return fmt.Errorf("wrong LastCommit")
    76  		}
    77  	}
    78  	if err := ValidateHash(b.LastCommitHash); err != nil {
    79  		return fmt.Errorf("wrong Header.LastCommitHash: %w", err)
    80  	}
    81  	if !bytes.Equal(b.LastCommitHash, b.LastCommit.Hash()) {
    82  		return fmt.Errorf("wrong Header.LastCommitHash. Expected %v, got %v",
    83  			b.LastCommit.Hash(),
    84  			b.LastCommitHash,
    85  		)
    86  	}
    87  
    88  	// Validate the hash of the transactions.
    89  	// NOTE: b.Data.Txs may be nil, but b.Data.Hash()
    90  	// still works fine
    91  	if err := ValidateHash(b.DataHash); err != nil {
    92  		return fmt.Errorf("wrong Header.DataHash: %w", err)
    93  	}
    94  	if !bytes.Equal(b.DataHash, b.Data.Hash()) {
    95  		return fmt.Errorf(
    96  			"wrong Header.DataHash. Expected %v, got %v",
    97  			b.Data.Hash(),
    98  			b.DataHash,
    99  		)
   100  	}
   101  
   102  	// Basic validation of hashes related to application data.
   103  	// Will validate fully against state in state#ValidateBlock.
   104  	if err := ValidateHash(b.ValidatorsHash); err != nil {
   105  		return fmt.Errorf("wrong Header.ValidatorsHash: %w", err)
   106  	}
   107  	if err := ValidateHash(b.NextValidatorsHash); err != nil {
   108  		return fmt.Errorf("wrong Header.NextValidatorsHash: %w", err)
   109  	}
   110  	if err := ValidateHash(b.ConsensusHash); err != nil {
   111  		return fmt.Errorf("wrong Header.ConsensusHash: %w", err)
   112  	}
   113  	// NOTE: AppHash is arbitrary length
   114  	if err := ValidateHash(b.LastResultsHash); err != nil {
   115  		return fmt.Errorf("wrong Header.LastResultsHash: %w", err)
   116  	}
   117  
   118  	if len(b.ProposerAddress) != crypto.AddressSize {
   119  		return fmt.Errorf("expected len(Header.ProposerAddress) to be %d, got %d",
   120  			crypto.AddressSize, len(b.ProposerAddress))
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  // fillHeader fills in any remaining header fields that are a function of the block data
   127  func (b *Block) fillHeader() {
   128  	if b.LastCommitHash == nil {
   129  		b.LastCommitHash = b.LastCommit.Hash()
   130  	}
   131  	if b.DataHash == nil {
   132  		b.DataHash = b.Data.Hash()
   133  	}
   134  }
   135  
   136  // Hash computes and returns the block hash.
   137  // If the block is incomplete, block hash is nil for safety.
   138  func (b *Block) Hash() []byte {
   139  	if b == nil {
   140  		return nil
   141  	}
   142  	b.mtx.Lock()
   143  	defer b.mtx.Unlock()
   144  
   145  	if b.LastCommit == nil {
   146  		return nil
   147  	}
   148  	b.fillHeader()
   149  	return b.Header.Hash()
   150  }
   151  
   152  // MakePartSet returns a PartSet containing parts of a serialized block.
   153  // This is the form in which the block is gossipped to peers.
   154  // CONTRACT: partSize is greater than zero.
   155  func (b *Block) MakePartSet(partSize int) *PartSet {
   156  	if b == nil {
   157  		return nil
   158  	}
   159  	b.mtx.Lock()
   160  	defer b.mtx.Unlock()
   161  
   162  	// We prefix the byte length, so that unmarshaling
   163  	// can easily happen via a reader.
   164  	bz, err := amino.MarshalSized(b)
   165  	if err != nil {
   166  		panic(err)
   167  	}
   168  	return NewPartSetFromData(bz, partSize)
   169  }
   170  
   171  // HashesTo is a convenience function that checks if a block hashes to the given argument.
   172  // Returns false if the block is nil or the hash is empty.
   173  func (b *Block) HashesTo(hash []byte) bool {
   174  	if len(hash) == 0 {
   175  		return false
   176  	}
   177  	if b == nil {
   178  		return false
   179  	}
   180  	return bytes.Equal(b.Hash(), hash)
   181  }
   182  
   183  // Size returns size of the block in bytes.
   184  func (b *Block) Size() int {
   185  	bz, err := amino.Marshal(b)
   186  	if err != nil {
   187  		return 0
   188  	}
   189  	return len(bz)
   190  }
   191  
   192  // String returns a string representation of the block
   193  func (b *Block) String() string {
   194  	return b.StringIndented("")
   195  }
   196  
   197  // StringIndented returns a string representation of the block
   198  func (b *Block) StringIndented(indent string) string {
   199  	if b == nil {
   200  		return "nil-Block"
   201  	}
   202  	return fmt.Sprintf(`Block{
   203  %s  %v
   204  %s  %v
   205  %s  %v
   206  %s}#%v`,
   207  		indent, b.Header.StringIndented(indent+"  "),
   208  		indent, b.Data.StringIndented(indent+"  "),
   209  		indent, b.LastCommit.StringIndented(indent+"  "),
   210  		indent, b.Hash())
   211  }
   212  
   213  // StringShort returns a shortened string representation of the block
   214  func (b *Block) StringShort() string {
   215  	if b == nil {
   216  		return "nil-Block"
   217  	}
   218  	return fmt.Sprintf("Block#%v", b.Hash())
   219  }
   220  
   221  //-----------------------------------------------------------------------------
   222  
   223  // Header defines the structure of a Tendermint block header.
   224  // NOTE: changes to the Header should be duplicated in:
   225  // - header.Hash()
   226  // - abci.Header
   227  // - /docs/spec/blockchain/blockchain.md
   228  type Header struct {
   229  	// basic block info
   230  	Version    string    `json:"version"`
   231  	ChainID    string    `json:"chain_id"`
   232  	Height     int64     `json:"height"`
   233  	Time       time.Time `json:"time"`
   234  	NumTxs     int64     `json:"num_txs"`
   235  	TotalTxs   int64     `json:"total_txs"`
   236  	AppVersion string    `json:"app_version"`
   237  
   238  	// prev block info
   239  	LastBlockID BlockID `json:"last_block_id"`
   240  
   241  	// hashes of block data
   242  	LastCommitHash []byte `json:"last_commit_hash"` // commit from validators from the last block
   243  	DataHash       []byte `json:"data_hash"`        // transactions
   244  
   245  	// hashes from the app output from the prev block
   246  	ValidatorsHash     []byte `json:"validators_hash"`      // validators for the current block
   247  	NextValidatorsHash []byte `json:"next_validators_hash"` // validators for the next block
   248  	ConsensusHash      []byte `json:"consensus_hash"`       // consensus params for current block
   249  	AppHash            []byte `json:"app_hash"`             // state after txs from the previous block
   250  	LastResultsHash    []byte `json:"last_results_hash"`    // root hash of all results from the txs from the previous block
   251  
   252  	// consensus info
   253  	ProposerAddress Address `json:"proposer_address"` // original proposer of the block
   254  }
   255  
   256  // Implements abci.Header
   257  func (h *Header) AssertABCIHeader()  {}
   258  func (h *Header) GetChainID() string { return h.ChainID }
   259  func (h *Header) GetHeight() int64   { return h.Height }
   260  func (h *Header) GetTime() time.Time { return h.Time }
   261  
   262  // MakeBlock returns a new block with an empty header, except what can be
   263  // computed from itself.
   264  // It populates the same set of fields validated by ValidateBasic.
   265  func MakeBlock(height int64, txs []Tx, lastCommit *Commit) *Block {
   266  	block := &Block{
   267  		Header: Header{
   268  			Height: height,
   269  			NumTxs: int64(len(txs)),
   270  		},
   271  		Data: Data{
   272  			Txs: txs,
   273  		},
   274  		LastCommit: lastCommit,
   275  	}
   276  	block.fillHeader()
   277  	return block
   278  }
   279  
   280  func (h *Header) Copy() *Header {
   281  	return amino.DeepCopy(h).(*Header)
   282  }
   283  
   284  // Populate the Header with state-derived data.
   285  // Call this after MakeBlock to complete the Header.
   286  func (h *Header) Populate(
   287  	chainID string,
   288  	timestamp time.Time, lastBlockID BlockID, totalTxs int64,
   289  	appVersion string,
   290  	valHash, nextValHash []byte,
   291  	consensusHash, appHash, lastResultsHash []byte,
   292  	proposerAddress Address,
   293  ) {
   294  	h.Version = typesver.BlockVersion
   295  	h.ChainID = chainID
   296  	h.Time = timestamp
   297  	h.LastBlockID = lastBlockID
   298  	h.TotalTxs = totalTxs
   299  	h.AppVersion = appVersion
   300  	h.ValidatorsHash = valHash
   301  	h.NextValidatorsHash = nextValHash
   302  	h.ConsensusHash = consensusHash
   303  	h.AppHash = appHash
   304  	h.LastResultsHash = lastResultsHash
   305  	h.ProposerAddress = proposerAddress
   306  }
   307  
   308  // Hash returns the hash of the header.
   309  // It computes a Merkle tree from the header fields
   310  // ordered as they appear in the Header.
   311  // Returns nil if ValidatorHash is missing,
   312  // since a Header is not valid unless there is
   313  // a ValidatorsHash (corresponding to the validator set).
   314  func (h *Header) Hash() []byte {
   315  	if h == nil || len(h.ValidatorsHash) == 0 {
   316  		return nil
   317  	}
   318  	return merkle.SimpleHashFromByteSlices([][]byte{
   319  		bytesOrNil(h.Version),
   320  		bytesOrNil(h.ChainID),
   321  		bytesOrNil(h.Height),
   322  		bytesOrNil(h.Time),
   323  		bytesOrNil(h.NumTxs),
   324  		bytesOrNil(h.TotalTxs),
   325  		bytesOrNil(h.AppVersion),
   326  		bytesOrNil(h.LastBlockID),
   327  		bytesOrNil(h.LastCommitHash),
   328  		bytesOrNil(h.DataHash),
   329  		bytesOrNil(h.ValidatorsHash),
   330  		bytesOrNil(h.NextValidatorsHash),
   331  		bytesOrNil(h.ConsensusHash),
   332  		bytesOrNil(h.AppHash),
   333  		bytesOrNil(h.LastResultsHash),
   334  		bytesOrNil(h.ProposerAddress),
   335  	})
   336  }
   337  
   338  // StringIndented returns a string representation of the header
   339  func (h *Header) StringIndented(indent string) string {
   340  	if h == nil {
   341  		return "nil-Header"
   342  	}
   343  	return fmt.Sprintf(`Header{
   344  %s  Version:        %v
   345  %s  ChainID:        %v
   346  %s  Height:         %v
   347  %s  Time:           %v
   348  %s  NumTxs:         %v
   349  %s  TotalTxs:       %v
   350  %s  AppVersion:     %v
   351  %s  LastBlockID:    %v
   352  %s  LastCommit:     %v
   353  %s  Data:           %v
   354  %s  Validators:     %v
   355  %s  NextValidators: %v
   356  %s  App:            %v
   357  %s  Consensus:      %v
   358  %s  Results:        %v
   359  %s  Proposer:       %v
   360  %s}#%v`,
   361  		indent, h.Version,
   362  		indent, h.ChainID,
   363  		indent, h.Height,
   364  		indent, h.Time,
   365  		indent, h.NumTxs,
   366  		indent, h.TotalTxs,
   367  		indent, h.AppVersion,
   368  		indent, h.LastBlockID,
   369  		indent, h.LastCommitHash,
   370  		indent, h.DataHash,
   371  		indent, h.ValidatorsHash,
   372  		indent, h.NextValidatorsHash,
   373  		indent, h.AppHash,
   374  		indent, h.ConsensusHash,
   375  		indent, h.LastResultsHash,
   376  		indent, h.ProposerAddress,
   377  		indent, h.Hash())
   378  }
   379  
   380  //-------------------------------------
   381  
   382  // CommitSig is a vote included in a Commit.
   383  // For now, it is identical to a vote,
   384  // but in the future it will contain fewer fields
   385  // to eliminate the redundancy in commits.
   386  // See https://github.com/tendermint/classic/issues/1648.
   387  type CommitSig Vote
   388  
   389  // String returns the underlying Vote.String()
   390  func (cs *CommitSig) String() string {
   391  	return cs.toVote().String()
   392  }
   393  
   394  // toVote converts the CommitSig to a vote.
   395  // TODO: deprecate for #1648. Converting to Vote will require
   396  // access to ValidatorSet.
   397  func (cs *CommitSig) toVote() *Vote {
   398  	if cs == nil {
   399  		return nil
   400  	}
   401  	v := Vote(*cs)
   402  	return &v
   403  }
   404  
   405  //-------------------------------------
   406  
   407  // Commit contains the evidence that a block was committed by a set of validators.
   408  // NOTE: Commit is empty for height 1, but never nil.
   409  type Commit struct {
   410  	// NOTE: The Precommits are in order of address to preserve the bonded ValidatorSet order.
   411  	// Any peer with a block can gossip precommits by index with a peer without recalculating the
   412  	// active ValidatorSet.
   413  	BlockID    BlockID      `json:"block_id"`
   414  	Precommits []*CommitSig `json:"precommits" amino:"nil_elements"`
   415  
   416  	// memoized in first call to corresponding method
   417  	// NOTE: can't memoize in constructor because constructor
   418  	// isn't used for unmarshaling
   419  	height   int64
   420  	round    int
   421  	hash     []byte
   422  	bitArray *bitarray.BitArray
   423  }
   424  
   425  // NewCommit returns a new Commit with the given blockID and precommits.
   426  // TODO: memoize ValidatorSet in constructor so votes can be easily reconstructed
   427  // from CommitSig after #1648.
   428  func NewCommit(blockID BlockID, precommits []*CommitSig) *Commit {
   429  	return &Commit{
   430  		BlockID:    blockID,
   431  		Precommits: precommits,
   432  	}
   433  }
   434  
   435  // Construct a VoteSet from the Commit and validator set. Panics
   436  // if precommits from the commit can't be added to the voteset.
   437  // Inverse of VoteSet.MakeCommit().
   438  func CommitToVoteSet(chainID string, commit *Commit, vals *ValidatorSet) *VoteSet {
   439  	height, round, typ := commit.Height(), commit.Round(), PrecommitType
   440  	voteSet := NewVoteSet(chainID, height, round, typ, vals)
   441  	for idx, precommit := range commit.Precommits {
   442  		if precommit == nil {
   443  			continue
   444  		}
   445  		added, err := voteSet.AddVote(commit.GetVote(idx))
   446  		if !added || err != nil {
   447  			panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err))
   448  		}
   449  	}
   450  	return voteSet
   451  }
   452  
   453  // GetVote converts the CommitSig for the given valIdx to a Vote.
   454  // Returns nil if the precommit at valIdx is nil.
   455  // Panics if valIdx >= commit.Size().
   456  func (commit *Commit) GetVote(valIdx int) *Vote {
   457  	commitSig := commit.Precommits[valIdx]
   458  	if commitSig == nil {
   459  		return nil
   460  	}
   461  
   462  	// NOTE: this commitSig might be for a nil blockID,
   463  	// so we can't just use commit.BlockID here.
   464  	// For #1648, CommitSig will need to indicate what BlockID it's for !
   465  	blockID := commitSig.BlockID
   466  	commit.memoizeHeightRound()
   467  	return &Vote{
   468  		Type:             PrecommitType,
   469  		Height:           commit.height,
   470  		Round:            commit.round,
   471  		BlockID:          blockID,
   472  		Timestamp:        commitSig.Timestamp,
   473  		ValidatorAddress: commitSig.ValidatorAddress,
   474  		ValidatorIndex:   valIdx,
   475  		Signature:        commitSig.Signature,
   476  	}
   477  }
   478  
   479  // VoteSignBytes constructs the SignBytes for the given CommitSig.
   480  // The only unique part of the SignBytes is the Timestamp - all other fields
   481  // signed over are otherwise the same for all validators.
   482  // Panics if valIdx >= commit.Size().
   483  func (commit *Commit) VoteSignBytes(chainID string, valIdx int) []byte {
   484  	return commit.GetVote(valIdx).SignBytes(chainID)
   485  }
   486  
   487  // memoizeHeightRound memoizes the height and round of the commit using
   488  // the first non-nil vote.
   489  // Should be called before any attempt to access `commit.height` or `commit.round`.
   490  func (commit *Commit) memoizeHeightRound() {
   491  	if len(commit.Precommits) == 0 {
   492  		return
   493  	}
   494  	if commit.height > 0 {
   495  		return
   496  	}
   497  	for _, precommit := range commit.Precommits {
   498  		if precommit != nil {
   499  			commit.height = precommit.Height
   500  			commit.round = precommit.Round
   501  			return
   502  		}
   503  	}
   504  }
   505  
   506  // Height returns the height of the commit
   507  func (commit *Commit) Height() int64 {
   508  	commit.memoizeHeightRound()
   509  	return commit.height
   510  }
   511  
   512  // Round returns the round of the commit
   513  func (commit *Commit) Round() int {
   514  	commit.memoizeHeightRound()
   515  	return commit.round
   516  }
   517  
   518  // Type returns the vote type of the commit, which is always VoteTypePrecommit
   519  func (commit *Commit) Type() byte {
   520  	return byte(PrecommitType)
   521  }
   522  
   523  // Size returns the number of votes in the commit
   524  func (commit *Commit) Size() int {
   525  	if commit == nil {
   526  		return 0
   527  	}
   528  	return len(commit.Precommits)
   529  }
   530  
   531  // BitArray returns a BitArray of which validators voted in this commit
   532  func (commit *Commit) BitArray() *bitarray.BitArray {
   533  	if commit.bitArray == nil {
   534  		commit.bitArray = bitarray.NewBitArray(len(commit.Precommits))
   535  		for i, precommit := range commit.Precommits {
   536  			// TODO: need to check the BlockID otherwise we could be counting conflicts,
   537  			// not just the one with +2/3 !
   538  			commit.bitArray.SetIndex(i, precommit != nil)
   539  		}
   540  	}
   541  	return commit.bitArray
   542  }
   543  
   544  // GetByIndex returns the vote corresponding to a given validator index.
   545  // Panics if `index >= commit.Size()`.
   546  // Implements VoteSetReader.
   547  func (commit *Commit) GetByIndex(valIdx int) *Vote {
   548  	return commit.GetVote(valIdx)
   549  }
   550  
   551  // IsCommit returns true if there is at least one vote.
   552  func (commit *Commit) IsCommit() bool {
   553  	return len(commit.Precommits) != 0
   554  }
   555  
   556  // ValidateBasic performs basic validation that doesn't involve state data.
   557  // Does not actually check the cryptographic signatures.
   558  func (commit *Commit) ValidateBasic() error {
   559  	if commit.BlockID.IsZero() {
   560  		return errors.New("Commit cannot be for nil block")
   561  	}
   562  	if len(commit.Precommits) == 0 {
   563  		return errors.New("No precommits in commit")
   564  	}
   565  	height, round := commit.Height(), commit.Round()
   566  
   567  	// Validate the precommits.
   568  	for _, precommit := range commit.Precommits {
   569  		// It's OK for precommits to be missing.
   570  		if precommit == nil {
   571  			continue
   572  		}
   573  		// Ensure that all votes are precommits.
   574  		if precommit.Type != PrecommitType {
   575  			return fmt.Errorf("invalid commit vote. Expected precommit, got %v",
   576  				precommit.Type)
   577  		}
   578  		// Ensure that all heights are the same.
   579  		if precommit.Height != height {
   580  			return fmt.Errorf("invalid commit precommit height. Expected %v, got %v",
   581  				height, precommit.Height)
   582  		}
   583  		// Ensure that all rounds are the same.
   584  		if precommit.Round != round {
   585  			return fmt.Errorf("invalid commit precommit round. Expected %v, got %v",
   586  				round, precommit.Round)
   587  		}
   588  	}
   589  	return nil
   590  }
   591  
   592  // Hash returns the hash of the commit
   593  func (commit *Commit) Hash() []byte {
   594  	if commit == nil {
   595  		return nil
   596  	}
   597  	if commit.hash == nil {
   598  		bs := make([][]byte, len(commit.Precommits))
   599  		for i, precommit := range commit.Precommits {
   600  			bs[i] = bytesOrNil(precommit)
   601  		}
   602  		commit.hash = merkle.SimpleHashFromByteSlices(bs)
   603  	}
   604  	return commit.hash
   605  }
   606  
   607  // StringIndented returns a string representation of the commit
   608  func (commit *Commit) StringIndented(indent string) string {
   609  	if commit == nil {
   610  		return "nil-Commit"
   611  	}
   612  	precommitStrings := make([]string, len(commit.Precommits))
   613  	for i, precommit := range commit.Precommits {
   614  		precommitStrings[i] = precommit.String()
   615  	}
   616  	return fmt.Sprintf(`Commit{
   617  %s  BlockID:    %v
   618  %s  Precommits:
   619  %s    %v
   620  %s}#%v`,
   621  		indent, commit.BlockID,
   622  		indent,
   623  		indent, strings.Join(precommitStrings, "\n"+indent+"    "),
   624  		indent, commit.hash)
   625  }
   626  
   627  //-----------------------------------------------------------------------------
   628  
   629  // SignedHeader is a header along with the commits that prove it.
   630  // It is the basis of the lite client.
   631  type SignedHeader struct {
   632  	*Header `json:"header"`
   633  	Commit  *Commit `json:"commit"`
   634  }
   635  
   636  // ValidateBasic does basic consistency checks and makes sure the header
   637  // and commit are consistent.
   638  //
   639  // NOTE: This does not actually check the cryptographic signatures.  Make
   640  // sure to use a Verifier to validate the signatures actually provide a
   641  // significantly strong proof for this header's validity.
   642  func (sh SignedHeader) ValidateBasic(chainID string) error {
   643  	// Make sure the header is consistent with the commit.
   644  	if sh.Header == nil {
   645  		return errors.New("SignedHeader missing header.")
   646  	}
   647  	if sh.Commit == nil {
   648  		return errors.New("SignedHeader missing commit (precommit votes).")
   649  	}
   650  
   651  	// Check ChainID.
   652  	if sh.ChainID != chainID {
   653  		return fmt.Errorf("Header belongs to another chain '%s' not '%s'",
   654  			sh.ChainID, chainID)
   655  	}
   656  	// Check Height.
   657  	if sh.Commit.Height() != sh.Height {
   658  		return fmt.Errorf("SignedHeader header and commit height mismatch: %v vs %v",
   659  			sh.Height, sh.Commit.Height())
   660  	}
   661  	// Check Hash.
   662  	hhash := sh.Hash()
   663  	chash := sh.Commit.BlockID.Hash
   664  	if !bytes.Equal(hhash, chash) {
   665  		return fmt.Errorf("SignedHeader commit signs block %X, header is block %X",
   666  			chash, hhash)
   667  	}
   668  	// ValidateBasic on the Commit.
   669  	err := sh.Commit.ValidateBasic()
   670  	if err != nil {
   671  		return errors.Wrap(err, "commit.ValidateBasic failed during SignedHeader.ValidateBasic")
   672  	}
   673  	return nil
   674  }
   675  
   676  func (sh SignedHeader) String() string {
   677  	return sh.StringIndented("")
   678  }
   679  
   680  // StringIndented returns a string representation of the SignedHeader.
   681  func (sh SignedHeader) StringIndented(indent string) string {
   682  	return fmt.Sprintf(`SignedHeader{
   683  %s  %v
   684  %s  %v
   685  %s}`,
   686  		indent, sh.Header.StringIndented(indent+"  "),
   687  		indent, sh.Commit.StringIndented(indent+"  "),
   688  		indent)
   689  }
   690  
   691  //-----------------------------------------------------------------------------
   692  
   693  // Data contains the set of transactions included in the block
   694  type Data struct {
   695  	// Txs that will be applied by state @ block.Height+1.
   696  	// NOTE: not all txs here are valid.  We're just agreeing on the order first.
   697  	// This means that block.AppHash does not include these txs.
   698  	Txs Txs `json:"txs"`
   699  
   700  	// Volatile
   701  	hash []byte
   702  }
   703  
   704  // Hash returns the hash of the data
   705  func (data *Data) Hash() []byte {
   706  	if data == nil {
   707  		return (Txs{}).Hash()
   708  	}
   709  	if data.hash == nil {
   710  		data.hash = data.Txs.Hash() // NOTE: leaves of merkle tree are TxIDs
   711  	}
   712  	return data.hash
   713  }
   714  
   715  // StringIndented returns a string representation of the transactions
   716  func (data *Data) StringIndented(indent string) string {
   717  	if data == nil {
   718  		return "nil-Data"
   719  	}
   720  	txStrings := make([]string, min(len(data.Txs), 21))
   721  	for i, tx := range data.Txs {
   722  		if i == 20 {
   723  			txStrings[i] = fmt.Sprintf("... (%v total)", len(data.Txs))
   724  			break
   725  		}
   726  		txStrings[i] = fmt.Sprintf("%X (%d bytes)", tx.Hash(), len(tx))
   727  	}
   728  	return fmt.Sprintf(`Data{
   729  %s  %v
   730  %s}#%v`,
   731  		indent, strings.Join(txStrings, "\n"+indent+"  "),
   732  		indent, data.hash)
   733  }
   734  
   735  //--------------------------------------------------------------------------------
   736  
   737  // BlockID defines the unique ID of a block as its Hash and its PartSetHeader
   738  type BlockID struct {
   739  	Hash        []byte        `json:"hash"`
   740  	PartsHeader PartSetHeader `json:"parts"`
   741  }
   742  
   743  // Equals returns true if the BlockID matches the given BlockID
   744  func (blockID BlockID) Equals(other BlockID) bool {
   745  	return bytes.Equal(blockID.Hash, other.Hash) &&
   746  		blockID.PartsHeader.Equals(other.PartsHeader)
   747  }
   748  
   749  // Key returns a machine-readable string representation of the BlockID
   750  func (blockID BlockID) Key() string {
   751  	bz, err := amino.Marshal(blockID.PartsHeader)
   752  	if err != nil {
   753  		panic(err)
   754  	}
   755  	return string(blockID.Hash) + string(bz)
   756  }
   757  
   758  // ValidateBasic performs basic validation.
   759  func (blockID BlockID) ValidateBasic() error {
   760  	// Hash can be empty in case of POLBlockID in Proposal.
   761  	if err := ValidateHash(blockID.Hash); err != nil {
   762  		return fmt.Errorf("wrong Hash")
   763  	}
   764  	if err := blockID.PartsHeader.ValidateBasic(); err != nil {
   765  		return fmt.Errorf("wrong PartsHeader: %w", err)
   766  	}
   767  	return nil
   768  }
   769  
   770  // IsZero returns true if this is the BlockID of a nil block.
   771  func (blockID BlockID) IsZero() bool {
   772  	return len(blockID.Hash) == 0 &&
   773  		blockID.PartsHeader.IsZero()
   774  }
   775  
   776  // IsComplete returns true if this is a valid BlockID of a non-nil block.
   777  func (blockID BlockID) IsComplete() bool {
   778  	return len(blockID.Hash) == tmhash.Size &&
   779  		blockID.PartsHeader.Total > 0 &&
   780  		len(blockID.PartsHeader.Hash) == tmhash.Size
   781  }
   782  
   783  // String returns a human readable string representation of the BlockID
   784  func (blockID BlockID) String() string {
   785  	return fmt.Sprintf(`%X:%v`, blockID.Hash, blockID.PartsHeader)
   786  }