Skip to content

Commit

Permalink
Add height in exported genesis (#7089)
Browse files Browse the repository at this point in the history
* Add height in exported genesis

* +1

* Add test

* Refactor ctx in setupApp

* Use amino in export

* Use tmjson

* Add custom initialVersion (set to 0 for now)

* Add comment

* Add mount in initChainer

* app.LastBlockheight

* InitializeAndSeal in InitChain?

* Revert create store with initial version

* Update to latest iavl

* Check height in test

* Make it work

* Add more tests

* Rename interface

* Use struct isntead of 6 args

* Fix lint

* Remove stray fmt

* Revert go mod/sum

* Install iavl rc3

* Update comments

* Add fee in network

* Typo

* Fix logic in commit

* Fix tests

* Only set initial version on > 1

* Genesis block num = 1

* Fresh chain, genesis block = 0

* Add comments

* Revert Mutable/ImmutableTree

* Allow for zero height

* Fix restart

* Add comments

* Add comments, fix test

* Fix remaining one test

* Add panic test

* Update comment

* Add test for --height

* No cast

* Add check that genesis file exists

* Remove duplicate imports

* Fail early

Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Jack Zampolin <jack.zampolin@gmail.com>
Co-authored-by: Cory <cjlevinson@gmail.com>
  • Loading branch information
4 people committed Sep 3, 2020
1 parent 9e85e81 commit 3b9b58c
Show file tree
Hide file tree
Showing 22 changed files with 412 additions and 99 deletions.
19 changes: 16 additions & 3 deletions baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,21 @@ import (
// InitChain implements the ABCI interface. It runs the initialization logic
// directly on the CommitMultiStore.
func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
// On a new chain, we consider the init chain block height as 0, even though
// req.InitialHeight is 1 by default.
initHeader := tmproto.Header{ChainID: req.ChainId, Time: req.Time}

// If req.InitialHeight is > 1, then we set the initial version in the
// stores.
if req.InitialHeight > 1 {
app.initialHeight = req.InitialHeight
initHeader = tmproto.Header{ChainID: req.ChainId, Height: req.InitialHeight, Time: req.Time}
err := app.cms.SetInitialVersion(req.InitialHeight)
if err != nil {
panic(err)
}
}

// initialize the deliver state and check state with a correct header
app.setDeliverState(initHeader)
app.setCheckState(initHeader)
Expand Down Expand Up @@ -60,13 +73,13 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
sort.Sort(abci.ValidatorUpdates(res.Validators))

for i := range res.Validators {
if proto.Equal(&res.Validators[i], &req.Validators[i]) {
if !proto.Equal(&res.Validators[i], &req.Validators[i]) {
panic(fmt.Errorf("genesisValidators[%d] != req.Validators[%d] ", i, i))
}
}
}

// NOTE: We don't commit, but BeginBlock for block 1 starts from this
// NOTE: We don't commit, but BeginBlock for block `initial_height` starts from this
// deliverState.
return res
}
Expand Down Expand Up @@ -97,7 +110,7 @@ func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery {
return abci.ResponseQuery{}
}

// FilterPeerByIDfilters peers by node ID.
// FilterPeerByID filters peers by node ID.
func (app *BaseApp) FilterPeerByID(info string) abci.ResponseQuery {
if app.idPeerFilter != nil {
return app.idPeerFilter(info)
Expand Down
29 changes: 20 additions & 9 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ type BaseApp struct { // nolint: maligned
// transaction. This is mainly used for DoS and spam prevention.
minGasPrices sdk.DecCoins

// initialHeight is the initial height at which we start the baseapp
initialHeight int64

// flag for sealing options and parameters to a BaseApp
sealed bool

Expand Down Expand Up @@ -206,12 +209,6 @@ func (app *BaseApp) MountMemoryStores(keys map[string]*sdk.MemoryStoreKey) {
}
}

// MountStoreWithDB mounts a store to the provided key in the BaseApp
// multistore, using a specified DB.
func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) {
app.cms.MountStoreWithDB(key, typ, db)
}

// MountStore mounts a store to the provided key in the BaseApp multistore,
// using the default DB.
func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
Expand Down Expand Up @@ -422,9 +419,23 @@ func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error {
return fmt.Errorf("invalid height: %d", req.Header.Height)
}

prevHeight := app.LastBlockHeight()
if req.Header.Height != prevHeight+1 {
return fmt.Errorf("invalid height: %d; expected: %d", req.Header.Height, prevHeight+1)
// expectedHeight holds the expected height to validate.
var expectedHeight int64
if app.LastBlockHeight() == 0 && app.initialHeight > 1 {
// In this case, we're validating the first block of the chain (no
// previous commit). The height we're expecting is the initial height.
expectedHeight = app.initialHeight
} else {
// This case can means two things:
// - either there was already a previous commit in the store, in which
// case we increment the version from there,
// - or there was no previous commit, and initial version was not set,
// in which case we start at version 1.
expectedHeight = app.LastBlockHeight() + 1
}

if req.Header.Height != expectedHeight {
return fmt.Errorf("invalid height: %d; expected: %d", req.Header.Height, expectedHeight)
}

return nil
Expand Down
47 changes: 46 additions & 1 deletion baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,52 @@ func TestInitChainer(t *testing.T) {
require.Equal(t, value, res.Value)
}

func TestInitChain_WithInitialHeight(t *testing.T) {
name := t.Name()
db := dbm.NewMemDB()
logger := defaultLogger()
app := NewBaseApp(name, logger, db, nil)

app.InitChain(
abci.RequestInitChain{
InitialHeight: 3,
},
)
app.Commit()

require.Equal(t, int64(3), app.LastBlockHeight())
}

func TestBeginBlock_WithInitialHeight(t *testing.T) {
name := t.Name()
db := dbm.NewMemDB()
logger := defaultLogger()
app := NewBaseApp(name, logger, db, nil)

app.InitChain(
abci.RequestInitChain{
InitialHeight: 3,
},
)

require.PanicsWithError(t, "invalid height: 4; expected: 3", func() {
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: 4,
},
})
})

app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: 3,
},
})
app.Commit()

