From 50b098e2fc1799b570782f9cbe16967b6ed799fe Mon Sep 17 00:00:00 2001 From: Alexander Peters Date: Thu, 28 Oct 2021 17:02:05 +0200 Subject: [PATCH] Contract abstraction (#169) * Add POE setup to genesis * Move bonded denum up in genesis * Start refactoring into contract adapters * Introduce poetesting package --- x/poe/bootstrap.go | 4 +- x/poe/bootstrap_test.go | 9 +- x/poe/contract/query_integration_test.go | 209 +------------- x/poe/contract/tg4_stake.go | 89 ++++-- x/poe/contract/tg4_stake_integration_test.go | 92 ++++++ x/poe/contract/tgrade_distribution.go | 10 +- .../tgrade_distribution_integration_test.go | 5 +- x/poe/contract/tgrade_valset.go | 84 ++++-- .../tgrade_valset_integration_test.go | 128 +++++++++ x/poe/keeper/contracts.go | 27 +- x/poe/keeper/grpc_query.go | 77 +---- x/poe/keeper/grpc_query_test.go | 270 +++++++----------- x/poe/keeper/keeper.go | 12 +- x/poe/keeper/legacy_distribution_querier.go | 9 +- .../legacy_distribution_querier_test.go | 43 +-- x/poe/keeper/legacy_slashing_querier.go | 9 +- x/poe/keeper/legacy_staking_querier.go | 21 +- x/poe/keeper/legacy_staking_querier_test.go | 133 ++++----- x/poe/keeper/msg_server.go | 15 +- x/poe/keeper/msg_server_test.go | 11 + x/poe/keeper/poetesting/mock_contracts.go | 80 ++++++ x/poe/keeper/test_mocks.go | 52 ++-- x/poe/module.go | 16 +- x/poe/module_test.go | 5 +- 24 files changed, 740 insertions(+), 670 deletions(-) create mode 100644 x/poe/contract/tg4_stake_integration_test.go create mode 100644 x/poe/contract/tgrade_valset_integration_test.go create mode 100644 x/poe/keeper/poetesting/mock_contracts.go diff --git a/x/poe/bootstrap.go b/x/poe/bootstrap.go index c0f0e6f9..29ffb9cd 100644 --- a/x/poe/bootstrap.go +++ b/x/poe/bootstrap.go @@ -39,6 +39,7 @@ func ClearEmbeddedContracts() { type poeKeeper interface { keeper.ContractSource SetPoEContractAddress(ctx sdk.Context, ctype types.PoEContractType, contractAddr sdk.AccAddress) + ValsetContract(ctx sdk.Context) keeper.ValsetContract } // bootstrapPoEContracts stores and instantiates all PoE contracts: @@ -135,8 +136,9 @@ func bootstrapPoEContracts(ctx sdk.Context, k wasmtypes.ContractOpsKeeper, tk tw return sdkerrors.Wrap(err, "instantiate valset") } poeKeeper.SetPoEContractAddress(ctx, types.PoEContractTypeValset, valsetContractAddr) + // store distribution contract address - valsetCfg, err := contract.QueryValsetConfig(ctx, tk, valsetContractAddr) + valsetCfg, err := poeKeeper.ValsetContract(ctx).QueryConfig(ctx) if err != nil { return sdkerrors.Wrap(err, "query valset config") } diff --git a/x/poe/bootstrap_test.go b/x/poe/bootstrap_test.go index 5c91121a..0a0669cc 100644 --- a/x/poe/bootstrap_test.go +++ b/x/poe/bootstrap_test.go @@ -6,6 +6,8 @@ import ( "path/filepath" "testing" + "github.com/confio/tgrade/x/poe/keeper/poetesting" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" sdk "github.com/cosmos/cosmos-sdk/types" @@ -76,7 +78,7 @@ func TestBootstrapPoEContracts(t *testing.T) { InitialKeys: []contract.Validator{}, ValidatorsRewardRatio: contract.DecimalFromPercentage(sdk.NewDec(50)), RewardsCodeId: 1, - DistributionContract: "cosmos156r47kpk4va938pmtpuee4fh77847gqcq4xu6e", + DistributionContract: engagementContractAddr.String(), }, }, } @@ -105,6 +107,11 @@ func TestBootstrapPoEContracts(t *testing.T) { sFn, capSetAddr := keeper.CaptureSetPoEContractAddressFn() pm := keeper.PoEKeeperMock{ SetPoEContractAddressFn: sFn, + ValsetContractFn: func(ctx sdk.Context) keeper.ValsetContract { + return poetesting.ValsetContractMock{QueryConfigFn: func(ctx sdk.Context) (*contract.ValsetConfigResponse, error) { + return &contract.ValsetConfigResponse{DistributionContract: distributionContractAddr.String()}, nil + }} + }, } // when ctx := sdk.Context{} diff --git a/x/poe/contract/query_integration_test.go b/x/poe/contract/query_integration_test.go index 4d9a3167..dca0296d 100644 --- a/x/poe/contract/query_integration_test.go +++ b/x/poe/contract/query_integration_test.go @@ -2,9 +2,7 @@ package contract_test import ( "encoding/json" - "sort" "testing" - "time" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -22,149 +20,6 @@ import ( "github.com/confio/tgrade/x/poe/types" ) -func TestListValidators(t *testing.T) { - // setup contracts and seed some data - ctx, example := keeper.CreateDefaultTestInput(t) - deliverTXFn := unAuthorizedDeliverTXFn(t, ctx, example.PoEKeeper, example.TWasmKeeper.GetContractKeeper(), example.EncodingConfig.TxConfig.TxDecoder()) - module := poe.NewAppModule(example.PoEKeeper, example.TWasmKeeper, deliverTXFn, example.EncodingConfig.TxConfig, example.TWasmKeeper.GetContractKeeper()) - - mutator, expValidators := withRandomValidators(t, ctx, example, 3) - gs := types.GenesisStateFixture(mutator) - expValidators = resetTokenAmount(expValidators) - - genesisBz := example.EncodingConfig.Marshaler.MustMarshalJSON(&gs) - module.InitGenesis(ctx, example.EncodingConfig.Marshaler, genesisBz) - - // when - contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) - require.NoError(t, err) - vals, err := contract.ListValidators(ctx, example.TWasmKeeper, contractAddr) - - // then - require.NoError(t, err) - sort.Slice(expValidators, func(i, j int) bool { - return expValidators[i].OperatorAddress < expValidators[j].OperatorAddress - }) - gotValidators := make([]stakingtypes.Validator, len(vals)) - for i, v := range vals { - gotValidators[i], err = v.ToValidator() - require.NoError(t, err) - } - assert.Equal(t, expValidators, gotValidators) -} - -func TestGetValidator(t *testing.T) { - // setup contracts and seed some data - ctx, example := keeper.CreateDefaultTestInput(t) - deliverTXFn := unAuthorizedDeliverTXFn(t, ctx, example.PoEKeeper, example.TWasmKeeper.GetContractKeeper(), example.EncodingConfig.TxConfig.TxDecoder()) - module := poe.NewAppModule(example.PoEKeeper, example.TWasmKeeper, deliverTXFn, example.EncodingConfig.TxConfig, example.TWasmKeeper.GetContractKeeper()) - - mutator, expValidators := withRandomValidators(t, ctx, example, 2) - gs := types.GenesisStateFixture(mutator) - expValidators = resetTokenAmount(expValidators) - - genesisBz := example.EncodingConfig.Marshaler.MustMarshalJSON(&gs) - module.InitGenesis(ctx, example.EncodingConfig.Marshaler, genesisBz) - - specs := map[string]struct { - operatorAddr string - expVal stakingtypes.Validator - expEmpty bool - }{ - "query one validator": { - operatorAddr: expValidators[0].OperatorAddress, - expVal: expValidators[0], - }, - "query other validator": { - operatorAddr: expValidators[1].OperatorAddress, - expVal: expValidators[1], - }, - "query with unknown address": { - operatorAddr: sdk.AccAddress(rand.Bytes(sdk.AddrLen)).String(), - expEmpty: true, - }, - "query with invalid address": { - operatorAddr: "not an address", - expEmpty: true, - }, - } - for name, spec := range specs { - t.Run(name, func(t *testing.T) { - contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) - require.NoError(t, err) - opAddr, _ := sdk.AccAddressFromBech32(spec.operatorAddr) - - // when - val, err := contract.QueryValidator(ctx, example.TWasmKeeper, contractAddr, opAddr) - - // then - if spec.expEmpty { - assert.Nil(t, val) - return - } - gotVal, err := val.ToValidator() - require.NoError(t, err) - assert.Equal(t, spec.expVal, gotVal) - }) - } -} - -func TestQueryUnbondingPeriod(t *testing.T) { - // setup contracts and seed some data - ctx, example := keeper.CreateDefaultTestInput(t) - deliverTXFn := unAuthorizedDeliverTXFn(t, ctx, example.PoEKeeper, example.TWasmKeeper.GetContractKeeper(), example.EncodingConfig.TxConfig.TxDecoder()) - module := poe.NewAppModule(example.PoEKeeper, example.TWasmKeeper, deliverTXFn, example.EncodingConfig.TxConfig, example.TWasmKeeper.GetContractKeeper()) - - mutator, _ := withRandomValidators(t, ctx, example, 1) - gs := types.GenesisStateFixture(mutator) - genesisBz := example.EncodingConfig.Marshaler.MustMarshalJSON(&gs) - module.InitGenesis(ctx, example.EncodingConfig.Marshaler, genesisBz) - - contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) - require.NoError(t, err) - // when - res, err := contract.QueryStakingUnbondingPeriod(ctx, example.TWasmKeeper, contractAddr) - - // then - const configuredTime uint64 = 21 * 24 * 60 * 60 // in bootstrap - assert.Equal(t, configuredTime, res) -} - -func TestQueryValsetConfig(t *testing.T) { - // setup contracts and seed some data - ctx, example := keeper.CreateDefaultTestInput(t) - deliverTXFn := unAuthorizedDeliverTXFn(t, ctx, example.PoEKeeper, example.TWasmKeeper.GetContractKeeper(), example.EncodingConfig.TxConfig.TxDecoder()) - module := poe.NewAppModule(example.PoEKeeper, example.TWasmKeeper, deliverTXFn, example.EncodingConfig.TxConfig, example.TWasmKeeper.GetContractKeeper()) - - mutator, _ := withRandomValidators(t, ctx, example, 1) - gs := types.GenesisStateFixture(mutator) - genesisBz := example.EncodingConfig.Marshaler.MustMarshalJSON(&gs) - module.InitGenesis(ctx, example.EncodingConfig.Marshaler, genesisBz) - - valsetContractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) - require.NoError(t, err) - mixerContractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeMixer) - require.NoError(t, err) - - // when - res, err := contract.QueryValsetConfig(ctx, example.TWasmKeeper, valsetContractAddr) - - // then - expConfig := &contract.ValsetConfigResponse{ - Membership: mixerContractAddr.String(), - MinWeight: 1, - MaxValidators: 100, - Scaling: 1, - EpochReward: sdk.NewInt64Coin("utgd", 100000), - FeePercentage: sdk.MustNewDecFromStr("0.50"), - ValidatorsRewardRatio: sdk.MustNewDecFromStr("0.50"), - DistributionContract: "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhuc53mp6", - RewardsContract: "cosmos1cnuw3f076wgdyahssdkd0g3nr96ckq8caf5mdm", - AutoUnjail: false, - } - assert.Equal(t, expConfig, res) -} - func TestQueryValidatorSelfDelegation(t *testing.T) { // setup contracts and seed some data ctx, example := keeper.CreateDefaultTestInput(t) @@ -204,68 +59,6 @@ func TestQueryValidatorSelfDelegation(t *testing.T) { } } -func TestQueryValidatorUnboding(t *testing.T) { - // setup contracts and seed some data - ctx, example := keeper.CreateDefaultTestInput(t) - deliverTXFn := unAuthorizedDeliverTXFn(t, ctx, example.PoEKeeper, example.TWasmKeeper.GetContractKeeper(), example.EncodingConfig.TxConfig.TxDecoder()) - module := poe.NewAppModule(example.PoEKeeper, example.TWasmKeeper, deliverTXFn, example.EncodingConfig.TxConfig, example.TWasmKeeper.GetContractKeeper()) - - mutator, vals := withRandomValidators(t, ctx, example, 2) - gs := types.GenesisStateFixture(mutator) - genesisBz := example.EncodingConfig.Marshaler.MustMarshalJSON(&gs) - module.InitGenesis(ctx, example.EncodingConfig.Marshaler, genesisBz) - - contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) - require.NoError(t, err) - op1Addr, err := sdk.AccAddressFromBech32(vals[0].OperatorAddress) - require.NoError(t, err) - - // unbond some tokens for operator 1 - now := time.Now().UTC() - ctx = ctx.WithBlockTime(now).WithBlockHeight(12) - unbondedAmount := sdk.NewInt(10) - contract.UnbondDelegation(ctx, contractAddr, op1Addr, unbondedAmount, example.TWasmKeeper.GetContractKeeper()) - - op2Addr, err := sdk.AccAddressFromBech32(vals[1].OperatorAddress) - require.NoError(t, err) - unbodingPeriod, err := contract.QueryStakingUnbondingPeriod(ctx, example.TWasmKeeper, contractAddr) - require.NoError(t, err) - specs := map[string]struct { - srcOpAddr sdk.AccAddress - expResult contract.TG4StakeClaimsResponse - }{ - "unbondings": { - srcOpAddr: op1Addr, - expResult: contract.TG4StakeClaimsResponse{Claims: []contract.TG4StakeClaim{ - { - Addr: op1Addr.String(), - Amount: sdk.NewInt(10), - ReleaseAt: uint64(now.Add(time.Duration(unbodingPeriod) * time.Second).UTC().UnixNano()), - CreationHeight: 12, - }, - }}, - }, - "no unbondings with existing operator": { - srcOpAddr: op2Addr, - expResult: contract.TG4StakeClaimsResponse{Claims: []contract.TG4StakeClaim{}}, - }, - "unknown operator": { - srcOpAddr: rand.Bytes(sdk.AddrLen), - expResult: contract.TG4StakeClaimsResponse{Claims: []contract.TG4StakeClaim{}}, - }, - } - for name, spec := range specs { - t.Run(name, func(t *testing.T) { - // when - gotRes, gotErr := contract.QueryStakingUnbonding(ctx, example.TWasmKeeper, contractAddr, spec.srcOpAddr) - // then - require.NoError(t, gotErr) - require.NotNil(t, gotRes) - assert.Equal(t, spec.expResult, gotRes) - }) - } -} - // unAuthorizedDeliverTXFn applies the TX without ante handler checks for testing purpose func unAuthorizedDeliverTXFn(t *testing.T, ctx sdk.Context, k keeper.Keeper, contractKeeper wasmtypes.ContractOpsKeeper, txDecoder sdk.TxDecoder) func(tx abci.RequestDeliverTx) abci.ResponseDeliverTx { t.Helper() @@ -297,7 +90,7 @@ func withRandomValidators(t *testing.T, ctx sdk.Context, example keeper.TestKeep desc stakingtypes.Description ) f.NilChance(0).Fuzz(&power) // must be > 0 so that staked amount is > 0 - f.Fuzz(&engagement) + f.NilChance(0).Fuzz(&engagement) for len(desc.Moniker) < 3 { // ensure min length is met f.Fuzz(&desc) } diff --git a/x/poe/contract/tg4_stake.go b/x/poe/contract/tg4_stake.go index 05361bf6..81d620da 100644 --- a/x/poe/contract/tg4_stake.go +++ b/x/poe/contract/tg4_stake.go @@ -3,6 +3,11 @@ package contract import ( "encoding/json" "testing" + "time" + + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" wasmvmtypes "github.com/CosmWasm/wasmvm/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -86,23 +91,6 @@ type UnbondingPeriodResponse struct { UnbondingPeriod uint64 `json:"unbonding_period"` } -// QueryStakingUnbondingPeriod query the unbonding period from PoE staking contract -func QueryStakingUnbondingPeriod(ctx sdk.Context, k types.SmartQuerier, stakeAddr sdk.AccAddress) (uint64, error) { - query := TG4StakeQuery{UnbondingPeriod: &struct{}{}} - var response UnbondingPeriodResponse - err := doQuery(ctx, k, stakeAddr, query, &response) - return response.UnbondingPeriod, err -} - -// QueryStakingUnbonding query PoE staking contract for unbonded self delegations -// TODO: add pagination support here! -func QueryStakingUnbonding(ctx sdk.Context, k types.SmartQuerier, stakeAddr sdk.AccAddress, opAddr sdk.AccAddress) (TG4StakeClaimsResponse, error) { - query := TG4StakeQuery{Claims: &ListClaimsQuery{Address: opAddr.String()}} - var response TG4StakeClaimsResponse - err := doQuery(ctx, k, stakeAddr, query, &response) - return response, err -} - // QueryStakedAmount query PoE staking contract for bonded self delegation amount func QueryStakedAmount(ctx sdk.Context, k types.SmartQuerier, stakeAddr sdk.AccAddress, opAddr sdk.AccAddress) (TG4StakedAmountsResponse, error) { query := TG4StakeQuery{Staked: &StakedQuery{Address: opAddr.String()}} @@ -122,3 +110,70 @@ func doQuery(ctx sdk.Context, k types.SmartQuerier, contractAddr sdk.AccAddress, } return json.Unmarshal(res, result) } + +type StakeContractAdapter struct { + contractAddr sdk.AccAddress + contractQuerier types.SmartQuerier + addressLookupErr error +} + +// NewStakeContractAdapter constructor +func NewStakeContractAdapter(contractAddr sdk.AccAddress, contractQuerier types.SmartQuerier, addressLookupErr error) *StakeContractAdapter { + return &StakeContractAdapter{contractAddr: contractAddr, contractQuerier: contractQuerier, addressLookupErr: addressLookupErr} +} + +// QueryStakingUnbondingPeriod query the unbonding period from PoE staking contract +func (v StakeContractAdapter) QueryStakingUnbondingPeriod(ctx sdk.Context) (time.Duration, error) { + if v.addressLookupErr != nil { + return 0, v.addressLookupErr + } + + query := TG4StakeQuery{UnbondingPeriod: &struct{}{}} + var resp UnbondingPeriodResponse + err := doQuery(ctx, v.contractQuerier, v.contractAddr, query, &resp) + if err != nil { + return 0, sdkerrors.Wrap(err, "contract query") + } + return time.Duration(resp.UnbondingPeriod) * time.Second, nil +} + +func (v StakeContractAdapter) QueryStakedAmount(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + query := TG4Query{Member: &MemberQuery{Addr: opAddr.String()}} + var resp TG4MemberResponse + err := doQuery(ctx, v.contractQuerier, v.contractAddr, query, &resp) + if err != nil { + return nil, sdkerrors.Wrap(err, "contract query") + } + if resp.Weight == nil { + return nil, nil + } + amount := sdk.NewInt(int64(*resp.Weight)) + // we should return Coin instead: https://github.com/confio/tgrade-contracts/issues/265 + return &amount, nil +} + +// QueryStakingUnbonding query PoE staking contract for unbonded self delegations +// TODO: add pagination support here! +func (v StakeContractAdapter) QueryStakingUnbonding(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { + if v.addressLookupErr != nil { + return nil, v.addressLookupErr + } + query := TG4StakeQuery{Claims: &ListClaimsQuery{Address: opAddr.String()}} + var resp TG4StakeClaimsResponse + err := doQuery(ctx, v.contractQuerier, v.contractAddr, query, &resp) + if err != nil { + return nil, sdkerrors.Wrap(err, "contract query") + } + + // add all unbonded amounts + unbodings := make([]stakingtypes.UnbondingDelegationEntry, len(resp.Claims)) + for i, v := range resp.Claims { + unbodings[i] = stakingtypes.UnbondingDelegationEntry{ + InitialBalance: v.Amount, + CompletionTime: time.Unix(0, int64(v.ReleaseAt)).UTC(), + Balance: v.Amount, + CreationHeight: int64(v.CreationHeight), + } + } + return unbodings, nil +} diff --git a/x/poe/contract/tg4_stake_integration_test.go b/x/poe/contract/tg4_stake_integration_test.go new file mode 100644 index 00000000..0785febe --- /dev/null +++ b/x/poe/contract/tg4_stake_integration_test.go @@ -0,0 +1,92 @@ +package contract_test + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/tendermint/tendermint/libs/rand" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/confio/tgrade/x/poe/contract" + "github.com/confio/tgrade/x/poe/types" +) + +func TestQueryUnbondingPeriod(t *testing.T) { + // setup contracts and seed some data + ctx, example, _ := setupPoEContracts(t) + + contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) + require.NoError(t, err) + + // when + adaptor := contract.NewStakeContractAdapter(contractAddr, example.TWasmKeeper, nil) + + res, err := adaptor.QueryStakingUnbondingPeriod(ctx) + + // then + require.NoError(t, err) + const configuredTime = 21 * 24 * 60 * 60 * time.Second // in bootstrap + assert.Equal(t, configuredTime, res) +} + +func TestQueryValidatorUnboding(t *testing.T) { + // setup contracts and seed some data + ctx, example, vals := setupPoEContracts(t) + + op1Addr, err := sdk.AccAddressFromBech32(vals[0].OperatorAddress) + require.NoError(t, err) + + // unbond some tokens for operator 1 + now := time.Now().UTC() + ctx = ctx.WithBlockTime(now).WithBlockHeight(12) + unbondedAmount := sdk.NewInt(10) + contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) + require.NoError(t, err) + err = contract.UnbondDelegation(ctx, contractAddr, op1Addr, unbondedAmount, example.TWasmKeeper.GetContractKeeper()) + require.NoError(t, err) + + op2Addr, err := sdk.AccAddressFromBech32(vals[1].OperatorAddress) + require.NoError(t, err) + unbodingPeriod, err := example.PoEKeeper.StakeContract(ctx).QueryStakingUnbondingPeriod(ctx) + require.NoError(t, err) + + specs := map[string]struct { + srcOpAddr sdk.AccAddress + expResult []stakingtypes.UnbondingDelegationEntry + }{ + "unbondings": { + srcOpAddr: op1Addr, + expResult: []stakingtypes.UnbondingDelegationEntry{ + { + InitialBalance: sdk.NewInt(10), + Balance: sdk.NewInt(10), + CompletionTime: now.Add(unbodingPeriod).UTC(), + CreationHeight: 12, + }, + }, + }, + "no unbondings with existing operator": { + srcOpAddr: op2Addr, + expResult: []stakingtypes.UnbondingDelegationEntry{}, + }, + "unknown operator": { + srcOpAddr: rand.Bytes(sdk.AddrLen), + expResult: []stakingtypes.UnbondingDelegationEntry{}, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + // when + adapter := contract.NewStakeContractAdapter(contractAddr, example.TWasmKeeper, nil) + gotRes, gotErr := adapter.QueryStakingUnbonding(ctx, spec.srcOpAddr) + // then + require.NoError(t, gotErr) + require.NotNil(t, gotRes) + assert.Equal(t, spec.expResult, gotRes) + }) + } +} diff --git a/x/poe/contract/tgrade_distribution.go b/x/poe/contract/tgrade_distribution.go index 00aa499e..fb5775a8 100644 --- a/x/poe/contract/tgrade_distribution.go +++ b/x/poe/contract/tgrade_distribution.go @@ -19,18 +19,18 @@ type FundsResponse struct { Funds sdk.Coin } -type DistributionContractImpl struct { +type DistributionContractAdapter struct { contractAddr sdk.AccAddress contractQuerier types.SmartQuerier addressLookupErr error } -// NewDistributionContractImpl constructor -func NewDistributionContractImpl(contractAddr sdk.AccAddress, contractQuerier types.SmartQuerier, addressLookupErr error) *DistributionContractImpl { - return &DistributionContractImpl{contractAddr: contractAddr, contractQuerier: contractQuerier, addressLookupErr: addressLookupErr} +// NewDistributionContractAdapter constructor +func NewDistributionContractAdapter(contractAddr sdk.AccAddress, contractQuerier types.SmartQuerier, addressLookupErr error) *DistributionContractAdapter { + return &DistributionContractAdapter{contractAddr: contractAddr, contractQuerier: contractQuerier, addressLookupErr: addressLookupErr} } -func (d DistributionContractImpl) ValidatorOutstandingReward(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { +func (d DistributionContractAdapter) ValidatorOutstandingReward(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { if d.addressLookupErr != nil { return sdk.Coin{}, d.addressLookupErr } diff --git a/x/poe/contract/tgrade_distribution_integration_test.go b/x/poe/contract/tgrade_distribution_integration_test.go index a80ad241..11c929c1 100644 --- a/x/poe/contract/tgrade_distribution_integration_test.go +++ b/x/poe/contract/tgrade_distribution_integration_test.go @@ -9,7 +9,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" "github.com/confio/tgrade/x/poe" "github.com/confio/tgrade/x/poe/contract" @@ -46,7 +45,7 @@ func TestQueryWithdrawableFunds(t *testing.T) { "with rewards after epoche": { setup: func(ctx sdk.Context) sdk.Context { ctx = ctx.WithBlockTime(ctx.BlockTime().Add(types.DefaultGenesisState().ValsetContractConfig.EpochLength)) - module.EndBlock(ctx, abci.RequestEndBlock{}) + poe.EndBlocker(ctx, example.TWasmKeeper) return ctx }, src: opAddr, @@ -64,7 +63,7 @@ func TestQueryWithdrawableFunds(t *testing.T) { if spec.setup != nil { tCtx = spec.setup(tCtx) } - gotAmount, gotErr := contract.NewDistributionContractImpl(contractAddr, example.TWasmKeeper, nil).ValidatorOutstandingReward(tCtx, spec.src) + gotAmount, gotErr := contract.NewDistributionContractAdapter(contractAddr, example.TWasmKeeper, nil).ValidatorOutstandingReward(tCtx, spec.src) if spec.expErr != nil { assert.True(t, spec.expErr.Is(gotErr), "got %s", gotErr) return diff --git a/x/poe/contract/tgrade_valset.go b/x/poe/contract/tgrade_valset.go index db42e414..e32cde08 100644 --- a/x/poe/contract/tgrade_valset.go +++ b/x/poe/contract/tgrade_valset.go @@ -239,13 +239,6 @@ type ListActiveValidatorsResponse struct { type SimulateActiveValidatorsResponse = ListActiveValidatorsResponse -func QueryValsetConfig(ctx sdk.Context, k types.SmartQuerier, valset sdk.AccAddress) (*ValsetConfigResponse, error) { - query := ValsetQuery{Config: &struct{}{}} - var response ValsetConfigResponse - err := doQuery(ctx, k, valset, query, &response) - return &response, err -} - func QueryValsetEpoch(ctx sdk.Context, k types.SmartQuerier, valset sdk.AccAddress) (*ValsetEpochResponse, error) { query := ValsetQuery{Epoch: &struct{}{}} var response ValsetEpochResponse @@ -253,20 +246,6 @@ func QueryValsetEpoch(ctx sdk.Context, k types.SmartQuerier, valset sdk.AccAddre return &response, err } -func QueryValidator(ctx sdk.Context, k types.SmartQuerier, valset sdk.AccAddress, operator sdk.AccAddress) (*OperatorResponse, error) { - query := ValsetQuery{Validator: &ValidatorQuery{Operator: operator.String()}} - var response ValidatorResponse - err := doQuery(ctx, k, valset, query, &response) - return response.Validator, err -} - -func ListValidators(ctx sdk.Context, k types.SmartQuerier, valset sdk.AccAddress) ([]OperatorResponse, error) { - query := ValsetQuery{ListValidators: &ListValidatorsQuery{Limit: 30}} - var response ListValidatorsResponse - err := doQuery(ctx, k, valset, query, &response) - return response.Validators, err -} - func ListActiveValidators(ctx sdk.Context, k types.SmartQuerier, valset sdk.AccAddress) ([]ValidatorInfo, error) { query := ValsetQuery{ListActiveValidators: &struct{}{}} var response ListActiveValidatorsResponse @@ -280,3 +259,66 @@ func SimulateActiveValidators(ctx sdk.Context, k types.SmartQuerier, valset sdk. err := doQuery(ctx, k, valset, query, &response) return response.Validators, err } + +type ValsetContractAdapter struct { + contractAddr sdk.AccAddress + contractQuerier types.SmartQuerier + addressLookupErr error +} + +// NewValsetContractAdapter constructor +func NewValsetContractAdapter(contractAddr sdk.AccAddress, contractQuerier types.SmartQuerier, addressLookupErr error) *ValsetContractAdapter { + return &ValsetContractAdapter{contractAddr: contractAddr, contractQuerier: contractQuerier, addressLookupErr: addressLookupErr} +} + +func (v ValsetContractAdapter) QueryValidator(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { + if v.addressLookupErr != nil { + return nil, v.addressLookupErr + } + query := ValsetQuery{Validator: &ValidatorQuery{Operator: opAddr.String()}} + var rsp ValidatorResponse + err := doQuery(ctx, v.contractQuerier, v.contractAddr, query, &rsp) + if err != nil { + return nil, sdkerrors.Wrap(err, "contract query") + } + if rsp.Validator == nil { + return nil, nil + } + val, err := rsp.Validator.ToValidator() + return &val, err +} + +func (v ValsetContractAdapter) ListValidators(ctx sdk.Context) ([]stakingtypes.Validator, error) { + if v.addressLookupErr != nil { + return nil, v.addressLookupErr + } + query := ValsetQuery{ListValidators: &ListValidatorsQuery{Limit: 30}} + var rsp ListValidatorsResponse + err := doQuery(ctx, v.contractQuerier, v.contractAddr, query, &rsp) + if err != nil { + return nil, sdkerrors.Wrap(err, "contract query") + } + + vals := make([]stakingtypes.Validator, len(rsp.Validators)) + for i, v := range rsp.Validators { + vals[i], err = v.ToValidator() + if err != nil { + return nil, err + } + } + return vals, nil +} + +// QueryConfig query contract configuration +func (v ValsetContractAdapter) QueryConfig(ctx sdk.Context) (*ValsetConfigResponse, error) { + if v.addressLookupErr != nil { + return nil, v.addressLookupErr + } + query := ValsetQuery{Config: &struct{}{}} + var rsp ValsetConfigResponse + err := doQuery(ctx, v.contractQuerier, v.contractAddr, query, &rsp) + if err != nil { + return nil, sdkerrors.Wrap(err, "contract query") + } + return &rsp, err +} diff --git a/x/poe/contract/tgrade_valset_integration_test.go b/x/poe/contract/tgrade_valset_integration_test.go new file mode 100644 index 00000000..4b722741 --- /dev/null +++ b/x/poe/contract/tgrade_valset_integration_test.go @@ -0,0 +1,128 @@ +package contract_test + +import ( + "sort" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/rand" + + "github.com/confio/tgrade/x/poe" + "github.com/confio/tgrade/x/poe/contract" + "github.com/confio/tgrade/x/poe/keeper" + "github.com/confio/tgrade/x/poe/types" +) + +func TestQueryValidator(t *testing.T) { + // setup contracts and seed some data + ctx, example, vals := setupPoEContracts(t) + vals = resetTokenAmount(vals) + + contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) + require.NoError(t, err) + + specs := map[string]struct { + operatorAddr string + expVal stakingtypes.Validator + expEmpty bool + }{ + "query one validator": { + operatorAddr: vals[0].OperatorAddress, + expVal: vals[0], + }, + "query other validator": { + operatorAddr: vals[1].OperatorAddress, + expVal: vals[1], + }, + "query with unknown address": { + operatorAddr: sdk.AccAddress(rand.Bytes(sdk.AddrLen)).String(), + expEmpty: true, + }, + "query with invalid address": { + operatorAddr: "not an address", + expEmpty: true, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + opAddr, _ := sdk.AccAddressFromBech32(spec.operatorAddr) + + // when + adaptor := contract.NewValsetContractAdapter(contractAddr, example.TWasmKeeper, nil) + gotVal, err := adaptor.QueryValidator(ctx, opAddr) + + // then + if spec.expEmpty { + assert.Nil(t, gotVal) + return + } + require.NoError(t, err) + assert.Equal(t, spec.expVal, *gotVal) + }) + } +} + +func TestListValidators(t *testing.T) { + // setup contracts and seed some data + ctx, example, expValidators := setupPoEContracts(t) + expValidators = resetTokenAmount(expValidators) + + contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) + require.NoError(t, err) + + // when + gotValidators, err := contract.NewValsetContractAdapter(contractAddr, example.TWasmKeeper, nil).ListValidators(ctx) + + // then + require.NoError(t, err) + sort.Slice(expValidators, func(i, j int) bool { + return expValidators[i].OperatorAddress < expValidators[j].OperatorAddress + }) + assert.Equal(t, expValidators, gotValidators) +} + +func TestQueryValsetConfig(t *testing.T) { + // setup contracts and seed some data + ctx, example, _ := setupPoEContracts(t) + mixerContractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeMixer) + require.NoError(t, err) + contractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) + require.NoError(t, err) + + // when + adapter := contract.NewValsetContractAdapter(contractAddr, example.TWasmKeeper, nil) + res, gotErr := adapter.QueryConfig(ctx) + + // then + require.NoError(t, gotErr) + + expConfig := &contract.ValsetConfigResponse{ + Membership: mixerContractAddr.String(), + MinWeight: 1, + MaxValidators: 100, + Scaling: 1, + EpochReward: sdk.NewInt64Coin("utgd", 100000), + FeePercentage: sdk.MustNewDecFromStr("0.50"), + ValidatorsRewardRatio: sdk.MustNewDecFromStr("0.50"), + DistributionContract: "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhuc53mp6", + RewardsContract: "cosmos1cnuw3f076wgdyahssdkd0g3nr96ckq8caf5mdm", + AutoUnjail: false, + } + assert.Equal(t, expConfig, res) +} + +func setupPoEContracts(t *testing.T) (sdk.Context, keeper.TestKeepers, []stakingtypes.Validator) { + ctx, example := keeper.CreateDefaultTestInput(t) + deliverTXFn := unAuthorizedDeliverTXFn(t, ctx, example.PoEKeeper, example.TWasmKeeper.GetContractKeeper(), example.EncodingConfig.TxConfig.TxDecoder()) + module := poe.NewAppModule(example.PoEKeeper, example.TWasmKeeper, deliverTXFn, example.EncodingConfig.TxConfig, example.TWasmKeeper.GetContractKeeper()) + + mutator, expValidators := withRandomValidators(t, ctx, example, 3) + gs := types.GenesisStateFixture(mutator) + + genesisBz := example.EncodingConfig.Marshaler.MustMarshalJSON(&gs) + module.InitGenesis(ctx, example.EncodingConfig.Marshaler, genesisBz) + return ctx, example, expValidators +} diff --git a/x/poe/keeper/contracts.go b/x/poe/keeper/contracts.go index 24fd7902..9ce68119 100644 --- a/x/poe/keeper/contracts.go +++ b/x/poe/keeper/contracts.go @@ -1,7 +1,10 @@ package keeper import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/confio/tgrade/x/poe/contract" "github.com/confio/tgrade/x/poe/types" @@ -13,5 +16,27 @@ type DistributionContract interface { func (k Keeper) DistributionContract(ctx sdk.Context) DistributionContract { distContractAddr, err := k.GetPoEContractAddress(ctx, types.PoEContractTypeDistribution) - return contract.NewDistributionContractImpl(distContractAddr, k.twasmKeeper, err) + return contract.NewDistributionContractAdapter(distContractAddr, k.twasmKeeper, err) +} + +type ValsetContract interface { + ListValidators(ctx sdk.Context) ([]stakingtypes.Validator, error) + QueryValidator(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) + QueryConfig(ctx sdk.Context) (*contract.ValsetConfigResponse, error) +} + +func (k Keeper) ValsetContract(ctx sdk.Context) ValsetContract { + distContractAddr, err := k.GetPoEContractAddress(ctx, types.PoEContractTypeValset) + return contract.NewValsetContractAdapter(distContractAddr, k.twasmKeeper, err) +} + +type StakeContract interface { + QueryStakedAmount(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) + QueryStakingUnbondingPeriod(ctx sdk.Context) (time.Duration, error) + QueryStakingUnbonding(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) +} + +func (k Keeper) StakeContract(ctx sdk.Context) StakeContract { + distContractAddr, err := k.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) + return contract.NewStakeContractAdapter(distContractAddr, k.twasmKeeper, err) } diff --git a/x/poe/keeper/grpc_query.go b/x/poe/keeper/grpc_query.go index 9fffa0d9..45885475 100644 --- a/x/poe/keeper/grpc_query.go +++ b/x/poe/keeper/grpc_query.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "time" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -10,7 +9,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/confio/tgrade/x/poe/contract" "github.com/confio/tgrade/x/poe/types" ) @@ -26,16 +24,17 @@ type ViewKeeper interface { GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) GetBondDenom(ctx sdk.Context) string DistributionContract(ctx sdk.Context) DistributionContract + ValsetContract(ctx sdk.Context) ValsetContract + StakeContract(ctx sdk.Context) StakeContract } type grpcQuerier struct { - keeper ViewKeeper - contractQuerier types.SmartQuerier + keeper ViewKeeper } // NewGrpcQuerier constructor -func NewGrpcQuerier(keeper ViewKeeper, contractQuerier types.SmartQuerier) *grpcQuerier { - return &grpcQuerier{keeper: keeper, contractQuerier: contractQuerier} +func NewGrpcQuerier(keeper ViewKeeper) *grpcQuerier { + return &grpcQuerier{keeper: keeper} } // ContractAddress query PoE contract address for given type @@ -68,23 +67,10 @@ func (q grpcQuerier) Validators(c context.Context, req *stakingtypes.QueryValida } ctx := sdk.UnwrapSDKContext(c) - addr, err := q.keeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) + vals, err := q.keeper.ValsetContract(ctx).ListValidators(ctx) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - - valsRsp, err := contract.ListValidators(ctx, q.contractQuerier, addr) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - - vals := make([]stakingtypes.Validator, len(valsRsp)) - for i, v := range valsRsp { - vals[i], err = v.ToValidator() - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - } return &stakingtypes.QueryValidatorsResponse{ Validators: vals, }, nil @@ -107,23 +93,14 @@ func (q grpcQuerier) Validator(c context.Context, req *stakingtypes.QueryValidat } ctx := sdk.UnwrapSDKContext(c) - contractAddr, err := q.keeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) + val, err := q.keeper.ValsetContract(ctx).QueryValidator(ctx, opAddr) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - - valRsp, err := contract.QueryValidator(ctx, q.contractQuerier, contractAddr, opAddr) - if err != nil { - return nil, status.Errorf(codes.Internal, err.Error()) - } - if valRsp == nil { - return nil, status.Errorf(codes.NotFound, "validator %s not found", req.ValidatorAddr) - } - val, err := valRsp.ToValidator() - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) + if val == nil { + return nil, status.Error(codes.NotFound, "by address") } - return &stakingtypes.QueryValidatorResponse{Validator: val}, nil + return &stakingtypes.QueryValidatorResponse{Validator: *val}, nil } // UnbondingPeriod query the global unbonding period @@ -132,17 +109,12 @@ func (q grpcQuerier) UnbondingPeriod(c context.Context, req *types.QueryUnbondin return nil, status.Error(codes.InvalidArgument, "empty request") } ctx := sdk.UnwrapSDKContext(c) - contractAddr, err := q.keeper.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) + period, err := q.keeper.StakeContract(ctx).QueryStakingUnbondingPeriod(ctx) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - - rsp, err := contract.QueryStakingUnbondingPeriod(ctx, q.contractQuerier, contractAddr) - if err != nil { - return nil, status.Errorf(codes.Internal, err.Error()) - } return &types.QueryUnbondingPeriodResponse{ - Time: time.Duration(rsp) * time.Second, + Time: period, }, nil } @@ -156,20 +128,16 @@ func (q grpcQuerier) ValidatorDelegation(c context.Context, req *types.QueryVali } ctx := sdk.UnwrapSDKContext(c) - stakingContractAddr, err := q.keeper.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) + amount, err := q.keeper.StakeContract(ctx).QueryStakedAmount(ctx, opAddr) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - amount, err := contract.QueryTG4Member(ctx, q.contractQuerier, stakingContractAddr, opAddr) - if err != nil { - return nil, status.Errorf(codes.Internal, err.Error()) - } if amount == nil { return nil, status.Error(codes.NotFound, "not a validator operator address") } return &types.QueryValidatorDelegationResponse{ - Balance: sdk.NewCoin(q.keeper.GetBondDenom(ctx), sdk.NewInt(int64(*amount))), + Balance: sdk.NewCoin(q.keeper.GetBondDenom(ctx), *amount), }, nil } @@ -183,25 +151,10 @@ func (q grpcQuerier) ValidatorUnbondingDelegations(c context.Context, req *types } ctx := sdk.UnwrapSDKContext(c) - stakingContractAddr, err := q.keeper.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - res, err := contract.QueryStakingUnbonding(ctx, q.contractQuerier, stakingContractAddr, opAddr) + unbodings, err := q.keeper.StakeContract(ctx).QueryStakingUnbonding(ctx, opAddr) if err != nil { return nil, status.Errorf(codes.Internal, err.Error()) } - - // add all unbonded amounts - var unbodings []stakingtypes.UnbondingDelegationEntry - for _, v := range res.Claims { - unbodings = append(unbodings, stakingtypes.UnbondingDelegationEntry{ - InitialBalance: v.Amount, - CompletionTime: time.Unix(0, int64(v.ReleaseAt)).UTC(), - Balance: v.Amount, - CreationHeight: int64(v.CreationHeight), - }) - } return &types.QueryValidatorUnbondingDelegationsResponse{Entries: unbodings}, nil } diff --git a/x/poe/keeper/grpc_query_test.go b/x/poe/keeper/grpc_query_test.go index ed29694f..98989c3f 100644 --- a/x/poe/keeper/grpc_query_test.go +++ b/x/poe/keeper/grpc_query_test.go @@ -2,11 +2,12 @@ package keeper import ( "context" - "encoding/json" "errors" "testing" "time" + "github.com/confio/tgrade/x/poe/keeper/poetesting" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" @@ -18,7 +19,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/confio/tgrade/x/poe/contract" "github.com/confio/tgrade/x/poe/types" ) @@ -56,7 +56,7 @@ func TestQueryContractAddress(t *testing.T) { } for name, spec := range specs { t.Run(name, func(t *testing.T) { - q := NewGrpcQuerier(PoEKeeperMock{GetPoEContractAddressFn: spec.mockFn}, nil) + q := NewGrpcQuerier(PoEKeeperMock{GetPoEContractAddressFn: spec.mockFn}) ctx := sdk.Context{}.WithContext(context.Background()) gotRes, gotErr := q.ContractAddress(sdk.WrapSDKContext(ctx), &spec.srcMsg) if spec.expErrCode != 0 { @@ -72,7 +72,7 @@ func TestQueryContractAddress(t *testing.T) { func TestQueryValidators(t *testing.T) { var myValsetContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) - contractSource := newContractSourceMock(t, myValsetContract, nil) + poeKeeper := newContractSourceMock(t, myValsetContract, nil) pubKey := ed25519.GenPrivKey().PubKey() expValidator := types.ValidatorFixture(func(m *stakingtypes.Validator) { @@ -80,66 +80,54 @@ func TestQueryValidators(t *testing.T) { m.ConsensusPubkey = pkAny }) - querier := SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.ListValidatorsResponse{Validators: []contract.OperatorResponse{ - { - Operator: expValidator.OperatorAddress, - Pubkey: contract.ValidatorPubkey{Ed25519: pubKey.Bytes()}, - Metadata: contract.MetadataFromDescription(expValidator.Description), - }, - }}) - }} specs := map[string]struct { - src *stakingtypes.QueryValidatorsRequest - querier types.SmartQuerier - exp *stakingtypes.QueryValidatorsResponse - expErr bool + src *stakingtypes.QueryValidatorsRequest + mock poetesting.ValsetContractMock + exp *stakingtypes.QueryValidatorsResponse + expErr bool }{ "all good": { - src: &stakingtypes.QueryValidatorsRequest{}, - querier: querier, + src: &stakingtypes.QueryValidatorsRequest{}, + mock: poetesting.ValsetContractMock{ + ListValidatorsFn: func(ctx sdk.Context) ([]stakingtypes.Validator, error) { + return []stakingtypes.Validator{expValidator}, nil + }, + }, exp: &stakingtypes.QueryValidatorsResponse{ Validators: []stakingtypes.Validator{expValidator}, }, }, "nil request": { - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - t.Fatalf("not expected to be called") - return nil, nil - }}, + mock: poetesting.ValsetContractMock{}, expErr: true, }, "empty result": { src: &stakingtypes.QueryValidatorsRequest{}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - r := contract.ListValidatorsResponse{} - return json.Marshal(r) - }}, + mock: poetesting.ValsetContractMock{ + ListValidatorsFn: func(ctx sdk.Context) ([]stakingtypes.Validator, error) { + return []stakingtypes.Validator{}, nil + }, + }, exp: &stakingtypes.QueryValidatorsResponse{ Validators: []stakingtypes.Validator{}, }, }, - "nil result": { - src: &stakingtypes.QueryValidatorsRequest{}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return nil, nil - }}, - expErr: true, - }, "contract returns error": { src: &stakingtypes.QueryValidatorsRequest{}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return nil, errors.New("testing") - }}, + mock: poetesting.ValsetContractMock{ + ListValidatorsFn: func(ctx sdk.Context) ([]stakingtypes.Validator, error) { + return nil, errors.New("testing") + }, + }, expErr: true, }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) - + poeKeeper.ValsetContractFn = func(ctx sdk.Context) ValsetContract { return spec.mock } // when - s := NewGrpcQuerier(contractSource, spec.querier) + s := NewGrpcQuerier(poeKeeper) gotRes, gotErr := s.Validators(ctx, spec.src) // then @@ -154,62 +142,48 @@ func TestQueryValidators(t *testing.T) { } func TestQueryValidator(t *testing.T) { - var myValsetContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) var myOperator sdk.AccAddress = rand.Bytes(sdk.AddrLen) - contractSource := newContractSourceMock(t, myValsetContract, nil) - pubKey := ed25519.GenPrivKey().PubKey() expValidator := types.ValidatorFixture(func(m *stakingtypes.Validator) { pkAny, _ := codectypes.NewAnyWithValue(pubKey) m.ConsensusPubkey = pkAny }) - querier := SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.ValidatorResponse{Validator: &contract.OperatorResponse{ - Operator: expValidator.OperatorAddress, - Pubkey: contract.ValidatorPubkey{Ed25519: pubKey.Bytes()}, - Metadata: contract.MetadataFromDescription(expValidator.Description), - }}) - }} specs := map[string]struct { - src *stakingtypes.QueryValidatorRequest - querier types.SmartQuerier - exp *stakingtypes.QueryValidatorResponse - expErr bool + src *stakingtypes.QueryValidatorRequest + mock poetesting.ValsetContractMock + exp *stakingtypes.QueryValidatorResponse + expErr bool }{ "all good": { - src: &stakingtypes.QueryValidatorRequest{ValidatorAddr: myOperator.String()}, - querier: querier, + src: &stakingtypes.QueryValidatorRequest{ValidatorAddr: myOperator.String()}, + mock: poetesting.ValsetContractMock{QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { + return &expValidator, nil + }}, exp: &stakingtypes.QueryValidatorResponse{ Validator: expValidator, }, }, "nil request": { - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - t.Fatalf("not expected to be called") - return nil, nil - }}, + mock: poetesting.ValsetContractMock{}, expErr: true, }, "empty address": { - src: &stakingtypes.QueryValidatorRequest{}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - t.Fatalf("not expected to be called") - return nil, nil - }}, + src: &stakingtypes.QueryValidatorRequest{}, + mock: poetesting.ValsetContractMock{}, expErr: true, }, "not found": { src: &stakingtypes.QueryValidatorRequest{ValidatorAddr: myOperator.String()}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + mock: poetesting.ValsetContractMock{QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { return nil, nil }}, expErr: true, }, "contract returns error": { src: &stakingtypes.QueryValidatorRequest{ValidatorAddr: myOperator.String()}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + mock: poetesting.ValsetContractMock{QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { return nil, errors.New("testing") }}, expErr: true, @@ -218,9 +192,9 @@ func TestQueryValidator(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) - + poeKeeper := PoEKeeperMock{ValsetContractFn: func(ctx sdk.Context) ValsetContract { return spec.mock }} // when - s := NewGrpcQuerier(contractSource, spec.querier) + s := NewGrpcQuerier(poeKeeper) gotRes, gotErr := s.Validator(ctx, spec.src) // then @@ -235,44 +209,29 @@ func TestQueryValidator(t *testing.T) { } func TestQueryUnbondingPeriod(t *testing.T) { - var myStakingContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) - contractSource := newContractSourceMock(t, nil, myStakingContract) specs := map[string]struct { - src *types.QueryUnbondingPeriodRequest - querier types.SmartQuerier - exp *types.QueryUnbondingPeriodResponse - expErr bool + src *types.QueryUnbondingPeriodRequest + mock poetesting.StakeContractMock + exp *types.QueryUnbondingPeriodResponse + expErr bool }{ "all good": { src: &types.QueryUnbondingPeriodRequest{}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.UnbondingPeriodResponse{ - UnbondingPeriod: 1, - }) + mock: poetesting.StakeContractMock{QueryStakingUnbondingPeriodFn: func(ctx sdk.Context) (time.Duration, error) { + return time.Second, nil }}, exp: &types.QueryUnbondingPeriodResponse{ Time: time.Second, }, }, "nil request": { - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - t.Fatalf("not expected to be called") - return nil, nil - }}, - expErr: true, - }, - "contract returns nil": { - src: &types.QueryUnbondingPeriodRequest{}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return nil, nil - }}, expErr: true, }, "contract returns error": { src: &types.QueryUnbondingPeriodRequest{}, - querier: SmartQuerierMock{QuerySmartFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return nil, errors.New("testing") + mock: poetesting.StakeContractMock{QueryStakingUnbondingPeriodFn: func(ctx sdk.Context) (time.Duration, error) { + return 0, errors.New("testing") }}, expErr: true, }, @@ -280,9 +239,12 @@ func TestQueryUnbondingPeriod(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) + poeKeeper := PoEKeeperMock{StakeContractFn: func(ctx sdk.Context) StakeContract { + return spec.mock + }} // when - s := NewGrpcQuerier(contractSource, spec.querier) + s := NewGrpcQuerier(poeKeeper) gotRes, gotErr := s.UnbondingPeriod(ctx, spec.src) // then @@ -297,30 +259,23 @@ func TestQueryUnbondingPeriod(t *testing.T) { } func TestValidatorDelegation(t *testing.T) { - var myStakingContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) var myOperatorAddr sdk.AccAddress = rand.Bytes(sdk.AddrLen) - contractSource := PoEKeeperMock{ - GetPoEContractAddressFn: func(ctx sdk.Context, ctype types.PoEContractType) (sdk.AccAddress, error) { - require.Equal(t, types.PoEContractTypeStaking, ctype) - return myStakingContract, nil - }, + poeKeeper := PoEKeeperMock{ GetBondDenomFn: func(ctx sdk.Context) string { return "utgd" }, } specs := map[string]struct { - src *types.QueryValidatorDelegationRequest - querier types.SmartQuerier - exp *types.QueryValidatorDelegationResponse - expErr bool + src *types.QueryValidatorDelegationRequest + mock poetesting.StakeContractMock + exp *types.QueryValidatorDelegationResponse + expErr bool }{ "delegation": { src: &types.QueryValidatorDelegationRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - var amount = 10 - return json.Marshal(contract.TG4MemberResponse{ - Weight: &amount, - }) + mock: poetesting.StakeContractMock{QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + var amount = sdk.NewInt(10) + return &amount, nil }}, exp: &types.QueryValidatorDelegationResponse{ Balance: sdk.NewCoin("utgd", sdk.NewInt(10)), @@ -328,14 +283,14 @@ func TestValidatorDelegation(t *testing.T) { }, "empty": { src: &types.QueryValidatorDelegationRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.TG4MemberResponse{}) + mock: poetesting.StakeContractMock{QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + return nil, nil }}, expErr: true, }, "error": { src: &types.QueryValidatorDelegationRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + mock: poetesting.StakeContractMock{QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { return nil, errors.New("testing") }}, expErr: true, @@ -344,9 +299,11 @@ func TestValidatorDelegation(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) - + poeKeeper.StakeContractFn = func(ctx sdk.Context) StakeContract { + return spec.mock + } // when - q := NewGrpcQuerier(contractSource, spec.querier) + q := NewGrpcQuerier(poeKeeper) gotRes, gotErr := q.ValidatorDelegation(ctx, spec.src) // then @@ -363,37 +320,24 @@ func TestValidatorDelegation(t *testing.T) { func TestValidatorUnbondingDelegations(t *testing.T) { var ( - myStakingContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) - myOperatorAddr sdk.AccAddress = rand.Bytes(sdk.AddrLen) - myTime = time.Now().UTC() - myHeight int64 = 123 + myOperatorAddr sdk.AccAddress = rand.Bytes(sdk.AddrLen) + myTime = time.Now().UTC() + myHeight int64 = 123 ) - contractSource := PoEKeeperMock{ - GetPoEContractAddressFn: func(ctx sdk.Context, ctype types.PoEContractType) (sdk.AccAddress, error) { - require.Equal(t, types.PoEContractTypeStaking, ctype) - return myStakingContract, nil - }, - GetBondDenomFn: func(ctx sdk.Context) string { return "utgd" }, - } + poeKeeper := PoEKeeperMock{} specs := map[string]struct { - src *types.QueryValidatorUnbondingDelegationsRequest - querier types.SmartQuerier - exp *types.QueryValidatorUnbondingDelegationsResponse - expErr bool + src *types.QueryValidatorUnbondingDelegationsRequest + mock poetesting.StakeContractMock + exp *types.QueryValidatorUnbondingDelegationsResponse + expErr bool }{ "one delegation": { src: &types.QueryValidatorUnbondingDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - - return json.Marshal(contract.TG4StakeClaimsResponse{ - Claims: []contract.TG4StakeClaim{{ - Amount: sdk.NewInt(10), - ReleaseAt: uint64(myTime.UnixNano()), - CreationHeight: uint64(myHeight), - }, - }, - }) + mock: poetesting.StakeContractMock{QueryStakingUnbondingFn: func(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { + return []stakingtypes.UnbondingDelegationEntry{ + {CompletionTime: myTime, Balance: sdk.NewInt(10), InitialBalance: sdk.NewInt(10), CreationHeight: myHeight}, + }, nil }}, exp: &types.QueryValidatorUnbondingDelegationsResponse{ Entries: []stakingtypes.UnbondingDelegationEntry{ @@ -403,20 +347,11 @@ func TestValidatorUnbondingDelegations(t *testing.T) { }, "multiple delegations": { src: &types.QueryValidatorUnbondingDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - - return json.Marshal(contract.TG4StakeClaimsResponse{ - Claims: []contract.TG4StakeClaim{{ - Amount: sdk.NewInt(10), - ReleaseAt: uint64(myTime.UnixNano()), - CreationHeight: uint64(myHeight), - }, { - Amount: sdk.NewInt(11), - ReleaseAt: uint64(myTime.Add(time.Minute).UnixNano()), - CreationHeight: uint64(myHeight + 1), - }, - }, - }) + mock: poetesting.StakeContractMock{QueryStakingUnbondingFn: func(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { + return []stakingtypes.UnbondingDelegationEntry{ + {CompletionTime: myTime, Balance: sdk.NewInt(10), InitialBalance: sdk.NewInt(10), CreationHeight: myHeight}, + {CompletionTime: myTime.Add(time.Minute), Balance: sdk.NewInt(11), InitialBalance: sdk.NewInt(11), CreationHeight: myHeight + 1}, + }, nil }}, exp: &types.QueryValidatorUnbondingDelegationsResponse{ Entries: []stakingtypes.UnbondingDelegationEntry{ @@ -427,14 +362,14 @@ func TestValidatorUnbondingDelegations(t *testing.T) { }, "none": { src: &types.QueryValidatorUnbondingDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.TG4StakeClaimsResponse{}) + mock: poetesting.StakeContractMock{QueryStakingUnbondingFn: func(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { + return []stakingtypes.UnbondingDelegationEntry{}, nil }}, - exp: &types.QueryValidatorUnbondingDelegationsResponse{}, + exp: &types.QueryValidatorUnbondingDelegationsResponse{Entries: []stakingtypes.UnbondingDelegationEntry{}}, }, "error": { src: &types.QueryValidatorUnbondingDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + mock: poetesting.StakeContractMock{QueryStakingUnbondingFn: func(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { return nil, errors.New("testing") }}, expErr: true, @@ -443,9 +378,11 @@ func TestValidatorUnbondingDelegations(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) - + poeKeeper.StakeContractFn = func(ctx sdk.Context) StakeContract { + return spec.mock + } // when - q := NewGrpcQuerier(contractSource, spec.querier) + q := NewGrpcQuerier(poeKeeper) gotRes, gotErr := q.ValidatorUnbondingDelegations(ctx, spec.src) // then @@ -471,7 +408,7 @@ func TestValidatorOutstandingReward(t *testing.T) { }{ "reward": { src: &types.QueryValidatorOutstandingRewardRequest{ValidatorAddress: anyAddr.String()}, - mock: DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { + mock: poetesting.DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { require.Equal(t, anyAddr, addr) return sdk.NewCoin("utgd", sdk.OneInt()), nil }}, @@ -481,14 +418,14 @@ func TestValidatorOutstandingReward(t *testing.T) { }, "not found": { src: &types.QueryValidatorOutstandingRewardRequest{ValidatorAddress: anyAddr.String()}, - mock: DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { + mock: poetesting.DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { return sdk.Coin{}, types.ErrNotFound }}, expErr: status.Error(codes.NotFound, "address"), }, "any error": { src: &types.QueryValidatorOutstandingRewardRequest{ValidatorAddress: anyAddr.String()}, - mock: DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { + mock: poetesting.DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { return sdk.Coin{}, errors.New("testing") }}, expErr: status.Error(codes.Internal, "testing"), @@ -502,7 +439,7 @@ func TestValidatorOutstandingReward(t *testing.T) { c := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) // when - s := NewGrpcQuerier(keeperMock, nil) + s := NewGrpcQuerier(keeperMock) gotResp, gotErr := s.ValidatorOutstandingReward(c, spec.src) // then if spec.expErr != nil { @@ -514,14 +451,3 @@ func TestValidatorOutstandingReward(t *testing.T) { }) } } - -type DistributionContractMock struct { - ValidatorOutstandingRewardFn func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) -} - -func (m DistributionContractMock) ValidatorOutstandingReward(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { - if m.ValidatorOutstandingRewardFn == nil { - panic("not expected to be called") - } - return m.ValidatorOutstandingRewardFn(ctx, addr) -} diff --git a/x/poe/keeper/keeper.go b/x/poe/keeper/keeper.go index da5673b6..19c28069 100644 --- a/x/poe/keeper/keeper.go +++ b/x/poe/keeper/keeper.go @@ -12,7 +12,6 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" - "github.com/confio/tgrade/x/poe/contract" "github.com/confio/tgrade/x/poe/types" ) @@ -86,16 +85,11 @@ func (k Keeper) GetPoESystemAdminAddress(ctx sdk.Context) sdk.AccAddress { // UnbondingTime returns the unbonding period from the staking contract func (k Keeper) UnbondingTime(ctx sdk.Context) time.Duration { - contractAddr, err := k.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) + rsp, err := k.StakeContract(ctx).QueryStakingUnbondingPeriod(ctx) if err != nil { - panic(fmt.Sprintf("get contract address: %s", err)) + panic(fmt.Sprintf("unboding period: %s", err)) } - - rsp, err := contract.QueryStakingUnbondingPeriod(ctx, k.twasmKeeper, contractAddr) - if err != nil { - panic(fmt.Sprintf("query unponding period from %s: %s", contractAddr.String(), err)) - } - return time.Duration(rsp) * time.Second + return rsp } func (k Keeper) GetBondDenom(ctx sdk.Context) string { diff --git a/x/poe/keeper/legacy_distribution_querier.go b/x/poe/keeper/legacy_distribution_querier.go index 6872295b..4a1b3cb4 100644 --- a/x/poe/keeper/legacy_distribution_querier.go +++ b/x/poe/keeper/legacy_distribution_querier.go @@ -15,13 +15,12 @@ import ( var _ distributiontypes.QueryServer = &legacyDistributionGRPCQuerier{} type legacyDistributionGRPCQuerier struct { - keeper ContractSource - contractQuerier types.SmartQuerier - queryServer types.QueryServer + keeper ContractSource + queryServer types.QueryServer } -func NewLegacyDistributionGRPCQuerier(keeper ViewKeeper, contractQuerier types.SmartQuerier) *legacyDistributionGRPCQuerier { //nolint:golint - return &legacyDistributionGRPCQuerier{keeper: keeper, contractQuerier: contractQuerier, queryServer: NewGrpcQuerier(keeper, contractQuerier)} +func NewLegacyDistributionGRPCQuerier(keeper ViewKeeper) *legacyDistributionGRPCQuerier { //nolint:golint + return &legacyDistributionGRPCQuerier{keeper: keeper, queryServer: NewGrpcQuerier(keeper)} } func (q legacyDistributionGRPCQuerier) ValidatorOutstandingRewards(c context.Context, req *distributiontypes.QueryValidatorOutstandingRewardsRequest) (*distributiontypes.QueryValidatorOutstandingRewardsResponse, error) { diff --git a/x/poe/keeper/legacy_distribution_querier_test.go b/x/poe/keeper/legacy_distribution_querier_test.go index bb867a87..7b936f6d 100644 --- a/x/poe/keeper/legacy_distribution_querier_test.go +++ b/x/poe/keeper/legacy_distribution_querier_test.go @@ -2,60 +2,46 @@ package keeper import ( "context" - "encoding/json" "errors" "testing" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/confio/tgrade/x/poe/keeper/poetesting" + + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + sdk "github.com/cosmos/cosmos-sdk/types" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/rand" - - "github.com/confio/tgrade/x/poe/contract" - "github.com/confio/tgrade/x/poe/types" ) func TestDelegatorValidators(t *testing.T) { - var myValsetContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) var myOperatorAddr sdk.AccAddress = rand.Bytes(sdk.AddrLen) - contractSource := PoEKeeperMock{ - GetPoEContractAddressFn: func(ctx sdk.Context, ctype types.PoEContractType) (sdk.AccAddress, error) { - require.Equal(t, types.PoEContractTypeValset, ctype) - return myValsetContract, nil - }, - } - specs := map[string]struct { - src *distributiontypes.QueryDelegatorValidatorsRequest - querier types.SmartQuerier - exp *distributiontypes.QueryDelegatorValidatorsResponse - expErr bool + src *distributiontypes.QueryDelegatorValidatorsRequest + mock poetesting.ValsetContractMock + exp *distributiontypes.QueryDelegatorValidatorsResponse + expErr bool }{ "delegation": { src: &distributiontypes.QueryDelegatorValidatorsRequest{DelegatorAddress: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - pubKey := ed25519.GenPrivKey().PubKey() - return json.Marshal(contract.ValidatorResponse{Validator: &contract.OperatorResponse{ - Operator: myOperatorAddr.String(), - Pubkey: contract.ValidatorPubkey{Ed25519: pubKey.Bytes()}, - Metadata: contract.MetadataFromDescription(types.ValidatorFixture().Description), - }}) + mock: poetesting.ValsetContractMock{QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { + return &stakingtypes.Validator{OperatorAddress: opAddr.String()}, nil }}, exp: &distributiontypes.QueryDelegatorValidatorsResponse{Validators: []string{myOperatorAddr.String()}}, }, "unknown": { src: &distributiontypes.QueryDelegatorValidatorsRequest{DelegatorAddress: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.ValidatorResponse{}) + mock: poetesting.ValsetContractMock{QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { + return nil, nil }}, exp: &distributiontypes.QueryDelegatorValidatorsResponse{Validators: []string{}}, }, "error": { src: &distributiontypes.QueryDelegatorValidatorsRequest{DelegatorAddress: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + mock: poetesting.ValsetContractMock{QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { return nil, errors.New("testing") }}, expErr: true, @@ -64,9 +50,10 @@ func TestDelegatorValidators(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) + poeKeeper := PoEKeeperMock{ValsetContractFn: func(ctx sdk.Context) ValsetContract { return spec.mock }} // when - q := NewLegacyDistributionGRPCQuerier(contractSource, spec.querier) + q := NewLegacyDistributionGRPCQuerier(poeKeeper) gotRes, gotErr := q.DelegatorValidators(ctx, spec.src) // then diff --git a/x/poe/keeper/legacy_slashing_querier.go b/x/poe/keeper/legacy_slashing_querier.go index 5e52970e..cbb90e1e 100644 --- a/x/poe/keeper/legacy_slashing_querier.go +++ b/x/poe/keeper/legacy_slashing_querier.go @@ -7,19 +7,16 @@ import ( slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - - "github.com/confio/tgrade/x/poe/types" ) var _ slashingtypes.QueryServer = &legacySlashingGRPCQuerier{} type legacySlashingGRPCQuerier struct { - keeper ContractSource - contractQuerier types.SmartQuerier + keeper ContractSource } -func NewLegacySlashingGRPCQuerier(keeper Keeper, contractQuerier types.SmartQuerier) *legacySlashingGRPCQuerier { - return &legacySlashingGRPCQuerier{keeper: keeper, contractQuerier: contractQuerier} +func NewLegacySlashingGRPCQuerier(keeper Keeper) *legacySlashingGRPCQuerier { //nolint:golint + return &legacySlashingGRPCQuerier{keeper: keeper} } // SigningInfo legacy support for cosmos-sdk signing info. Note that not all field are available on tgrade diff --git a/x/poe/keeper/legacy_staking_querier.go b/x/poe/keeper/legacy_staking_querier.go index 1ad50fcc..ea1251b8 100644 --- a/x/poe/keeper/legacy_staking_querier.go +++ b/x/poe/keeper/legacy_staking_querier.go @@ -2,14 +2,12 @@ package keeper import ( "context" - "time" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/confio/tgrade/x/poe/contract" "github.com/confio/tgrade/x/poe/types" ) @@ -18,16 +16,14 @@ var _ stakingtypes.QueryServer = &legacyStakingGRPCQuerier{} type stakingQuerierKeeper interface { ViewKeeper HistoricalEntries(ctx sdk.Context) uint32 - UnbondingTime(ctx sdk.Context) time.Duration } type legacyStakingGRPCQuerier struct { - keeper stakingQuerierKeeper - contractQuerier types.SmartQuerier - queryServer types.QueryServer + keeper stakingQuerierKeeper + queryServer types.QueryServer } -func NewLegacyStakingGRPCQuerier(poeKeeper stakingQuerierKeeper, q types.SmartQuerier) *legacyStakingGRPCQuerier { //nolint:golint - return &legacyStakingGRPCQuerier{keeper: poeKeeper, contractQuerier: q, queryServer: NewGrpcQuerier(poeKeeper, q)} +func NewLegacyStakingGRPCQuerier(poeKeeper stakingQuerierKeeper) *legacyStakingGRPCQuerier { //nolint:golint + return &legacyStakingGRPCQuerier{keeper: poeKeeper, queryServer: NewGrpcQuerier(poeKeeper)} } // Validators legacy support for querying all validators that match the given status @@ -269,19 +265,18 @@ func (q legacyStakingGRPCQuerier) Params(c context.Context, req *stakingtypes.Qu return nil, status.Error(codes.InvalidArgument, "empty request") } ctx := sdk.UnwrapSDKContext(c) - valsetContractAddr, err := q.keeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) + valsetConfig, err := q.keeper.ValsetContract(ctx).QueryConfig(ctx) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - valsetConfig, err := contract.QueryValsetConfig(ctx, q.contractQuerier, valsetContractAddr) + unbondingTime, err := q.keeper.StakeContract(ctx).QueryStakingUnbondingPeriod(ctx) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - return &stakingtypes.QueryParamsResponse{ Params: stakingtypes.Params{ - UnbondingTime: q.keeper.UnbondingTime(ctx), - MaxValidators: uint32(valsetConfig.MaxValidators), + UnbondingTime: unbondingTime, + MaxValidators: valsetConfig.MaxValidators, MaxEntries: 0, HistoricalEntries: q.keeper.HistoricalEntries(ctx), BondDenom: q.keeper.GetBondDenom(ctx), diff --git a/x/poe/keeper/legacy_staking_querier_test.go b/x/poe/keeper/legacy_staking_querier_test.go index 518ec9a9..c3377331 100644 --- a/x/poe/keeper/legacy_staking_querier_test.go +++ b/x/poe/keeper/legacy_staking_querier_test.go @@ -2,11 +2,12 @@ package keeper import ( "context" - "encoding/json" "errors" "testing" "time" + "github.com/confio/tgrade/x/poe/keeper/poetesting" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/assert" @@ -18,30 +19,23 @@ import ( ) func TestStakingValidatorDelegations(t *testing.T) { - var myStakingContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) var myOperatorAddr sdk.AccAddress = rand.Bytes(sdk.AddrLen) - contractSource := PoEKeeperMock{ - GetPoEContractAddressFn: func(ctx sdk.Context, ctype types.PoEContractType) (sdk.AccAddress, error) { - require.Equal(t, types.PoEContractTypeStaking, ctype) - return myStakingContract, nil - }, + poeKeeper := PoEKeeperMock{ GetBondDenomFn: func(ctx sdk.Context) string { return "utgd" }, } specs := map[string]struct { - src *stakingtypes.QueryValidatorDelegationsRequest - querier types.SmartQuerier - exp *stakingtypes.QueryValidatorDelegationsResponse - expErr bool + src *stakingtypes.QueryValidatorDelegationsRequest + mock poetesting.StakeContractMock + exp *stakingtypes.QueryValidatorDelegationsResponse + expErr bool }{ "delegation": { src: &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - var amount = 10 - return json.Marshal(contract.TG4MemberResponse{ - Weight: &amount, - }) + mock: poetesting.StakeContractMock{QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + var amount = sdk.NewInt(10) + return &amount, nil }}, exp: &stakingtypes.QueryValidatorDelegationsResponse{DelegationResponses: stakingtypes.DelegationResponses{ { @@ -56,14 +50,14 @@ func TestStakingValidatorDelegations(t *testing.T) { }, "empty": { src: &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.TG4MemberResponse{}) + mock: poetesting.StakeContractMock{QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + return nil, nil }}, exp: &stakingtypes.QueryValidatorDelegationsResponse{}, }, "error": { src: &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + mock: poetesting.StakeContractMock{QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { return nil, errors.New("testing") }}, expErr: true, @@ -72,9 +66,12 @@ func TestStakingValidatorDelegations(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) + poeKeeper.StakeContractFn = func(ctx sdk.Context) StakeContract { + return spec.mock + } // when - q := NewLegacyStakingGRPCQuerier(contractSource, spec.querier) + q := NewLegacyStakingGRPCQuerier(poeKeeper) gotRes, gotErr := q.ValidatorDelegations(ctx, spec.src) // then @@ -90,34 +87,25 @@ func TestStakingValidatorDelegations(t *testing.T) { } func TestStakingValidatorUnbondingDelegations(t *testing.T) { - var myStakingContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) - var myOperatorAddr sdk.AccAddress = rand.Bytes(sdk.AddrLen) + var ( + myOperatorAddr sdk.AccAddress = rand.Bytes(sdk.AddrLen) + myTime = time.Now().UTC() + myHeight int64 = 123 + ) - contractSource := PoEKeeperMock{ - GetPoEContractAddressFn: func(ctx sdk.Context, ctype types.PoEContractType) (sdk.AccAddress, error) { - require.Equal(t, types.PoEContractTypeStaking, ctype) - return myStakingContract, nil - }, - GetBondDenomFn: func(ctx sdk.Context) string { return "utgd" }, - } - anyTime := time.Now().UTC() + poeKeeper := PoEKeeperMock{} specs := map[string]struct { - src *stakingtypes.QueryValidatorUnbondingDelegationsRequest - querier types.SmartQuerier - exp *stakingtypes.QueryValidatorUnbondingDelegationsResponse - expErr bool + src *stakingtypes.QueryValidatorUnbondingDelegationsRequest + mock poetesting.StakeContractMock + exp *stakingtypes.QueryValidatorUnbondingDelegationsResponse + expErr bool }{ "one delegation": { src: &stakingtypes.QueryValidatorUnbondingDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - - return json.Marshal(contract.TG4StakeClaimsResponse{ - Claims: []contract.TG4StakeClaim{{ - Amount: sdk.NewInt(10), - ReleaseAt: uint64(anyTime.UnixNano()), - }, - }, - }) + mock: poetesting.StakeContractMock{QueryStakingUnbondingFn: func(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { + return []stakingtypes.UnbondingDelegationEntry{ + {CompletionTime: myTime, Balance: sdk.NewInt(10), InitialBalance: sdk.NewInt(10), CreationHeight: myHeight}, + }, nil }}, exp: &stakingtypes.QueryValidatorUnbondingDelegationsResponse{ UnbondingResponses: []stakingtypes.UnbondingDelegation{ @@ -125,25 +113,18 @@ func TestStakingValidatorUnbondingDelegations(t *testing.T) { DelegatorAddress: myOperatorAddr.String(), ValidatorAddress: myOperatorAddr.String(), Entries: []stakingtypes.UnbondingDelegationEntry{ - {CompletionTime: anyTime, Balance: sdk.NewInt(10), InitialBalance: sdk.NewInt(10)}, + {CompletionTime: myTime, Balance: sdk.NewInt(10), InitialBalance: sdk.NewInt(10), CreationHeight: myHeight}, }, }, }}, }, "multiple delegations": { src: &stakingtypes.QueryValidatorUnbondingDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - - return json.Marshal(contract.TG4StakeClaimsResponse{ - Claims: []contract.TG4StakeClaim{{ - Amount: sdk.NewInt(10), - ReleaseAt: uint64(anyTime.UnixNano()), - }, { - Amount: sdk.NewInt(11), - ReleaseAt: uint64(anyTime.Add(time.Minute).UnixNano()), - }, - }, - }) + mock: poetesting.StakeContractMock{QueryStakingUnbondingFn: func(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { + return []stakingtypes.UnbondingDelegationEntry{ + {CompletionTime: myTime, Balance: sdk.NewInt(10), InitialBalance: sdk.NewInt(10), CreationHeight: myHeight}, + {CompletionTime: myTime.Add(time.Minute), Balance: sdk.NewInt(11), InitialBalance: sdk.NewInt(11), CreationHeight: myHeight + 1}, + }, nil }}, exp: &stakingtypes.QueryValidatorUnbondingDelegationsResponse{ UnbondingResponses: []stakingtypes.UnbondingDelegation{ @@ -151,28 +132,29 @@ func TestStakingValidatorUnbondingDelegations(t *testing.T) { DelegatorAddress: myOperatorAddr.String(), ValidatorAddress: myOperatorAddr.String(), Entries: []stakingtypes.UnbondingDelegationEntry{ - {CompletionTime: anyTime, Balance: sdk.NewInt(10), InitialBalance: sdk.NewInt(10)}, - {CompletionTime: anyTime.Add(time.Minute), Balance: sdk.NewInt(11), InitialBalance: sdk.NewInt(11)}, + {CompletionTime: myTime, Balance: sdk.NewInt(10), InitialBalance: sdk.NewInt(10), CreationHeight: myHeight}, + {CompletionTime: myTime.Add(time.Minute), Balance: sdk.NewInt(11), InitialBalance: sdk.NewInt(11), CreationHeight: myHeight + 1}, }, }, }}, }, "none": { src: &stakingtypes.QueryValidatorUnbondingDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.TG4StakeClaimsResponse{}) + mock: poetesting.StakeContractMock{QueryStakingUnbondingFn: func(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { + return []stakingtypes.UnbondingDelegationEntry{}, nil }}, exp: &stakingtypes.QueryValidatorUnbondingDelegationsResponse{ UnbondingResponses: []stakingtypes.UnbondingDelegation{ { DelegatorAddress: myOperatorAddr.String(), ValidatorAddress: myOperatorAddr.String(), + Entries: []stakingtypes.UnbondingDelegationEntry{}, }, }}, }, "error": { src: &stakingtypes.QueryValidatorUnbondingDelegationsRequest{ValidatorAddr: myOperatorAddr.String()}, - querier: SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + mock: poetesting.StakeContractMock{QueryStakingUnbondingFn: func(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { return nil, errors.New("testing") }}, expErr: true, @@ -181,9 +163,12 @@ func TestStakingValidatorUnbondingDelegations(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) + poeKeeper.StakeContractFn = func(ctx sdk.Context) StakeContract { + return spec.mock + } // when - q := NewLegacyStakingGRPCQuerier(contractSource, spec.querier) + q := NewLegacyStakingGRPCQuerier(poeKeeper) gotRes, gotErr := q.ValidatorUnbondingDelegations(ctx, spec.src) // then @@ -201,23 +186,25 @@ func TestStakingValidatorUnbondingDelegations(t *testing.T) { func TestStakingParams(t *testing.T) { var myStakingContract sdk.AccAddress = rand.Bytes(sdk.AddrLen) - keeperMock := PoEKeeperMock{ + poeKeeper := PoEKeeperMock{ GetPoEContractAddressFn: func(ctx sdk.Context, ctype types.PoEContractType) (sdk.AccAddress, error) { require.Equal(t, types.PoEContractTypeValset, ctype) return myStakingContract, nil }, - GetBondDenomFn: func(ctx sdk.Context) string { return "utgd" }, - UnbondingTimeFn: func(ctx sdk.Context) time.Duration { return time.Hour }, - HistoricalEntriesFn: func(ctx sdk.Context) uint32 { - return 1 + GetBondDenomFn: func(ctx sdk.Context) string { return "utgd" }, + HistoricalEntriesFn: func(ctx sdk.Context) uint32 { return 1 }, + StakeContractFn: func(ctx sdk.Context) StakeContract { + return poetesting.StakeContractMock{QueryStakingUnbondingPeriodFn: func(ctx sdk.Context) (time.Duration, error) { + return time.Hour, nil + }} + }, + ValsetContractFn: func(ctx sdk.Context) ValsetContract { + return poetesting.ValsetContractMock{QueryConfigFn: func(ctx sdk.Context) (*contract.ValsetConfigResponse, error) { + return &contract.ValsetConfigResponse{MaxValidators: 2}, nil + }} }, } - smartQuerier := SmartQuerierMock{func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { - return json.Marshal(contract.ValsetConfigResponse{ - MaxValidators: 2, - }) - }} - q := NewLegacyStakingGRPCQuerier(keeperMock, smartQuerier) + q := NewLegacyStakingGRPCQuerier(poeKeeper) ctx := sdk.WrapSDKContext(sdk.Context{}.WithContext(context.Background())) gotRes, gotErr := q.Params(ctx, &stakingtypes.QueryParamsRequest{}) require.NoError(t, gotErr) diff --git a/x/poe/keeper/msg_server.go b/x/poe/keeper/msg_server.go index edf8acd3..583c1950 100644 --- a/x/poe/keeper/msg_server.go +++ b/x/poe/keeper/msg_server.go @@ -22,6 +22,7 @@ type PoEKeeper interface { ContractSource SetValidatorInitialEngagementPoints(ctx sdk.Context, address sdk.AccAddress, value sdk.Coin) error GetBondDenom(ctx sdk.Context) string + ValsetContract(ctx sdk.Context) ValsetContract } type msgServer struct { @@ -120,18 +121,11 @@ func (m msgServer) UpdateValidator(c context.Context, msg *types.MsgUpdateValida } // client sends a diff. we need to query the old description and merge it - current, err := contract.QueryValidator(ctx, m.twasmKeeper, valsetContractAddr, operatorAddress) + current, err := m.keeper.ValsetContract(ctx).QueryValidator(ctx, operatorAddress) if err != nil { - return nil, sdkerrors.Wrap(err, "query current description") + return nil, sdkerrors.Wrap(err, "query contract") } - if current == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownAddress, "operator") - } - validator, err := current.ToValidator() - if err != nil { - return nil, sdkerrors.Wrap(err, "to validator") - } - newDescr, err := validator.Description.UpdateDescription(msg.Description) + newDescr, err := current.Description.UpdateDescription(msg.Description) if err != nil { return nil, sdkerrors.Wrap(err, "merge description") } @@ -153,7 +147,6 @@ func (m msgServer) UpdateValidator(c context.Context, msg *types.MsgUpdateValida sdk.NewAttribute(types.AttributeKeyMoniker, msg.Description.Moniker), ), }) - return &types.MsgUpdateValidatorResponse{}, nil } diff --git a/x/poe/keeper/msg_server_test.go b/x/poe/keeper/msg_server_test.go index 25b27f13..38826a0c 100644 --- a/x/poe/keeper/msg_server_test.go +++ b/x/poe/keeper/msg_server_test.go @@ -6,6 +6,8 @@ import ( "strings" "testing" + "github.com/confio/tgrade/x/poe/keeper/poetesting" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" @@ -116,6 +118,14 @@ func TestUpdateValidator(t *testing.T) { SetValidatorInitialEngagementPointsFn: func(ctx sdk.Context, address sdk.AccAddress, value sdk.Coin) error { return nil }, + ValsetContractFn: func(ctx sdk.Context) ValsetContract { + return poetesting.ValsetContractMock{QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { + v := types.ValidatorFixture(func(m *stakingtypes.Validator) { + m.OperatorAddress = myOperatorAddr.String() + }) + return &v, nil + }} + }, } desc := contract.MetadataFromDescription(stakingtypes.Description{ @@ -134,6 +144,7 @@ func TestUpdateValidator(t *testing.T) { }} specs := map[string]struct { src *types.MsgUpdateValidator + mock poetesting.ValsetContractMock exp *contract.ValidatorMetadata expErr *sdkerrors.Error }{ diff --git a/x/poe/keeper/poetesting/mock_contracts.go b/x/poe/keeper/poetesting/mock_contracts.go new file mode 100644 index 00000000..627adf50 --- /dev/null +++ b/x/poe/keeper/poetesting/mock_contracts.go @@ -0,0 +1,80 @@ +package poetesting + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/confio/tgrade/x/poe/contract" +) + +//var _ keeper.DistributionContract = DistributionContractMock{} + +type DistributionContractMock struct { + ValidatorOutstandingRewardFn func(ctx types.Context, addr types.AccAddress) (types.Coin, error) +} + +func (m DistributionContractMock) ValidatorOutstandingReward(ctx types.Context, addr types.AccAddress) (types.Coin, error) { + if m.ValidatorOutstandingRewardFn == nil { + panic("not expected to be called") + } + return m.ValidatorOutstandingRewardFn(ctx, addr) +} + +//var _ keeper.ValsetContract = ValsetContractMock{} + +type ValsetContractMock struct { + QueryValidatorFn func(ctx types.Context, opAddr types.AccAddress) (*stakingtypes.Validator, error) + ListValidatorsFn func(ctx types.Context) ([]stakingtypes.Validator, error) + QueryConfigFn func(ctx types.Context) (*contract.ValsetConfigResponse, error) +} + +func (m ValsetContractMock) QueryValidator(ctx types.Context, opAddr types.AccAddress) (*stakingtypes.Validator, error) { + if m.QueryValidatorFn == nil { + panic("not expected to be called") + } + return m.QueryValidatorFn(ctx, opAddr) +} + +func (m ValsetContractMock) ListValidators(ctx types.Context) ([]stakingtypes.Validator, error) { + if m.ListValidatorsFn == nil { + panic("not expected to be called") + } + return m.ListValidatorsFn(ctx) +} + +func (m ValsetContractMock) QueryConfig(ctx types.Context) (*contract.ValsetConfigResponse, error) { + if m.QueryConfigFn == nil { + panic("not expected to be called") + } + return m.QueryConfigFn(ctx) +} + +//var _ keeper.StakeContract = StakeContractMock{} + +type StakeContractMock struct { + QueryStakingUnbondingPeriodFn func(ctx types.Context) (time.Duration, error) + QueryStakingUnbondingFn func(ctx types.Context, opAddr types.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) + QueryStakedAmountFn func(ctx types.Context, opAddr types.AccAddress) (*types.Int, error) +} + +func (m StakeContractMock) QueryStakedAmount(ctx types.Context, opAddr types.AccAddress) (*types.Int, error) { + if m.QueryStakedAmountFn == nil { + panic("not expected to be called") + } + return m.QueryStakedAmountFn(ctx, opAddr) +} + +func (m StakeContractMock) QueryStakingUnbondingPeriod(ctx types.Context) (time.Duration, error) { + if m.QueryStakingUnbondingPeriodFn == nil { + panic("not expected to be called") + } + return m.QueryStakingUnbondingPeriodFn(ctx) +} +func (m StakeContractMock) QueryStakingUnbonding(ctx types.Context, opAddr types.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) { + if m.QueryStakingUnbondingFn == nil { + panic("not expected to be called") + } + return m.QueryStakingUnbondingFn(ctx, opAddr) +} diff --git a/x/poe/keeper/test_mocks.go b/x/poe/keeper/test_mocks.go index 4c23d123..1e7351fc 100644 --- a/x/poe/keeper/test_mocks.go +++ b/x/poe/keeper/test_mocks.go @@ -11,7 +11,7 @@ import ( ) var _ PoEKeeper = PoEKeeperMock{} -var _ stakingQuerierKeeper = PoEKeeperMock{} +var _ stakingQuerierKeeper = &PoEKeeperMock{} // PoEKeeperMock mocks Keeper methods type PoEKeeperMock struct { @@ -25,6 +25,8 @@ type PoEKeeperMock struct { UnbondingTimeFn func(ctx sdk.Context) time.Duration GetHistoricalInfoFn func(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) DistributionContractFn func(ctx sdk.Context) DistributionContract + ValsetContractFn func(ctx sdk.Context) ValsetContract + StakeContractFn func(ctx sdk.Context) StakeContract } func (m PoEKeeperMock) setParams(ctx sdk.Context, params types.Params) { @@ -61,6 +63,7 @@ func (m PoEKeeperMock) GetBondDenom(ctx sdk.Context) string { } return m.GetBondDenomFn(ctx) } + func (m PoEKeeperMock) HistoricalEntries(ctx sdk.Context) uint32 { if m.HistoricalEntriesFn == nil { panic("not expected to be called") @@ -68,18 +71,39 @@ func (m PoEKeeperMock) HistoricalEntries(ctx sdk.Context) uint32 { return m.HistoricalEntriesFn(ctx) } -func (m PoEKeeperMock) UnbondingTime(ctx sdk.Context) time.Duration { - if m.UnbondingTimeFn == nil { +func (m PoEKeeperMock) GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) { + if m.GetHistoricalInfoFn == nil { panic("not expected to be called") } - return m.UnbondingTimeFn(ctx) + return m.GetHistoricalInfoFn(ctx, height) } -func (m PoEKeeperMock) GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) { - if m.GetHistoricalInfoFn == nil { +func (m PoEKeeperMock) SetValidatorInitialEngagementPoints(ctx sdk.Context, opAddr sdk.AccAddress, points sdk.Coin) error { + if m.SetValidatorInitialEngagementPointsFn == nil { panic("not expected to be called") } - return m.GetHistoricalInfoFn(ctx, height) + return m.SetValidatorInitialEngagementPointsFn(ctx, opAddr, points) +} + +func (m PoEKeeperMock) DistributionContract(ctx sdk.Context) DistributionContract { + if m.DistributionContractFn == nil { + panic("not expected to be called") + } + return m.DistributionContractFn(ctx) +} + +func (m PoEKeeperMock) StakeContract(ctx sdk.Context) StakeContract { + if m.StakeContractFn == nil { + panic("not expected to be called") + } + return m.StakeContractFn(ctx) +} + +func (m PoEKeeperMock) ValsetContract(ctx sdk.Context) ValsetContract { + if m.ValsetContractFn == nil { + panic("not expected to be called") + } + return m.ValsetContractFn(ctx) } // CapturedPoEContractAddress data type @@ -110,20 +134,6 @@ func (m SmartQuerierMock) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddres return m.QuerySmartFn(ctx, contractAddr, req) } -func (m PoEKeeperMock) SetValidatorInitialEngagementPoints(ctx sdk.Context, opAddr sdk.AccAddress, points sdk.Coin) error { - if m.SetValidatorInitialEngagementPointsFn == nil { - panic("not expected to be called") - } - return m.SetValidatorInitialEngagementPointsFn(ctx, opAddr, points) -} - -func (m PoEKeeperMock) DistributionContract(ctx sdk.Context) DistributionContract { - if m.DistributionContractFn == nil { - panic("not expected to be called") - } - return m.DistributionContractFn(ctx) -} - // return matching type or fail func newContractSourceMock(t *testing.T, myValsetContract sdk.AccAddress, myStakingContract sdk.AccAddress) PoEKeeperMock { return PoEKeeperMock{ diff --git a/x/poe/module.go b/x/poe/module.go index 0ddc27d4..6e7ba75b 100644 --- a/x/poe/module.go +++ b/x/poe/module.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "sync" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" "github.com/cosmos/cosmos-sdk/client" @@ -102,7 +101,6 @@ type AppModule struct { twasmKeeper twasmKeeper contractKeeper wasmtypes.ContractOpsKeeper poeKeeper keeper.Keeper - doOnce sync.Once } // twasmKeeper subset of keeper to decouple from twasm module @@ -142,13 +140,13 @@ func (am AppModule) LegacyQuerierHandler(amino *codec.LegacyAmino) sdk.Querier { } func (am AppModule) RegisterServices(cfg module.Configurator) { - types.RegisterQueryServer(cfg.QueryServer(), keeper.NewGrpcQuerier(am.poeKeeper, am.twasmKeeper)) + types.RegisterQueryServer(cfg.QueryServer(), keeper.NewGrpcQuerier(am.poeKeeper)) types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.poeKeeper, am.contractKeeper, am.twasmKeeper)) // support cosmos query path - stakingtypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewLegacyStakingGRPCQuerier(am.poeKeeper, am.twasmKeeper)) - slashingtypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewLegacySlashingGRPCQuerier(am.poeKeeper, am.twasmKeeper)) - distributiontypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewLegacyDistributionGRPCQuerier(am.poeKeeper, am.twasmKeeper)) + stakingtypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewLegacyStakingGRPCQuerier(am.poeKeeper)) + slashingtypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewLegacySlashingGRPCQuerier(am.poeKeeper)) + distributiontypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewLegacyDistributionGRPCQuerier(am.poeKeeper)) } func (am AppModule) BeginBlock(ctx sdk.Context, block abci.RequestBeginBlock) { @@ -156,7 +154,7 @@ func (am AppModule) BeginBlock(ctx sdk.Context, block abci.RequestBeginBlock) { } func (am AppModule) EndBlock(ctx sdk.Context, block abci.RequestEndBlock) []abci.ValidatorUpdate { - am.doOnce.Do(ClearEmbeddedContracts) // release memory + ClearEmbeddedContracts() // release memory return EndBlocker(ctx, am.twasmKeeper) } @@ -171,11 +169,11 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data j if genesisState.SeedContracts { if err := bootstrapPoEContracts(ctx, am.contractKeeper, am.twasmKeeper, am.poeKeeper, genesisState); err != nil { - panic(fmt.Sprintf("bootstrap PoE contracts: %s", err)) + panic(fmt.Sprintf("bootstrap PoE contracts: %+v", err)) } } else { if err := verifyPoEContracts(ctx, am.contractKeeper, am.twasmKeeper, am.poeKeeper, genesisState); err != nil { - panic(fmt.Sprintf("verify PoE bootstrap contracts: %s", err)) + panic(fmt.Sprintf("verify PoE bootstrap contracts: %+v", err)) } } diff --git a/x/poe/module_test.go b/x/poe/module_test.go index e3e7ff0c..6114b53c 100644 --- a/x/poe/module_test.go +++ b/x/poe/module_test.go @@ -59,10 +59,7 @@ func TestInitGenesis(t *testing.T) { assert.Equal(t, myValidators.expStakingGroup(), gotMembers) // and valset config - addr, err = example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeValset) - require.NoError(t, err) - - gotValsetConfig, err := contract.QueryValsetConfig(ctx, example.TWasmKeeper, addr) + gotValsetConfig, err := example.PoEKeeper.ValsetContract(ctx).QueryConfig(ctx) require.NoError(t, err) mixerAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeMixer)