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 }