Add height in exported genesis (#7089)

* 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>
This commit is contained in:
Amaury Martiny
2020-09-03 10:11:46 +00:00
committed by GitHub
co-authored by Alexander Bezobchuk Jack Zampolin Cory
parent 9e85e81e0e
commit 3b9b58c931
22 changed files with 422 additions and 109 deletions
+20 -15
View File
@@ -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"
@@ -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
@@ -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)
}
@@ -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
}
+133 -36
View File
@@ -1,4 +1,4 @@
package server
package server_test
import (
"bytes"
@@ -10,15 +10,20 @@ import (
"path"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmjson "github.com/tendermint/tendermint/libs/json"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/types/errors"
@@ -29,40 +34,7 @@ func TestExportCmd_ConsensusParams(t *testing.T) {
tempDir, clean := testutil.NewTestCaseDir(t)
defer clean()
err := createConfigFolder(tempDir)
if err != nil {
t.Fatalf("error creating config folder: %s", err)
}
db := dbm.NewMemDB()
app := simapp.NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, tempDir, 0, simapp.MakeEncodingConfig())
serverCtx := NewDefaultContext()
serverCtx.Config.RootDir = tempDir
clientCtx := client.Context{}.WithJSONMarshaler(app.AppCodec())
genDoc := newDefaultGenesisDoc()
err = saveGenesisFile(genDoc, serverCtx.Config.GenesisFile())
app.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: simapp.DefaultConsensusParams,
AppStateBytes: genDoc.AppState,
},
)
app.Commit()
cmd := ExportCmd(
func(logger log.Logger, db dbm.DB, writer io.Writer, i int64, b bool, strings []string) (json.RawMessage, []tmtypes.GenesisValidator, *abci.ConsensusParams, error) {
return app.ExportAppStateAndValidators(true, []string{})
}, tempDir)
ctx := context.Background()
ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx)
ctx = context.WithValue(ctx, ServerContextKey, serverCtx)
_, ctx, genDoc, cmd := setupApp(t, tempDir)
output := &bytes.Buffer{}
cmd.SetOut(output)
@@ -70,7 +42,7 @@ func TestExportCmd_ConsensusParams(t *testing.T) {
require.NoError(t, cmd.ExecuteContext(ctx))
var exportedGenDoc tmtypes.GenesisDoc
err = json.Unmarshal(output.Bytes(), &exportedGenDoc)
err := tmjson.Unmarshal(output.Bytes(), &exportedGenDoc)
if err != nil {
t.Fatalf("error unmarshaling exported genesis doc: %s", err)
}
@@ -85,6 +57,131 @@ func TestExportCmd_ConsensusParams(t *testing.T) {
require.Equal(t, simapp.DefaultConsensusParams.Validator.PubKeyTypes, exportedGenDoc.ConsensusParams.Validator.PubKeyTypes)
}
func TestExportCmd_HomeDir(t *testing.T) {
tempDir, clean := testutil.NewTestCaseDir(t)
defer clean()
_, ctx, _, cmd := setupApp(t, tempDir)
cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", flags.FlagHome, "foobar")})
err := cmd.ExecuteContext(ctx)
require.EqualError(t, err, "stat foobar/config/genesis.json: no such file or directory")
}
func TestExportCmd_Height(t *testing.T) {
testCases := []struct {
name string
flags []string
fastForward int64
expHeight int64
}{
{
"should export correct height",
[]string{},
5, 6,
},
{
"should export correct height with --height",
[]string{
fmt.Sprintf("--%s=%d", server.FlagHeight, 3),
},
5, 4,
},
{
"should export height 0 with --for-zero-height",
[]string{
fmt.Sprintf("--%s=%s", server.FlagForZeroHeight, "true"),
},
2, 0,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tempDir, clean := testutil.NewTestCaseDir(t)
defer clean()
app, ctx, _, cmd := setupApp(t, tempDir)
// Fast forward to block `tc.fastForward`.
for i := int64(2); i <= tc.fastForward; i++ {
app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: i}})
app.Commit()
}
output := &bytes.Buffer{}
cmd.SetOut(output)
args := append(tc.flags, fmt.Sprintf("--%s=%s", flags.FlagHome, tempDir))
cmd.SetArgs(args)
require.NoError(t, cmd.ExecuteContext(ctx))
var exportedGenDoc tmtypes.GenesisDoc
err := tmjson.Unmarshal(output.Bytes(), &exportedGenDoc)
if err != nil {
t.Fatalf("error unmarshaling exported genesis doc: %s", err)
}
require.Equal(t, tc.expHeight, exportedGenDoc.InitialHeight)
})
}
}
func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *tmtypes.GenesisDoc, *cobra.Command) {
err := createConfigFolder(tempDir)
if err != nil {
t.Fatalf("error creating config folder: %s", err)
}
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
db := dbm.NewMemDB()
encCfg := simapp.MakeEncodingConfig()
app := simapp.NewSimApp(logger, db, nil, true, map[int64]bool{}, tempDir, 0, encCfg)
serverCtx := server.NewDefaultContext()
serverCtx.Config.RootDir = tempDir
clientCtx := client.Context{}.WithJSONMarshaler(app.AppCodec())
genDoc := newDefaultGenesisDoc()
err = saveGenesisFile(genDoc, serverCtx.Config.GenesisFile())
require.NoError(t, err)
app.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: simapp.DefaultConsensusParams,
AppStateBytes: genDoc.AppState,
},
)
app.Commit()
cmd := server.ExportCmd(
func(_ log.Logger, _ dbm.DB, _ io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string) (types.ExportedApp, error) {
encCfg := simapp.MakeEncodingConfig()
var simApp *simapp.SimApp
if height != -1 {
simApp = simapp.NewSimApp(logger, db, nil, false, map[int64]bool{}, "", 0, encCfg)
if err := simApp.LoadHeight(height); err != nil {
return types.ExportedApp{}, err
}
} else {
simApp = simapp.NewSimApp(logger, db, nil, true, map[int64]bool{}, "", 0, encCfg)
}
return simApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs)
}, tempDir)
ctx := context.Background()
ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx)
ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx)
return app, ctx, genDoc, cmd
}
func createConfigFolder(dir string) error {
return os.Mkdir(path.Join(dir, "config"), 0700)
}
+4
View File
@@ -99,6 +99,10 @@ func (ms multiStore) SetInterBlockCache(_ sdk.MultiStorePersistentCache) {
panic("not implemented")
}
func (ms multiStore) SetInitialVersion(version int64) error {
panic("not implemented")
}
var _ sdk.KVStore = kvStore{}
type kvStore struct {
+14 -1
View File
@@ -42,7 +42,20 @@ type (
// application using various configurations.
AppCreator func(log.Logger, dbm.DB, io.Writer, AppOptions) Application
// ExportedApp represents an exported app state, along with
// validators, consensus params and latest app height.
ExportedApp struct {
// AppState is the application state as JSON.
AppState json.RawMessage
// Validators is the exported validator set.
Validators []tmtypes.GenesisValidator
// Height is the app's latest block height.
Height int64
// ConsensusParams are the exported consensus params for ABCI.
ConsensusParams *abci.ConsensusParams
}
// AppExporter is a function that dumps all app state to
// JSON-serializable structure and returns the current validator set.
AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool, []string) (json.RawMessage, []tmtypes.GenesisValidator, *abci.ConsensusParams, error)
AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool, []string) (ExportedApp, error)
)