diff --git a/app/ante/ante.go b/app/ante/ante.go new file mode 100644 index 00000000..ec7c67f4 --- /dev/null +++ b/app/ante/ante.go @@ -0,0 +1,32 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// NewAnteHandler returns an 'AnteHandler' that will run actions before a tx is sent to a module's handler. +func NewAnteHandler(ak keeper.AccountKeeper, supplyKeeper types.SupplyKeeper, sigGasConsumer ante.SignatureVerificationGasConsumer, addressFetchers ...AddressFetcher) sdk.AnteHandler { + decorators := []sdk.AnteDecorator{} + + decorators = append(decorators, ante.NewSetUpContextDecorator()) // outermost AnteDecorator. SetUpContext must be called first + + if len(addressFetchers) > 0 { + decorators = append(decorators, NewAuthenticatedMempoolDecorator(addressFetchers...)) + } + decorators = append(decorators, + ante.NewMempoolFeeDecorator(), + ante.NewValidateBasicDecorator(), + ante.NewValidateMemoDecorator(ak), + ante.NewConsumeGasForTxSizeDecorator(ak), + ante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators + ante.NewValidateSigCountDecorator(ak), + ante.NewDeductFeeDecorator(ak, supplyKeeper), + ante.NewSigGasConsumeDecorator(ak, sigGasConsumer), + ante.NewSigVerificationDecorator(ak), + ante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator + ) + return sdk.ChainAnteDecorators(decorators...) +} diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go new file mode 100644 index 00000000..f22b9233 --- /dev/null +++ b/app/ante/ante_test.go @@ -0,0 +1,202 @@ +package ante_test + +import ( + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/simapp/helpers" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/log" + tmdb "github.com/tendermint/tm-db" + + "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/bep3" + "github.com/kava-labs/kava/x/pricefeed" +) + +func TestAppAnteHandler(t *testing.T) { + testPrivKeys, testAddresses := app.GeneratePrivKeyAddressPairs(10) + unauthed := testAddresses[0:2] + unathedKeys := testPrivKeys[0:2] + deputy := testAddresses[2] + deputyKey := testPrivKeys[2] + oracles := testAddresses[3:6] + oraclesKeys := testPrivKeys[3:6] + manual := testAddresses[6:] + manualKeys := testPrivKeys[6:] + + db := tmdb.NewMemDB() + tApp := app.TestApp{ + App: *app.NewApp(log.NewNopLogger(), db, nil, + app.AppOptions{ + MempoolEnableAuth: true, + MempoolAuthAddresses: manual, + }, + ), + } + + chainID := "internal-test-chain" + tApp = tApp.InitializeFromGenesisStatesWithTimeAndChainID( + time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC), + chainID, + NewAuthGenStateWithSameCoins( + sdk.NewCoins(sdk.NewInt64Coin("ukava", 1_000_000_000)), + testAddresses, + ), + newBep3GenStateMulti(deputy), + newPricefeedGenStateMulti(oracles), + ) + + testcases := []struct { + name string + address sdk.AccAddress + privKey crypto.PrivKey + expectPass bool + }{ + { + name: "unauthorized", + address: unauthed[1], + privKey: unathedKeys[1], + expectPass: false, + }, + { + name: "oracle", + address: oracles[1], + privKey: oraclesKeys[1], + expectPass: true, + }, + { + name: "deputy", + address: deputy, + privKey: deputyKey, + expectPass: true, + }, + { + name: "manual", + address: manual[1], + privKey: manualKeys[1], + expectPass: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + stdTx := helpers.GenTx( + []sdk.Msg{ + bank.NewMsgSend( + tc.address, + testAddresses[0], + sdk.NewCoins(sdk.NewInt64Coin("ukava", 1_000_000)), + ), + }, + sdk.NewCoins(), // no fee + helpers.DefaultGenTxGas, + chainID, + []uint64{0}, + []uint64{0}, // fixed sequence numbers will cause tests to fail sig verification if the same address is used twice + tc.privKey, + ) + txBytes, err := auth.DefaultTxEncoder(tApp.Codec())(stdTx) + require.NoError(t, err) + + res := tApp.CheckTx( + abci.RequestCheckTx{ + Tx: txBytes, + Type: abci.CheckTxType_New, + }, + ) + + if tc.expectPass { + require.Zero(t, res.Code, res.Log) + } else { + require.NotZero(t, res.Code) + } + }) + } +} + +func NewAuthGenStateWithSameCoins(coins sdk.Coins, addresses []sdk.AccAddress) app.GenesisState { + coinsList := make([]sdk.Coins, len(addresses)) + for i := range addresses { + coinsList[i] = coins + } + return app.NewAuthGenState(addresses, coinsList) +} + +func newPricefeedGenStateMulti(oracles []sdk.AccAddress) app.GenesisState { + pfGenesis := pricefeed.GenesisState{ + Params: pricefeed.Params{ + Markets: []pricefeed.Market{ + {MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: oracles, Active: true}, + {MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: oracles, Active: true}, + }, + }, + } + return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)} +} + +func newBep3GenStateMulti(deputyAddress sdk.AccAddress) app.GenesisState { + bep3Genesis := bep3.GenesisState{ + Params: bep3.Params{ + AssetParams: bep3.AssetParams{ + bep3.AssetParam{ + Denom: "bnb", + CoinID: 714, + SupplyLimit: bep3.SupplyLimit{ + Limit: sdk.NewInt(350000000000000), + TimeLimited: false, + TimeBasedLimit: sdk.ZeroInt(), + TimePeriod: time.Hour, + }, + Active: true, + DeputyAddress: deputyAddress, + FixedFee: sdk.NewInt(1000), + MinSwapAmount: sdk.OneInt(), + MaxSwapAmount: sdk.NewInt(1000000000000), + MinBlockLock: bep3.DefaultMinBlockLock, + MaxBlockLock: bep3.DefaultMaxBlockLock, + }, + bep3.AssetParam{ + Denom: "inc", + CoinID: 9999, + SupplyLimit: bep3.SupplyLimit{ + Limit: sdk.NewInt(100000000000000), + TimeLimited: true, + TimeBasedLimit: sdk.NewInt(50000000000), + TimePeriod: time.Hour, + }, + Active: false, + DeputyAddress: deputyAddress, + FixedFee: sdk.NewInt(1000), + MinSwapAmount: sdk.OneInt(), + MaxSwapAmount: sdk.NewInt(100000000000), + MinBlockLock: bep3.DefaultMinBlockLock, + MaxBlockLock: bep3.DefaultMaxBlockLock, + }, + }, + }, + Supplies: bep3.AssetSupplies{ + bep3.NewAssetSupply( + sdk.NewCoin("bnb", sdk.ZeroInt()), + sdk.NewCoin("bnb", sdk.ZeroInt()), + sdk.NewCoin("bnb", sdk.ZeroInt()), + sdk.NewCoin("bnb", sdk.ZeroInt()), + time.Duration(0), + ), + bep3.NewAssetSupply( + sdk.NewCoin("inc", sdk.ZeroInt()), + sdk.NewCoin("inc", sdk.ZeroInt()), + sdk.NewCoin("inc", sdk.ZeroInt()), + sdk.NewCoin("inc", sdk.ZeroInt()), + time.Duration(0), + ), + }, + PreviousBlockTime: bep3.DefaultPreviousBlockTime, + } + return app.GenesisState{bep3.ModuleName: bep3.ModuleCdc.MustMarshalJSON(bep3Genesis)} +} diff --git a/app/ante/authorized.go b/app/ante/authorized.go new file mode 100644 index 00000000..6804d154 --- /dev/null +++ b/app/ante/authorized.go @@ -0,0 +1,63 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var _ sigVerifiableTx = (*authtypes.StdTx)(nil) // assert StdTx implements SigVerifiableTx + +// SigVerifiableTx defines a Tx interface for all signature verification decorators +type sigVerifiableTx interface { + GetSigners() []sdk.AccAddress +} + +// AddressFetcher is a type signature for functions used by the AuthenticatedMempoolDecorator to get authorized addresses. +type AddressFetcher func(sdk.Context) []sdk.AccAddress + +// AuthenticatedMempoolDecorator blocks all txs from reaching the mempool unless they're signed by one of the authorzed addresses. +// It only runs before entry to mempool (CheckTx), and not in consensus (DeliverTx) +type AuthenticatedMempoolDecorator struct { + addressFetchers []AddressFetcher +} + +func NewAuthenticatedMempoolDecorator(fetchers ...AddressFetcher) AuthenticatedMempoolDecorator { + return AuthenticatedMempoolDecorator{ + addressFetchers: fetchers, + } +} + +func (amd AuthenticatedMempoolDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + // This is only for local mempool purposes, and thus is only run on check tx. + if ctx.IsCheckTx() && !simulate { + sigTx, ok := tx.(sigVerifiableTx) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx must be sig verifiable tx") + } + if !commonAddressesExist(sigTx.GetSigners(), amd.fetchAuthorizedAddresses(ctx)) { + return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "tx contains no signers authorized for this mempool") + } + } + return next(ctx, tx, simulate) +} + +func (amd AuthenticatedMempoolDecorator) fetchAuthorizedAddresses(ctx sdk.Context) []sdk.AccAddress { + addrs := []sdk.AccAddress{} + for _, fetch := range amd.addressFetchers { + addrs = append(addrs, fetch(ctx)...) + } + return addrs +} + +// commonAddressesExist checks if there is any intersection between two lists of addresses +func commonAddressesExist(addresses1, addresses2 []sdk.AccAddress) bool { + for _, a1 := range addresses1 { + for _, a2 := range addresses2 { + if a1.Equals(a2) { + return true + } + } + } + return false +} diff --git a/app/ante/authorized_test.go b/app/ante/authorized_test.go new file mode 100644 index 00000000..d5ca9f26 --- /dev/null +++ b/app/ante/authorized_test.go @@ -0,0 +1,144 @@ +package ante + +import ( + "math/rand" + "testing" + + "github.com/cosmos/cosmos-sdk/simapp/helpers" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/kava-labs/kava/x/bep3" +) + +var ( + _ sdk.AnteHandler = (&MockAnteHandler{}).AnteHandle +) + +type MockAnteHandler struct { + WasCalled bool +} + +func (mah *MockAnteHandler) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { + mah.WasCalled = true + return ctx, nil +} + +func mockAddressFetcher(addresses ...sdk.AccAddress) AddressFetcher { + return func(sdk.Context) []sdk.AccAddress { return addresses } +} + +func TestAuthenticatedMempoolDecorator_AnteHandle_NotCheckTx(t *testing.T) { + testPrivKeys, testAddresses := generatePrivKeyAddressPairs(5) + fetcher := mockAddressFetcher(testAddresses[1:]...) + + decorator := NewAuthenticatedMempoolDecorator(fetcher) + tx := helpers.GenTx( + []sdk.Msg{ + bep3.NewMsgClaimAtomicSwap( + testAddresses[0], + []byte{}, + []byte{}, + ), + }, + sdk.NewCoins(), // no fee + helpers.DefaultGenTxGas, + "testing-chain-id", + []uint64{0}, + []uint64{0}, + testPrivKeys[0], // address is not authorized + ) + mmd := MockAnteHandler{} + ctx := sdk.Context{}.WithIsCheckTx(false) // run as it would be during block update ('DeliverTx'), not just checking entry to mempool + + _, err := decorator.AnteHandle(ctx, tx, false, mmd.AnteHandle) + + require.NoError(t, err) + require.True(t, mmd.WasCalled) +} + +func TestAuthenticatedMempoolDecorator_AnteHandle_Pass(t *testing.T) { + testPrivKeys, testAddresses := generatePrivKeyAddressPairs(5) + fetcher := mockAddressFetcher(testAddresses[1:]...) + + decorator := NewAuthenticatedMempoolDecorator(fetcher) + + tx := helpers.GenTx( + []sdk.Msg{ + bank.NewMsgSend( + testAddresses[0], + testAddresses[1], + sdk.NewCoins(sdk.NewInt64Coin("ukava", 100_000_000)), + ), + bep3.NewMsgClaimAtomicSwap( + testAddresses[2], + nil, + nil, + ), + }, + sdk.NewCoins(), // no fee + helpers.DefaultGenTxGas, + "testing-chain-id", + []uint64{0, 123}, + []uint64{0, 123}, + testPrivKeys[0], // not in list of authorized addresses + testPrivKeys[2], + ) + mmd := MockAnteHandler{} + ctx := sdk.Context{}.WithIsCheckTx(true) + + _, err := decorator.AnteHandle(ctx, tx, false, mmd.AnteHandle) + + require.NoError(t, err) + require.True(t, mmd.WasCalled) +} + +func TestAuthenticatedMempoolDecorator_AnteHandle_Reject(t *testing.T) { + testPrivKeys, testAddresses := generatePrivKeyAddressPairs(5) + fetcher := mockAddressFetcher(testAddresses[1:]...) + + decorator := NewAuthenticatedMempoolDecorator(fetcher) + + tx := helpers.GenTx( + []sdk.Msg{ + bank.NewMsgSend( + testAddresses[0], + testAddresses[1], + sdk.NewCoins(sdk.NewInt64Coin("ukava", 100_000_000)), + ), + }, + sdk.NewCoins(), // no fee + helpers.DefaultGenTxGas, + "testing-chain-id", + []uint64{0}, + []uint64{0}, + testPrivKeys[0], // not in list of authorized addresses + ) + mmd := MockAnteHandler{} + ctx := sdk.Context{}.WithIsCheckTx(true) + + _, err := decorator.AnteHandle(ctx, tx, false, mmd.AnteHandle) + + require.Error(t, err) + require.False(t, mmd.WasCalled) +} + +// generatePrivKeyAddressPairsFromRand generates (deterministically) a total of n private keys and addresses. +func generatePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccAddress) { + r := rand.New(rand.NewSource(12345)) // make the generation deterministic + keys = make([]crypto.PrivKey, n) + addrs = make([]sdk.AccAddress, n) + for i := 0; i < n; i++ { + secret := make([]byte, 32) + _, err := r.Read(secret) + if err != nil { + panic("Could not read randomness") + } + keys[i] = secp256k1.GenPrivKeySecp256k1(secret) + addrs[i] = sdk.AccAddress(keys[i].PubKey().Address()) + } + return +} diff --git a/app/app.go b/app/app.go index 7c0a1c33..d91ddeb1 100644 --- a/app/app.go +++ b/app/app.go @@ -33,6 +33,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/upgrade" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + "github.com/kava-labs/kava/app/ante" "github.com/kava-labs/kava/x/auction" "github.com/kava-labs/kava/x/bep3" "github.com/kava-labs/kava/x/cdp" @@ -116,6 +117,16 @@ var ( // Verify app interface at compile time var _ simapp.App = (*App)(nil) +// AppOptions bundles several configuration params for an App. +// The zero value can be used as a sensible default. +type AppOptions struct { + SkipLoadLatest bool + SkipUpgradeHeights map[int64]bool + InvariantCheckPeriod uint + MempoolEnableAuth bool + MempoolAuthAddresses []sdk.AccAddress +} + // App represents an extended ABCI application type App struct { *bam.BaseApp @@ -159,9 +170,7 @@ type App struct { } // NewApp returns a reference to an initialized App. -func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, - skipUpgradeHeights map[int64]bool, invCheckPeriod uint, - baseAppOptions ...func(*bam.BaseApp)) *App { +func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptions, baseAppOptions ...func(*bam.BaseApp)) *App { cdc := MakeCodec() @@ -182,7 +191,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, var app = &App{ BaseApp: bApp, cdc: cdc, - invCheckPeriod: invCheckPeriod, + invCheckPeriod: appOpts.InvariantCheckPeriod, keys: keys, tkeys: tkeys, } @@ -257,12 +266,12 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, ) app.crisisKeeper = crisis.NewKeeper( crisisSubspace, - invCheckPeriod, + app.invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName, ) app.upgradeKeeper = upgrade.NewKeeper( - skipUpgradeHeights, + appOpts.SkipUpgradeHeights, keys[upgrade.StoreKey], app.cdc, ) @@ -473,11 +482,18 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, // initialize the app app.SetInitChainer(app.InitChainer) app.SetBeginBlocker(app.BeginBlocker) - app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.supplyKeeper, auth.DefaultSigVerificationGasConsumer)) + var antehandler sdk.AnteHandler + if appOpts.MempoolEnableAuth { + var getAuthorizedAddresses ante.AddressFetcher = func(sdk.Context) []sdk.AccAddress { return appOpts.MempoolAuthAddresses } + antehandler = ante.NewAnteHandler(app.accountKeeper, app.supplyKeeper, auth.DefaultSigVerificationGasConsumer, app.bep3Keeper.GetAuthorizedAddresses, app.pricefeedKeeper.GetAuthorizedAddresses, getAuthorizedAddresses) + } else { + antehandler = ante.NewAnteHandler(app.accountKeeper, app.supplyKeeper, auth.DefaultSigVerificationGasConsumer) + } + app.SetAnteHandler(antehandler) app.SetEndBlocker(app.EndBlocker) // load store - if loadLatest { + if !appOpts.SkipLoadLatest { err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) if err != nil { tmos.Exit(err.Error()) diff --git a/app/app_test.go b/app/app_test.go index 82a6a939..1adcfd7c 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -16,11 +16,11 @@ import ( func TestExport(t *testing.T) { db := db.NewMemDB() - app := NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) + app := NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, AppOptions{}) setGenesis(app) // Making a new app object with the db, so that initchain hasn't been called - newApp := NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) + newApp := NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, AppOptions{}) _, _, err := newApp.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } @@ -28,7 +28,7 @@ func TestExport(t *testing.T) { // ensure that black listed addresses are properly set in bank keeper func TestBlackListedAddrs(t *testing.T) { db := db.NewMemDB() - app := NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) + app := NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, AppOptions{}) for acc := range mAccPerms { require.Equal(t, !allowedReceivingModAcc[acc], app.bankKeeper.BlacklistedAddr(app.supplyKeeper.GetModuleAddress(acc))) diff --git a/app/sim_test.go b/app/sim_test.go index e49136db..662e4758 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -85,7 +85,7 @@ func TestFullAppSimulation(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewApp(logger, db, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + app := NewApp(logger, db, nil, AppOptions{InvariantCheckPeriod: simapp.FlagPeriodValue}, fauxMerkleModeOpt) require.Equal(t, appName, app.Name()) // run randomized simulation @@ -117,7 +117,7 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewApp(logger, db, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + app := NewApp(logger, db, nil, AppOptions{InvariantCheckPeriod: simapp.FlagPeriodValue}, fauxMerkleModeOpt) require.Equal(t, appName, app.Name()) // Run randomized simulation @@ -151,7 +151,7 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + newApp := NewApp(log.NewNopLogger(), newDB, nil, AppOptions{InvariantCheckPeriod: simapp.FlagPeriodValue}, fauxMerkleModeOpt) require.Equal(t, appName, newApp.Name()) var genesisState GenesisState @@ -212,7 +212,7 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewApp(logger, db, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + app := NewApp(logger, db, nil, AppOptions{InvariantCheckPeriod: simapp.FlagPeriodValue}, fauxMerkleModeOpt) require.Equal(t, appName, app.Name()) // Run randomized simulation @@ -251,7 +251,7 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + newApp := NewApp(log.NewNopLogger(), newDB, nil, AppOptions{InvariantCheckPeriod: simapp.FlagPeriodValue}, fauxMerkleModeOpt) require.Equal(t, appName, newApp.Name()) newApp.InitChain(abci.RequestInitChain{ @@ -284,7 +284,7 @@ func TestAppStateDeterminism(t *testing.T) { for j := 0; j < numTimesToRunPerSeed; j++ { logger := log.NewNopLogger() db := dbm.NewMemDB() - app := NewApp(logger, db, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, interBlockCacheOpt()) + app := NewApp(logger, db, nil, AppOptions{InvariantCheckPeriod: simapp.FlagPeriodValue}, interBlockCacheOpt()) fmt.Printf( "running non-determinism simulation; seed %d: attempt: %d/%d\n", diff --git a/app/test_common.go b/app/test_common.go index da3e2b6e..86d2e6f7 100644 --- a/app/test_common.go +++ b/app/test_common.go @@ -11,7 +11,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/libs/log" @@ -62,7 +61,7 @@ func NewTestApp() TestApp { SetBip44CoinType(config) db := tmdb.NewMemDB() - app := NewApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0) + app := NewApp(log.NewNopLogger(), db, nil, AppOptions{}) return TestApp{App: *app} } @@ -188,8 +187,7 @@ func NewAuthGenState(addresses []sdk.AccAddress, coins []sdk.Coins) GenesisState return GenesisState{auth.ModuleName: auth.ModuleCdc.MustMarshalJSON(authGenesis)} } -// GeneratePrivKeyAddressPairsFromRand generates (deterministically) a total of n private keys and addresses. -// TODO only generate secp256 keys? +// GeneratePrivKeyAddressPairsFromRand generates (deterministically) a total of n secp256k1 private keys and addresses. func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccAddress) { r := rand.New(rand.NewSource(12345)) // make the generation deterministic keys = make([]crypto.PrivKey, n) @@ -200,11 +198,7 @@ func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccA if err != nil { panic("Could not read randomness") } - if r.Int63()%2 == 0 { - keys[i] = secp256k1.GenPrivKeySecp256k1(secret) - } else { - keys[i] = ed25519.GenPrivKeyFromSecret(secret) - } + keys[i] = secp256k1.GenPrivKeySecp256k1(secret) addrs[i] = sdk.AccAddress(keys[i].PubKey().Address()) } return diff --git a/app/utils.go b/app/utils.go deleted file mode 100644 index 9c5742b4..00000000 --- a/app/utils.go +++ /dev/null @@ -1,17 +0,0 @@ -package app - -import ( - "fmt" - "io/ioutil" -) - -// ExportStateToJSON util function to export the app state to JSON -func ExportStateToJSON(app *App, path string) error { - fmt.Println("exporting app state...") - appState, _, err := app.ExportAppStateAndValidators(false, nil) - if err != nil { - return err - } - - return ioutil.WriteFile(path, []byte(appState), 0644) -} diff --git a/cmd/kvd/main.go b/cmd/kvd/main.go index 2b1337d9..878e8424 100644 --- a/cmd/kvd/main.go +++ b/cmd/kvd/main.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "io" "github.com/spf13/cobra" @@ -28,7 +29,11 @@ import ( ) // kvd custom flags -const flagInvCheckPeriod = "inv-check-period" +const ( + flagInvCheckPeriod = "inv-check-period" + flagMempoolEnableAuth = "mempool.enable-authentication" + flagMempoolAuthAddresses = "mempool.authorized-addresses" +) var invCheckPeriod uint @@ -72,7 +77,23 @@ func main() { executor := cli.PrepareBaseCmd(rootCmd, "KA", app.DefaultNodeHome) rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod, 0, "Assert registered invariants every N blocks") - err := executor.Execute() + startCmd, _, err := rootCmd.Find([]string{"start"}) + if err != nil { + panic(fmt.Sprintf("could not find 'start' command on root command: %s", err)) + } + startCmd.Flags().Bool(flagMempoolEnableAuth, false, "Configure the mempool to only accept transactions from authorized addresses") + err = viper.BindPFlag(flagMempoolEnableAuth, startCmd.Flags().Lookup(flagMempoolEnableAuth)) + if err != nil { + panic(fmt.Sprintf("failed to bind flag: %s", err)) + } + startCmd.Flags().StringSlice(flagMempoolAuthAddresses, []string{}, "Additional addresses to accept transactions from when the mempool is running in authorized mode (comma separated kava addresses)") + err = viper.BindPFlag(flagMempoolAuthAddresses, startCmd.Flags().Lookup(flagMempoolAuthAddresses)) + if err != nil { + panic(fmt.Sprintf("failed to bind flag: %s", err)) + } + + // run main command + err = executor.Execute() if err != nil { panic(err) } @@ -95,8 +116,21 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application panic(err) } + mempoolEnableAuth := viper.GetBool(flagMempoolEnableAuth) + mempoolAuthAddresses, err := accAddressesFromBech32(viper.GetStringSlice(flagMempoolAuthAddresses)...) + if err != nil { + panic(fmt.Sprintf("could not get authorized address from config: %v", err)) + } + return app.NewApp( - logger, db, traceStore, true, skipUpgradeHeights, invCheckPeriod, + logger, db, traceStore, + app.AppOptions{ + SkipLoadLatest: false, + SkipUpgradeHeights: skipUpgradeHeights, + InvariantCheckPeriod: invCheckPeriod, + MempoolEnableAuth: mempoolEnableAuth, + MempoolAuthAddresses: mempoolAuthAddresses, + }, baseapp.SetPruning(pruningOpts), baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), baseapp.SetHaltHeight(viper.GetUint64(server.FlagHaltHeight)), @@ -110,13 +144,33 @@ func exportAppStateAndTMValidators( ) (json.RawMessage, []tmtypes.GenesisValidator, error) { if height != -1 { - tempApp := app.NewApp(logger, db, traceStore, false, map[int64]bool{}, uint(1)) + opts := app.AppOptions{ + SkipLoadLatest: true, + InvariantCheckPeriod: uint(1), + } + tempApp := app.NewApp(logger, db, traceStore, opts) err := tempApp.LoadHeight(height) if err != nil { return nil, nil, err } return tempApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } - tempApp := app.NewApp(logger, db, traceStore, true, map[int64]bool{}, uint(1)) + opts := app.AppOptions{ + SkipLoadLatest: false, + InvariantCheckPeriod: uint(1), + } + tempApp := app.NewApp(logger, db, traceStore, opts) return tempApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } + +func accAddressesFromBech32(addresses ...string) ([]sdk.AccAddress, error) { + var decodedAddresses []sdk.AccAddress + for _, s := range addresses { + a, err := sdk.AccAddressFromBech32(s) + if err != nil { + return nil, err + } + decodedAddresses = append(decodedAddresses, a) + } + return decodedAddresses, nil +} diff --git a/go.mod b/go.mod index 3dab775b..68b1183f 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/kava-labs/kava go 1.13 require ( - github.com/cosmos/cosmos-sdk v0.39.1 + github.com/cosmos/cosmos-sdk v0.39.2 github.com/gorilla/mux v1.7.4 github.com/spf13/cobra v1.0.0 github.com/spf13/viper v1.6.3 github.com/stretchr/testify v1.6.1 - github.com/tendermint/tendermint v0.33.7 + github.com/tendermint/tendermint v0.33.9 github.com/tendermint/tm-db v0.5.1 gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index 1b56abec..2ef26e57 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/99designs/keyring v1.1.3 h1:mEV3iyZWjkxQ7R8ia8GcG97vCX5zQQ7n4o8R2BylwQY= -github.com/99designs/keyring v1.1.3/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904= +github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= +github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= @@ -68,8 +68,8 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosmos/cosmos-sdk v0.39.1 h1:vhjf9PZh9ph8btAj9aBpHoVITgVVjNBpM3x5Gl/Vwac= -github.com/cosmos/cosmos-sdk v0.39.1/go.mod h1:ry2ROl5n+f2/QXpKJo3rdWNJwll00z7KhIVcxNcl16M= +github.com/cosmos/cosmos-sdk v0.39.2 h1:nLfCJMkUuFt7ansi/YvCxwwxLFrgHCA3cYP4sJKYQdk= +github.com/cosmos/cosmos-sdk v0.39.2/go.mod h1:VNUluciWBFj2vkhpMcp8rYZL/kCw0FtNc7SseUjE1KM= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= @@ -88,8 +88,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a h1:mq+R6XEM6lJX5VlLyZIrUSP8tSuJp82xTK89hvBwJbU= -github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= +github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -150,9 +150,13 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -160,6 +164,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -285,6 +291,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -426,11 +434,11 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= github.com/tendermint/go-amino v0.15.1 h1:D2uk35eT4iTsvJd9jWIetzthE5C0/k2QmMFkCN+4JgQ= github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/iavl v0.14.0 h1:Jkff+IFrXxRWtH9Jn/ga/2cxNnzMTv58xEKgCJsKUBg= -github.com/tendermint/iavl v0.14.0/go.mod h1:QmfViflFiXzxKLQE4tAUuWQHq+RSuQFxablW5oJZ6sE= +github.com/tendermint/iavl v0.14.1 h1:jz7YOvGiPwmcqqVMcSMjxCu4WXtQYGhKdKrWTTJ5EKs= +github.com/tendermint/iavl v0.14.1/go.mod h1:QmfViflFiXzxKLQE4tAUuWQHq+RSuQFxablW5oJZ6sE= github.com/tendermint/tendermint v0.33.5/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= -github.com/tendermint/tendermint v0.33.7 h1:b5CQD8ggDtl4u0EbXzabi0MaOw9NrcXker6ijEkAE74= -github.com/tendermint/tendermint v0.33.7/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= +github.com/tendermint/tendermint v0.33.9 h1:rRKIfu5qAXX5f9bwX1oUXSZz/ALFJjDuivhkbGUQxiU= +github.com/tendermint/tendermint v0.33.9/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY= github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -470,6 +478,8 @@ golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+ golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -499,6 +509,8 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -529,9 +541,14 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgm golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb h1:HS9IzC4UFbpMBLQUDSQcU+ViVT1vdFCQVjdPVpTlZrs= +golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -565,6 +582,8 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -575,18 +594,21 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.28.1 h1:C1QC6KzgSiLyBabDi87BbjaGreoRgGUF5nOyvfrAZ1k= google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/x/bep3/keeper/params.go b/x/bep3/keeper/params.go index 6215740b..bd3a678a 100644 --- a/x/bep3/keeper/params.go +++ b/x/bep3/keeper/params.go @@ -139,3 +139,28 @@ func (k Keeper) GetSupplyLimit(ctx sdk.Context, denom string) (types.SupplyLimit } return asset.SupplyLimit, nil } + +// ------------------------------------------ +// Cross Asset Getters +// ------------------------------------------ + +// GetAuthorizedAddresses returns a list of addresses that have special authorization within this module, eg all the deputies. +func (k Keeper) GetAuthorizedAddresses(ctx sdk.Context) []sdk.AccAddress { + assetParams, found := k.GetAssets(ctx) + if !found { + // no assets params is a valid genesis state + return nil + } + addresses := []sdk.AccAddress{} + uniqueAddresses := map[string]bool{} + + for _, ap := range assetParams { + a := ap.DeputyAddress + // de-dup addresses + if _, found := uniqueAddresses[a.String()]; !found { + addresses = append(addresses, a) + } + uniqueAddresses[a.String()] = true + } + return addresses +} diff --git a/x/bep3/keeper/params_test.go b/x/bep3/keeper/params_test.go index e962510f..5ffdfb46 100644 --- a/x/bep3/keeper/params_test.go +++ b/x/bep3/keeper/params_test.go @@ -116,6 +116,14 @@ func (suite *ParamsTestSuite) TestGetAssetByCoinID() { suite.Equal(asset, res) } +func (suite *ParamsTestSuite) TestGetAuthorizedAddresses() { + deputyAddresses := suite.keeper.GetAuthorizedAddresses(suite.ctx) + // the test params use the same deputy address for two assets + expectedAddresses := []sdk.AccAddress{suite.addrs[0]} + + suite.Require().ElementsMatch(expectedAddresses, deputyAddresses) +} + func (suite *AssetTestSuite) TestValidateLiveAsset() { type args struct { coin sdk.Coin diff --git a/x/pricefeed/keeper/params.go b/x/pricefeed/keeper/params.go index b4f4658f..e2ca8636 100644 --- a/x/pricefeed/keeper/params.go +++ b/x/pricefeed/keeper/params.go @@ -59,3 +59,20 @@ func (k Keeper) GetMarket(ctx sdk.Context, marketID string) (types.Market, bool) } return types.Market{}, false } + +// GetAuthorizedAddresses returns a list of addresses that have special authorization within this module, eg the oracles of all markets. +func (k Keeper) GetAuthorizedAddresses(ctx sdk.Context) []sdk.AccAddress { + oracles := []sdk.AccAddress{} + uniqueOracles := map[string]bool{} + + for _, m := range k.GetMarkets(ctx) { + for _, o := range m.Oracles { + // de-dup list of oracles + if _, found := uniqueOracles[o.String()]; !found { + oracles = append(oracles, o) + } + uniqueOracles[o.String()] = true + } + } + return oracles +} diff --git a/x/pricefeed/keeper/params_test.go b/x/pricefeed/keeper/params_test.go index 3c293c74..38e75a16 100644 --- a/x/pricefeed/keeper/params_test.go +++ b/x/pricefeed/keeper/params_test.go @@ -11,6 +11,7 @@ import ( tmtime "github.com/tendermint/tendermint/types/time" "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/pricefeed" "github.com/kava-labs/kava/x/pricefeed/keeper" ) @@ -38,15 +39,32 @@ func (suite *KeeperTestSuite) SetupTest() { func (suite *KeeperTestSuite) TestGetSetOracles() { params := suite.keeper.GetParams(suite.ctx) suite.Equal([]sdk.AccAddress(nil), params.Markets[0].Oracles) + params.Markets[0].Oracles = suite.addrs suite.NotPanics(func() { suite.keeper.SetParams(suite.ctx, params) }) params = suite.keeper.GetParams(suite.ctx) suite.Equal(suite.addrs, params.Markets[0].Oracles) + addr, err := suite.keeper.GetOracle(suite.ctx, params.Markets[0].MarketID, suite.addrs[0]) suite.NoError(err) suite.Equal(suite.addrs[0], addr) } +func (suite *KeeperTestSuite) TestGetAuthorizedAddresses() { + _, oracles := app.GeneratePrivKeyAddressPairs(5) + params := pricefeed.Params{ + Markets: []pricefeed.Market{ + {MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: oracles[:3], Active: true}, + {MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: oracles[2:], Active: true}, + {MarketID: "xrp:usd:30", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: nil, Active: true}, + }, + } + suite.keeper.SetParams(suite.ctx, params) + + actualOracles := suite.keeper.GetAuthorizedAddresses(suite.ctx) + + suite.Require().ElementsMatch(oracles, actualOracles) +} func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) }