diff --git a/consensus/state_test.go b/consensus/state_test.go index 8403f49506..cb8b94d64f 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -250,9 +250,11 @@ func TestStateOversizedBlock(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) voteCh := subscribe(cs1.eventBus, types.EventQueryVote) + var err error propBlock, _ := cs1.createProposalBlock() propBlock.Data.Txs = []types.Tx{tmrand.Bytes(2001)} - propBlock.Header.DataHash = propBlock.Data.Hash() + propBlock.Header.DataHash, err = propBlock.Data.Hash() + assert.Nil(t, err) // make the second validator the proposer by incrementing round round++ diff --git a/types/block.go b/types/block.go index fe915a32c8..a5d5cb5e88 100644 --- a/types/block.go +++ b/types/block.go @@ -85,7 +85,11 @@ func (b *Block) ValidateBasic() error { } // NOTE: b.Data.Txs may be nil, but b.Data.Hash() still works fine. - if w, g := b.Data.Hash(), b.DataHash; !bytes.Equal(w, g) { + w, err := b.Data.Hash() + if err != nil { + return fmt.Errorf("square size for the block data to be hashed over is not a power of 2: %v", err) + } + if g := b.DataHash; !bytes.Equal(w, g) { return fmt.Errorf("wrong Header.DataHash. Expected %X, got %X", w, g) } @@ -106,21 +110,29 @@ func (b *Block) ValidateBasic() error { return nil } -// fillHeader fills in any remaining header fields that are a function of the block data -func (b *Block) fillHeader() { +// fillHeader fills in any remaining header fields that are a function of the block data. +// Returns an error if square size for the block data to be hashed over is invalid, +// that is, not a power of 2 +func (b *Block) fillHeader() error { + var err error if b.LastCommitHash == nil { b.LastCommitHash = b.LastCommit.Hash() } if b.DataHash == nil { - b.DataHash = b.Data.Hash() + b.DataHash, err = b.Data.Hash() + if err != nil { + return err + } } if b.EvidenceHash == nil { b.EvidenceHash = b.Evidence.Hash() } + return nil } // Hash computes and returns the block hash. // If the block is incomplete, block hash is nil for safety. +// Block hash can also be nil if `fillHeader` fails func (b *Block) Hash() tmbytes.HexBytes { if b == nil { return nil @@ -131,7 +143,9 @@ func (b *Block) Hash() tmbytes.HexBytes { if b.LastCommit == nil { return nil } - b.fillHeader() + if err := b.fillHeader(); err != nil { + return nil + } return b.Header.Hash() } @@ -331,7 +345,9 @@ func MakeBlock( Data: data, LastCommit: lastCommit, } - block.fillHeader() + if err := block.fillHeader(); err != nil { + return nil + } return block } @@ -1038,30 +1054,29 @@ type Data struct { } // Hash returns the hash of the data -func (data *Data) Hash() tmbytes.HexBytes { +func (data *Data) Hash() (tmbytes.HexBytes, error) { if data.hash != nil { - return data.hash + return data.hash, nil } // compute the data availability header // todo(evan): add the non redundant shares back into the header shares, _, err := data.ComputeShares(data.OriginalSquareSize) if err != nil { - // todo(evan): see if we can get rid of this panic - panic(err) + return nil, err } rawShares := shares.RawShares() eds, err := da.ExtendShares(data.OriginalSquareSize, rawShares) if err != nil { - panic(err) + return nil, err } dah := da.NewDataAvailabilityHeader(eds) data.hash = dah.Hash() - return data.hash + return data.hash, nil } // ComputeShares splits block data into shares of an original data square and diff --git a/types/block_test.go b/types/block_test.go index ae3ad0311f..83e32cf0f4 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -693,11 +693,13 @@ func TestBlockDataProtobuf(t *testing.T) { for _, tt := range tests { d := Data{Txs: tt.txs, Evidence: tt.evd, Messages: Messages{MessagesList: tt.msgs}} - firstHash := d.Hash() + firstHash, err1 := d.Hash() + assert.Nil(t, err1) pd := d.ToProto() d2, err := DataFromProto(&pd) require.NoError(t, err) - secondHash := d2.Hash() + secondHash, err2 := d2.Hash() + assert.Nil(t, err2) assert.Equal(t, firstHash, secondHash, tt.name) } }