diff --git a/cmd/tendermint/commands/reindex_event_test.go b/cmd/tendermint/commands/reindex_event_test.go index 5d9459f5a..2e4fadebb 100644 --- a/cmd/tendermint/commands/reindex_event_test.go +++ b/cmd/tendermint/commands/reindex_event_test.go @@ -9,12 +9,14 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - abcitypes "github.com/tendermint/tendermint/abci/types" tmcfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/pkg/abci" + "github.com/tendermint/tendermint/pkg/block" + "github.com/tendermint/tendermint/pkg/mempool" prototmstate "github.com/tendermint/tendermint/proto/tendermint/state" "github.com/tendermint/tendermint/state/indexer" + evmocks "github.com/tendermint/tendermint/state/indexer/mocks" "github.com/tendermint/tendermint/state/mocks" - "github.com/tendermint/tendermint/types" ) const ( @@ -116,27 +118,27 @@ func TestLoadBlockStore(t *testing.T) { func TestReIndexEvent(t *testing.T) { mockBlockStore := &mocks.BlockStore{} mockStateStore := &mocks.Store{} - mockEventSink := &mocks.EventSink{} + mockEventSink := &evmocks.EventSink{} mockBlockStore. On("Base").Return(base). On("Height").Return(height). On("LoadBlock", base).Return(nil).Once(). - On("LoadBlock", base).Return(&types.Block{Data: types.Data{Txs: types.Txs{make(types.Tx, 1)}}}). - On("LoadBlock", height).Return(&types.Block{Data: types.Data{Txs: types.Txs{make(types.Tx, 1)}}}) + On("LoadBlock", base).Return(&block.Block{Data: block.Data{Txs: mempool.Txs{make(mempool.Tx, 1)}}}). + On("LoadBlock", height).Return(&block.Block{Data: block.Data{Txs: mempool.Txs{make(mempool.Tx, 1)}}}) mockEventSink. On("Type").Return(indexer.KV). - On("IndexBlockEvents", mock.AnythingOfType("types.EventDataNewBlockHeader")).Return(errors.New("")).Once(). - On("IndexBlockEvents", mock.AnythingOfType("types.EventDataNewBlockHeader")).Return(nil). - On("IndexTxEvents", mock.AnythingOfType("[]*types.TxResult")).Return(errors.New("")).Once(). - On("IndexTxEvents", mock.AnythingOfType("[]*types.TxResult")).Return(nil) + On("IndexBlockEvents", mock.AnythingOfType("events.EventDataNewBlockHeader")).Return(errors.New("")).Once(). + On("IndexBlockEvents", mock.AnythingOfType("events.EventDataNewBlockHeader")).Return(nil). + On("IndexTxEvents", mock.AnythingOfType("[]*abci.TxResult")).Return(errors.New("")).Once(). + On("IndexTxEvents", mock.AnythingOfType("[]*abci.TxResult")).Return(nil) - dtx := abcitypes.ResponseDeliverTx{} + dtx := abci.ResponseDeliverTx{} abciResp := &prototmstate.ABCIResponses{ - DeliverTxs: []*abcitypes.ResponseDeliverTx{&dtx}, - EndBlock: &abcitypes.ResponseEndBlock{}, - BeginBlock: &abcitypes.ResponseBeginBlock{}, + DeliverTxs: []*abci.ResponseDeliverTx{&dtx}, + EndBlock: &abci.ResponseEndBlock{}, + BeginBlock: &abci.ResponseBeginBlock{}, } mockStateStore. diff --git a/pkg/block/block.go b/pkg/block/block.go index 857212b0b..5ca61af7b 100644 --- a/pkg/block/block.go +++ b/pkg/block/block.go @@ -23,9 +23,9 @@ type Block struct { mtx tmsync.Mutex metadata.Header `json:"header"` - Data `json:"data"` - Evidence EvidenceData `json:"evidence"` - LastCommit *metadata.Commit `json:"last_commit"` + Data `json:"data"` + Evidence EvidenceData `json:"evidence"` + LastCommit *metadata.Commit `json:"last_commit"` } // ValidateBasic performs basic validation that doesn't involve state data. diff --git a/pkg/block/block_meta.go b/pkg/block/block_meta.go index cf9fbb2f7..fac52de04 100644 --- a/pkg/block/block_meta.go +++ b/pkg/block/block_meta.go @@ -12,9 +12,9 @@ import ( // BlockMeta contains meta information. type BlockMeta struct { BlockID metadata.BlockID `json:"block_id"` - BlockSize int `json:"block_size"` + BlockSize int `json:"block_size"` Header metadata.Header `json:"header"` - NumTxs int `json:"num_txs"` + NumTxs int `json:"num_txs"` } // NewBlockMeta returns a new BlockMeta. diff --git a/pkg/consensus/proposal.go b/pkg/consensus/proposal.go index b72810b7e..03a4dd740 100644 --- a/pkg/consensus/proposal.go +++ b/pkg/consensus/proposal.go @@ -25,12 +25,12 @@ var ( // If POLRound >= 0, then BlockID corresponds to the block that is locked in POLRound. type Proposal struct { Type tmproto.SignedMsgType - Height int64 `json:"height"` - Round int32 `json:"round"` // there can not be greater than 2_147_483_647 rounds - POLRound int32 `json:"pol_round"` // -1 if null. + Height int64 `json:"height"` + Round int32 `json:"round"` // there can not be greater than 2_147_483_647 rounds + POLRound int32 `json:"pol_round"` // -1 if null. BlockID metadata.BlockID `json:"block_id"` - Timestamp time.Time `json:"timestamp"` - Signature []byte `json:"signature"` + Timestamp time.Time `json:"timestamp"` + Signature []byte `json:"signature"` } // NewProposal returns a new Proposal. diff --git a/pkg/consensus/protobuf.go b/pkg/consensus/protobuf.go index 5aa4c4a82..df6105025 100644 --- a/pkg/consensus/protobuf.go +++ b/pkg/consensus/protobuf.go @@ -1,8 +1,8 @@ package consensus import ( - abci "github.com/tendermint/tendermint/abci/types" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" + "github.com/tendermint/tendermint/pkg/abci" ) //------------------------------------------------------- diff --git a/pkg/consensus/protobuf_test.go b/pkg/consensus/protobuf_test.go index 29a3bd798..5f2c82378 100644 --- a/pkg/consensus/protobuf_test.go +++ b/pkg/consensus/protobuf_test.go @@ -6,10 +6,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" + "github.com/tendermint/tendermint/pkg/abci" "github.com/tendermint/tendermint/pkg/consensus" ) diff --git a/pkg/consensus/validator.go b/pkg/consensus/validator.go index d6e465bc4..ce17a9241 100644 --- a/pkg/consensus/validator.go +++ b/pkg/consensus/validator.go @@ -171,3 +171,8 @@ func ValidatorFromProto(vp *tmproto.Validator) (*Validator, error) { return v, nil } + +type ValidatorInfo struct { + Address Address `json:"address"` + Index int32 `json:"index"` +} diff --git a/pkg/consensus/vote.go b/pkg/consensus/vote.go index 71fd9cfa7..3029d5c75 100644 --- a/pkg/consensus/vote.go +++ b/pkg/consensus/vote.go @@ -52,7 +52,7 @@ type Vote struct { Type tmproto.SignedMsgType `json:"type"` Height int64 `json:"height"` Round int32 `json:"round"` // assume there will not be greater than 2_147_483_647 rounds - BlockID metadata.BlockID `json:"block_id"` // zero if vote is nil. + BlockID metadata.BlockID `json:"block_id"` // zero if vote is nil. Timestamp time.Time `json:"timestamp"` ValidatorAddress Address `json:"validator_address"` ValidatorIndex int32 `json:"validator_index"` diff --git a/pkg/consensus/vote_set.go b/pkg/consensus/vote_set.go index f6de4b811..d24be0bf3 100644 --- a/pkg/consensus/vote_set.go +++ b/pkg/consensus/vote_set.go @@ -68,10 +68,10 @@ type VoteSet struct { mtx tmsync.Mutex votesBitArray *bits.BitArray - votes []*Vote // Primary votes to share - sum int64 // Sum of voting power for seen votes, discounting conflicts + votes []*Vote // Primary votes to share + sum int64 // Sum of voting power for seen votes, discounting conflicts maj23 *metadata.BlockID // First 2/3 majority seen - votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes + votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes peerMaj23s map[P2PID]metadata.BlockID // Maj23 for each peer } @@ -521,8 +521,8 @@ func (voteSet *VoteSet) MarshalJSON() ([]byte, error) { // NOTE: insufficient for unmarshaling from (compressed votes) // TODO: make the peerMaj23s nicer to read (eg just the block hash) type VoteSetJSON struct { - Votes []string `json:"votes"` - VotesBitArray string `json:"votes_bit_array"` + Votes []string `json:"votes"` + VotesBitArray string `json:"votes_bit_array"` PeerMaj23s map[P2PID]metadata.BlockID `json:"peer_maj_23s"` } diff --git a/pkg/events/event_bus.go b/pkg/events/event_bus.go index 7333bec56..7d67891e6 100644 --- a/pkg/events/event_bus.go +++ b/pkg/events/event_bus.go @@ -5,10 +5,10 @@ import ( "fmt" "strings" - "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" "github.com/tendermint/tendermint/libs/service" + "github.com/tendermint/tendermint/pkg/abci" "github.com/tendermint/tendermint/pkg/mempool" ) @@ -107,9 +107,9 @@ func (b *EventBus) Publish(eventValue string, eventData TMEventData) error { ctx := context.Background() tokens := strings.Split(EventTypeKey, ".") - event := types.Event{ + event := abci.Event{ Type: tokens[0], - Attributes: []types.EventAttribute{ + Attributes: []abci.EventAttribute{ { Key: tokens[1], Value: eventValue, @@ -117,7 +117,7 @@ func (b *EventBus) Publish(eventValue string, eventData TMEventData) error { }, } - return b.pubsub.PublishWithEvents(ctx, eventData, []types.Event{event}) + return b.pubsub.PublishWithEvents(ctx, eventData, []abci.Event{event}) } func (b *EventBus) PublishEventNewBlock(data EventDataNewBlock) error { @@ -174,9 +174,9 @@ func (b *EventBus) PublishEventTx(data EventDataTx) error { events = append(events, EventTx) tokens := strings.Split(TxHashKey, ".") - events = append(events, types.Event{ + events = append(events, abci.Event{ Type: tokens[0], - Attributes: []types.EventAttribute{ + Attributes: []abci.EventAttribute{ { Key: tokens[1], Value: fmt.Sprintf("%X", mempool.Tx(data.Tx).Hash()), @@ -185,9 +185,9 @@ func (b *EventBus) PublishEventTx(data EventDataTx) error { }) tokens = strings.Split(TxHeightKey, ".") - events = append(events, types.Event{ + events = append(events, abci.Event{ Type: tokens[0], - Attributes: []types.EventAttribute{ + Attributes: []abci.EventAttribute{ { Key: tokens[1], Value: fmt.Sprintf("%d", data.Height), diff --git a/pkg/events/event_bus_test.go b/pkg/events/event_bus_test.go index 7fde12b78..6f8953d7f 100644 --- a/pkg/events/event_bus_test.go +++ b/pkg/events/event_bus_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" + "github.com/tendermint/tendermint/pkg/abci" "github.com/tendermint/tendermint/pkg/block" "github.com/tendermint/tendermint/pkg/events" "github.com/tendermint/tendermint/pkg/evidence" diff --git a/pkg/events/events.go b/pkg/events/events.go index c0b869a57..27d4542d2 100644 --- a/pkg/events/events.go +++ b/pkg/events/events.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - abci "github.com/tendermint/tendermint/abci/types" tmjson "github.com/tendermint/tendermint/libs/json" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" + "github.com/tendermint/tendermint/pkg/abci" "github.com/tendermint/tendermint/pkg/block" "github.com/tendermint/tendermint/pkg/consensus" @@ -118,7 +118,7 @@ func init() { // but some (an input to a call tx or a receive) are more exotic type EventDataNewBlock struct { - Block *block.Block `json:"block"` + Block *block.Block `json:"block"` BlockID metadata.BlockID `json:"block_id"` ResultBeginBlock abci.ResponseBeginBlock `json:"result_begin_block"` @@ -151,17 +151,12 @@ type EventDataRoundState struct { Step string `json:"step"` } -type ValidatorInfo struct { - Address consensus.Address `json:"address"` - Index int32 `json:"index"` -} - type EventDataNewRound struct { Height int64 `json:"height"` Round int32 `json:"round"` Step string `json:"step"` - Proposer ValidatorInfo `json:"proposer"` + Proposer consensus.ValidatorInfo `json:"proposer"` } type EventDataCompleteProposal struct { diff --git a/pkg/evidence/evidence.go b/pkg/evidence/evidence.go index ac970f552..acb1bdd52 100644 --- a/pkg/evidence/evidence.go +++ b/pkg/evidence/evidence.go @@ -10,11 +10,11 @@ import ( "strings" "time" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/tmhash" tmjson "github.com/tendermint/tendermint/libs/json" tmrand "github.com/tendermint/tendermint/libs/rand" + "github.com/tendermint/tendermint/pkg/abci" "github.com/tendermint/tendermint/pkg/consensus" "github.com/tendermint/tendermint/pkg/light" "github.com/tendermint/tendermint/pkg/metadata" @@ -313,7 +313,7 @@ func (l *LightClientAttackEvidence) Bytes() []byte { // the malicious validators were and returns them. This is used both for forming the ByzantineValidators // field and for validating that it is correct. Validators are ordered based on validator power func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *consensus.ValidatorSet, - trusted *light.SignedHeader) []*consensus.Validator { + trusted *metadata.SignedHeader) []*consensus.Validator { var validators []*consensus.Validator // First check if the header is invalid. This means that it is a lunatic attack and therefore we take the // validators who are in the commonVals and voted for the lunatic header @@ -454,7 +454,7 @@ func (l *LightClientAttackEvidence) ValidateBasic() error { // invalid. func (l *LightClientAttackEvidence) ValidateABCI( commonVals *consensus.ValidatorSet, - trustedHeader *light.SignedHeader, + trustedHeader *metadata.SignedHeader, evidenceTime time.Time, ) error { @@ -510,7 +510,7 @@ func (l *LightClientAttackEvidence) ValidateABCI( // total voting power and byantine validators func (l *LightClientAttackEvidence) GenerateABCI( commonVals *consensus.ValidatorSet, - trustedHeader *light.SignedHeader, + trustedHeader *metadata.SignedHeader, evidenceTime time.Time, ) { l.Timestamp = evidenceTime diff --git a/pkg/evidence/evidence_test.go b/pkg/evidence/evidence_test.go index b08d13e65..80ccb6e17 100644 --- a/pkg/evidence/evidence_test.go +++ b/pkg/evidence/evidence_test.go @@ -109,7 +109,7 @@ func TestLightClientAttackEvidenceBasic(t *testing.T) { require.NoError(t, err) lcae := &evidence.LightClientAttackEvidence{ ConflictingBlock: &light.LightBlock{ - SignedHeader: &light.SignedHeader{ + SignedHeader: &metadata.SignedHeader{ Header: header, Commit: commit, }, @@ -139,7 +139,7 @@ func TestLightClientAttackEvidenceBasic(t *testing.T) { for _, tc := range testCases { lcae := &evidence.LightClientAttackEvidence{ ConflictingBlock: &light.LightBlock{ - SignedHeader: &light.SignedHeader{ + SignedHeader: &metadata.SignedHeader{ Header: header, Commit: commit, }, @@ -169,7 +169,7 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) { require.NoError(t, err) lcae := &evidence.LightClientAttackEvidence{ ConflictingBlock: &light.LightBlock{ - SignedHeader: &light.SignedHeader{ + SignedHeader: &metadata.SignedHeader{ Header: header, Commit: commit, }, @@ -209,7 +209,7 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) { t.Run(tc.testName, func(t *testing.T) { lcae := &evidence.LightClientAttackEvidence{ ConflictingBlock: &light.LightBlock{ - SignedHeader: &light.SignedHeader{ + SignedHeader: &metadata.SignedHeader{ Header: header, Commit: commit, }, @@ -358,7 +358,7 @@ func TestEvidenceVectors(t *testing.T) { require.NoError(t, err) lcae := &evidence.LightClientAttackEvidence{ ConflictingBlock: &light.LightBlock{ - SignedHeader: &light.SignedHeader{ + SignedHeader: &metadata.SignedHeader{ Header: header, Commit: commit, }, diff --git a/pkg/light/light.go b/pkg/light/light.go index 43941870b..432b9f99c 100644 --- a/pkg/light/light.go +++ b/pkg/light/light.go @@ -6,15 +6,15 @@ import ( "fmt" "github.com/tendermint/tendermint/pkg/consensus" - meta "github.com/tendermint/tendermint/pkg/metadata" + "github.com/tendermint/tendermint/pkg/metadata" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) // LightBlock is a SignedHeader and a ValidatorSet. // It is the basis of the light client type LightBlock struct { - *SignedHeader `json:"signed_header"` - ValidatorSet *consensus.ValidatorSet `json:"validator_set"` + *metadata.SignedHeader `json:"signed_header"` + ValidatorSet *consensus.ValidatorSet `json:"validator_set"` } // ValidateBasic checks that the data is correct and consistent @@ -95,7 +95,7 @@ func LightBlockFromProto(pb *tmproto.LightBlock) (*LightBlock, error) { lb := new(LightBlock) if pb.SignedHeader != nil { - sh, err := SignedHeaderFromProto(pb.SignedHeader) + sh, err := metadata.SignedHeaderFromProto(pb.SignedHeader) if err != nil { return nil, err } @@ -112,112 +112,3 @@ func LightBlockFromProto(pb *tmproto.LightBlock) (*LightBlock, error) { return lb, nil } - -//----------------------------------------------------------------------------- - -// SignedHeader is a header along with the commits that prove it. -type SignedHeader struct { - *meta.Header `json:"header"` - - Commit *meta.Commit `json:"commit"` -} - -// ValidateBasic does basic consistency checks and makes sure the header -// and commit are consistent. -// -// NOTE: This does not actually check the cryptographic signatures. Make sure -// to use a Verifier to validate the signatures actually provide a -// significantly strong proof for this header's validity. -func (sh SignedHeader) ValidateBasic(chainID string) error { - if sh.Header == nil { - return errors.New("missing header") - } - if sh.Commit == nil { - return errors.New("missing commit") - } - - if err := sh.Header.ValidateBasic(); err != nil { - return fmt.Errorf("invalid header: %w", err) - } - if err := sh.Commit.ValidateBasic(); err != nil { - return fmt.Errorf("invalid commit: %w", err) - } - - if sh.ChainID != chainID { - return fmt.Errorf("header belongs to another chain %q, not %q", sh.ChainID, chainID) - } - - // Make sure the header is consistent with the commit. - if sh.Commit.Height != sh.Height { - return fmt.Errorf("header and commit height mismatch: %d vs %d", sh.Height, sh.Commit.Height) - } - if hhash, chash := sh.Header.Hash(), sh.Commit.BlockID.Hash; !bytes.Equal(hhash, chash) { - return fmt.Errorf("commit signs block %X, header is block %X", chash, hhash) - } - - return nil -} - -// String returns a string representation of SignedHeader. -func (sh SignedHeader) String() string { - return sh.StringIndented("") -} - -// StringIndented returns an indented string representation of SignedHeader. -// -// Header -// Commit -func (sh SignedHeader) StringIndented(indent string) string { - return fmt.Sprintf(`SignedHeader{ -%s %v -%s %v -%s}`, - indent, sh.Header.StringIndented(indent+" "), - indent, sh.Commit.StringIndented(indent+" "), - indent) -} - -// ToProto converts SignedHeader to protobuf -func (sh *SignedHeader) ToProto() *tmproto.SignedHeader { - if sh == nil { - return nil - } - - psh := new(tmproto.SignedHeader) - if sh.Header != nil { - psh.Header = sh.Header.ToProto() - } - if sh.Commit != nil { - psh.Commit = sh.Commit.ToProto() - } - - return psh -} - -// FromProto sets a protobuf SignedHeader to the given pointer. -// It returns an error if the header or the commit is invalid. -func SignedHeaderFromProto(shp *tmproto.SignedHeader) (*SignedHeader, error) { - if shp == nil { - return nil, errors.New("nil SignedHeader") - } - - sh := new(SignedHeader) - - if shp.Header != nil { - h, err := meta.HeaderFromProto(shp.Header) - if err != nil { - return nil, err - } - sh.Header = &h - } - - if shp.Commit != nil { - c, err := meta.CommitFromProto(shp.Commit) - if err != nil { - return nil, err - } - sh.Commit = c - } - - return sh, nil -} diff --git a/pkg/light/light_test.go b/pkg/light/light_test.go index 9444ce036..ff0dce46f 100644 --- a/pkg/light/light_test.go +++ b/pkg/light/light_test.go @@ -1,14 +1,11 @@ package light_test import ( - "math" "testing" "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" test "github.com/tendermint/tendermint/internal/test/factory" "github.com/tendermint/tendermint/pkg/consensus" "github.com/tendermint/tendermint/pkg/light" @@ -29,21 +26,21 @@ func TestLightBlockValidateBasic(t *testing.T) { vals3.Proposer = &consensus.Validator{} commit.BlockID.Hash = header.Hash() - sh := &light.SignedHeader{ + sh := &metadata.SignedHeader{ Header: header, Commit: commit, } testCases := []struct { name string - sh *light.SignedHeader + sh *metadata.SignedHeader vals *consensus.ValidatorSet expectErr bool }{ {"valid light block", sh, vals, false}, {"hashes don't match", sh, vals2, true}, {"invalid validator set", sh, vals3, true}, - {"invalid signed header", &light.SignedHeader{Header: header, Commit: test.MakeRandomCommit(time.Now())}, vals, true}, + {"invalid signed header", &metadata.SignedHeader{Header: header, Commit: test.MakeRandomCommit(time.Now())}, vals, true}, } for _, tc := range testCases { @@ -73,22 +70,22 @@ func TestLightBlockProtobuf(t *testing.T) { vals3.Proposer = &consensus.Validator{} commit.BlockID.Hash = header.Hash() - sh := &light.SignedHeader{ + sh := &metadata.SignedHeader{ Header: header, Commit: commit, } testCases := []struct { name string - sh *light.SignedHeader + sh *metadata.SignedHeader vals *consensus.ValidatorSet toProtoErr bool toBlockErr bool }{ {"valid light block", sh, vals, false, false}, - {"empty signed header", &light.SignedHeader{}, vals, false, false}, + {"empty signed header", &metadata.SignedHeader{}, vals, false, false}, {"empty validator set", sh, &consensus.ValidatorSet{}, false, true}, - {"empty light block", &light.SignedHeader{}, &consensus.ValidatorSet{}, false, true}, + {"empty light block", &metadata.SignedHeader{}, &consensus.ValidatorSet{}, false, true}, } for _, tc := range testCases { @@ -113,87 +110,3 @@ func TestLightBlockProtobuf(t *testing.T) { } } - -func TestSignedHeaderValidateBasic(t *testing.T) { - commit := test.MakeRandomCommit(time.Now()) - chainID := "𠜎" - timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) - h := metadata.Header{ - Version: version.Consensus{Block: version.BlockProtocol, App: math.MaxInt64}, - ChainID: chainID, - Height: commit.Height, - Time: timestamp, - LastBlockID: commit.BlockID, - LastCommitHash: commit.Hash(), - DataHash: commit.Hash(), - ValidatorsHash: commit.Hash(), - NextValidatorsHash: commit.Hash(), - ConsensusHash: commit.Hash(), - AppHash: commit.Hash(), - LastResultsHash: commit.Hash(), - EvidenceHash: commit.Hash(), - ProposerAddress: crypto.AddressHash([]byte("proposer_address")), - } - - validSignedHeader := light.SignedHeader{Header: &h, Commit: commit} - validSignedHeader.Commit.BlockID.Hash = validSignedHeader.Hash() - invalidSignedHeader := light.SignedHeader{} - - testCases := []struct { - testName string - shHeader *metadata.Header - shCommit *metadata.Commit - expectErr bool - }{ - {"Valid Signed Header", validSignedHeader.Header, validSignedHeader.Commit, false}, - {"Invalid Signed Header", invalidSignedHeader.Header, validSignedHeader.Commit, true}, - {"Invalid Signed Header", validSignedHeader.Header, invalidSignedHeader.Commit, true}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.testName, func(t *testing.T) { - sh := light.SignedHeader{ - Header: tc.shHeader, - Commit: tc.shCommit, - } - err := sh.ValidateBasic(validSignedHeader.Header.ChainID) - assert.Equalf( - t, - tc.expectErr, - err != nil, - "Validate Basic had an unexpected result", - err, - ) - }) - } -} - -func TestSignedHeaderProtoBuf(t *testing.T) { - commit := test.MakeRandomCommit(time.Now()) - h := test.MakeRandomHeader() - - sh := light.SignedHeader{Header: h, Commit: commit} - - testCases := []struct { - msg string - sh1 *light.SignedHeader - expPass bool - }{ - {"empty SignedHeader 2", &light.SignedHeader{}, true}, - {"success", &sh, true}, - {"failure nil", nil, false}, - } - for _, tc := range testCases { - protoSignedHeader := tc.sh1.ToProto() - - sh, err := light.SignedHeaderFromProto(protoSignedHeader) - - if tc.expPass { - require.NoError(t, err, tc.msg) - require.Equal(t, tc.sh1, sh, tc.msg) - } else { - require.Error(t, err, tc.msg) - } - } -} diff --git a/pkg/metadata/header.go b/pkg/metadata/header.go index b3c0d738f..477a5222f 100644 --- a/pkg/metadata/header.go +++ b/pkg/metadata/header.go @@ -1,6 +1,7 @@ package metadata import ( + "bytes" "errors" "fmt" "time" @@ -291,3 +292,112 @@ func HeaderFromProto(ph *tmproto.Header) (Header, error) { } //------------------------------------- + +//----------------------------------------------------------------------------- + +// SignedHeader is a header along with the commits that prove it. +type SignedHeader struct { + *Header `json:"header"` + + Commit *Commit `json:"commit"` +} + +// ValidateBasic does basic consistency checks and makes sure the header +// and commit are consistent. +// +// NOTE: This does not actually check the cryptographic signatures. Make sure +// to use a Verifier to validate the signatures actually provide a +// significantly strong proof for this header's validity. +func (sh SignedHeader) ValidateBasic(chainID string) error { + if sh.Header == nil { + return errors.New("missing header") + } + if sh.Commit == nil { + return errors.New("missing commit") + } + + if err := sh.Header.ValidateBasic(); err != nil { + return fmt.Errorf("invalid header: %w", err) + } + if err := sh.Commit.ValidateBasic(); err != nil { + return fmt.Errorf("invalid commit: %w", err) + } + + if sh.ChainID != chainID { + return fmt.Errorf("header belongs to another chain %q, not %q", sh.ChainID, chainID) + } + + // Make sure the header is consistent with the commit. + if sh.Commit.Height != sh.Height { + return fmt.Errorf("header and commit height mismatch: %d vs %d", sh.Height, sh.Commit.Height) + } + if hhash, chash := sh.Header.Hash(), sh.Commit.BlockID.Hash; !bytes.Equal(hhash, chash) { + return fmt.Errorf("commit signs block %X, header is block %X", chash, hhash) + } + + return nil +} + +// String returns a string representation of SignedHeader. +func (sh SignedHeader) String() string { + return sh.StringIndented("") +} + +// StringIndented returns an indented string representation of SignedHeader. +// +// Header +// Commit +func (sh SignedHeader) StringIndented(indent string) string { + return fmt.Sprintf(`SignedHeader{ +%s %v +%s %v +%s}`, + indent, sh.Header.StringIndented(indent+" "), + indent, sh.Commit.StringIndented(indent+" "), + indent) +} + +// ToProto converts SignedHeader to protobuf +func (sh *SignedHeader) ToProto() *tmproto.SignedHeader { + if sh == nil { + return nil + } + + psh := new(tmproto.SignedHeader) + if sh.Header != nil { + psh.Header = sh.Header.ToProto() + } + if sh.Commit != nil { + psh.Commit = sh.Commit.ToProto() + } + + return psh +} + +// FromProto sets a protobuf SignedHeader to the given pointer. +// It returns an error if the header or the commit is invalid. +func SignedHeaderFromProto(shp *tmproto.SignedHeader) (*SignedHeader, error) { + if shp == nil { + return nil, errors.New("nil SignedHeader") + } + + sh := new(SignedHeader) + + if shp.Header != nil { + h, err := HeaderFromProto(shp.Header) + if err != nil { + return nil, err + } + sh.Header = &h + } + + if shp.Commit != nil { + c, err := CommitFromProto(shp.Commit) + if err != nil { + return nil, err + } + sh.Commit = c + } + + return sh, nil +} diff --git a/pkg/metadata/header_test.go b/pkg/metadata/header_test.go index e150ebd81..dfbe15221 100644 --- a/pkg/metadata/header_test.go +++ b/pkg/metadata/header_test.go @@ -494,3 +494,87 @@ func TestHeaderHashVector(t *testing.T) { require.Equal(t, tc.expBytes, hex.EncodeToString(hash)) } } + +func TestSignedHeaderValidateBasic(t *testing.T) { + commit := test.MakeRandomCommit(time.Now()) + chainID := "𠜎" + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + h := metadata.Header{ + Version: version.Consensus{Block: version.BlockProtocol, App: math.MaxInt64}, + ChainID: chainID, + Height: commit.Height, + Time: timestamp, + LastBlockID: commit.BlockID, + LastCommitHash: commit.Hash(), + DataHash: commit.Hash(), + ValidatorsHash: commit.Hash(), + NextValidatorsHash: commit.Hash(), + ConsensusHash: commit.Hash(), + AppHash: commit.Hash(), + LastResultsHash: commit.Hash(), + EvidenceHash: commit.Hash(), + ProposerAddress: crypto.AddressHash([]byte("proposer_address")), + } + + validSignedHeader := metadata.SignedHeader{Header: &h, Commit: commit} + validSignedHeader.Commit.BlockID.Hash = validSignedHeader.Hash() + invalidSignedHeader := metadata.SignedHeader{} + + testCases := []struct { + testName string + shHeader *metadata.Header + shCommit *metadata.Commit + expectErr bool + }{ + {"Valid Signed Header", validSignedHeader.Header, validSignedHeader.Commit, false}, + {"Invalid Signed Header", invalidSignedHeader.Header, validSignedHeader.Commit, true}, + {"Invalid Signed Header", validSignedHeader.Header, invalidSignedHeader.Commit, true}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + sh := metadata.SignedHeader{ + Header: tc.shHeader, + Commit: tc.shCommit, + } + err := sh.ValidateBasic(validSignedHeader.Header.ChainID) + assert.Equalf( + t, + tc.expectErr, + err != nil, + "Validate Basic had an unexpected result", + err, + ) + }) + } +} + +func TestSignedHeaderProtoBuf(t *testing.T) { + commit := test.MakeRandomCommit(time.Now()) + h := test.MakeRandomHeader() + + sh := metadata.SignedHeader{Header: h, Commit: commit} + + testCases := []struct { + msg string + sh1 *metadata.SignedHeader + expPass bool + }{ + {"empty SignedHeader 2", &metadata.SignedHeader{}, true}, + {"success", &sh, true}, + {"failure nil", nil, false}, + } + for _, tc := range testCases { + protoSignedHeader := tc.sh1.ToProto() + + sh, err := metadata.SignedHeaderFromProto(protoSignedHeader) + + if tc.expPass { + require.NoError(t, err, tc.msg) + require.Equal(t, tc.sh1, sh, tc.msg) + } else { + require.Error(t, err, tc.msg) + } + } +}