From d8679cc18db8778f030f74b697a77547eccacff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Tue, 17 Jan 2023 18:40:09 +0100 Subject: [PATCH] Integration with go-header (#685) Initial version of header interface is used to unblock work on go-header library. Resolves #641. --- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 2 +- block/manager.go | 26 ++--- conv/abci/block.go | 17 ++-- da/mock/mock.go | 2 +- da/test/da_test.go | 4 +- go.mod | 9 +- go.sum | 8 ++ rpc/client/client.go | 17 ++-- rpc/client/client_test.go | 28 +++--- state/executor.go | 41 ++++---- state/executor_test.go | 18 ++-- store/store.go | 24 ++--- store/store_test.go | 16 +-- store/types.go | 6 +- types/block.go | 46 +-------- types/hashing.go | 20 ++-- types/header.go | 193 ++++++++++++++++++++++++++++++++++++ types/serialization.go | 18 ++-- types/serialization_test.go | 10 +- types/validation.go | 10 +- 21 files changed, 345 insertions(+), 172 deletions(-) create mode 100644 types/header.go diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 81a604d2196..502f5e11528 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.19 - name: golangci-lint uses: golangci/golangci-lint-action@v3.3.1 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7cd0ac7c478..d84cb7682c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.19 - name: Build run: go build -v ./... diff --git a/block/manager.go b/block/manager.go index e24236aed2f..8aecbe38ac2 100644 --- a/block/manager.go +++ b/block/manager.go @@ -15,6 +15,8 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "go.uber.org/multierr" + "github.com/celestiaorg/go-header" + "github.com/celestiaorg/rollmint/config" "github.com/celestiaorg/rollmint/da" "github.com/celestiaorg/rollmint/log" @@ -195,8 +197,8 @@ func (m *Manager) SyncLoop(ctx context.Context) { case <-daTicker.C: m.retrieveCond.Signal() case header := <-m.HeaderInCh: - m.logger.Debug("block header received", "height", header.Header.Height, "hash", header.Header.Hash()) - newHeight := header.Header.Height + m.logger.Debug("block header received", "height", header.Header.Height(), "hash", header.Header.Hash()) + newHeight := header.Header.BaseHeader.Height currentHeight := m.store.Height() // in case of client reconnecting after being offline // newHeight may be significantly larger than currentHeight @@ -223,7 +225,7 @@ func (m *Manager) SyncLoop(ctx context.Context) { "daHeight", daHeight, "hash", block.Hash(), ) - m.syncCache[block.Header.Height] = block + m.syncCache[block.Header.BaseHeader.Height] = block m.retrieveCond.Signal() err := m.trySyncNextBlock(ctx, daHeight) @@ -279,7 +281,7 @@ func (m *Manager) trySyncNextBlock(ctx context.Context, daHeight uint64) error { } if b1 != nil && commit != nil { - m.logger.Info("Syncing block", "height", b1.Header.Height) + m.logger.Info("Syncing block", "height", b1.Header.Height()) newState, responses, err := m.executor.ApplyBlock(ctx, m.lastState, b1) if err != nil { return fmt.Errorf("failed to ApplyBlock: %w", err) @@ -292,9 +294,9 @@ func (m *Manager) trySyncNextBlock(ctx context.Context, daHeight uint64) error { if err != nil { return fmt.Errorf("failed to Commit: %w", err) } - m.store.SetHeight(b1.Header.Height) + m.store.SetHeight(uint64(b1.Header.Height())) - err = m.store.SaveBlockResponses(b1.Header.Height, responses) + err = m.store.SaveBlockResponses(uint64(b1.Header.Height()), responses) if err != nil { return fmt.Errorf("failed to save block responses: %w", err) } @@ -412,7 +414,7 @@ func (m *Manager) getCommit(header types.Header) (*types.Commit, error) { return nil, err } return &types.Commit{ - Height: header.Height, + Height: uint64(header.Height()), HeaderHash: header.Hash(), Signatures: []types.Signature{sign}, }, nil @@ -420,14 +422,14 @@ func (m *Manager) getCommit(header types.Header) (*types.Commit, error) { func (m *Manager) publishBlock(ctx context.Context) error { var lastCommit *types.Commit - var lastHeaderHash [32]byte + var lastHeaderHash header.Hash var err error height := m.store.Height() newHeight := height + 1 // this is a special case, when first block is produced - there is no previous commit if newHeight == uint64(m.genesis.InitialHeight) { - lastCommit = &types.Commit{Height: height, HeaderHash: [32]byte{}} + lastCommit = &types.Commit{Height: height} } else { lastCommit, err = m.store.LoadCommit(height) if err != nil { @@ -499,7 +501,7 @@ func (m *Manager) publishBlock(ctx context.Context) error { } // SaveBlockResponses commits the DB tx - err = m.store.SaveBlockResponses(block.Header.Height, responses) + err = m.store.SaveBlockResponses(uint64(block.Header.Height()), responses) if err != nil { return err } @@ -515,13 +517,13 @@ func (m *Manager) publishBlock(ctx context.Context) error { } // SaveValidators commits the DB tx - err = m.store.SaveValidators(block.Header.Height, m.lastState.Validators) + err = m.store.SaveValidators(uint64(block.Header.Height()), m.lastState.Validators) if err != nil { return err } // Only update the stored height after successfully submitting to DA layer and committing to the DB - m.store.SetHeight(block.Header.Height) + m.store.SetHeight(uint64(block.Header.Height())) m.publishSignedHeader(block, commit) diff --git a/conv/abci/block.go b/conv/abci/block.go index 44821a5ee5b..28fb4ab90f9 100644 --- a/conv/abci/block.go +++ b/conv/abci/block.go @@ -1,8 +1,7 @@ package abci import ( - "time" - + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmversion "github.com/tendermint/tendermint/proto/tendermint/version" tmtypes "github.com/tendermint/tendermint/types" @@ -18,8 +17,8 @@ func ToABCIHeaderPB(header *types.Header) (tmproto.Header, error) { Block: header.Version.Block, App: header.Version.App, }, - Height: int64(header.Height), - Time: time.Unix(int64(header.Time), 0), + Height: int64(header.Height()), + Time: header.Time(), LastBlockId: tmproto.BlockID{ Hash: header.LastHeaderHash[:], PartSetHeader: tmproto.PartSetHeader{ @@ -36,7 +35,7 @@ func ToABCIHeaderPB(header *types.Header) (tmproto.Header, error) { LastResultsHash: header.LastResultsHash[:], EvidenceHash: new(tmtypes.EvidenceData).Hash(), ProposerAddress: header.ProposerAddress, - ChainID: header.ChainID, + ChainID: header.ChainID(), }, nil } @@ -48,8 +47,8 @@ func ToABCIHeader(header *types.Header) (tmtypes.Header, error) { Block: header.Version.Block, App: header.Version.App, }, - Height: int64(header.Height), - Time: time.Unix(int64(header.Time), 0), + Height: int64(header.Height()), + Time: header.Time(), LastBlockID: tmtypes.BlockID{ Hash: header.LastHeaderHash[:], PartSetHeader: tmtypes.PartSetHeader{ @@ -66,7 +65,7 @@ func ToABCIHeader(header *types.Header) (tmtypes.Header, error) { LastResultsHash: header.LastResultsHash[:], EvidenceHash: new(tmtypes.EvidenceData).Hash(), ProposerAddress: header.ProposerAddress, - ChainID: header.ChainID, + ChainID: header.ChainID(), }, nil } @@ -122,7 +121,7 @@ func ToABCICommit(commit *types.Commit) *tmtypes.Commit { Height: int64(commit.Height), Round: 0, BlockID: tmtypes.BlockID{ - Hash: commit.HeaderHash[:], + Hash: tmbytes.HexBytes(commit.HeaderHash), PartSetHeader: tmtypes.PartSetHeader{}, }, } diff --git a/da/mock/mock.go b/da/mock/mock.go index 66233240208..90a3d937988 100644 --- a/da/mock/mock.go +++ b/da/mock/mock.go @@ -81,7 +81,7 @@ func (m *DataAvailabilityLayerClient) SubmitBlock(ctx context.Context, block *ty return da.ResultSubmitBlock{BaseResult: da.BaseResult{Code: da.StatusError, Message: err.Error()}} } - err = m.dalcKV.Put(ctx, getKey(daHeight, block.Header.Height), hash[:]) + err = m.dalcKV.Put(ctx, getKey(daHeight, uint64(block.Header.Height())), hash[:]) if err != nil { return da.ResultSubmitBlock{BaseResult: da.BaseResult{Code: da.StatusError, Message: err.Error()}} } diff --git a/da/test/da_test.go b/da/test/da_test.go index 11e044d9bc3..a9ff52253c6 100644 --- a/da/test/da_test.go +++ b/da/test/da_test.go @@ -241,7 +241,9 @@ func doTestRetrieve(t *testing.T, dalc da.DataAvailabilityLayerClient) { func getRandomBlock(height uint64, nTxs int) *types.Block { block := &types.Block{ Header: types.Header{ - Height: height, + BaseHeader: types.BaseHeader{ + Height: height, + }, }, Data: types.Data{ Txs: make(types.Txs, nTxs), diff --git a/go.mod b/go.mod index 4ebf9a0c407..781535e5f9e 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/celestiaorg/go-cnc v0.2.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/go-kit/kit v0.12.0 - github.com/gogo/protobuf v1.3.2 + github.com/gogo/protobuf v1.3.3 github.com/google/orderedcode v0.0.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/rpc v1.2.0 @@ -35,6 +35,7 @@ require ( github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd v0.22.1 // indirect + github.com/celestiaorg/go-header v0.0.0-20230103073037-63e9cfae040e // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cheekybits/genny v1.0.0 // indirect @@ -70,7 +71,7 @@ require ( github.com/gtank/merlin v0.1.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huin/goupnp v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect @@ -85,7 +86,7 @@ require ( github.com/jmhodges/levigo v1.0.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/klauspost/compress v1.15.9 // indirect - github.com/klauspost/cpuid/v2 v2.1.0 // indirect + github.com/klauspost/cpuid/v2 v2.1.1 // indirect github.com/koron/go-ssdp v0.0.3 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect @@ -122,7 +123,7 @@ require ( github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.1.1 // indirect github.com/multiformats/go-multicodec v0.6.0 // indirect - github.com/multiformats/go-multihash v0.2.1 // indirect + github.com/multiformats/go-multihash v0.2.2-0.20221030163302-608669da49b6 // indirect github.com/multiformats/go-multistream v0.3.3 // indirect github.com/multiformats/go-varint v0.0.6 // indirect github.com/nxadm/tail v1.4.8 // indirect diff --git a/go.sum b/go.sum index cd8b08c0167..76806ccf452 100644 --- a/go.sum +++ b/go.sum @@ -74,6 +74,8 @@ github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pY github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/celestiaorg/go-cnc v0.2.0 h1:QBcWz1v6341r+5Urbr/eaCm9S8D2wwEwielKcwBc1Z4= github.com/celestiaorg/go-cnc v0.2.0/go.mod h1:CZBVUhQnJnAVcfQnnEAqREF+PNWr97m/BhJ5fp1K44Q= +github.com/celestiaorg/go-header v0.0.0-20230103073037-63e9cfae040e h1:hSL6X6wbDjL8W8aiJvjR33cKAKMVCb7MN3zPgWpxFvg= +github.com/celestiaorg/go-header v0.0.0-20230103073037-63e9cfae040e/go.mod h1:DHwX+lgMeSjyLrjMD8qHFs4W6eWCD5RRfCQw9WYIalI= github.com/celestiaorg/tendermint v0.34.22-0.20221013213714-8be9b54c8c21 h1:M1fprJ+U7Z3SZzDBeiuJ/vx21QgguOu+Ld9ALVDyLuY= github.com/celestiaorg/tendermint v0.34.22-0.20221013213714-8be9b54c8c21/go.mod h1:zoyyiiihvTW8DnOr63YLxhYn/WK/QmE74CeIpS++hBE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -282,6 +284,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -343,6 +347,8 @@ github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0= +github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= @@ -468,6 +474,8 @@ github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= +github.com/multiformats/go-multihash v0.2.2-0.20221030163302-608669da49b6 h1:qLF997Rz0X1WvdcZ2r5CUkLZ2rvdiXwG1JRSrJZEAuE= +github.com/multiformats/go-multihash v0.2.2-0.20221030163302-608669da49b6/go.mod h1:kaHxr8TfO1cxIR/tYxgZ7e59HraJq8arEQQR8E/YNvI= github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= diff --git a/rpc/client/client.go b/rpc/client/client.go index c51c127c72d..31e4df20e27 100644 --- a/rpc/client/client.go +++ b/rpc/client/client.go @@ -435,10 +435,7 @@ func (c *Client) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, // BlockByHash returns BlockID and block itself for given hash. func (c *Client) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) { - var h [32]byte - copy(h[:], hash) - - block, err := c.node.Store.LoadBlockByHash(h) + block, err := c.node.Store.LoadBlockByHash(hash) if err != nil { return nil, err } @@ -449,7 +446,7 @@ func (c *Client) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBl } return &ctypes.ResultBlock{ BlockID: types.BlockID{ - Hash: h[:], + Hash: hash, PartSetHeader: types.PartSetHeader{ Total: 0, Hash: nil, @@ -703,7 +700,7 @@ func (c *Client) Status(ctx context.Context) (*ctypes.ResultStatus, error) { return nil, fmt.Errorf("failed to find earliest block: %w", err) } - validators, err := c.node.Store.LoadValidators(latest.Header.Height) + validators, err := c.node.Store.LoadValidators(uint64(latest.Header.Height())) if err != nil { return nil, fmt.Errorf("failed to fetch the validator info at latest block: %w", err) } @@ -737,12 +734,12 @@ func (c *Client) Status(ctx context.Context) (*ctypes.ResultStatus, error) { SyncInfo: ctypes.SyncInfo{ LatestBlockHash: latest.Header.DataHash[:], LatestAppHash: latest.Header.AppHash[:], - LatestBlockHeight: int64(latest.Header.Height), - LatestBlockTime: time.Unix(0, int64(latest.Header.Time)), + LatestBlockHeight: latest.Header.Height(), + LatestBlockTime: latest.Header.Time(), EarliestBlockHash: initial.Header.DataHash[:], EarliestAppHash: initial.Header.AppHash[:], - EarliestBlockHeight: int64(initial.Header.Height), - EarliestBlockTime: time.Unix(0, int64(initial.Header.Time)), + EarliestBlockHeight: initial.Header.Height(), + EarliestBlockTime: initial.Header.Time(), CatchingUp: true, // the client is always syncing in the background to the latest height }, ValidatorInfo: ctypes.ValidatorInfo{ diff --git a/rpc/client/client_test.go b/rpc/client/client_test.go index 5667e4d417a..0a9550f4147 100644 --- a/rpc/client/client_test.go +++ b/rpc/client/client_test.go @@ -248,7 +248,7 @@ func TestGetBlock(t *testing.T) { block := getRandomBlock(1, 10) err = rpc.node.Store.SaveBlock(block, &types.Commit{}) - rpc.node.Store.SetHeight(block.Header.Height) + rpc.node.Store.SetHeight(uint64(block.Header.Height())) require.NoError(err) blockResp, err := rpc.Block(context.Background(), nil) @@ -274,17 +274,17 @@ func TestGetCommit(t *testing.T) { require.NoError(err) for _, b := range blocks { - err = rpc.node.Store.SaveBlock(b, &types.Commit{Height: b.Header.Height}) - rpc.node.Store.SetHeight(b.Header.Height) + err = rpc.node.Store.SaveBlock(b, &types.Commit{Height: uint64(b.Header.Height())}) + rpc.node.Store.SetHeight(uint64(b.Header.Height())) require.NoError(err) } t.Run("Fetch all commits", func(t *testing.T) { for _, b := range blocks { - h := int64(b.Header.Height) + h := b.Header.Height() commit, err := rpc.Commit(context.Background(), &h) require.NoError(err) require.NotNil(commit) - assert.Equal(b.Header.Height, uint64(commit.Height)) + assert.Equal(b.Header.Height(), commit.Height) } }) @@ -292,7 +292,7 @@ func TestGetCommit(t *testing.T) { commit, err := rpc.Commit(context.Background(), nil) require.NoError(err) require.NotNil(commit) - assert.Equal(blocks[3].Header.Height, uint64(commit.Height)) + assert.Equal(blocks[3].Header.Height(), commit.Height) }) err = rpc.node.Stop() @@ -378,7 +378,7 @@ func TestGetBlockByHash(t *testing.T) { abciBlock, err := abciconv.ToABCIBlock(block) require.NoError(err) - height := int64(block.Header.Height) + height := block.Header.Height() retrievedBlock, err := rpc.Block(context.Background(), &height) require.NoError(err) require.NotNil(retrievedBlock) @@ -567,7 +567,7 @@ func TestBlockchainInfo(t *testing.T) { Height: uint64(h), HeaderHash: block.Header.Hash(), }) - rpc.node.Store.SetHeight(block.Header.Height) + rpc.node.Store.SetHeight(uint64(block.Header.Height())) require.NoError(err) } @@ -721,7 +721,9 @@ func getRandomBlock(height uint64, nTxs int) *types.Block { func getRandomBlockWithProposer(height uint64, nTxs int, proposerAddr []byte) *types.Block { block := &types.Block{ Header: types.Header{ - Height: height, + BaseHeader: types.BaseHeader{ + Height: height, + }, Version: types.Version{Block: types.InitStateVersion.Consensus.Block}, ProposerAddress: proposerAddr, }, @@ -970,13 +972,13 @@ func TestStatus(t *testing.T) { assert.NotNil(rpc) earliestBlock := getRandomBlockWithProposer(1, 1, validators[0].Address.Bytes()) - err = rpc.node.Store.SaveBlock(earliestBlock, &types.Commit{Height: earliestBlock.Header.Height}) - rpc.node.Store.SetHeight(earliestBlock.Header.Height) + err = rpc.node.Store.SaveBlock(earliestBlock, &types.Commit{Height: uint64(earliestBlock.Header.Height())}) + rpc.node.Store.SetHeight(uint64(earliestBlock.Header.Height())) require.NoError(err) latestBlock := getRandomBlockWithProposer(2, 1, validators[1].Address.Bytes()) - err = rpc.node.Store.SaveBlock(latestBlock, &types.Commit{Height: latestBlock.Header.Height}) - rpc.node.Store.SetHeight(latestBlock.Header.Height) + err = rpc.node.Store.SaveBlock(latestBlock, &types.Commit{Height: uint64(latestBlock.Header.Height())}) + rpc.node.Store.SetHeight(uint64(latestBlock.Header.Height())) require.NoError(err) err = node.Start() diff --git a/state/executor.go b/state/executor.go index 30d9fb32fbc..846125c251b 100644 --- a/state/executor.go +++ b/state/executor.go @@ -9,12 +9,15 @@ import ( abci "github.com/tendermint/tendermint/abci/types" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmstate "github.com/tendermint/tendermint/proto/tendermint/state" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/proxy" tmtypes "github.com/tendermint/tendermint/types" "go.uber.org/multierr" + "github.com/celestiaorg/go-header" + abciconv "github.com/celestiaorg/rollmint/conv/abci" "github.com/celestiaorg/rollmint/log" "github.com/celestiaorg/rollmint/mempool" @@ -86,7 +89,7 @@ func (e *BlockExecutor) InitChain(genesis *tmtypes.GenesisDoc) (*abci.ResponseIn } // CreateBlock reaps transactions from mempool and builds a block. -func (e *BlockExecutor) CreateBlock(height uint64, lastCommit *types.Commit, lastHeaderHash [32]byte, state types.State) *types.Block { +func (e *BlockExecutor) CreateBlock(height uint64, lastCommit *types.Commit, lastHeaderHash header.Hash, state types.State) *types.Block { maxBytes := state.ConsensusParams.Block.MaxBytes maxGas := state.ConsensusParams.Block.MaxGas @@ -98,11 +101,13 @@ func (e *BlockExecutor) CreateBlock(height uint64, lastCommit *types.Commit, las Block: state.Version.Consensus.Block, App: state.Version.Consensus.App, }, - ChainID: e.chainID, - NamespaceID: e.namespaceID, - Height: height, - Time: uint64(time.Now().Unix()), // TODO(tzdybal): how to get TAI64? - LastHeaderHash: lastHeaderHash, + BaseHeader: types.BaseHeader{ + ChainID: e.chainID, + Height: height, + Time: uint64(time.Now().Unix()), // TODO(tzdybal): how to get TAI64? + }, + NamespaceID: e.namespaceID, + //LastHeaderHash: lastHeaderHash, //LastCommitHash: lastCommitHash, DataHash: [32]byte{}, ConsensusHash: [32]byte{}, @@ -118,6 +123,7 @@ func (e *BlockExecutor) CreateBlock(height uint64, lastCommit *types.Commit, las LastCommit: *lastCommit, } copy(block.Header.LastCommitHash[:], e.getLastCommitHash(lastCommit, &block.Header)) + copy(block.Header.LastHeaderHash[:], lastHeaderHash) copy(block.Header.AggregatorsHash[:], state.Validators.Hash()) return block @@ -202,22 +208,21 @@ func (e *BlockExecutor) updateState(state types.State, block *types.Block, abciR return state, nil } // Change results from this height but only applies to the next next height. - lastHeightValSetChanged = int64(block.Header.Height + 1 + 1) + lastHeightValSetChanged = block.Header.Height() + 1 + 1 } // TODO(tzdybal): right now, it's for backward compatibility, may need to change this nValSet.IncrementProposerPriority(1) } - hash := block.Header.Hash() s := types.State{ Version: state.Version, ChainID: state.ChainID, InitialHeight: state.InitialHeight, - LastBlockHeight: int64(block.Header.Height), - LastBlockTime: time.Unix(int64(block.Header.Time), 0), + LastBlockHeight: block.Header.Height(), + LastBlockTime: block.Header.Time(), LastBlockID: tmtypes.BlockID{ - Hash: hash[:], + Hash: tmbytes.HexBytes(block.Header.Hash()), // for now, we don't care about part set headers }, NextValidators: nValSet, @@ -249,7 +254,7 @@ func (e *BlockExecutor) commit(ctx context.Context, state types.State, block *ty maxBytes := state.ConsensusParams.Block.MaxBytes maxGas := state.ConsensusParams.Block.MaxGas - err = e.mempool.Update(int64(block.Header.Height), fromRollmintTxs(block.Data.Txs), deliverTxs, mempool.PreCheckMaxBytes(maxBytes), mempool.PostCheckMaxGas(maxGas)) + err = e.mempool.Update(int64(block.Header.Height()), fromRollmintTxs(block.Data.Txs), deliverTxs, mempool.PreCheckMaxBytes(maxBytes), mempool.PostCheckMaxGas(maxGas)) if err != nil { return nil, 0, err } @@ -266,10 +271,10 @@ func (e *BlockExecutor) validate(state types.State, block *types.Block) error { block.Header.Version.Block != state.Version.Consensus.Block { return errors.New("block version mismatch") } - if state.LastBlockHeight <= 0 && block.Header.Height != uint64(state.InitialHeight) { + if state.LastBlockHeight <= 0 && block.Header.Height() != state.InitialHeight { return errors.New("initial block height mismatch") } - if state.LastBlockHeight > 0 && block.Header.Height != uint64(state.LastBlockHeight)+1 { + if state.LastBlockHeight > 0 && block.Header.Height() != state.LastBlockHeight+1 { return errors.New("block height mismatch") } if !bytes.Equal(block.Header.AppHash[:], state.AppHash[:]) { @@ -381,7 +386,7 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return nil, err } } - endBlockRequest := abci.RequestEndBlock{Height: int64(block.Header.Height)} + endBlockRequest := abci.RequestEndBlock{Height: block.Header.Height()} abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(endBlockRequest) if err != nil { return nil, err @@ -438,7 +443,7 @@ func (e *BlockExecutor) getLastCommitHash(lastCommit *types.Commit, header *type lastABCICommit := abciconv.ToABCICommit(lastCommit) if len(lastCommit.Signatures) == 1 { lastABCICommit.Signatures[0].ValidatorAddress = e.proposerAddress - lastABCICommit.Signatures[0].Timestamp = time.UnixMilli(int64(header.Time)) + lastABCICommit.Signatures[0].Timestamp = header.Time() } return lastABCICommit.Hash() } @@ -468,13 +473,13 @@ func (e *BlockExecutor) publishEvents(resp *tmstate.ABCIResponses, block *types. for _, ev := range abciBlock.Evidence.Evidence { err = multierr.Append(err, e.eventBus.PublishEventNewEvidence(tmtypes.EventDataNewEvidence{ Evidence: ev, - Height: int64(block.Header.Height), + Height: block.Header.Height(), })) } for i, dtx := range resp.DeliverTxs { err = multierr.Append(err, e.eventBus.PublishEventTx(tmtypes.EventDataTx{ TxResult: abci.TxResult{ - Height: int64(block.Header.Height), + Height: block.Header.Height(), Index: uint32(i), Tx: abciBlock.Data.Txs[i], Result: *dtx, diff --git a/state/executor_test.go b/state/executor_test.go index 5c3392a7a61..cbcd68b9552 100644 --- a/state/executor_test.go +++ b/state/executor_test.go @@ -47,17 +47,17 @@ func doTestCreateBlock(t *testing.T, fraudProofsEnabled bool) { state.Validators = tmtypes.NewValidatorSet(nil) // empty block - block := executor.CreateBlock(1, &types.Commit{}, [32]byte{}, state) + block := executor.CreateBlock(1, &types.Commit{}, []byte{}, state) require.NotNil(block) assert.Empty(block.Data.Txs) - assert.Equal(uint64(1), block.Header.Height) + assert.Equal(int64(1), block.Header.Height()) // one small Tx err = mpool.CheckTx([]byte{1, 2, 3, 4}, func(r *abci.Response) {}, mempool.TxInfo{}) require.NoError(err) - block = executor.CreateBlock(2, &types.Commit{}, [32]byte{}, state) + block = executor.CreateBlock(2, &types.Commit{}, []byte{}, state) require.NotNil(block) - assert.Equal(uint64(2), block.Header.Height) + assert.Equal(int64(2), block.Header.Height()) assert.Len(block.Data.Txs, 1) // now there are 3 Txs, and only two can fit into single block @@ -65,7 +65,7 @@ func doTestCreateBlock(t *testing.T, fraudProofsEnabled bool) { require.NoError(err) err = mpool.CheckTx(make([]byte, 100), func(r *abci.Response) {}, mempool.TxInfo{}) require.NoError(err) - block = executor.CreateBlock(3, &types.Commit{}, [32]byte{}, state) + block = executor.CreateBlock(3, &types.Commit{}, []byte{}, state) require.NotNil(block) assert.Len(block.Data.Txs, 2) } @@ -136,9 +136,9 @@ func doTestApplyBlock(t *testing.T, fraudProofsEnabled bool) { _ = mpool.CheckTx([]byte{1, 2, 3, 4}, func(r *abci.Response) {}, mempool.TxInfo{}) require.NoError(err) - block := executor.CreateBlock(1, &types.Commit{}, [32]byte{}, state) + block := executor.CreateBlock(1, &types.Commit{}, []byte{}, state) require.NotNil(block) - assert.Equal(uint64(1), block.Header.Height) + assert.Equal(int64(1), block.Header.Height()) assert.Len(block.Data.Txs, 1) newState, resp, err := executor.ApplyBlock(context.Background(), state, block) @@ -154,9 +154,9 @@ func doTestApplyBlock(t *testing.T, fraudProofsEnabled bool) { require.NoError(mpool.CheckTx([]byte{5, 6, 7, 8, 9}, func(r *abci.Response) {}, mempool.TxInfo{})) require.NoError(mpool.CheckTx([]byte{1, 2, 3, 4, 5}, func(r *abci.Response) {}, mempool.TxInfo{})) require.NoError(mpool.CheckTx(make([]byte, 90), func(r *abci.Response) {}, mempool.TxInfo{})) - block = executor.CreateBlock(2, &types.Commit{}, [32]byte{}, newState) + block = executor.CreateBlock(2, &types.Commit{}, []byte{}, newState) require.NotNil(block) - assert.Equal(uint64(2), block.Header.Height) + assert.Equal(int64(2), block.Header.Height()) assert.Len(block.Data.Txs, 3) newState, resp, err = executor.ApplyBlock(context.Background(), newState, block) diff --git a/store/store.go b/store/store.go index ae0210fa611..f31bbb1a381 100644 --- a/store/store.go +++ b/store/store.go @@ -13,6 +13,8 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "go.uber.org/multierr" + "github.com/celestiaorg/go-header" + "github.com/celestiaorg/rollmint/types" pb "github.com/celestiaorg/rollmint/types/pb/rollmint" ) @@ -78,7 +80,7 @@ func (s *DefaultStore) SaveBlock(block *types.Block, commit *types.Commit) error err = multierr.Append(err, bb.Put(s.ctx, ds.NewKey(getBlockKey(hash)), blockBlob)) err = multierr.Append(err, bb.Put(s.ctx, ds.NewKey(getCommitKey(hash)), commitBlob)) - err = multierr.Append(err, bb.Put(s.ctx, ds.NewKey(getIndexKey(block.Header.Height)), hash[:])) + err = multierr.Append(err, bb.Put(s.ctx, ds.NewKey(getIndexKey(uint64(block.Header.Height()))), hash[:])) if err != nil { bb.Discard(s.ctx) @@ -105,7 +107,7 @@ func (s *DefaultStore) LoadBlock(height uint64) (*types.Block, error) { } // LoadBlockByHash returns block with given block header hash, or error if it's not found in Store. -func (s *DefaultStore) LoadBlockByHash(hash [32]byte) (*types.Block, error) { +func (s *DefaultStore) LoadBlockByHash(hash header.Hash) (*types.Block, error) { blockData, err := s.db.Get(s.ctx, ds.NewKey(getBlockKey(hash))) if err != nil { return nil, fmt.Errorf("failed to load block data: %w", err) @@ -152,7 +154,7 @@ func (s *DefaultStore) LoadCommit(height uint64) (*types.Commit, error) { } // LoadCommitByHash returns commit for a block with given block header hash, or error if it's not found in Store. -func (s *DefaultStore) LoadCommitByHash(hash [32]byte) (*types.Commit, error) { +func (s *DefaultStore) LoadCommitByHash(hash header.Hash) (*types.Commit, error) { commitData, err := s.db.Get(s.ctx, ds.NewKey(getCommitKey(hash))) if err != nil { return nil, fmt.Errorf("failed to retrieve commit from hash %v: %w", hash, err) @@ -227,25 +229,23 @@ func (s *DefaultStore) LoadValidators(height uint64) (*tmtypes.ValidatorSet, err } // loadHashFromIndex returns the hash of a block given its height -func (s *DefaultStore) loadHashFromIndex(height uint64) ([32]byte, error) { +func (s *DefaultStore) loadHashFromIndex(height uint64) (header.Hash, error) { blob, err := s.db.Get(s.ctx, ds.NewKey(getIndexKey(height))) - var hash [32]byte if err != nil { - return hash, fmt.Errorf("failed to load block hash for height %v: %w", height, err) + return nil, fmt.Errorf("failed to load block hash for height %v: %w", height, err) } - if len(blob) != len(hash) { - return hash, errors.New("invalid hash length") + if len(blob) != 32 { + return nil, errors.New("invalid hash length") } - copy(hash[:], blob) - return hash, nil + return blob, nil } -func getBlockKey(hash [32]byte) string { +func getBlockKey(hash header.Hash) string { return GenerateKey([]interface{}{blockPrefix, hex.EncodeToString(hash[:])}) } -func getCommitKey(hash [32]byte) string { +func getCommitKey(hash header.Hash) string { return GenerateKey([]interface{}{commitPrefix, hex.EncodeToString(hash[:])}) } diff --git a/store/store_test.go b/store/store_test.go index 9dee2c8cc95..be2218569a3 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -51,7 +51,7 @@ func TestStoreHeight(t *testing.T) { for _, block := range c.blocks { err := bstore.SaveBlock(block, &types.Commit{}) - bstore.SetHeight(block.Header.Height) + bstore.SetHeight(uint64(block.Header.Height())) assert.NoError(err) } @@ -102,7 +102,7 @@ func TestStoreLoad(t *testing.T) { lastCommit := &types.Commit{} for _, block := range c.blocks { - commit := &types.Commit{Height: block.Header.Height, HeaderHash: block.Header.Hash()} + commit := &types.Commit{Height: uint64(block.Header.Height()), HeaderHash: block.Header.Hash()} block.LastCommit = *lastCommit err := bstore.SaveBlock(block, commit) require.NoError(err) @@ -110,18 +110,18 @@ func TestStoreLoad(t *testing.T) { } for _, expected := range c.blocks { - block, err := bstore.LoadBlock(expected.Header.Height) + block, err := bstore.LoadBlock(uint64(expected.Header.Height())) assert.NoError(err) assert.NotNil(block) assert.Equal(expected, block) - assert.Equal(expected.Header.Height-1, block.LastCommit.Height) + assert.Equal(expected.Header.Height()-1, int64(block.LastCommit.Height)) assert.Equal(expected.LastCommit.Height, block.LastCommit.Height) assert.Equal(expected.LastCommit.HeaderHash, block.LastCommit.HeaderHash) - commit, err := bstore.LoadCommit(expected.Header.Height) + commit, err := bstore.LoadCommit(uint64(expected.Header.Height())) assert.NoError(err) assert.NotNil(commit) - assert.Equal(expected.Header.Height, commit.Height) + assert.Equal(uint64(expected.Header.Height()), commit.Height) headerHash := expected.Header.Hash() assert.Equal(headerHash, commit.HeaderHash) } @@ -202,7 +202,9 @@ func TestBlockResponses(t *testing.T) { func getRandomBlock(height uint64, nTxs int) *types.Block { block := &types.Block{ Header: types.Header{ - Height: height, + BaseHeader: types.BaseHeader{ + Height: height, + }, }, Data: types.Data{ Txs: make(types.Txs, nTxs), diff --git a/store/types.go b/store/types.go index f5b512709b1..aff45657104 100644 --- a/store/types.go +++ b/store/types.go @@ -4,6 +4,8 @@ import ( tmstate "github.com/tendermint/tendermint/proto/tendermint/state" tmtypes "github.com/tendermint/tendermint/types" + "github.com/celestiaorg/go-header" + "github.com/celestiaorg/rollmint/types" ) @@ -21,7 +23,7 @@ type Store interface { // LoadBlock returns block at given height, or error if it's not found in Store. LoadBlock(height uint64) (*types.Block, error) // LoadBlockByHash returns block with given block header hash, or error if it's not found in Store. - LoadBlockByHash(hash [32]byte) (*types.Block, error) + LoadBlockByHash(hash header.Hash) (*types.Block, error) // SaveBlockResponses saves block responses (events, tx responses, validator set updates, etc) in Store. SaveBlockResponses(height uint64, responses *tmstate.ABCIResponses) error @@ -32,7 +34,7 @@ type Store interface { // LoadCommit returns commit for a block at given height, or error if it's not found in Store. LoadCommit(height uint64) (*types.Commit, error) // LoadCommitByHash returns commit for a block with given block header hash, or error if it's not found in Store. - LoadCommitByHash(hash [32]byte) (*types.Commit, error) + LoadCommitByHash(hash header.Hash) (*types.Commit, error) // UpdateState updates state saved in Store. Only one State is stored. // If there is no State in Store, state will be saved. diff --git a/types/block.go b/types/block.go index a8b6bcbadb2..e77797045bd 100644 --- a/types/block.go +++ b/types/block.go @@ -2,52 +2,12 @@ package types import ( "encoding" + + "github.com/celestiaorg/go-header" ) type NamespaceID [8]byte -// Header defines the structure of rollmint block header. -type Header struct { - // Block and App version - Version Version - // NamespaceID identifies this chain e.g. when connected to other rollups via IBC. - // TODO(ismail): figure out if we want to use namespace.ID here instead (downside is that it isn't fixed size) - // at least extract the used constants (32, 8) as package variables though. - NamespaceID NamespaceID - - Height uint64 - Time uint64 // time in tai64 format - - // prev block info - LastHeaderHash [32]byte - - // hashes of block data - LastCommitHash [32]byte // commit from aggregator(s) from the last block - DataHash [32]byte // Block.Data root aka Transactions - ConsensusHash [32]byte // consensus params for current block - AppHash [32]byte // state after applying txs from the current block - - // Root hash of all results from the txs from the previous block. - // This is ABCI specific but smart-contract chains require some way of committing - // to transaction receipts/results. - LastResultsHash [32]byte - - // Note that the address can be derived from the pubkey which can be derived - // from the signature when using secp256k. - // We keep this in case users choose another signature format where the - // pubkey can't be recovered by the signature (e.g. ed25519). - ProposerAddress []byte // original proposer of the block - - // Hash of block aggregator set, at a time of block creation - AggregatorsHash [32]byte - - // The Chain ID - ChainID string -} - -var _ encoding.BinaryMarshaler = &Header{} -var _ encoding.BinaryUnmarshaler = &Header{} - // Version captures the consensus rules for processing a block in the blockchain, // including all blockchain data structures and the rules of the application's // state transition machine. @@ -82,7 +42,7 @@ type EvidenceData struct { // Commit contains evidence of block creation. type Commit struct { Height uint64 - HeaderHash [32]byte + HeaderHash header.Hash Signatures []Signature // most of the time this is a single signature } diff --git a/types/hashing.go b/types/hashing.go index 0d53bd9bb80..c91d4354100 100644 --- a/types/hashing.go +++ b/types/hashing.go @@ -1,21 +1,21 @@ package types import ( - "time" - tmversion "github.com/tendermint/tendermint/proto/tendermint/version" tmtypes "github.com/tendermint/tendermint/types" + + "github.com/celestiaorg/go-header" ) // Hash returns ABCI-compatible hash of a header. -func (h *Header) Hash() [32]byte { +func (h *Header) Hash() header.Hash { abciHeader := tmtypes.Header{ Version: tmversion.Consensus{ Block: h.Version.Block, App: h.Version.App, }, - Height: int64(h.Height), - Time: time.Unix(int64(h.Time), 0), + Height: int64(h.Height()), + Time: h.Time(), LastBlockID: tmtypes.BlockID{ Hash: h.LastHeaderHash[:], PartSetHeader: tmtypes.PartSetHeader{ @@ -32,14 +32,14 @@ func (h *Header) Hash() [32]byte { LastResultsHash: h.LastResultsHash[:], EvidenceHash: new(tmtypes.EvidenceData).Hash(), ProposerAddress: h.ProposerAddress, - ChainID: h.ChainID, + ChainID: h.ChainID(), } - var hash [32]byte - copy(hash[:], abciHeader.Hash()) - return hash + return header.Hash(abciHeader.Hash()) } // Hash returns ABCI-compatible hash of a block. func (b *Block) Hash() [32]byte { - return b.Header.Hash() + var hash [32]byte + copy(hash[:], b.Header.Hash()) + return hash } diff --git a/types/header.go b/types/header.go new file mode 100644 index 00000000000..b01f76c5d6a --- /dev/null +++ b/types/header.go @@ -0,0 +1,193 @@ +package types + +import ( + "bytes" + "encoding" + "fmt" + "time" + + "github.com/celestiaorg/go-header" +) + +// BaseHeader contains the most basic data of a header +type BaseHeader struct { + // Height represents the block height (aka block number) of a given header + Height uint64 + // Time contains Unix time of a block + Time uint64 + // The Chain ID + ChainID string +} + +// Header defines the structure of rollmint block header. +type Header struct { + BaseHeader + // Block and App version + Version Version + // NamespaceID identifies this chain e.g. when connected to other rollups via IBC. + // TODO(ismail): figure out if we want to use namespace.ID here instead (downside is that it isn't fixed size) + // at least extract the used constants (32, 8) as package variables though. + NamespaceID NamespaceID + + // prev block info + LastHeaderHash [32]byte + + // hashes of block data + LastCommitHash [32]byte // commit from aggregator(s) from the last block + DataHash [32]byte // Block.Data root aka Transactions + ConsensusHash [32]byte // consensus params for current block + AppHash [32]byte // state after applying txs from the current block + + // Root hash of all results from the txs from the previous block. + // This is ABCI specific but smart-contract chains require some way of committing + // to transaction receipts/results. + LastResultsHash [32]byte + + // Note that the address can be derived from the pubkey which can be derived + // from the signature when using secp256k. + // We keep this in case users choose another signature format where the + // pubkey can't be recovered by the signature (e.g. ed25519). + ProposerAddress []byte // original proposer of the block + + // Hash of block aggregator set, at a time of block creation + AggregatorsHash [32]byte +} + +func (h *Header) New() header.Header { + return new(Header) +} + +func (h *Header) ChainID() string { + return h.BaseHeader.ChainID +} + +func (h *Header) Height() int64 { + return int64(h.BaseHeader.Height) +} + +func (h *Header) LastHeader() header.Hash { + return h.LastHeaderHash[:] +} + +func (h *Header) Time() time.Time { + return time.Unix(int64(h.BaseHeader.Time), 0) +} + +func (h *Header) IsRecent(blockTime time.Duration) bool { + return time.Since(h.Time()) <= blockTime +} + +func (h *Header) IsExpired() bool { + // TODO(tzdybal): TrustingPeriod will be configurable soon (https://github.com/celestiaorg/celestia-node/pull/1544) + const TrustingPeriod = 168 * time.Hour + expirationTime := h.Time().Add(TrustingPeriod) + return !expirationTime.After(time.Now()) +} + +func (h *Header) VerifyAdjacent(untrst header.Header) error { + untrstH, ok := untrst.(*Header) + if !ok { + return &header.VerifyError{ + Reason: fmt.Errorf("%T is not of type %T", untrst, h), + } + } + + if untrstH.Height() != h.Height()+1 { + return &header.VerifyError{ + Reason: fmt.Errorf("headers must be adjacent in height: trusted %d, untrusted %d", h.Height(), untrstH.Height()), + } + } + + if err := verifyNewHeaderAndVals(h, untrstH); err != nil { + return &header.VerifyError{Reason: err} + } + + // Check the validator hashes are the same + // TODO: next validator set is not available + if !bytes.Equal(untrstH.AggregatorsHash[:], h.AggregatorsHash[:]) { + return &header.VerifyError{ + Reason: fmt.Errorf("expected old header next validators (%X) to match those from new header (%X)", + h.AggregatorsHash, + untrstH.AggregatorsHash, + ), + } + } + + return nil + +} + +func (h *Header) VerifyNonAdjacent(untrst header.Header) error { + untrstH, ok := untrst.(*Header) + if !ok { + return &header.VerifyError{ + Reason: fmt.Errorf("%T is not of type %T", untrst, h), + } + } + if untrstH.Height() == h.Height()+1 { + return &header.VerifyError{ + Reason: fmt.Errorf( + "headers must be non adjacent in height: trusted %d, untrusted %d", + h.Height(), + untrstH.Height(), + ), + } + } + + if err := verifyNewHeaderAndVals(h, untrstH); err != nil { + return &header.VerifyError{Reason: err} + } + + // Ensure that untrusted commit has enough of trusted commit's power. + // err := h.ValidatorSet.VerifyCommitLightTrusting(eh.ChainID, untrst.Commit, light.DefaultTrustLevel) + // if err != nil { + // return &VerifyError{err} + // } + + return nil +} + +func (h *Header) Verify(h2 header.Header) error { + // TODO(tzdybal): deprecated + panic("implement me") +} + +func (h *Header) Validate() error { + return h.ValidateBasic() +} + +// clockDrift defines how much new header's time can drift into +// the future relative to the now time during verification. +var maxClockDrift = 10 * time.Second + +// verifyNewHeaderAndVals performs basic verification of untrusted header. +func verifyNewHeaderAndVals(trusted, untrusted *Header) error { + if err := untrusted.ValidateBasic(); err != nil { + return fmt.Errorf("untrusted.ValidateBasic failed: %w", err) + } + + if untrusted.Height() <= trusted.Height() { + return fmt.Errorf("expected new header height %d to be greater than one of old header %d", + untrusted.Height(), + trusted.Height()) + } + + if !untrusted.Time().After(trusted.Time()) { + return fmt.Errorf("expected new header time %v to be after old header time %v", + untrusted.Time(), + trusted.Time()) + } + + if !untrusted.Time().Before(time.Now().Add(maxClockDrift)) { + return fmt.Errorf("new header has a time from the future %v (now: %v; max clock drift: %v)", + untrusted.Time(), + time.Now(), + maxClockDrift) + } + + return nil +} + +var _ header.Header = &Header{} +var _ encoding.BinaryMarshaler = &Header{} +var _ encoding.BinaryUnmarshaler = &Header{} diff --git a/types/serialization.go b/types/serialization.go index 7243ec1ab67..78865c6d1ae 100644 --- a/types/serialization.go +++ b/types/serialization.go @@ -189,8 +189,8 @@ func (h *Header) ToProto() *pb.Header { App: h.Version.App, }, NamespaceId: h.NamespaceID[:], - Height: h.Height, - Time: h.Time, + Height: h.BaseHeader.Height, + Time: h.BaseHeader.Time, LastHeaderHash: h.LastHeaderHash[:], LastCommitHash: h.LastCommitHash[:], DataHash: h.DataHash[:], @@ -199,7 +199,7 @@ func (h *Header) ToProto() *pb.Header { LastResultsHash: h.LastResultsHash[:], ProposerAddress: h.ProposerAddress[:], AggregatorsHash: h.AggregatorsHash[:], - ChainId: h.ChainID, + ChainId: h.BaseHeader.ChainID, } } @@ -207,12 +207,12 @@ func (h *Header) ToProto() *pb.Header { func (h *Header) FromProto(other *pb.Header) error { h.Version.Block = other.Version.Block h.Version.App = other.Version.App - h.ChainID = other.ChainId + h.BaseHeader.ChainID = other.ChainId if !safeCopy(h.NamespaceID[:], other.NamespaceId) { return errors.New("invalid length of 'NamespaceId'") } - h.Height = other.Height - h.Time = other.Time + h.BaseHeader.Height = other.Height + h.BaseHeader.Time = other.Time if !safeCopy(h.LastHeaderHash[:], other.LastHeaderHash) { return errors.New("invalid length of 'LastHeaderHash'") } @@ -293,7 +293,7 @@ func (b *Block) FromProto(other *pb.Block) error { func (c *Commit) ToProto() *pb.Commit { return &pb.Commit{ Height: c.Height, - HeaderHash: c.HeaderHash[:], + HeaderHash: c.HeaderHash, Signatures: signaturesToByteSlices(c.Signatures), } } @@ -301,9 +301,7 @@ func (c *Commit) ToProto() *pb.Commit { // FromProto fills Commit with data from its protobuf representation. func (c *Commit) FromProto(other *pb.Commit) error { c.Height = other.Height - if !safeCopy(c.HeaderHash[:], other.HeaderHash) { - return errors.New("invalid length of HeaderHash") - } + c.HeaderHash = other.HeaderHash c.Signatures = byteSlicesToSignatures(other.Signatures) return nil diff --git a/types/serialization_test.go b/types/serialization_test.go index 2761edb4d95..f4a8d8ed8a2 100644 --- a/types/serialization_test.go +++ b/types/serialization_test.go @@ -43,9 +43,11 @@ func TestBlockSerializationRoundTrip(t *testing.T) { Block: 1, App: 2, }, - NamespaceID: NamespaceID{0, 1, 2, 3, 4, 5, 6, 7}, - Height: 3, - Time: 4567, + NamespaceID: NamespaceID{0, 1, 2, 3, 4, 5, 6, 7}, + BaseHeader: BaseHeader{ + Height: 3, + Time: 4567, + }, LastHeaderHash: h[0], LastCommitHash: h[1], DataHash: h[2], @@ -63,7 +65,7 @@ func TestBlockSerializationRoundTrip(t *testing.T) { }, LastCommit: Commit{ Height: 8, - HeaderHash: h[7], + HeaderHash: h[7][:], Signatures: []Signature{Signature([]byte{1, 1, 1}), Signature([]byte{2, 2, 2})}, }, }}, diff --git a/types/validation.go b/types/validation.go index a6498e30fed..ee8503956c2 100644 --- a/types/validation.go +++ b/types/validation.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "encoding/hex" "errors" "fmt" @@ -62,12 +63,11 @@ func (h *SignedHeader) ValidateBasic() error { return err } - if h.Commit.Height != h.Header.Height { - return fmt.Errorf("height missmatch - header height: %d, commit height: %d", h.Header.Height, h.Commit.Height) + if h.Commit.Height != uint64(h.Header.Height()) { + return fmt.Errorf("height missmatch - header height: %d, commit height: %d", h.Header.Height(), h.Commit.Height) } - if h.Commit.HeaderHash != h.Header.Hash() { - hash := h.Header.Hash() - return fmt.Errorf("hash missmatch - header hash: %s, commit hash: %s", hex.EncodeToString(hash[:]), hex.EncodeToString(h.Commit.HeaderHash[:])) + if !bytes.Equal(h.Commit.HeaderHash[:], h.Header.Hash()) { + return fmt.Errorf("hash missmatch - header hash: %s, commit hash: %s", hex.EncodeToString(h.Header.Hash()), hex.EncodeToString(h.Commit.HeaderHash[:])) } return nil }