diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 964f80acb817..46196a52c25d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,4 +4,4 @@ # most precedence. # Primary repo maintainers -* @liamsi @evan-forbes +* @celestiaorg/celestia-core diff --git a/baseapp/abci.go b/baseapp/abci.go index bb9fb0566ab5..1b2ff1dac976 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -14,6 +14,7 @@ import ( "github.com/gogo/protobuf/proto" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmversion "github.com/tendermint/tendermint/proto/tendermint/version" "google.golang.org/grpc/codes" grpcstatus "google.golang.org/grpc/status" @@ -38,12 +39,15 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC // 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.ConsensusParams != nil && req.ConsensusParams.Version != nil { + initHeader.Version = tmversion.Consensus{App: req.ConsensusParams.Version.AppVersion} + } // 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} + initHeader.Height = req.InitialHeight err := app.cms.SetInitialVersion(req.InitialHeight) if err != nil { panic(err) @@ -131,8 +135,8 @@ func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { if err != nil { panic(err) } - // get and set the app version - _ = app.AppVersion(ctx) + // initialise the app version by checking if it is already in state + app.InitAppVersion(ctx) } return abci.ResponseInfo{ Data: app.name, @@ -339,6 +343,36 @@ func (app *BaseApp) Commit() abci.ResponseCommit { // The write to the DeliverTx state writes all state transitions to the root // MultiStore (app.cms) so when Commit() is called is persists those values. app.deliverState.ms.Write() + + // Check if there has been an app version change. If so and there is a migrator + // set, begin to run migrations. This needs to be done before the commit so + // that the migrations are part of the app hash + if header.Version.App < app.appVersion && + app.migrator.storeMigrator != nil && + app.migrator.moduleMigrator != nil { + + // first update the stores themselves by adding and removing them as necessary + storeMigrations, err := app.migrator.storeMigrator(header.Version.App, app.appVersion) + if err != nil { + panic(fmt.Sprintf("failed to get store migrations: %v", err)) + } + app.MountKVStores(storeMigrations.Added) + err = app.cms.LoadLatestVersionAndUpgrade(storeMigrations.ToStoreUpgrades()) + if err != nil { + panic(fmt.Sprintf("failed to upgrade stores: %v", err)) + } + + // create a new cached branch of the store to apply migrations to + app.setDeliverState(header) + err = app.migrator.moduleMigrator(app.deliverState.ctx, header.Version.App, app.appVersion) + if err != nil { + panic(fmt.Sprintf("failed to migrate modules: %v", err)) + } + + // write the new state to the branch + app.deliverState.ms.Write() + } + commitID := app.cms.Commit() res := abci.ResponseCommit{ diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index c9877ddb012a..78a9efb173a1 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -39,6 +39,21 @@ type ( // an older version of the software. In particular, if a module changed the substore key name // (or removed a substore) between two versions of the software. StoreLoader func(ms sdk.CommitMultiStore) error + + // MigrateModuleFn gets called when there is a change in the app version. It is + // triggered in the Commit stage after all state transitions have occurred. + MigrateModuleFn func(ctx sdk.Context, fromVersion, toVersion uint64) error + + // MigrateStoreFn gets called when there is a change in the app version. It is + // triggered in the Commit stage after all state transitions have occurred. + MigrateStoreFn func(fromVersion, toVersion uint64) (StoreMigrations, error) + + // StoreMigrations is a type that contains the added and removed stores for a + // migration. + StoreMigrations struct { + Added map[string]*storetypes.KVStoreKey + Deleted map[string]*storetypes.KVStoreKey + } ) // BaseApp reflects the ABCI application implementation. @@ -57,6 +72,7 @@ type BaseApp struct { // nolint: maligned snapshotData abciData moduleRouter + migrator // volatile states: // @@ -135,6 +151,11 @@ type appStore struct { fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed. } +type migrator struct { + moduleMigrator MigrateModuleFn + storeMigrator MigrateStoreFn +} + type moduleRouter struct { router sdk.Router // handle any kind of message queryRouter sdk.QueryRouter // router for redirecting query calls @@ -202,15 +223,17 @@ func (app *BaseApp) Name() string { return app.name } -// AppVersion returns the application's protocol version. -func (app *BaseApp) AppVersion(ctx sdk.Context) uint64 { +func (app *BaseApp) InitAppVersion(ctx sdk.Context) { if app.appVersion == 0 && app.paramStore.Has(ctx, ParamStoreKeyVersionParams) { var vp tmproto.VersionParams - app.paramStore.Get(ctx, ParamStoreKeyVersionParams, &vp) // set the app version app.appVersion = vp.AppVersion } +} + +// AppVersion returns the application's protocol version. +func (app *BaseApp) AppVersion() uint64 { return app.appVersion } @@ -491,6 +514,8 @@ func (app *BaseApp) GetConsensusParams(ctx sdk.Context) *abci.ConsensusParams { app.paramStore.Get(ctx, ParamStoreKeyVersionParams, &vp) cp.Version = &vp + } else if app.appVersion != 0 { + cp.Version = &tmproto.VersionParams{AppVersion: app.appVersion} } return cp @@ -516,12 +541,19 @@ func (app *BaseApp) StoreConsensusParams(ctx sdk.Context, cp *abci.ConsensusPara app.paramStore.Set(ctx, ParamStoreKeyBlockParams, cp.Block) app.paramStore.Set(ctx, ParamStoreKeyEvidenceParams, cp.Evidence) app.paramStore.Set(ctx, ParamStoreKeyValidatorParams, cp.Validator) - // NOTE: we only persist the app version from v2 onwards - if cp.Version != nil && cp.Version.AppVersion >= 2 { + if app.paramStore.Has(ctx, ParamStoreKeyVersionParams) { app.paramStore.Set(ctx, ParamStoreKeyVersionParams, cp.Version) } } +// SetInitialAppVersionInConsensusParams sets the initial app version +// in the consensus params if it has not yet been set. +func (app *BaseApp) SetInitialAppVersionInConsensusParams(ctx sdk.Context, version uint64) { + if !app.paramStore.Has(ctx, ParamStoreKeyVersionParams) { + app.paramStore.Set(ctx, ParamStoreKeyVersionParams, &tmproto.VersionParams{AppVersion: version}) + } +} + // getMaximumBlockGas gets the maximum gas from the consensus params. It panics // if maximum block gas is less than negative one and returns zero if negative // one. diff --git a/baseapp/options.go b/baseapp/options.go index ba29aa977f36..0e2b8caf1ce9 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -3,6 +3,7 @@ package baseapp import ( "fmt" "io" + "sort" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" @@ -12,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -113,9 +115,7 @@ func (app *BaseApp) SetVersion(v string) { // SetAppVersion sets the application's protocol version func (app *BaseApp) SetAppVersion(ctx sdk.Context, version uint64) { - // TODO: could make this less hacky in the future since the SDK - // shouldn't have to know about the app versioning scheme - if version >= 2 { + if app.paramStore.Has(ctx, ParamStoreKeyVersionParams) { vp := &tmproto.VersionParams{AppVersion: version} app.paramStore.Set(ctx, ParamStoreKeyVersionParams, vp) } @@ -162,6 +162,20 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) { app.endBlocker = endBlocker } +func (app *BaseApp) SetMigrateModuleFn(migrator MigrateModuleFn) { + if app.sealed { + panic("cannot set migrate module fn: baseapp is sealed") + } + app.migrator.moduleMigrator = migrator +} + +func (app *BaseApp) SetMigrateStoreFn(migrator MigrateStoreFn) { + if app.sealed { + panic("cannot set migrate store fn: baseapp is sealed") + } + app.migrator.storeMigrator = migrator +} + func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) { if app.sealed { panic("SetAnteHandler() on sealed BaseApp") @@ -265,3 +279,26 @@ func (app *BaseApp) SetQueryMultiStore(ms sdk.MultiStore) { } app.qms = ms } + +// ToStoreUpgrades converts the StoreMigrations to StoreUpgrades. +func (sm StoreMigrations) ToStoreUpgrades() *storetypes.StoreUpgrades { + added := make([]string, len(sm.Added)) + deleted := make([]string, len(sm.Deleted)) + i := 0 + for name := range sm.Added { + added[i] = name + i++ + } + i = 0 + for name := range sm.Deleted { + deleted[i] = name + i++ + } + // sort them to ensure deterministic order + sort.Strings(added) + sort.Strings(deleted) + return &storetypes.StoreUpgrades{ + Added: added, + Deleted: deleted, + } +} diff --git a/client/v2/go.mod b/client/v2/go.mod index d83f8b8ab04b..d3c529031e83 100644 --- a/client/v2/go.mod +++ b/client/v2/go.mod @@ -19,9 +19,9 @@ require ( github.com/google/go-cmp v0.5.6 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect - golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect - golang.org/x/text v0.3.5 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect ) diff --git a/client/v2/go.sum b/client/v2/go.sum index 8ff37c70268b..895078f4b394 100644 --- a/client/v2/go.sum +++ b/client/v2/go.sum @@ -99,8 +99,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -117,13 +118,15 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/crypto/keyring/keyring.go b/crypto/keyring/keyring.go index 33d1cf095e87..bfcd473ff50f 100644 --- a/crypto/keyring/keyring.go +++ b/crypto/keyring/keyring.go @@ -585,13 +585,21 @@ func SignWithLedger(k *Record, msg []byte) (sig []byte, pub types.PubKey, err er if err != nil { return } + ledgerPubKey := priv.PubKey() + pubKey, err := k.GetPubKey() + if err != nil { + return nil, nil, err + } + if !pubKey.Equals(ledgerPubKey) { + return nil, nil, fmt.Errorf("the public key that the user attempted to sign with does not match the public key on the ledger device. %v does not match %v", pubKey.String(), ledgerPubKey.String()) + } sig, err = priv.Sign(msg) if err != nil { return nil, nil, err } - return sig, priv.PubKey(), nil + return sig, ledgerPubKey, nil } func newOSBackendKeyringConfig(appName, dir string, buf io.Reader) keyring.Config { diff --git a/crypto/keyring/keyring_ledger_test.go b/crypto/keyring/keyring_ledger_test.go index 6845b6088212..53286438d950 100644 --- a/crypto/keyring/keyring_ledger_test.go +++ b/crypto/keyring/keyring_ledger_test.go @@ -8,9 +8,13 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/ledger" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/types" ) @@ -133,3 +137,65 @@ func TestAltKeyring_SaveLedgerKey(t *testing.T) { path := ledgerInfo.GetPath() require.Equal(t, "m/44'/118'/3'/0/1", path.String()) } + +func TestSignWithLedger(t *testing.T) { + // Create two distinct Ledger records: recordA and recordB. + // RecordA is added to the Ledger but recordB is not added. + pathA := hd.NewFundraiserParams(0, types.CoinType, 0) + privA, _, err := ledger.NewPrivKeySecp256k1(*pathA, "cosmos") + require.NoError(t, err) + recordA, err := NewLedgerRecord("ledgerA", privA.PubKey(), pathA) + require.NoError(t, err) + pubA, err := recordA.GetPubKey() + require.NoError(t, err) + + pathB := hd.NewFundraiserParams(0, types.CoinType, 1) + // privB won't be added to the Ledger because it doesn't use ledger.NewPrivKeySecp256k1 + privB := secp256k1.GenPrivKey() + recordB, err := NewLedgerRecord("ledgerB", privB.PubKey(), pathB) + require.NoError(t, err) + pubB, err := recordB.GetPubKey() + require.NoError(t, err) + + require.NotEqual(t, pubA, pubB) + type testCase struct { + name string + record *Record + msg []byte + wantSig []byte + wantPub cryptotypes.PubKey + wantErr bool + wantErrContains string + } + testCases := []testCase{ + { + name: "ordinary ledger tx", + record: recordA, + msg: []byte("msg"), + wantSig: []byte{0xfb, 0x93, 0x1b, 0xb9, 0x75, 0x25, 0xe7, 0x99, 0x64, 0xc2, 0x78, 0xf7, 0x94, 0x9a, 0x63, 0x83, 0xe2, 0x59, 0x76, 0x48, 0x1d, 0x2, 0xbc, 0xc2, 0x83, 0x21, 0x24, 0x4b, 0x95, 0x99, 0x25, 0x8b, 0x30, 0x38, 0x6, 0x61, 0x79, 0x9a, 0x9e, 0x8, 0x98, 0xfd, 0x34, 0xc6, 0x7e, 0x47, 0x4d, 0x5f, 0xe, 0xf3, 0xc3, 0xe7, 0xdd, 0xe3, 0x89, 0x80, 0xda, 0x8b, 0x48, 0x15, 0x34, 0xce, 0xdf, 0x1c}, + wantPub: pubA, + wantErr: false, + }, + { + name: "want error when the public key the user attempted to sign with doesn't match the public key on the ledger", + record: recordB, + msg: []byte("msg"), + wantSig: []byte(nil), + wantPub: nil, + wantErr: true, + wantErrContains: "the public key that the user attempted to sign with does not match the public key on the ledger device", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sig, pub, err := SignWithLedger(tc.record, tc.msg) + assert.Equal(t, tc.wantSig, sig) + assert.Equal(t, tc.wantPub, pub) + if tc.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tc.wantErrContains) + } + }) + } +} diff --git a/x/genutil/gentx.go b/x/genutil/gentx.go index f7e4250def1d..1598ccd603a5 100644 --- a/x/genutil/gentx.go +++ b/x/genutil/gentx.go @@ -107,7 +107,7 @@ func DeliverGenTxs( res := deliverTx(abci.RequestDeliverTx{Tx: bz}) if !res.IsOK() { - return nil, fmt.Errorf("failed to execute DelverTx for '%s': %s", genTx, res.Log) + return nil, fmt.Errorf("failed to execute DeliverTx for '%s': %s", genTx, res.Log) } } diff --git a/x/upgrade/keeper/keeper_test.go b/x/upgrade/keeper/keeper_test.go index b37ebcb62339..19fee1054ec9 100644 --- a/x/upgrade/keeper/keeper_test.go +++ b/x/upgrade/keeper/keeper_test.go @@ -203,7 +203,7 @@ func (s *KeeperTestSuite) TestSetUpgradedClient() { // Test that the protocol version successfully increments after an // upgrade and is successfully set on BaseApp's appVersion. func (s *KeeperTestSuite) TestIncrementProtocolVersion() { - oldProtocolVersion := s.app.BaseApp.AppVersion(s.ctx) + oldProtocolVersion := s.app.BaseApp.AppVersion() s.app.UpgradeKeeper.SetUpgradeHandler("dummy", func(_ sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) { return vm, nil }) dummyPlan := types.Plan{ Name: "dummy", @@ -211,7 +211,7 @@ func (s *KeeperTestSuite) TestIncrementProtocolVersion() { Height: 100, } s.app.UpgradeKeeper.ApplyUpgrade(s.ctx, dummyPlan) - upgradedProtocolVersion := s.app.BaseApp.AppVersion(s.ctx) + upgradedProtocolVersion := s.app.BaseApp.AppVersion() s.Require().Equal(oldProtocolVersion+1, upgradedProtocolVersion) }