require.Equal(t, int64(3), app.LastBlockHeight())
}

// Simple tx with a list of Msgs.
type txTest struct {
Msgs []sdk.Msg
Expand Down Expand Up @@ -1292,7 +1338,6 @@ func TestCustomRunTxPanicHandler(t *testing.T) {
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
panic(sdkerrors.Wrap(anteErr, "anteHandler"))
return
})
}
routerOpt := func(bapp *BaseApp) {
Expand Down
35 changes: 20 additions & 15 deletions server/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package server
// DONTCOVER

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"

"github.com/spf13/cobra"
tmjson "github.com/tendermint/tendermint/libs/json"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtypes "github.com/tendermint/tendermint/types"

Expand All @@ -35,6 +35,10 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com
homeDir, _ := cmd.Flags().GetString(flags.FlagHome)
config.SetRoot(homeDir)

if _, err := os.Stat(config.GenesisFile()); os.IsNotExist(err) {
return err
}

db, err := openDB(config.RootDir)
if err != nil {
return err
Expand Down Expand Up @@ -64,7 +68,7 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com
forZeroHeight, _ := cmd.Flags().GetBool(FlagForZeroHeight)
jailAllowedAddrs, _ := cmd.Flags().GetStringSlice(FlagJailAllowedAddrs)

appState, validators, cp, err := appExporter(serverCtx.Logger, db, traceWriter, height, forZeroHeight, jailAllowedAddrs)
exported, err := appExporter(serverCtx.Logger, db, traceWriter, height, forZeroHeight, jailAllowedAddrs)
if err != nil {
return fmt.Errorf("error exporting state: %v", err)
}
Expand All @@ -74,29 +78,30 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com
return err
}

doc.AppState = appState
doc.Validators = validators
doc.AppState = exported.AppState
doc.Validators = exported.Validators
doc.InitialHeight = exported.Height
doc.ConsensusParams = &tmproto.ConsensusParams{
Block: tmproto.BlockParams{
MaxBytes: cp.Block.MaxBytes,
MaxGas: cp.Block.MaxGas,
MaxBytes: exported.ConsensusParams.Block.MaxBytes,
MaxGas: exported.ConsensusParams.Block.MaxGas,
TimeIotaMs: doc.ConsensusParams.Block.TimeIotaMs,
},
Evidence: tmproto.EvidenceParams{
MaxAgeNumBlocks: cp.Evidence.MaxAgeNumBlocks,
MaxAgeDuration: cp.Evidence.MaxAgeDuration,
MaxNum: cp.Evidence.MaxNum,
ProofTrialPeriod: cp.Evidence.ProofTrialPeriod,
MaxAgeNumBlocks: exported.ConsensusParams.Evidence.MaxAgeNumBlocks,
MaxAgeDuration: exported.ConsensusParams.Evidence.MaxAgeDuration,
MaxNum: exported.ConsensusParams.Evidence.MaxNum,
ProofTrialPeriod: exported.ConsensusParams.Evidence.ProofTrialPeriod,
},
Validator: tmproto.ValidatorParams{
PubKeyTypes: cp.Validator.PubKeyTypes,
PubKeyTypes: exported.ConsensusParams.Validator.PubKeyTypes,
},
}

// NOTE: for now we're just using standard JSON marshaling for the root GenesisDoc.
// These types are in Tendermint, don't support proto and as far as we know, don't need it.
// All of the protobuf/amino state is inside AppState
encoded, err := json.MarshalIndent(doc, "", " ")
// NOTE: Tendermint uses a custom JSON decoder for GenesisDoc
// (except for stuff inside AppState). Inside AppState, we're free
// to encode as protobuf or amino.
encoded, err := tmjson.Marshal(doc)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 3b9b58c

Please sign in to comment.