From 506ae27e5d827293a22028d1a1523247d5497d00 Mon Sep 17 00:00:00 2001 From: zakir <80246097+zakir-code@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:15:29 +0800 Subject: [PATCH] test: refactor staking precompile test suite (#792) --- contract/contract.go | 12 + contract/erc20_token.go | 18 +- .../types/contract.go => contract/staking.go | 94 +-- contract/staking_precompile.go | 157 +++++ tests/staking_suite.go | 45 +- testutil/helpers/base_suite.go | 8 +- testutil/helpers/contract_base_suite.go | 6 +- testutil/helpers/staking_precompile_suite.go | 114 ++++ testutil/helpers/staking_suite.go | 80 +++ x/staking/precompile/allowance_shares.go | 8 +- x/staking/precompile/allowance_shares_test.go | 99 +--- x/staking/precompile/approve_shares.go | 7 +- x/staking/precompile/approve_shares_test.go | 103 +--- x/staking/precompile/contract_test.go | 453 ++------------- x/staking/precompile/delegate.go | 7 +- x/staking/precompile/delegate_test.go | 326 +++-------- x/staking/precompile/delegation.go | 8 +- x/staking/precompile/delegation_rewards.go | 8 +- .../precompile/delegation_rewards_test.go | 109 +--- x/staking/precompile/delegation_test.go | 112 +--- x/staking/precompile/keeper.go | 6 +- x/staking/precompile/redelegate.go | 11 +- x/staking/precompile/redelegate_test.go | 147 +++-- x/staking/precompile/slashing_info.go | 24 +- x/staking/precompile/slashing_info_test.go | 75 +-- x/staking/precompile/transfer_shares.go | 13 +- x/staking/precompile/transfer_shares_test.go | 537 ++++++++---------- x/staking/precompile/undelegate.go | 7 +- x/staking/precompile/undelegate_test.go | 121 ++-- x/staking/precompile/validator_list.go | 12 +- x/staking/precompile/validator_list_test.go | 93 +-- x/staking/precompile/withdraw.go | 7 +- x/staking/precompile/withdraw_test.go | 108 +--- x/staking/testutil/staking.go | 69 --- x/staking/testutil/staking_precompile.go | 135 ----- 35 files changed, 1132 insertions(+), 2007 deletions(-) rename x/staking/types/contract.go => contract/staking.go (83%) create mode 100644 contract/staking_precompile.go create mode 100644 testutil/helpers/staking_precompile_suite.go create mode 100644 testutil/helpers/staking_suite.go delete mode 100644 x/staking/testutil/staking.go delete mode 100644 x/staking/testutil/staking_precompile.go diff --git a/contract/contract.go b/contract/contract.go index 9107cc64..fda2f780 100644 --- a/contract/contract.go +++ b/contract/contract.go @@ -5,6 +5,7 @@ import ( "math/big" "strings" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -149,3 +150,14 @@ func PackBridgeCallCheckpoint(gravityID, methodName [32]byte, sender, refund com eventNonce, ) } + +func unpackRetIsOk(abi abi.ABI, method string, res *evmtypes.MsgEthereumTxResponse) (*evmtypes.MsgEthereumTxResponse, error) { + var ret struct{ Value bool } + if err := abi.UnpackIntoInterface(&ret, method, res.Ret); err != nil { + return res, sdkerrors.ErrInvalidType.Wrapf("failed to unpack %s: %s", method, err.Error()) + } + if !ret.Value { + return res, sdkerrors.ErrLogic.Wrapf("failed to execute %s", method) + } + return res, nil +} diff --git a/contract/erc20_token.go b/contract/erc20_token.go index 03986594..195cd885 100644 --- a/contract/erc20_token.go +++ b/contract/erc20_token.go @@ -4,7 +4,6 @@ import ( "context" "math/big" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/evmos/ethermint/x/evm/types" @@ -80,23 +79,12 @@ func (k ERC20TokenKeeper) Allowance(ctx context.Context, contractAddr, owner, sp return allowanceRes.Value, nil } -func (k ERC20TokenKeeper) unpackRet(method string, res *types.MsgEthereumTxResponse) (*types.MsgEthereumTxResponse, error) { - var result struct{ Value bool } - if err := k.abi.UnpackIntoInterface(&result, method, res.Ret); err != nil { - return res, sdkerrors.ErrInvalidType.Wrapf("failed to unpack transfer: %s", err.Error()) - } - if !result.Value { - return res, sdkerrors.ErrLogic.Wrapf("failed to execute %s", method) - } - return res, nil -} - func (k ERC20TokenKeeper) Approve(ctx context.Context, contractAddr, from, spender common.Address, amount *big.Int) (*types.MsgEthereumTxResponse, error) { res, err := k.ApplyContract(ctx, from, contractAddr, nil, k.abi, "approve", spender, amount) if err != nil { return nil, err } - return k.unpackRet("approve", res) + return unpackRetIsOk(k.abi, "approve", res) } // PackMint only used for testing @@ -117,7 +105,7 @@ func (k ERC20TokenKeeper) Transfer(ctx context.Context, contractAddr, from, rece if err != nil { return nil, err } - return k.unpackRet("transfer", res) + return unpackRetIsOk(k.abi, "transfer", res) } func (k ERC20TokenKeeper) TransferFrom(ctx context.Context, contractAddr, from, sender, receiver common.Address, amount *big.Int) (*types.MsgEthereumTxResponse, error) { @@ -125,7 +113,7 @@ func (k ERC20TokenKeeper) TransferFrom(ctx context.Context, contractAddr, from, if err != nil { return nil, err } - return k.unpackRet("transferFrom", res) + return unpackRetIsOk(k.abi, "transferFrom", res) } func (k ERC20TokenKeeper) TransferOwnership(ctx context.Context, contractAddr, owner, newOwner common.Address) (*types.MsgEthereumTxResponse, error) { diff --git a/x/staking/types/contract.go b/contract/staking.go similarity index 83% rename from x/staking/types/contract.go rename to contract/staking.go index d4a11bb6..ece3da57 100644 --- a/x/staking/types/contract.go +++ b/contract/staking.go @@ -1,4 +1,4 @@ -package types +package contract import ( "errors" @@ -59,24 +59,6 @@ func (args *ApproveSharesArgs) GetValidator() sdk.ValAddress { return valAddr } -type DelegateArgs struct { - Validator string `abi:"_val"` -} - -// Validate validates the args -func (args *DelegateArgs) Validate() error { - if _, err := sdk.ValAddressFromBech32(args.Validator); err != nil { - return fmt.Errorf("invalid validator address: %s", args.Validator) - } - return nil -} - -// GetValidator returns the validator address, caller must ensure the validator address is valid -func (args *DelegateArgs) GetValidator() sdk.ValAddress { - valAddr, _ := sdk.ValAddressFromBech32(args.Validator) - return valAddr -} - type DelegateV2Args struct { Validator string `abi:"_val"` Amount *big.Int `abi:"_amount"` @@ -93,6 +75,12 @@ func (args *DelegateV2Args) Validate() error { return nil } +// GetValidator returns the validator address, caller must ensure the validator address is valid +func (args *DelegateV2Args) GetValidator() sdk.ValAddress { + valAddr, _ := sdk.ValAddressFromBech32(args.Validator) + return valAddr +} + type DelegationArgs struct { Validator string `abi:"_val"` Delegator common.Address `abi:"_del"` @@ -131,58 +119,38 @@ func (args *DelegationRewardsArgs) GetValidator() sdk.ValAddress { return valAddr } -type RedelegateArgs struct { +type RedelegateV2Args struct { ValidatorSrc string `abi:"_valSrc"` ValidatorDst string `abi:"_valDst"` - Shares *big.Int `abi:"_shares"` + Amount *big.Int `abi:"_amount"` } // Validate validates the args -func (args *RedelegateArgs) Validate() error { +func (args *RedelegateV2Args) Validate() error { if _, err := sdk.ValAddressFromBech32(args.ValidatorSrc); err != nil { return fmt.Errorf("invalid validator src address: %s", args.ValidatorSrc) } if _, err := sdk.ValAddressFromBech32(args.ValidatorDst); err != nil { return fmt.Errorf("invalid validator dst address: %s", args.ValidatorDst) } - if args.Shares == nil || args.Shares.Sign() <= 0 { - return errors.New("invalid shares") + if args.Amount == nil || args.Amount.Sign() <= 0 { + return errors.New("invalid amount") } return nil } // GetValidatorSrc returns the validator src address, caller must ensure the validator address is valid -func (args *RedelegateArgs) GetValidatorSrc() sdk.ValAddress { +func (args *RedelegateV2Args) GetValidatorSrc() sdk.ValAddress { valAddr, _ := sdk.ValAddressFromBech32(args.ValidatorSrc) return valAddr } // GetValidatorDst returns the validator dest address, caller must ensure the validator address is valid -func (args *RedelegateArgs) GetValidatorDst() sdk.ValAddress { +func (args *RedelegateV2Args) GetValidatorDst() sdk.ValAddress { valAddr, _ := sdk.ValAddressFromBech32(args.ValidatorDst) return valAddr } -type RedelegateV2Args struct { - ValidatorSrc string `abi:"_valSrc"` - ValidatorDst string `abi:"_valDst"` - Amount *big.Int `abi:"_amount"` -} - -// Validate validates the args -func (args *RedelegateV2Args) Validate() error { - if _, err := sdk.ValAddressFromBech32(args.ValidatorSrc); err != nil { - return fmt.Errorf("invalid validator src address: %s", args.ValidatorSrc) - } - if _, err := sdk.ValAddressFromBech32(args.ValidatorDst); err != nil { - return fmt.Errorf("invalid validator dst address: %s", args.ValidatorDst) - } - if args.Amount == nil || args.Amount.Sign() <= 0 { - return errors.New("invalid amount") - } - return nil -} - type TransferSharesArgs struct { Validator string `abi:"_val"` To common.Address `abi:"_to"` @@ -206,6 +174,11 @@ func (args *TransferSharesArgs) GetValidator() sdk.ValAddress { return valAddr } +type TransferSharesRet struct { + Token *big.Int + Reward *big.Int +} + type TransferFromSharesArgs struct { Validator string `abi:"_val"` From common.Address `abi:"_from"` @@ -230,26 +203,9 @@ func (args *TransferFromSharesArgs) GetValidator() sdk.ValAddress { return valAddr } -type UndelegateArgs struct { - Validator string `abi:"_val"` - Shares *big.Int `abi:"_shares"` -} - -// Validate validates the args -func (args *UndelegateArgs) Validate() error { - if _, err := sdk.ValAddressFromBech32(args.Validator); err != nil { - return fmt.Errorf("invalid validator address: %s", args.Validator) - } - if args.Shares == nil || args.Shares.Sign() <= 0 { - return errors.New("invalid shares") - } - return nil -} - -// GetValidator returns the validator address, caller must ensure the validator address is valid -func (args *UndelegateArgs) GetValidator() sdk.ValAddress { - valAddr, _ := sdk.ValAddressFromBech32(args.Validator) - return valAddr +type TransferFromSharesRet struct { + Token *big.Int + Reward *big.Int } type UndelegateV2Args struct { @@ -268,6 +224,12 @@ func (args *UndelegateV2Args) Validate() error { return nil } +// GetValidator returns the validator address, caller must ensure the validator address is valid +func (args *UndelegateV2Args) GetValidator() sdk.ValAddress { + valAddr, _ := sdk.ValAddressFromBech32(args.Validator) + return valAddr +} + type WithdrawArgs struct { Validator string `abi:"_val"` } diff --git a/contract/staking_precompile.go b/contract/staking_precompile.go new file mode 100644 index 00000000..51235a8d --- /dev/null +++ b/contract/staking_precompile.go @@ -0,0 +1,157 @@ +package contract + +import ( + "context" + "math/big" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" +) + +type StakingPrecompileKeeper struct { + Caller + abi abi.ABI + contractAddr common.Address +} + +func NewStakingPrecompileKeeper(caller Caller, contractAddr common.Address) StakingPrecompileKeeper { + if IsZeroEthAddress(contractAddr) { + contractAddr = common.HexToAddress(StakingAddress) + } + return StakingPrecompileKeeper{ + Caller: caller, + abi: MustABIJson(IStakingMetaData.ABI), + contractAddr: contractAddr, + } +} + +func (k StakingPrecompileKeeper) WithContractAddr(c common.Address) StakingPrecompileKeeper { + stakingPrecompileKeeper := k + stakingPrecompileKeeper.contractAddr = c + return stakingPrecompileKeeper +} + +func (k StakingPrecompileKeeper) AllowanceShares(ctx context.Context, args AllowanceSharesArgs) (*big.Int, error) { + var output struct { + Shares *big.Int + } + err := k.QueryContract(ctx, common.Address{}, k.contractAddr, k.abi, "allowanceShares", &output, args.Validator, args.Owner, args.Spender) + if err != nil { + return nil, err + } + return output.Shares, nil +} + +func (k StakingPrecompileKeeper) Delegation(ctx context.Context, args DelegationArgs) (*big.Int, *big.Int, error) { + var output struct { + Shares *big.Int + DelegateAmount *big.Int + } + err := k.QueryContract(ctx, common.Address{}, k.contractAddr, k.abi, "delegation", &output, args.Validator, args.Delegator) + if err != nil { + return nil, nil, err + } + return output.Shares, output.DelegateAmount, nil +} + +func (k StakingPrecompileKeeper) DelegationRewards(ctx context.Context, args DelegationRewardsArgs) (*big.Int, error) { + var output struct { + Rewards *big.Int + } + err := k.QueryContract(ctx, common.Address{}, k.contractAddr, k.abi, "delegationRewards", &output, args.Validator, args.Delegator) + if err != nil { + return nil, err + } + return output.Rewards, nil +} + +func (k StakingPrecompileKeeper) ValidatorList(ctx context.Context, args ValidatorListArgs) ([]string, error) { + var valList []string + err := k.QueryContract(ctx, common.Address{}, k.contractAddr, k.abi, "validatorList", &valList, args.SortBy) + if err != nil { + return nil, err + } + return valList, nil +} + +func (k StakingPrecompileKeeper) SlashingInfo(ctx context.Context, args SlashingInfoArgs) (bool, *big.Int, error) { + var output struct { + Jailed bool + Missed *big.Int + } + err := k.QueryContract(ctx, common.Address{}, k.contractAddr, k.abi, "slashingInfo", &output, args.Validator) + if err != nil { + return false, nil, err + } + return output.Jailed, output.Missed, nil +} + +func (k StakingPrecompileKeeper) ApproveShares(ctx context.Context, from common.Address, args ApproveSharesArgs) (*evmtypes.MsgEthereumTxResponse, error) { + res, err := k.ApplyContract(ctx, from, k.contractAddr, nil, k.abi, "approveShares", args.Validator, args.Spender, args.Shares) + if err != nil { + return nil, err + } + return unpackRetIsOk(k.abi, "approveShares", res) +} + +func (k StakingPrecompileKeeper) TransferShares(ctx context.Context, from common.Address, args TransferSharesArgs) (*evmtypes.MsgEthereumTxResponse, *TransferSharesRet, error) { + res, err := k.ApplyContract(ctx, from, k.contractAddr, nil, k.abi, "transferShares", args.Validator, args.To, args.Shares) + if err != nil { + return nil, nil, err + } + ret := new(TransferSharesRet) + if err = k.abi.UnpackIntoInterface(ret, "transferShares", res.Ret); err != nil { + return res, nil, sdkerrors.ErrInvalidType.Wrapf("failed to unpack transferShares: %s", err.Error()) + } + return res, ret, nil +} + +func (k StakingPrecompileKeeper) TransferFromShares(ctx context.Context, from common.Address, args TransferFromSharesArgs) (*evmtypes.MsgEthereumTxResponse, *TransferFromSharesRet, error) { + res, err := k.ApplyContract(ctx, from, k.contractAddr, nil, k.abi, "transferFromShares", args.Validator, args.From, args.To, args.Shares) + if err != nil { + return nil, nil, err + } + ret := new(TransferFromSharesRet) + if err = k.abi.UnpackIntoInterface(ret, "transferFromShares", res.Ret); err != nil { + return res, nil, sdkerrors.ErrInvalidType.Wrapf("failed to unpack transferFromShares: %s", err.Error()) + } + return res, ret, nil +} + +func (k StakingPrecompileKeeper) Withdraw(ctx context.Context, from common.Address, args WithdrawArgs) (*evmtypes.MsgEthereumTxResponse, *big.Int, error) { + res, err := k.ApplyContract(ctx, from, k.contractAddr, nil, k.abi, "withdraw", args.Validator) + if err != nil { + return nil, nil, err + } + ret := struct{ Reward *big.Int }{} + if err = k.abi.UnpackIntoInterface(&ret, "withdraw", res.Ret); err != nil { + return res, nil, sdkerrors.ErrInvalidType.Wrapf("failed to unpack withdraw: %s", err.Error()) + } + return res, ret.Reward, nil +} + +func (k StakingPrecompileKeeper) DelegateV2(ctx context.Context, from common.Address, args DelegateV2Args) (*evmtypes.MsgEthereumTxResponse, error) { + res, err := k.ApplyContract(ctx, from, k.contractAddr, nil, k.abi, "delegateV2", args.Validator, args.Amount) + if err != nil { + return nil, err + } + return unpackRetIsOk(k.abi, "delegateV2", res) +} + +func (k StakingPrecompileKeeper) RedelegateV2(ctx context.Context, from common.Address, args RedelegateV2Args) (*evmtypes.MsgEthereumTxResponse, error) { + res, err := k.ApplyContract(ctx, from, k.contractAddr, nil, k.abi, "redelegateV2", args.ValidatorSrc, args.ValidatorDst, args.Amount) + if err != nil { + return nil, err + } + return unpackRetIsOk(k.abi, "redelegateV2", res) +} + +func (k StakingPrecompileKeeper) UndelegateV2(ctx context.Context, from common.Address, args UndelegateV2Args) (*evmtypes.MsgEthereumTxResponse, error) { + res, err := k.ApplyContract(ctx, from, k.contractAddr, nil, k.abi, "undelegateV2", args.Validator, args.Amount) + if err != nil { + return nil, err + } + return unpackRetIsOk(k.abi, "undelegateV2", res) +} diff --git a/tests/staking_suite.go b/tests/staking_suite.go index 5ea4d142..13798e2e 100644 --- a/tests/staking_suite.go +++ b/tests/staking_suite.go @@ -21,8 +21,7 @@ import ( "github.com/functionx/fx-core/v8/contract" testscontract "github.com/functionx/fx-core/v8/tests/contract" "github.com/functionx/fx-core/v8/testutil/helpers" - stakingprecompile "github.com/functionx/fx-core/v8/x/staking/precompile" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" + "github.com/functionx/fx-core/v8/x/staking/precompile" ) type StakingSuite struct { @@ -113,15 +112,15 @@ func (suite *StakingSuite) send(privateKey cryptotypes.PrivKey, value *big.Int, } func (suite *StakingSuite) DelegateV2(privateKey cryptotypes.PrivKey, valAddr string, delAmount *big.Int) *ethtypes.Receipt { - method := stakingprecompile.NewDelegateV2Method(nil) - pack, err := method.PackInput(fxstakingtypes.DelegateV2Args{Validator: valAddr, Amount: delAmount}) + method := precompile.NewDelegateV2Method(nil) + pack, err := method.PackInput(contract.DelegateV2Args{Validator: valAddr, Amount: delAmount}) suite.Require().NoError(err) return suite.send(privateKey, big.NewInt(0), pack) } func (suite *StakingSuite) RedelegateV2(privateKey cryptotypes.PrivKey, valSrc, valDst string, amount *big.Int) *ethtypes.Receipt { - method := stakingprecompile.NewRedelegateV2Method(nil) - pack, err := method.PackInput(fxstakingtypes.RedelegateV2Args{ValidatorSrc: valSrc, ValidatorDst: valDst, Amount: amount}) + method := precompile.NewRedelegateV2Method(nil) + pack, err := method.PackInput(contract.RedelegateV2Args{ValidatorSrc: valSrc, ValidatorDst: valDst, Amount: amount}) suite.Require().NoError(err) return suite.send(privateKey, nil, pack) } @@ -144,22 +143,22 @@ func (suite *StakingSuite) WithdrawByContract(privateKey cryptotypes.PrivKey, co } func (suite *StakingSuite) UnDelegateV2(privateKey cryptotypes.PrivKey, valAddr string, amount *big.Int) *ethtypes.Receipt { - method := stakingprecompile.NewUndelegateV2Method(nil) - pack, err := method.PackInput(fxstakingtypes.UndelegateV2Args{Validator: valAddr, Amount: amount}) + method := precompile.NewUndelegateV2Method(nil) + pack, err := method.PackInput(contract.UndelegateV2Args{Validator: valAddr, Amount: amount}) suite.Require().NoError(err) return suite.send(privateKey, nil, pack) } func (suite *StakingSuite) WithdrawReward(privateKey cryptotypes.PrivKey, valAddr string) *ethtypes.Receipt { - method := stakingprecompile.NewWithdrawMethod(nil) - pack, err := method.PackInput(fxstakingtypes.WithdrawArgs{Validator: valAddr}) + method := precompile.NewWithdrawMethod(nil) + pack, err := method.PackInput(contract.WithdrawArgs{Validator: valAddr}) suite.Require().NoError(err) return suite.send(privateKey, nil, pack) } func (suite *StakingSuite) Delegation(valAddr string, delAddr common.Address) (*big.Int, *big.Int) { - method := stakingprecompile.NewDelegationMethod(nil) - pack, err := method.PackInput(fxstakingtypes.DelegationArgs{Validator: valAddr, Delegator: delAddr}) + method := precompile.NewDelegationMethod(nil) + pack, err := method.PackInput(contract.DelegationArgs{Validator: valAddr, Delegator: delAddr}) suite.Require().NoError(err) output, err := suite.EthClient().CallContract(suite.ctx, ethereum.CallMsg{To: &suite.stakingContract, Data: pack}, nil) suite.Require().NoError(err) @@ -169,8 +168,8 @@ func (suite *StakingSuite) Delegation(valAddr string, delAddr common.Address) (* } func (suite *StakingSuite) Rewards(valAddr string, delAddr common.Address) *big.Int { - method := stakingprecompile.NewDelegationRewardsMethod(nil) - pack, err := method.PackInput(fxstakingtypes.DelegationRewardsArgs{Validator: valAddr, Delegator: delAddr}) + method := precompile.NewDelegationRewardsMethod(nil) + pack, err := method.PackInput(contract.DelegationRewardsArgs{Validator: valAddr, Delegator: delAddr}) suite.Require().NoError(err) output, err := suite.EthClient().CallContract(suite.ctx, ethereum.CallMsg{To: &suite.stakingContract, Data: pack}, nil) suite.Require().NoError(err) @@ -180,8 +179,8 @@ func (suite *StakingSuite) Rewards(valAddr string, delAddr common.Address) *big. } func (suite *StakingSuite) TransferShares(privateKey cryptotypes.PrivKey, valAddr string, receipt common.Address, shares *big.Int) *ethtypes.Receipt { - method := stakingprecompile.NewTransferSharesMethod(nil) - pack, err := method.PackInput(fxstakingtypes.TransferSharesArgs{Validator: valAddr, To: receipt, Shares: shares}) + method := precompile.NewTransferSharesMethod(nil) + pack, err := method.PackInput(contract.TransferSharesArgs{Validator: valAddr, To: receipt, Shares: shares}) suite.Require().NoError(err) return suite.send(privateKey, nil, pack) } @@ -204,8 +203,8 @@ func (suite *StakingSuite) TransferSharesByContract(privateKey cryptotypes.PrivK } func (suite *StakingSuite) TransferFromShares(privateKey cryptotypes.PrivKey, valAddr string, from, receipt common.Address, shares *big.Int) *ethtypes.Receipt { - method := stakingprecompile.NewTransferFromSharesMethod(nil) - pack, err := method.PackInput(fxstakingtypes.TransferFromSharesArgs{Validator: valAddr, From: from, To: receipt, Shares: shares}) + method := precompile.NewTransferFromSharesMethod(nil) + pack, err := method.PackInput(contract.TransferFromSharesArgs{Validator: valAddr, From: from, To: receipt, Shares: shares}) suite.Require().NoError(err) return suite.send(privateKey, nil, pack) } @@ -228,15 +227,15 @@ func (suite *StakingSuite) TransferFromSharesByContract(privateKey cryptotypes.P } func (suite *StakingSuite) ApproveShares(privateKey cryptotypes.PrivKey, valAddr string, spender common.Address, shares *big.Int) *ethtypes.Receipt { - method := stakingprecompile.NewApproveSharesMethod(nil) - pack, err := method.PackInput(fxstakingtypes.ApproveSharesArgs{Validator: valAddr, Spender: spender, Shares: shares}) + method := precompile.NewApproveSharesMethod(nil) + pack, err := method.PackInput(contract.ApproveSharesArgs{Validator: valAddr, Spender: spender, Shares: shares}) suite.Require().NoError(err) return suite.send(privateKey, nil, pack) } func (suite *StakingSuite) AllowanceShares(valAddr string, owner, spender common.Address) *big.Int { - method := stakingprecompile.NewAllowanceSharesMethod(nil) - pack, err := method.PackInput(fxstakingtypes.AllowanceSharesArgs{Validator: valAddr, Owner: owner, Spender: spender}) + method := precompile.NewAllowanceSharesMethod(nil) + pack, err := method.PackInput(contract.AllowanceSharesArgs{Validator: valAddr, Owner: owner, Spender: spender}) suite.Require().NoError(err) output, err := suite.EthClient().CallContract(suite.ctx, ethereum.CallMsg{To: &suite.stakingContract, Data: pack}, nil) suite.Require().NoError(err) @@ -246,7 +245,7 @@ func (suite *StakingSuite) AllowanceShares(valAddr string, owner, spender common } func (suite *StakingSuite) LogReward(logs []*ethtypes.Log, valAddr string, addr common.Address) *big.Int { - method := stakingprecompile.NewWithdrawMethod(nil) + method := precompile.NewWithdrawMethod(nil) for _, log := range logs { if log.Address == suite.stakingContract && log.Topics[0] == method.Event.ID && diff --git a/testutil/helpers/base_suite.go b/testutil/helpers/base_suite.go index ec85b1dd..d3378d29 100644 --- a/testutil/helpers/base_suite.go +++ b/testutil/helpers/base_suite.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/functionx/fx-core/v8/app" + fxtypes "github.com/functionx/fx-core/v8/types" crosschaintypes "github.com/functionx/fx-core/v8/x/crosschain/types" ) @@ -57,7 +58,7 @@ func (s *BaseSuite) SetupTest() { func (s *BaseSuite) AddTestSigner(amount ...int64) *Signer { signer := NewSigner(NewEthPrivKey()) - defAmount := int64(100) + defAmount := int64(10000) if len(amount) > 0 { defAmount = amount[0] } @@ -184,6 +185,11 @@ func (s *BaseSuite) CheckAllBalance(addr sdk.AccAddress, expBal ...sdk.Coin) { s.Equal(sdk.NewCoins(expBal...).String(), balances.String()) } +func (s *BaseSuite) GetStakingBalance(addr sdk.AccAddress) sdkmath.Int { + balances := s.App.BankKeeper.GetAllBalances(s.Ctx, addr) + return balances.AmountOf(fxtypes.DefaultDenom) +} + func (s *BaseSuite) NewCoin(amounts ...sdkmath.Int) sdk.Coin { amount := NewRandAmount() if len(amounts) > 0 && amounts[0].IsPositive() { diff --git a/testutil/helpers/contract_base_suite.go b/testutil/helpers/contract_base_suite.go index 95c45ad8..ef2611a1 100644 --- a/testutil/helpers/contract_base_suite.go +++ b/testutil/helpers/contract_base_suite.go @@ -20,12 +20,14 @@ func NewContractBaseSuite(require *require.Assertions, signer *Signer) *Contract } } -func (s *ContractBaseSuite) WithContract(addr common.Address) { +func (s *ContractBaseSuite) WithContract(addr common.Address) *ContractBaseSuite { s.contract = addr + return s } -func (s *ContractBaseSuite) WithSigner(signer *Signer) { +func (s *ContractBaseSuite) WithSigner(signer *Signer) *ContractBaseSuite { s.signer = signer + return s } func (s *ContractBaseSuite) HexAddress() common.Address { diff --git a/testutil/helpers/staking_precompile_suite.go b/testutil/helpers/staking_precompile_suite.go new file mode 100644 index 00000000..433949d0 --- /dev/null +++ b/testutil/helpers/staking_precompile_suite.go @@ -0,0 +1,114 @@ +package helpers + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/stretchr/testify/require" + + "github.com/functionx/fx-core/v8/contract" +) + +type StakingPrecompileSuite struct { + *ContractBaseSuite + err error + + contract.StakingPrecompileKeeper +} + +func NewStakingPrecompileSuite(require *require.Assertions, signer *Signer, caller contract.Caller, contractAddr common.Address) StakingPrecompileSuite { + contractBaseSuite := NewContractBaseSuite(require, signer) + contractBaseSuite.WithContract(contractAddr) + return StakingPrecompileSuite{ + ContractBaseSuite: contractBaseSuite, + StakingPrecompileKeeper: contract.NewStakingPrecompileKeeper(caller, contractAddr), + } +} + +func (s StakingPrecompileSuite) WithError(err error) StakingPrecompileSuite { + stakingPrecompileKeeper := s + stakingPrecompileKeeper.err = err + return stakingPrecompileKeeper +} + +func (s StakingPrecompileSuite) Error(err error) { + if s.err != nil { + s.require.ErrorIs(err, evmtypes.ErrVMExecution.Wrap(s.err.Error())) + return + } + s.require.NoError(err) +} + +func (s StakingPrecompileSuite) AllowanceShares(ctx context.Context, args contract.AllowanceSharesArgs) *big.Int { + shares, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).AllowanceShares(ctx, args) + s.Error(err) + return shares +} + +func (s StakingPrecompileSuite) Delegation(ctx context.Context, args contract.DelegationArgs) (*big.Int, *big.Int) { + amount, shares, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).Delegation(ctx, args) + s.Error(err) + return amount, shares +} + +func (s StakingPrecompileSuite) DelegationRewards(ctx context.Context, args contract.DelegationRewardsArgs) *big.Int { + rewards, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).DelegationRewards(ctx, args) + s.Error(err) + return rewards +} + +func (s StakingPrecompileSuite) ValidatorList(ctx context.Context, args contract.ValidatorListArgs) []string { + rewards, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).ValidatorList(ctx, args) + s.Error(err) + return rewards +} + +func (s StakingPrecompileSuite) SlashingInfo(ctx context.Context, args contract.SlashingInfoArgs) (bool, *big.Int) { + jailed, missed, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).SlashingInfo(ctx, args) + s.Error(err) + return jailed, missed +} + +func (s StakingPrecompileSuite) ApproveShares(ctx context.Context, args contract.ApproveSharesArgs) *evmtypes.MsgEthereumTxResponse { + res, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).ApproveShares(ctx, s.HexAddress(), args) + s.Error(err) + return res +} + +func (s StakingPrecompileSuite) TransferShares(ctx context.Context, args contract.TransferSharesArgs) (*evmtypes.MsgEthereumTxResponse, *contract.TransferSharesRet) { + res, ret, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).TransferShares(ctx, s.HexAddress(), args) + s.Error(err) + return res, ret +} + +func (s StakingPrecompileSuite) TransferFromShares(ctx context.Context, args contract.TransferFromSharesArgs) (*evmtypes.MsgEthereumTxResponse, *contract.TransferFromSharesRet) { + res, ret, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).TransferFromShares(ctx, s.HexAddress(), args) + s.Error(err) + return res, ret +} + +func (s StakingPrecompileSuite) Withdraw(ctx context.Context, args contract.WithdrawArgs) (*evmtypes.MsgEthereumTxResponse, *big.Int) { + res, reward, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).Withdraw(ctx, s.HexAddress(), args) + s.Error(err) + return res, reward +} + +func (s StakingPrecompileSuite) DelegateV2(ctx context.Context, args contract.DelegateV2Args) *evmtypes.MsgEthereumTxResponse { + res, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).DelegateV2(ctx, s.HexAddress(), args) + s.Error(err) + return res +} + +func (s StakingPrecompileSuite) RedelegateV2(ctx context.Context, args contract.RedelegateV2Args) *evmtypes.MsgEthereumTxResponse { + res, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).RedelegateV2(ctx, s.HexAddress(), args) + s.Error(err) + return res +} + +func (s StakingPrecompileSuite) UndelegateV2(ctx context.Context, args contract.UndelegateV2Args) *evmtypes.MsgEthereumTxResponse { + res, err := s.StakingPrecompileKeeper.WithContractAddr(s.contract).UndelegateV2(ctx, s.HexAddress(), args) + s.Error(err) + return res +} diff --git a/testutil/helpers/staking_suite.go b/testutil/helpers/staking_suite.go new file mode 100644 index 00000000..af7558bd --- /dev/null +++ b/testutil/helpers/staking_suite.go @@ -0,0 +1,80 @@ +package helpers + +import ( + "math/big" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func (s *BaseSuite) GetFirstValidator() stakingtypes.Validator { + validators, err := s.App.StakingKeeper.GetValidators(s.Ctx, 1) + s.NoError(err) + s.NotEmpty(validators) + return validators[0] +} + +func (s *BaseSuite) GetFirstValAddr() sdk.ValAddress { + val := s.GetFirstValidator() + operator, err := s.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) + s.NoError(err) + return operator +} + +func (s *BaseSuite) GetSecondValidator() stakingtypes.Validator { + validators, err := s.App.StakingKeeper.GetValidators(s.Ctx, 2) + s.NoError(err) + s.NotEmpty(validators) + return validators[1] +} + +func (s *BaseSuite) GetSecondValAddr() sdk.ValAddress { + val := s.GetSecondValidator() + operator, err := s.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) + s.NoError(err) + return operator +} + +func (s *BaseSuite) GetValidators() []stakingtypes.Validator { + validators, err := s.App.StakingKeeper.GetValidators(s.Ctx, 10) + s.NoError(err) + return validators +} + +func (s *BaseSuite) GetValidator(valAddr sdk.ValAddress) stakingtypes.Validator { + validator, err := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr) + s.NoError(err) + return validator +} + +func (s *BaseSuite) Delegate(delAddr sdk.AccAddress, delAmount sdkmath.Int, val sdk.ValAddress) { + validator, err := s.App.StakingKeeper.GetValidator(s.Ctx, val) + s.NoError(err) + _, err = s.App.StakingKeeper.Delegate(s.Ctx, delAddr, delAmount, stakingtypes.Unbonded, validator, true) + s.NoError(err) +} + +func (s *BaseSuite) GetDelegation(delAddr sdk.AccAddress, val sdk.ValAddress) stakingtypes.Delegation { + delegation, err := s.App.StakingKeeper.GetDelegation(s.Ctx, delAddr, val) + s.NoError(err) + return delegation +} + +func (s *BaseSuite) Undelegate(delAddr sdk.AccAddress, val sdk.ValAddress) { + delegation, err := s.App.StakingKeeper.GetDelegation(s.Ctx, delAddr, val) + s.NoError(err) + _, _, err = s.App.StakingKeeper.Undelegate(s.Ctx, delAddr, val, delegation.Shares) + s.NoError(err) +} + +func (s *BaseSuite) Redelegate(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) { + delegation, err := s.App.StakingKeeper.GetDelegation(s.Ctx, delAddr, valSrcAddr) + s.NoError(err) + _, err = s.App.StakingKeeper.BeginRedelegation(s.Ctx, delAddr, valSrcAddr, valDstAddr, delegation.Shares) + s.NoError(err) +} + +func (s *BaseSuite) SetAllowance(valAddr sdk.ValAddress, owner, spender sdk.AccAddress, shares *big.Int) { + s.App.StakingKeeper.SetAllowance(s.Ctx, valAddr, owner, spender, shares) +} diff --git a/x/staking/precompile/allowance_shares.go b/x/staking/precompile/allowance_shares.go index d60f8eed..00b5f1e5 100644 --- a/x/staking/precompile/allowance_shares.go +++ b/x/staking/precompile/allowance_shares.go @@ -6,8 +6,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/core/vm" + fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type AllowanceSharesMethod struct { @@ -56,7 +56,7 @@ func NewAllowanceSharesABI() AllowanceSharesABI { } } -func (m AllowanceSharesABI) PackInput(args fxstakingtypes.AllowanceSharesArgs) ([]byte, error) { +func (m AllowanceSharesABI) PackInput(args fxcontract.AllowanceSharesArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator, args.Owner, args.Spender) if err != nil { return nil, err @@ -64,8 +64,8 @@ func (m AllowanceSharesABI) PackInput(args fxstakingtypes.AllowanceSharesArgs) ( return append(m.Method.ID, arguments...), nil } -func (m AllowanceSharesABI) UnpackInput(data []byte) (*fxstakingtypes.AllowanceSharesArgs, error) { - args := new(fxstakingtypes.AllowanceSharesArgs) +func (m AllowanceSharesABI) UnpackInput(data []byte) (*fxcontract.AllowanceSharesArgs, error) { + args := new(fxcontract.AllowanceSharesArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/allowance_shares_test.go b/x/staking/precompile/allowance_shares_test.go index 2495e37a..146026bb 100644 --- a/x/staking/precompile/allowance_shares_test.go +++ b/x/staking/precompile/allowance_shares_test.go @@ -2,35 +2,33 @@ package precompile_test import ( "fmt" - "math/big" - "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" "github.com/functionx/fx-core/v8/x/staking/precompile" - "github.com/functionx/fx-core/v8/x/staking/types" ) func TestStakingAllowanceSharesABI(t *testing.T) { - allowanceSharesMethod := precompile.NewAllowanceSharesMethod(nil) + allowanceSharesABI := precompile.NewAllowanceSharesABI() - require.Len(t, allowanceSharesMethod.Method.Inputs, 3) - require.Len(t, allowanceSharesMethod.Method.Outputs, 1) + require.Len(t, allowanceSharesABI.Method.Inputs, 3) + require.Len(t, allowanceSharesABI.Method.Outputs, 1) } -func (suite *PrecompileTestSuite) TestAllowanceShares() { +func (suite *StakingPrecompileTestSuite) TestAllowanceShares() { testCases := []struct { name string - malleate func(val sdk.ValAddress, owner, spender *helpers.Signer) (types.AllowanceSharesArgs, error) + malleate func(val sdk.ValAddress, owner, spender *helpers.Signer) (contract.AllowanceSharesArgs, error) result bool }{ { name: "ok", - malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (types.AllowanceSharesArgs, error) { - return types.AllowanceSharesArgs{ + malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (contract.AllowanceSharesArgs, error) { + return contract.AllowanceSharesArgs{ Validator: val.String(), Owner: owner.Address(), Spender: spender.Address(), @@ -40,10 +38,10 @@ func (suite *PrecompileTestSuite) TestAllowanceShares() { }, { name: "ok - default allowance zero", - malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (types.AllowanceSharesArgs, error) { - return types.AllowanceSharesArgs{ + malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (contract.AllowanceSharesArgs, error) { + return contract.AllowanceSharesArgs{ Validator: val.String(), - Owner: suite.RandSigner().Address(), + Owner: suite.NewSigner().Address(), Spender: spender.Address(), }, nil }, @@ -51,47 +49,12 @@ func (suite *PrecompileTestSuite) TestAllowanceShares() { }, { name: "failed - invalid validator address", - malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (types.AllowanceSharesArgs, error) { + malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (contract.AllowanceSharesArgs, error) { valStr := val.String() + "1" - return types.AllowanceSharesArgs{ + return contract.AllowanceSharesArgs{ Validator: valStr, - Owner: suite.RandSigner().Address(), - Spender: spender.Address(), - }, fmt.Errorf("invalid validator address: %s", valStr) - }, - result: false, - }, - { - name: "contract - ok", - malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (types.AllowanceSharesArgs, error) { - return types.AllowanceSharesArgs{ - Validator: val.String(), - Owner: owner.Address(), - Spender: spender.Address(), - }, nil - }, - result: true, - }, - { - name: "contract - ok - default allowance zero", - malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (types.AllowanceSharesArgs, error) { - return types.AllowanceSharesArgs{ - Validator: val.String(), - Owner: suite.RandSigner().Address(), - Spender: spender.Address(), - }, nil - }, - result: true, - }, - { - name: "contract - failed - invalid validator address", - malleate: func(val sdk.ValAddress, owner, spender *helpers.Signer) (types.AllowanceSharesArgs, error) { - valStr := val.String() + "1" - - return types.AllowanceSharesArgs{ - Validator: valStr, - Owner: suite.RandSigner().Address(), + Owner: suite.NewSigner().Address(), Spender: spender.Address(), }, fmt.Errorf("invalid validator address: %s", valStr) }, @@ -101,37 +64,17 @@ func (suite *PrecompileTestSuite) TestAllowanceShares() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val := suite.GetFirstValidator() - owner := suite.RandSigner() - spender := suite.RandSigner() + valAddr := suite.GetFirstValAddr() + spender := suite.NewSigner() allowanceAmt := helpers.NewRandAmount() - // set allowance - operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - suite.Require().NoError(err) - suite.App.StakingKeeper.SetAllowance(suite.Ctx, operator, owner.AccAddress(), spender.AccAddress(), allowanceAmt.BigInt()) - - args, errResult := tc.malleate(operator, owner, spender) - - packData, err := suite.allowanceSharesMethod.PackInput(args) - suite.Require().NoError(err) - stakingContract := suite.stakingAddr - - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - } + suite.SetAllowance(valAddr, suite.signer.AccAddress(), spender.AccAddress(), allowanceAmt.BigInt()) - res := suite.EthereumTx(owner, stakingContract, big.NewInt(0), packData) + args, expectErr := tc.malleate(valAddr, suite.signer, spender) - if tc.result { - suite.Require().False(res.Failed(), res.VmError) - shares, err := suite.allowanceSharesMethod.UnpackOutput(res.Ret) - suite.Require().NoError(err) - if shares.Cmp(big.NewInt(0)) != 0 { - suite.Require().Equal(allowanceAmt.BigInt(), shares) - } - } else { - suite.Error(res, errResult) + shares := suite.WithError(expectErr).AllowanceShares(suite.Ctx, args) + if tc.result && shares.Sign() > 0 { + suite.Require().Equal(allowanceAmt.BigInt(), shares) } }) } diff --git a/x/staking/precompile/approve_shares.go b/x/staking/precompile/approve_shares.go index ca55c65b..71c56905 100644 --- a/x/staking/precompile/approve_shares.go +++ b/x/staking/precompile/approve_shares.go @@ -12,7 +12,6 @@ import ( fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type ApproveSharesMethod struct { @@ -84,7 +83,7 @@ func (m ApproveSharesABI) NewApproveSharesEvent(owner, spender common.Address, v return data, topic, nil } -func (m ApproveSharesABI) PackInput(args fxstakingtypes.ApproveSharesArgs) ([]byte, error) { +func (m ApproveSharesABI) PackInput(args fxcontract.ApproveSharesArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator, args.Spender, args.Shares) if err != nil { return nil, err @@ -92,8 +91,8 @@ func (m ApproveSharesABI) PackInput(args fxstakingtypes.ApproveSharesArgs) ([]by return append(m.Method.ID, arguments...), nil } -func (m ApproveSharesABI) UnpackInput(data []byte) (*fxstakingtypes.ApproveSharesArgs, error) { - args := new(fxstakingtypes.ApproveSharesArgs) +func (m ApproveSharesABI) UnpackInput(data []byte) (*fxcontract.ApproveSharesArgs, error) { + args := new(fxcontract.ApproveSharesArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/approve_shares_test.go b/x/staking/precompile/approve_shares_test.go index b16e3604..87dccb48 100644 --- a/x/staking/precompile/approve_shares_test.go +++ b/x/staking/precompile/approve_shares_test.go @@ -3,37 +3,36 @@ package precompile_test import ( "fmt" "math/big" - "strings" "testing" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" "github.com/functionx/fx-core/v8/x/staking/precompile" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) func TestStakingApproveSharesABI(t *testing.T) { - approveSharesMethod := precompile.NewApproveSharesMethod(nil) + approveSharesABI := precompile.NewApproveSharesABI() - require.Len(t, approveSharesMethod.Method.Inputs, 3) - require.Len(t, approveSharesMethod.Method.Outputs, 1) + require.Len(t, approveSharesABI.Method.Inputs, 3) + require.Len(t, approveSharesABI.Method.Outputs, 1) - require.Len(t, approveSharesMethod.Event.Inputs, 4) + require.Len(t, approveSharesABI.Event.Inputs, 4) } -func (suite *PrecompileTestSuite) TestApproveShares() { +func (suite *StakingPrecompileTestSuite) TestApproveShares() { testCases := []struct { name string - malleate func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxstakingtypes.ApproveSharesArgs, error) + malleate func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxcontract.ApproveSharesArgs, error) result bool }{ { name: "ok", - malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxstakingtypes.ApproveSharesArgs, error) { - return fxstakingtypes.ApproveSharesArgs{ + malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxcontract.ApproveSharesArgs, error) { + return fxcontract.ApproveSharesArgs{ Validator: val.String(), Spender: spender.Address(), Shares: allowance.BigInt(), @@ -43,8 +42,8 @@ func (suite *PrecompileTestSuite) TestApproveShares() { }, { name: "ok - approve zero", - malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxstakingtypes.ApproveSharesArgs, error) { - return fxstakingtypes.ApproveSharesArgs{ + malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxcontract.ApproveSharesArgs, error) { + return fxcontract.ApproveSharesArgs{ Validator: val.String(), Spender: spender.Address(), Shares: big.NewInt(0), @@ -54,43 +53,9 @@ func (suite *PrecompileTestSuite) TestApproveShares() { }, { name: "failed - invalid validator address", - malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxstakingtypes.ApproveSharesArgs, error) { + malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxcontract.ApproveSharesArgs, error) { valStr := val.String() + "1" - return fxstakingtypes.ApproveSharesArgs{ - Validator: valStr, - Spender: spender.Address(), - Shares: allowance.BigInt(), - }, fmt.Errorf("invalid validator address: %s", valStr) - }, - result: false, - }, - { - name: "contract - ok", - malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxstakingtypes.ApproveSharesArgs, error) { - return fxstakingtypes.ApproveSharesArgs{ - Validator: val.String(), - Spender: spender.Address(), - Shares: allowance.BigInt(), - }, nil - }, - result: true, - }, - { - name: "contract - ok - approve zero", - malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxstakingtypes.ApproveSharesArgs, error) { - return fxstakingtypes.ApproveSharesArgs{ - Validator: val.String(), - Spender: spender.Address(), - Shares: big.NewInt(0), - }, nil - }, - result: true, - }, - { - name: "contract - failed - invalid validator address", - malleate: func(val sdk.ValAddress, spender *helpers.Signer, allowance sdkmath.Int) (fxstakingtypes.ApproveSharesArgs, error) { - valStr := val.String() + "1" - return fxstakingtypes.ApproveSharesArgs{ + return fxcontract.ApproveSharesArgs{ Validator: valStr, Spender: spender.Address(), Shares: allowance.BigInt(), @@ -102,48 +67,36 @@ func (suite *PrecompileTestSuite) TestApproveShares() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val := suite.GetFirstValidator() - owner := suite.RandSigner() - spender := suite.RandSigner() + valAddr := suite.GetFirstValAddr() + spender := suite.NewSigner() allowanceAmt := helpers.NewRandAmount() - operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - suite.Require().NoError(err) - - args, errResult := tc.malleate(operator, spender, allowanceAmt) - - packData, err := suite.approveSharesMethod.PackInput(args) - suite.Require().NoError(err) - stakingContract := suite.stakingAddr - sender := owner.Address() - - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - sender = suite.stakingTestAddr - } - - allowance := suite.App.StakingKeeper.GetAllowance(suite.Ctx, operator, owner.AccAddress(), spender.AccAddress()) + allowance := suite.App.StakingKeeper.GetAllowance(suite.Ctx, valAddr, suite.signer.AccAddress(), spender.AccAddress()) suite.Require().Equal(0, allowance.Cmp(big.NewInt(0))) - res := suite.EthereumTx(owner, stakingContract, big.NewInt(0), packData) + args, expectErr := tc.malleate(valAddr, spender, allowanceAmt) + + delAddr := suite.GetDelAddr() + res := suite.WithError(expectErr).ApproveShares(suite.Ctx, args) if tc.result { suite.Require().False(res.Failed(), res.VmError) - allowance = suite.App.StakingKeeper.GetAllowance(suite.Ctx, operator, sender.Bytes(), spender.AccAddress()) + allowance = suite.App.StakingKeeper.GetAllowance(suite.Ctx, valAddr, delAddr.Bytes(), spender.AccAddress()) if allowance.Cmp(big.NewInt(0)) != 0 { suite.Require().Equal(0, allowance.Cmp(allowanceAmt.BigInt())) } existLog := false + approveSharesABI := precompile.NewApproveSharesABI() for _, log := range res.Logs { - if log.Topics[0] == suite.approveSharesMethod.Event.ID.String() { - suite.Require().Equal(log.Address, suite.stakingAddr.String()) - event, err := suite.approveSharesMethod.UnpackEvent(log.ToEthereum()) + if log.Topics[0] == approveSharesABI.Event.ID.String() { + suite.Require().Equal(fxcontract.StakingAddress, log.Address) + event, err := approveSharesABI.UnpackEvent(log.ToEthereum()) suite.Require().NoError(err) - suite.Require().Equal(event.Owner, sender) + suite.Require().Equal(event.Owner, delAddr) suite.Require().Equal(event.Spender, spender.Address()) - suite.Require().Equal(event.Validator, val.GetOperator()) + suite.Require().Equal(event.Validator, valAddr.String()) if allowance.Cmp(big.NewInt(0)) != 0 { suite.Require().Equal(event.Shares.String(), allowanceAmt.BigInt().String()) } @@ -151,8 +104,6 @@ func (suite *PrecompileTestSuite) TestApproveShares() { } } suite.Require().True(existLog) - } else { - suite.Error(res, errResult) } }) } diff --git a/x/staking/precompile/contract_test.go b/x/staking/precompile/contract_test.go index 231a765b..ac93037a 100644 --- a/x/staking/precompile/contract_test.go +++ b/x/staking/precompile/contract_test.go @@ -2,467 +2,112 @@ package precompile_test import ( "math/big" - "strings" "testing" - "time" sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/evmos/ethermint/crypto/ethsecp256k1" - evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/suite" "github.com/functionx/fx-core/v8/contract" testscontract "github.com/functionx/fx-core/v8/tests/contract" "github.com/functionx/fx-core/v8/testutil/helpers" fxtypes "github.com/functionx/fx-core/v8/types" - "github.com/functionx/fx-core/v8/x/staking/precompile" - "github.com/functionx/fx-core/v8/x/staking/testutil" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) -type PrecompileTestSuite struct { +type StakingPrecompileTestSuite struct { helpers.BaseSuite - testutil.StakingSuite - signer *helpers.Signer - stakingTestAddr common.Address - stakingAddr common.Address - allowanceSharesMethod *precompile.AllowanceSharesMethod - approveSharesMethod *precompile.ApproveSharesMethod - delegateV2Method *precompile.DelegateV2Method - delegationMethod *precompile.DelegationMethod - delegationRewardsMethod *precompile.DelegationRewardsMethod - redelegateV2Method *precompile.RedelegateV2Method - // slashingInfoMethod *precompile.SlashingInfoMethod - transferSharesMethod *precompile.TransferSharesMethod - transferFromSharesMethod *precompile.TransferFromSharesMethod - undelegateV2Method *precompile.UndelegateV2Method - validatorListMethod *precompile.ValidatorListMethod - withdrawMethod *precompile.WithdrawMethod + signer *helpers.Signer + stakingAddr common.Address + + helpers.StakingPrecompileSuite +} + +func TestStakingPrecompileTestSuite(t *testing.T) { + testingSuite := new(StakingPrecompileTestSuite) + testingSuite.stakingAddr = common.HexToAddress(contract.StakingAddress) + suite.Run(t, testingSuite) } -func TestPrecompileTestSuite(t *testing.T) { - fxtypes.SetConfig(true) - suite.Run(t, new(PrecompileTestSuite)) +func TestStakingPrecompileTestSuite_Contract(t *testing.T) { + suite.Run(t, new(StakingPrecompileTestSuite)) } -func (suite *PrecompileTestSuite) SetupSubTest() { +func (suite *StakingPrecompileTestSuite) SetupSubTest() { suite.SetupTest() } -func (suite *PrecompileTestSuite) SetupTest() { +func (suite *StakingPrecompileTestSuite) SetupTest() { suite.MintValNumber = 2 suite.BaseSuite.SetupTest() suite.Commit(10) - priv, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - suite.signer = helpers.NewSigner(priv) - suite.MintToken(suite.signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, sdkmath.NewInt(10000).Mul(sdkmath.NewInt(1e18)))) - - stakingContract, err := suite.App.EvmKeeper.DeployContract(suite.Ctx, suite.signer.Address(), contract.MustABIJson(testscontract.StakingTestMetaData.ABI), contract.MustDecodeHex(testscontract.StakingTestMetaData.Bin)) - suite.Require().NoError(err) - suite.stakingTestAddr = stakingContract + suite.signer = suite.AddTestSigner() - suite.stakingAddr = common.HexToAddress(contract.StakingAddress) + if !suite.IsCallPrecompile() { + stakingTestAddr, err := suite.App.EvmKeeper.DeployContract(suite.Ctx, suite.signer.Address(), contract.MustABIJson(testscontract.StakingTestMetaData.ABI), contract.MustDecodeHex(testscontract.StakingTestMetaData.Bin)) + suite.Require().NoError(err) + suite.stakingAddr = stakingTestAddr + suite.MintToken(suite.stakingAddr.Bytes(), helpers.NewStakingCoin(10000, 18)) + } - suite.MintToken(suite.signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, sdkmath.NewInt(10000).Mul(sdkmath.NewInt(1e18)))) + suite.StakingPrecompileSuite = helpers.NewStakingPrecompileSuite(suite.Require(), suite.signer, suite.App.EvmKeeper, suite.stakingAddr) +} - suite.StakingSuite.Init(suite.Require(), suite.Ctx, suite.App.StakingKeeper) +func (suite *StakingPrecompileTestSuite) GetDelAddr() common.Address { + if suite.IsCallPrecompile() { + return suite.signer.Address() + } + return suite.stakingAddr +} - suite.allowanceSharesMethod = precompile.NewAllowanceSharesMethod(nil) - suite.approveSharesMethod = precompile.NewApproveSharesMethod(nil) - suite.delegateV2Method = precompile.NewDelegateV2Method(nil) - suite.delegationMethod = precompile.NewDelegationMethod(nil) - suite.delegationRewardsMethod = precompile.NewDelegationRewardsMethod(nil) - suite.redelegateV2Method = precompile.NewRedelegateV2Method(nil) - // suite.slashingInfoMethod = precompile.NewSlashingInfoMethod(nil) - suite.transferSharesMethod = precompile.NewTransferSharesMethod(nil) - suite.transferFromSharesMethod = precompile.NewTransferFromSharesMethod(nil) - suite.undelegateV2Method = precompile.NewUndelegateV2Method(nil) - suite.validatorListMethod = precompile.NewValidatorListMethod(nil) - suite.withdrawMethod = precompile.NewWithdrawMethod(nil) +func (suite *StakingPrecompileTestSuite) IsCallPrecompile() bool { + return suite.stakingAddr.String() == contract.StakingAddress } -func (suite *PrecompileTestSuite) DistributionQueryClient(ctx sdk.Context) distributiontypes.QueryClient { +func (suite *StakingPrecompileTestSuite) DistributionQueryClient(ctx sdk.Context) distributiontypes.QueryClient { queryHelper := baseapp.NewQueryServerTestHelper(ctx, suite.App.InterfaceRegistry()) distributiontypes.RegisterQueryServer(queryHelper, distributionkeeper.NewQuerier(suite.App.DistrKeeper)) return distributiontypes.NewQueryClient(queryHelper) } -func (suite *PrecompileTestSuite) EthereumTx(signer *helpers.Signer, to common.Address, amount *big.Int, data []byte) *evmtypes.MsgEthereumTxResponse { - ethTx := evmtypes.NewTx( - fxtypes.EIP155ChainID(suite.Ctx.ChainID()), - suite.App.EvmKeeper.GetNonce(suite.Ctx, signer.Address()), - &to, - amount, - contract.DefaultGasCap, - nil, - nil, - nil, - data, - nil, - ) - ethTx.From = signer.Address().Bytes() - err := ethTx.Sign(ethtypes.LatestSignerForChainID(fxtypes.EIP155ChainID(suite.Ctx.ChainID())), signer) - suite.Require().NoError(err) - - res, err := suite.App.EvmKeeper.EthereumTx(suite.Ctx, ethTx) - suite.Require().NoError(err) - return res -} - -func (suite *PrecompileTestSuite) RandSigner() *helpers.Signer { - signer := helpers.NewSigner(helpers.NewEthPrivKey()) - account := suite.App.AccountKeeper.NewAccountWithAddress(suite.Ctx, signer.AccAddress()) - suite.App.AccountKeeper.SetAccount(suite.Ctx, account) - return signer -} - -func (suite *PrecompileTestSuite) delegateFromFunc(val sdk.ValAddress, from, _ common.Address, delAmount sdkmath.Int) { - suite.MintToken(from.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - _, err := stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ - DelegatorAddress: sdk.AccAddress(from.Bytes()).String(), - ValidatorAddress: val.String(), - Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), - }) - suite.Require().NoError(err) -} - -func (suite *PrecompileTestSuite) undelegateToFunc(val sdk.ValAddress, _, to common.Address, _ sdkmath.Int) { - toDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, to.Bytes(), val) - suite.Require().NoError(err) - _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, to.Bytes(), val, toDel.Shares) - suite.Require().NoError(err) -} - -func (suite *PrecompileTestSuite) delegateFromToFunc(val sdk.ValAddress, from, to common.Address, delAmount sdkmath.Int) { - suite.MintToken(from.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - _, err := stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ - DelegatorAddress: sdk.AccAddress(from.Bytes()).String(), - ValidatorAddress: val.String(), - Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), - }) - suite.Require().NoError(err) - - suite.MintToken(to.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - _, err = stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ - DelegatorAddress: sdk.AccAddress(to.Bytes()).String(), - ValidatorAddress: val.String(), - Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), - }) - suite.Require().NoError(err) -} - -func (suite *PrecompileTestSuite) delegateToFromFunc(val sdk.ValAddress, from, to common.Address, delAmount sdkmath.Int) { - suite.MintToken(to.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - _, err := stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ - DelegatorAddress: sdk.AccAddress(to.Bytes()).String(), - ValidatorAddress: val.String(), - Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), - }) - suite.Require().NoError(err) - - suite.MintToken(from.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - _, err = stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ - DelegatorAddress: sdk.AccAddress(from.Bytes()).String(), - ValidatorAddress: val.String(), - Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), - }) - suite.Require().NoError(err) -} - -func (suite *PrecompileTestSuite) undelegateFromToFunc(val sdk.ValAddress, from, to common.Address, _ sdkmath.Int) { - fromDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, from.Bytes(), val) - suite.Require().NoError(err) - _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, from.Bytes(), val, fromDel.Shares) - suite.Require().NoError(err) - - toDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, to.Bytes(), val) - suite.Require().NoError(err) - _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, to.Bytes(), val, toDel.Shares) - suite.Require().NoError(err) -} - -func (suite *PrecompileTestSuite) undelegateToFromFunc(val sdk.ValAddress, from, to common.Address, _ sdkmath.Int) { - toDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, to.Bytes(), val) - suite.Require().NoError(err) - _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, to.Bytes(), val, toDel.Shares) - suite.Require().NoError(err) - - fromDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, from.Bytes(), val) - suite.Require().NoError(err) - _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, from.Bytes(), val, fromDel.Shares) - suite.Require().NoError(err) -} - -func (suite *PrecompileTestSuite) approveFunc(val sdk.ValAddress, owner, spender common.Address, allowance *big.Int) { - suite.App.StakingKeeper.SetAllowance(suite.Ctx, val, owner.Bytes(), spender.Bytes(), allowance) -} +func (suite *StakingPrecompileTestSuite) PrecompileStakingDelegateV2(signer *helpers.Signer, val sdk.ValAddress, amt *big.Int) *big.Int { + suite.MintToken(signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, sdkmath.NewIntFromBigInt(amt))) -func (suite *PrecompileTestSuite) PrecompileStakingDelegation(val sdk.ValAddress, del common.Address) (*big.Int, *big.Int) { - input, err := suite.delegationMethod.PackInput(fxstakingtypes.DelegationArgs{ + _, amountBefore := suite.Delegation(suite.Ctx, contract.DelegationArgs{ Validator: val.String(), - Delegator: del, + Delegator: signer.Address(), }) - suite.Require().NoError(err) - res, err := suite.App.EvmKeeper.CallEVMWithoutGas(suite.Ctx, del, &suite.stakingAddr, nil, input, false) - suite.Require().NoError(err) - shares, amount, err := suite.delegationMethod.UnpackOutput(res.Ret) - suite.Require().NoError(err) - return shares, amount -} -func (suite *PrecompileTestSuite) PrecompileStakingDelegateV2(signer *helpers.Signer, val sdk.ValAddress, amt *big.Int) *big.Int { - suite.MintToken(signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, sdkmath.NewIntFromBigInt(amt))) - pack, err := suite.delegateV2Method.PackInput(fxstakingtypes.DelegateV2Args{ + suite.WithSigner(signer) + res := suite.DelegateV2(suite.Ctx, contract.DelegateV2Args{ Validator: val.String(), Amount: amt, }) - suite.Require().NoError(err) - - _, amountBefore := suite.PrecompileStakingDelegation(val, signer.Address()) - - res := suite.EthereumTx(signer, suite.stakingAddr, big.NewInt(0), pack) - suite.Require().False(res.Failed(), res.VmError) - - shares, amount := suite.PrecompileStakingDelegation(val, signer.Address()) - suite.Require().Equal(amt.String(), big.NewInt(0).Sub(amount, amountBefore).String()) - return shares -} - -func (suite *PrecompileTestSuite) PrecompileStakingWithdraw(signer *helpers.Signer, val sdk.ValAddress) *big.Int { - balanceBefore := suite.App.EvmKeeper.GetEVMDenomBalance(suite.Ctx, signer.Address()) - pack, err := suite.withdrawMethod.PackInput(fxstakingtypes.WithdrawArgs{Validator: val.String()}) - suite.Require().NoError(err) - - res := suite.EthereumTx(signer, suite.stakingAddr, big.NewInt(0), pack) suite.Require().False(res.Failed(), res.VmError) - balanceAfter := suite.App.EvmKeeper.GetEVMDenomBalance(suite.Ctx, signer.Address()) - rewards := big.NewInt(0).Sub(balanceAfter, balanceBefore) - return rewards -} - -func (suite *PrecompileTestSuite) PrecompileStakingTransferShares(signer *helpers.Signer, val sdk.ValAddress, receipt common.Address, shares *big.Int) (*big.Int, *big.Int) { - balanceBefore := suite.App.EvmKeeper.GetEVMDenomBalance(suite.Ctx, signer.Address()) - pack, err := suite.transferSharesMethod.PackInput(fxstakingtypes.TransferSharesArgs{ + shares, amount := suite.Delegation(suite.Ctx, contract.DelegationArgs{ Validator: val.String(), - To: receipt, - Shares: shares, + Delegator: signer.Address(), }) - suite.Require().NoError(err) - res := suite.EthereumTx(signer, suite.stakingAddr, big.NewInt(0), pack) - suite.Require().False(res.Failed(), res.VmError) - - signerShares, _ := suite.PrecompileStakingDelegation(val, signer.Address()) - - balanceAfter := suite.App.EvmKeeper.GetEVMDenomBalance(suite.Ctx, signer.Address()) - rewards := big.NewInt(0).Sub(balanceAfter, balanceBefore) - return signerShares, rewards + suite.Require().Equal(amt.String(), big.NewInt(0).Sub(amount, amountBefore).String()) + return shares } -func (suite *PrecompileTestSuite) PrecompileStakingUndelegateV2(signer *helpers.Signer, val sdk.ValAddress, shares *big.Int) *big.Int { - balanceBefore := suite.App.EvmKeeper.GetEVMDenomBalance(suite.Ctx, signer.Address()) - pack, err := suite.undelegateV2Method.PackInput(fxstakingtypes.UndelegateV2Args{ - Validator: val.String(), - Amount: shares, - }) - suite.Require().NoError(err) +func (suite *StakingPrecompileTestSuite) PrecompileStakingWithdraw(signer *helpers.Signer, val sdk.ValAddress) sdk.Coins { + balanceBefore := suite.App.BankKeeper.GetAllBalances(suite.Ctx, signer.AccAddress()) - res := suite.EthereumTx(signer, suite.stakingAddr, big.NewInt(0), pack) - suite.Require().False(res.Failed(), res.VmError) - - balanceAfter := suite.App.EvmKeeper.GetEVMDenomBalance(suite.Ctx, signer.Address()) - rewards := big.NewInt(0).Sub(balanceAfter, balanceBefore) - return rewards -} - -func (suite *PrecompileTestSuite) PrecompileStakingApproveShares(signer *helpers.Signer, val sdk.ValAddress, spender common.Address, shares *big.Int) { - pack, err := suite.approveSharesMethod.PackInput(fxstakingtypes.ApproveSharesArgs{ + suite.WithSigner(signer) + res, _ := suite.Withdraw(suite.Ctx, contract.WithdrawArgs{ Validator: val.String(), - Spender: spender, - Shares: shares, }) - suite.Require().NoError(err) - - res := suite.EthereumTx(signer, suite.stakingAddr, big.NewInt(0), pack) suite.Require().False(res.Failed(), res.VmError) -} - -func (suite *PrecompileTestSuite) PrecompileStakingTransferFromShares(signer *helpers.Signer, val sdk.ValAddress, from, receipt common.Address, shares *big.Int) { - pack, err := suite.transferFromSharesMethod.PackInput(fxstakingtypes.TransferFromSharesArgs{ - Validator: val.String(), - From: from, - To: receipt, - Shares: shares, - }) - suite.Require().NoError(err) - res := suite.EthereumTx(signer, suite.stakingAddr, big.NewInt(0), pack) - suite.Require().False(res.Failed(), res.VmError) -} - -func (suite *PrecompileTestSuite) Delegate(val sdk.ValAddress, amount sdkmath.Int, dels ...sdk.AccAddress) { - for _, del := range dels { - suite.MintToken(del, sdk.NewCoin(fxtypes.DefaultDenom, amount)) - validator, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, val) - suite.Require().NoError(err) - _, err = suite.App.StakingKeeper.Delegate(suite.Ctx, del, amount, stakingtypes.Unbonded, validator, true) - suite.Require().NoError(err) - } -} - -func (suite *PrecompileTestSuite) Redelegate(valSrc, valDest sdk.ValAddress, del sdk.AccAddress, shares sdkmath.LegacyDec) { - _, err := suite.App.StakingKeeper.BeginRedelegation(suite.Ctx, del, valSrc, valDest, shares) - suite.Require().NoError(err) -} -func (suite *PrecompileTestSuite) Error(res *evmtypes.MsgEthereumTxResponse, errResult error) { - suite.Require().True(res.Failed()) - if res.VmError != vm.ErrExecutionReverted.Error() { - suite.Require().Equal(errResult.Error(), res.VmError) - return - } - - if len(res.Ret) > 0 { - reason, err := abi.UnpackRevert(res.Ret) - suite.Require().NoError(err) - suite.Require().Equal(errResult.Error(), reason) - return - } - - suite.Require().Equal(errResult.Error(), vm.ErrExecutionReverted.Error()) -} - -func (suite *PrecompileTestSuite) CheckDelegateLogs(logs []*evmtypes.Log, delAddr common.Address, valAddr string, amount, shares *big.Int) { - delegateV2Method := precompile.NewDelegateV2Method(nil) - existLog := false - for _, log := range logs { - if log.Topics[0] == delegateV2Method.Event.ID.String() { - suite.Require().Equal(log.Address, suite.stakingAddr.String()) - - event, err := delegateV2Method.UnpackEvent(log.ToEthereum()) - suite.Require().NoError(err) - suite.Require().Equal(event.Delegator, delAddr) - suite.Require().Equal(event.Validator, valAddr) - suite.Require().Equal(event.Amount.String(), amount.String()) - existLog = true - } - } - suite.Require().True(existLog) -} - -func (suite *PrecompileTestSuite) CheckDelegateEvents(ctx sdk.Context, valAddr sdk.ValAddress, delAmount sdkmath.Int) { - existEvent := false - for _, event := range ctx.EventManager().Events() { - if event.Type == stakingtypes.EventTypeDelegate { - for _, attr := range event.Attributes { - if attr.Key == stakingtypes.AttributeKeyValidator { - suite.Require().Equal(attr.Value, valAddr.String()) - existEvent = true - } - if attr.Key == sdk.AttributeKeyAmount { - suite.Require().Equal(strings.TrimSuffix(attr.Value, fxtypes.DefaultDenom), delAmount.String()) - existEvent = true - } - } - } - } - suite.Require().True(existEvent) -} - -func (suite *PrecompileTestSuite) CheckRedelegateLogs(logs []*evmtypes.Log, delAddr common.Address, valSrc, valDst string, shares, amount *big.Int, completionTime int64) { - redelegateV2Method := precompile.NewRedelegateV2Method(nil) - existLog := false - for _, log := range logs { - if log.Topics[0] == redelegateV2Method.Event.ID.String() { - suite.Require().Equal(log.Address, suite.stakingAddr.String()) - event, err := redelegateV2Method.UnpackEvent(log.ToEthereum()) - suite.Require().NoError(err) - suite.Require().Equal(event.Sender, delAddr) - suite.Require().Equal(event.ValSrc, valSrc) - suite.Require().Equal(event.ValDst, valDst) - suite.Require().Equal(event.Amount.String(), amount.String()) - suite.Require().Equal(event.CompletionTime.Int64(), completionTime) - existLog = true - } - } - suite.Require().True(existLog) -} - -func (suite *PrecompileTestSuite) CheckRedelegateEvents(ctx sdk.Context, valSrc, valDst string, amount *big.Int, completionTime time.Time) { - existEvent := false - for _, event := range ctx.EventManager().Events() { - if event.Type != stakingtypes.EventTypeRedelegate { - continue - } - for _, attr := range event.Attributes { - if attr.Key == stakingtypes.AttributeKeySrcValidator { - suite.Require().Equal(attr.Value, valSrc) - } - if attr.Key == stakingtypes.AttributeKeyDstValidator { - suite.Require().Equal(attr.Value, valDst) - } - if attr.Key == sdk.AttributeKeyAmount { - suite.Require().Equal(strings.TrimSuffix(attr.Value, fxtypes.DefaultDenom), amount.String()) - } - if attr.Key == stakingtypes.AttributeKeyCompletionTime { - suite.Require().Equal(attr.Value, completionTime.Format(time.RFC3339)) - } - } - existEvent = true - } - suite.Require().True(existEvent) -} - -func (suite *PrecompileTestSuite) CheckUndelegateLogs(logs []*evmtypes.Log, delAddr common.Address, valAddr string, shares, amount *big.Int, completionTime time.Time) { - undelegateV2Method := precompile.NewUndelegateV2Method(nil) - existLog := false - for _, log := range logs { - if log.Topics[0] == undelegateV2Method.Event.ID.String() { - suite.Require().Equal(log.Address, suite.stakingAddr.String()) - event, err := undelegateV2Method.UnpackEvent(log.ToEthereum()) - suite.Require().NoError(err) - suite.Require().Equal(event.Sender, delAddr) - suite.Require().Equal(event.Validator, valAddr) - suite.Require().Equal(event.Amount.String(), amount.String()) - suite.Require().Equal(event.CompletionTime.Int64(), completionTime.Unix()) - existLog = true - } - } - suite.Require().True(existLog) -} - -func (suite *PrecompileTestSuite) CheckUndeledateEvents(ctx sdk.Context, valAddr string, amount *big.Int, completionTime time.Time) { - existEvent := false - for _, event := range ctx.EventManager().Events() { - if event.Type == stakingtypes.EventTypeUnbond { - for _, attr := range event.Attributes { - if attr.Key == stakingtypes.AttributeKeyValidator { - suite.Require().Equal(attr.Value, valAddr) - existEvent = true - } - if attr.Key == sdk.AttributeKeyAmount { - suite.Require().Equal(strings.TrimSuffix(attr.Value, fxtypes.DefaultDenom), amount.String()) - existEvent = true - } - if attr.Key == stakingtypes.AttributeKeyCompletionTime { - suite.Require().Equal(attr.Value, completionTime.Format(time.RFC3339)) - existEvent = true - } - } - } - } - suite.Require().True(existEvent) + balanceAfter := suite.App.BankKeeper.GetAllBalances(suite.Ctx, signer.AccAddress()) + return balanceAfter.Sub(balanceBefore...) } diff --git a/x/staking/precompile/delegate.go b/x/staking/precompile/delegate.go index 825a9391..1df746e5 100644 --- a/x/staking/precompile/delegate.go +++ b/x/staking/precompile/delegate.go @@ -13,7 +13,6 @@ import ( fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type DelegateV2Method struct { @@ -89,7 +88,7 @@ func (m DelegateV2ABI) NewDelegateEvent(sender common.Address, validator string, return data, topic, nil } -func (m DelegateV2ABI) PackInput(args fxstakingtypes.DelegateV2Args) ([]byte, error) { +func (m DelegateV2ABI) PackInput(args fxcontract.DelegateV2Args) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator, args.Amount) if err != nil { return nil, err @@ -97,8 +96,8 @@ func (m DelegateV2ABI) PackInput(args fxstakingtypes.DelegateV2Args) ([]byte, er return append(m.Method.ID, arguments...), nil } -func (m DelegateV2ABI) UnpackInput(data []byte) (*fxstakingtypes.DelegateV2Args, error) { - args := new(fxstakingtypes.DelegateV2Args) +func (m DelegateV2ABI) UnpackInput(data []byte) (*fxcontract.DelegateV2Args, error) { + args := new(fxcontract.DelegateV2Args) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/delegate_test.go b/x/staking/precompile/delegate_test.go index b2bef5b8..9b99ede3 100644 --- a/x/staking/precompile/delegate_test.go +++ b/x/staking/precompile/delegate_test.go @@ -10,170 +10,24 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/require" + "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" fxtypes "github.com/functionx/fx-core/v8/types" "github.com/functionx/fx-core/v8/x/staking/precompile" - "github.com/functionx/fx-core/v8/x/staking/types" ) -func (suite *PrecompileTestSuite) TestDelegate() { - testCases := []struct { - name string - malleate func(val sdk.ValAddress, delAmount sdkmath.Int) (interface{}, *big.Int, error) - error func(args []string) string - result bool - }{ - { - name: "ok - v2", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (interface{}, *big.Int, error) { - return types.DelegateV2Args{ - Validator: val.String(), - Amount: delAmount.BigInt(), - }, big.NewInt(0), nil - }, - result: true, - }, - { - name: "ok - v2 delegate - multiple", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (interface{}, *big.Int, error) { - suite.MintToken(suite.signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - - validator, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, val) - suite.Require().NoError(err) - _, err = suite.App.StakingKeeper.Delegate(suite.Ctx, suite.signer.AccAddress(), delAmount, stakingtypes.Unbonded, validator, true) - suite.Require().NoError(err) - - return types.DelegateV2Args{ - Validator: val.String(), - Amount: delAmount.BigInt(), - }, big.NewInt(0), nil - }, - result: true, - }, - { - name: "failed - v2 invalid validator address", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (interface{}, *big.Int, error) { - return types.DelegateV2Args{ - Validator: val.String() + "1", - Amount: delAmount.BigInt(), - }, big.NewInt(0), fmt.Errorf("invalid validator address: %s", val.String()+"1") - }, - result: false, - }, - - { - name: "contract - ok v2", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (interface{}, *big.Int, error) { - return types.DelegateV2Args{ - Validator: val.String(), - Amount: delAmount.BigInt(), - }, big.NewInt(0), nil - }, - result: true, - }, - { - name: "contract - ok - v2 delegate - multiple", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (interface{}, *big.Int, error) { - suite.MintToken(suite.signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - - suite.Require().NoError(suite.App.BankKeeper.SendCoins(suite.Ctx, suite.signer.AccAddress(), suite.stakingTestAddr.Bytes(), sdk.NewCoins(sdk.NewCoin(fxtypes.DefaultDenom, delAmount)))) - pack, err := suite.delegateV2Method.PackInput(types.DelegateV2Args{ - Validator: val.String(), - Amount: delAmount.BigInt(), - }) - suite.Require().NoError(err) - - res := suite.EthereumTx(suite.signer, suite.stakingTestAddr, big.NewInt(0), pack) - suite.Require().False(res.Failed(), res.VmError) - - return types.DelegateV2Args{ - Validator: val.String(), - Amount: delAmount.BigInt(), - }, big.NewInt(0), nil - }, - result: true, - }, - { - name: "contract - failed - v2 invalid validator address", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (interface{}, *big.Int, error) { - return types.DelegateV2Args{Validator: val.String() + "1", Amount: delAmount.BigInt()}, - big.NewInt(0), - fmt.Errorf("invalid validator address: %s", val.String()+"1") - }, - result: false, - }, +func (suite *StakingPrecompileTestSuite) TestDelegateCompare() { + if !suite.IsCallPrecompile() { + suite.T().Skip() } - - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val := suite.GetFirstValidator() - - delAmount := helpers.NewRandAmount() - - stakingContract := suite.stakingAddr - - var packData []byte - var err error - operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - suite.Require().NoError(err) - args, value, errResult := tc.malleate(operator, delAmount) - packData, err = suite.delegateV2Method.PackInput(args.(types.DelegateV2Args)) - suite.Require().NoError(err) - - delAddr := suite.signer.Address() - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - delAddr = suite.stakingTestAddr - - suite.Require().NoError(suite.App.BankKeeper.SendCoins(suite.Ctx, suite.signer.AccAddress(), suite.stakingTestAddr.Bytes(), sdk.NewCoins(sdk.NewCoin(fxtypes.DefaultDenom, delAmount)))) - suite.Require().NoError(err) - } - - delFound := true - delBefore, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, delAddr.Bytes(), operator) - if err != nil && errors.Is(err, stakingtypes.ErrNoDelegation) { - delFound = false - } else { - suite.Require().NoError(err) - } - - valBefore, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, operator) - suite.Require().NoError(err) - - res := suite.EthereumTx(suite.signer, stakingContract, value, packData) - - if tc.result { - suite.Require().False(res.Failed(), res.VmError) - - delAfter := suite.GetDelegation(delAddr.Bytes(), operator) - - vaAfter, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, operator) - suite.Require().NoError(err) - - if !delFound { - delBefore = stakingtypes.Delegation{Shares: sdkmath.LegacyZeroDec()} - } - suite.Require().Equal(delAfter.GetShares().Sub(delBefore.GetShares()), vaAfter.GetDelegatorShares().Sub(valBefore.GetDelegatorShares())) - suite.Require().Equal(delAmount, vaAfter.GetTokens().Sub(valBefore.GetTokens())) - - suite.CheckDelegateLogs(res.Logs, delAddr, val.GetOperator(), - delAmount.BigInt(), delAfter.GetShares().Sub(delBefore.GetShares()).TruncateInt().BigInt()) - - suite.CheckDelegateEvents(suite.Ctx, operator, delAmount) - } else { - suite.Error(res, errResult) - } - }) - } -} - -func (suite *PrecompileTestSuite) TestDelegateCompare() { val := suite.GetFirstValidator() delAmount := helpers.NewRandAmount() - signer1 := suite.RandSigner() - signer2 := suite.RandSigner() + signer1 := suite.NewSigner() + signer2 := suite.NewSigner() suite.MintToken(signer1.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) @@ -200,28 +54,28 @@ func (suite *PrecompileTestSuite) TestDelegateCompare() { rewards2 := suite.PrecompileStakingWithdraw(signer2, operator) // rewards1 should equal rewards2 - suite.Require().EqualValues(rewards1.AmountOf(fxtypes.DefaultDenom).BigInt(), rewards2) + suite.Require().EqualValues(rewards1.String(), rewards2.String()) } func TestStakingDelegateV2ABI(t *testing.T) { - delegateV2Method := precompile.NewDelegateV2Method(nil) + delegateV2ABI := precompile.NewDelegateV2ABI() - require.Len(t, delegateV2Method.Method.Inputs, 2) - require.Len(t, delegateV2Method.Method.Outputs, 1) + require.Len(t, delegateV2ABI.Method.Inputs, 2) + require.Len(t, delegateV2ABI.Method.Outputs, 1) - require.Len(t, delegateV2Method.Event.Inputs, 3) + require.Len(t, delegateV2ABI.Event.Inputs, 3) } -func (suite *PrecompileTestSuite) TestDelegateV2() { +func (suite *StakingPrecompileTestSuite) TestDelegateV2() { testCases := []struct { name string - malleate func(val sdk.ValAddress, delAmount sdkmath.Int) (types.DelegateV2Args, error) + malleate func(val sdk.ValAddress, delAmount sdkmath.Int) (contract.DelegateV2Args, error) result bool }{ { name: "ok", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (types.DelegateV2Args, error) { - return types.DelegateV2Args{ + malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (contract.DelegateV2Args, error) { + return contract.DelegateV2Args{ Validator: val.String(), Amount: delAmount.BigInt(), }, nil @@ -230,58 +84,14 @@ func (suite *PrecompileTestSuite) TestDelegateV2() { }, { name: "ok - delegate - multiple", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (types.DelegateV2Args, error) { - suite.MintToken(suite.signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - - validator, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, val) - suite.Require().NoError(err) - _, err = suite.App.StakingKeeper.Delegate(suite.Ctx, suite.signer.AccAddress(), delAmount, stakingtypes.Unbonded, validator, true) - suite.Require().NoError(err) - - return types.DelegateV2Args{ - Validator: val.String(), - Amount: delAmount.BigInt(), - }, nil - }, - result: true, - }, - { - name: "failed - invalid validator address", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (types.DelegateV2Args, error) { - return types.DelegateV2Args{ - Validator: val.String() + "1", - Amount: delAmount.BigInt(), - }, fmt.Errorf("invalid validator address: %s", val.String()+"1") - }, - result: false, - }, - - { - name: "contract - ok", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (types.DelegateV2Args, error) { - return types.DelegateV2Args{ - Validator: val.String(), - Amount: delAmount.BigInt(), - }, nil - }, - result: true, - }, - { - name: "contract - ok - delegate - multiple", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (types.DelegateV2Args, error) { - suite.MintToken(suite.signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - - suite.Require().NoError(suite.App.BankKeeper.SendCoins(suite.Ctx, suite.signer.AccAddress(), suite.stakingTestAddr.Bytes(), sdk.NewCoins(sdk.NewCoin(fxtypes.DefaultDenom, delAmount)))) - pack, err := suite.delegateV2Method.PackInput(types.DelegateV2Args{ + malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (contract.DelegateV2Args, error) { + res := suite.DelegateV2(suite.Ctx, contract.DelegateV2Args{ Validator: val.String(), Amount: delAmount.BigInt(), }) - suite.Require().NoError(err) - - res := suite.EthereumTx(suite.signer, suite.stakingTestAddr, big.NewInt(0), pack) suite.Require().False(res.Failed(), res.VmError) - return types.DelegateV2Args{ + return contract.DelegateV2Args{ Validator: val.String(), Amount: delAmount.BigInt(), }, nil @@ -289,10 +99,12 @@ func (suite *PrecompileTestSuite) TestDelegateV2() { result: true, }, { - name: "contract - failed - invalid validator address", - malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (types.DelegateV2Args, error) { - return types.DelegateV2Args{Validator: val.String() + "1", Amount: delAmount.BigInt()}, - fmt.Errorf("invalid validator address: %s", val.String()+"1") + name: "failed - invalid validator address", + malleate: func(val sdk.ValAddress, delAmount sdkmath.Int) (contract.DelegateV2Args, error) { + return contract.DelegateV2Args{ + Validator: val.String() + "1", + Amount: delAmount.BigInt(), + }, fmt.Errorf("invalid validator address: %s", val.String()+"1") }, result: false, }, @@ -300,26 +112,12 @@ func (suite *PrecompileTestSuite) TestDelegateV2() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val := suite.GetFirstValidator() + operator := suite.GetFirstValAddr() delAmount := helpers.NewRandAmount() - operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - suite.Require().NoError(err) - - args, errResult := tc.malleate(operator, delAmount) + args, expectErr := tc.malleate(operator, delAmount) - stakingContract := suite.stakingAddr - delAddr := suite.signer.Address() - - suite.MintToken(suite.signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) - packData, err := suite.delegateV2Method.PackInput(args) - suite.Require().NoError(err) - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - delAddr = suite.stakingTestAddr - - suite.Require().NoError(suite.App.BankKeeper.SendCoins(suite.Ctx, suite.signer.AccAddress(), suite.stakingTestAddr.Bytes(), sdk.NewCoins(sdk.NewCoin(fxtypes.DefaultDenom, delAmount)))) - } + delAddr := suite.GetDelAddr() delFound := true delBefore, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, delAddr.Bytes(), operator) @@ -328,42 +126,64 @@ func (suite *PrecompileTestSuite) TestDelegateV2() { } else { suite.Require().NoError(err) } - valBefore, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, operator) - suite.Require().NoError(err) - res := suite.EthereumTx(suite.signer, stakingContract, big.NewInt(0), packData) + valBefore := suite.GetValidator(operator) + + res := suite.WithError(expectErr).DelegateV2(suite.Ctx, args) if tc.result { suite.Require().False(res.Failed(), res.VmError) delAfter := suite.GetDelegation(delAddr.Bytes(), operator) - vaAfter, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, operator) - suite.Require().NoError(err) + valAfter := suite.GetValidator(operator) if !delFound { delBefore = stakingtypes.Delegation{Shares: sdkmath.LegacyZeroDec()} } - suite.Require().Equal(delAfter.GetShares().Sub(delBefore.GetShares()), vaAfter.GetDelegatorShares().Sub(valBefore.GetDelegatorShares())) - suite.Require().Equal(delAmount, vaAfter.GetTokens().Sub(valBefore.GetTokens())) + suite.Require().Equal(delAfter.GetShares().Sub(delBefore.GetShares()), valAfter.GetDelegatorShares().Sub(valBefore.GetDelegatorShares())) + suite.Require().Equal(delAmount, valAfter.GetTokens().Sub(valBefore.GetTokens())) + + suite.CheckDelegateLogs(res.Logs, delAddr, operator.String(), delAmount.BigInt()) + suite.CheckDelegateEvents(suite.Ctx, operator, delAmount) + } + }) + } +} - existLog := false - for _, log := range res.Logs { - if log.Topics[0] == suite.delegateV2Method.Event.ID.String() { - suite.Require().Equal(log.Address, suite.stakingAddr.String()) +func (suite *StakingPrecompileTestSuite) CheckDelegateLogs(logs []*evmtypes.Log, delAddr common.Address, valAddr string, amount *big.Int) { + delegateV2ABI := precompile.NewDelegateV2ABI() + existLog := false + for _, log := range logs { + if log.Topics[0] == delegateV2ABI.Event.ID.String() { + suite.Require().Equal(contract.StakingAddress, log.Address) - event, err := suite.delegateV2Method.UnpackEvent(log.ToEthereum()) - suite.Require().NoError(err) - suite.Require().Equal(event.Delegator, delAddr) - suite.Require().Equal(event.Validator, val.GetOperator()) - suite.Require().Equal(event.Amount.String(), delAmount.BigInt().String()) - existLog = true - } + event, err := delegateV2ABI.UnpackEvent(log.ToEthereum()) + suite.Require().NoError(err) + suite.Require().Equal(event.Delegator, delAddr) + suite.Require().Equal(event.Validator, valAddr) + suite.Require().Equal(event.Amount.String(), amount.String()) + existLog = true + } + } + suite.Require().True(existLog) +} + +func (suite *StakingPrecompileTestSuite) CheckDelegateEvents(ctx sdk.Context, valAddr sdk.ValAddress, delAmount sdkmath.Int) { + existEvent := false + for _, event := range ctx.EventManager().Events() { + if event.Type == stakingtypes.EventTypeDelegate { + for _, attr := range event.Attributes { + if attr.Key == stakingtypes.AttributeKeyValidator { + suite.Require().Equal(attr.Value, valAddr.String()) + existEvent = true + } + if attr.Key == sdk.AttributeKeyAmount { + suite.Require().Equal(strings.TrimSuffix(attr.Value, fxtypes.DefaultDenom), delAmount.String()) + existEvent = true } - suite.Require().True(existLog) - } else { - suite.Error(res, errResult) } - }) + } } + suite.Require().True(existEvent) } diff --git a/x/staking/precompile/delegation.go b/x/staking/precompile/delegation.go index f7d680e2..e4b1a60d 100644 --- a/x/staking/precompile/delegation.go +++ b/x/staking/precompile/delegation.go @@ -8,8 +8,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/core/vm" + fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type DelegationMethod struct { @@ -73,7 +73,7 @@ func NewDelegationABI() DelegationABI { } } -func (m DelegationABI) PackInput(args fxstakingtypes.DelegationArgs) ([]byte, error) { +func (m DelegationABI) PackInput(args fxcontract.DelegationArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator, args.Delegator) if err != nil { return nil, err @@ -81,8 +81,8 @@ func (m DelegationABI) PackInput(args fxstakingtypes.DelegationArgs) ([]byte, er return append(m.Method.ID, arguments...), nil } -func (m DelegationABI) UnpackInput(data []byte) (*fxstakingtypes.DelegationArgs, error) { - args := new(fxstakingtypes.DelegationArgs) +func (m DelegationABI) UnpackInput(data []byte) (*fxcontract.DelegationArgs, error) { + args := new(fxcontract.DelegationArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/delegation_rewards.go b/x/staking/precompile/delegation_rewards.go index 9e4bbf94..ccc771c3 100644 --- a/x/staking/precompile/delegation_rewards.go +++ b/x/staking/precompile/delegation_rewards.go @@ -8,8 +8,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/core/vm" + fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type DelegationRewardsMethod struct { @@ -79,7 +79,7 @@ func NewDelegationRewardsABI() DelegationRewardsABI { } } -func (m DelegationRewardsABI) PackInput(args fxstakingtypes.DelegationRewardsArgs) ([]byte, error) { +func (m DelegationRewardsABI) PackInput(args fxcontract.DelegationRewardsArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator, args.Delegator) if err != nil { return nil, err @@ -87,8 +87,8 @@ func (m DelegationRewardsABI) PackInput(args fxstakingtypes.DelegationRewardsArg return append(m.Method.ID, arguments...), nil } -func (m DelegationRewardsABI) UnpackInput(data []byte) (*fxstakingtypes.DelegationRewardsArgs, error) { - args := new(fxstakingtypes.DelegationRewardsArgs) +func (m DelegationRewardsABI) UnpackInput(data []byte) (*fxcontract.DelegationRewardsArgs, error) { + args := new(fxcontract.DelegationRewardsArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/delegation_rewards_test.go b/x/staking/precompile/delegation_rewards_test.go index 8ff84c28..5f5fca8d 100644 --- a/x/staking/precompile/delegation_rewards_test.go +++ b/x/staking/precompile/delegation_rewards_test.go @@ -2,39 +2,35 @@ package precompile_test import ( "fmt" - "math/big" - "strings" "testing" - sdkmath "cosmossdk.io/math" - tmrand "github.com/cometbft/cometbft/libs/rand" sdk "github.com/cosmos/cosmos-sdk/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - fxtypes "github.com/functionx/fx-core/v8/types" + "github.com/functionx/fx-core/v8/contract" + "github.com/functionx/fx-core/v8/testutil/helpers" "github.com/functionx/fx-core/v8/x/staking/precompile" - "github.com/functionx/fx-core/v8/x/staking/types" ) func TestStakingDelegationRewardsABI(t *testing.T) { - delegationRewardMethod := precompile.NewDelegationRewardsMethod(nil) + delegationRewardsABI := precompile.NewDelegationRewardsABI() - require.Len(t, delegationRewardMethod.Method.Inputs, 2) - require.Len(t, delegationRewardMethod.Method.Outputs, 1) + require.Len(t, delegationRewardsABI.Method.Inputs, 2) + require.Len(t, delegationRewardsABI.Method.Outputs, 1) } -func (suite *PrecompileTestSuite) TestDelegationRewards() { +func (suite *StakingPrecompileTestSuite) TestDelegationRewards() { testCases := []struct { name string - malleate func(val sdk.ValAddress, del common.Address) (types.DelegationRewardsArgs, error) + malleate func(val sdk.ValAddress, del common.Address) (contract.DelegationRewardsArgs, error) result bool }{ { name: "ok", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationRewardsArgs, error) { - return types.DelegationRewardsArgs{ + malleate: func(val sdk.ValAddress, del common.Address) (contract.DelegationRewardsArgs, error) { + return contract.DelegationRewardsArgs{ Validator: val.String(), Delegator: del, }, nil @@ -43,9 +39,9 @@ func (suite *PrecompileTestSuite) TestDelegationRewards() { }, { name: "failed - invalid validator address", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationRewardsArgs, error) { + malleate: func(val sdk.ValAddress, del common.Address) (contract.DelegationRewardsArgs, error) { newVal := val.String() + "1" - return types.DelegationRewardsArgs{ + return contract.DelegationRewardsArgs{ Validator: newVal, Delegator: del, }, fmt.Errorf("invalid validator address: %s", newVal) @@ -54,42 +50,10 @@ func (suite *PrecompileTestSuite) TestDelegationRewards() { }, { name: "failed - validator not found", - malleate: func(_ sdk.ValAddress, del common.Address) (types.DelegationRewardsArgs, error) { + malleate: func(_ sdk.ValAddress, del common.Address) (contract.DelegationRewardsArgs, error) { newVal := sdk.ValAddress(suite.signer.AccAddress()).String() - return types.DelegationRewardsArgs{ - Validator: newVal, - Delegator: del, - }, fmt.Errorf("validator does not exist") - }, - result: false, - }, - { - name: "contract - ok", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationRewardsArgs, error) { - return types.DelegationRewardsArgs{ - Validator: val.String(), - Delegator: del, - }, nil - }, - result: true, - }, - { - name: "contract - failed invalid validator address", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationRewardsArgs, error) { - newVal := val.String() + "1" - return types.DelegationRewardsArgs{ - Validator: newVal, - Delegator: del, - }, fmt.Errorf("invalid validator address: %s", newVal) - }, - result: false, - }, - { - name: "contract - failed validator not found", - malleate: func(_ sdk.ValAddress, del common.Address) (types.DelegationRewardsArgs, error) { - newVal := sdk.ValAddress(suite.signer.AccAddress()).String() - return types.DelegationRewardsArgs{ + return contract.DelegationRewardsArgs{ Validator: newVal, Delegator: del, }, fmt.Errorf("validator does not exist") @@ -100,52 +64,31 @@ func (suite *PrecompileTestSuite) TestDelegationRewards() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val0 := suite.GetFirstValidator() - - delAmt := sdkmath.NewInt(int64(tmrand.Intn(1000) + 100)).MulRaw(1e18) - signer := suite.RandSigner() - suite.MintToken(signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmt)) + operator0 := suite.GetFirstValAddr() + delAmt := helpers.NewRandAmount() + delAddr := suite.GetDelAddr() - stakingContract := suite.stakingAddr - delAddr := signer.Address() - - value := big.NewInt(0) - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - delAddr = suite.stakingTestAddr - value = delAmt.BigInt() - } - - operator0, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val0.GetOperator()) - suite.Require().NoError(err) - pack, err := suite.delegateV2Method.PackInput(types.DelegateV2Args{ - Validator: val0.GetOperator(), + res := suite.DelegateV2(suite.Ctx, contract.DelegateV2Args{ + Validator: operator0.String(), Amount: delAmt.BigInt(), }) - suite.Require().NoError(err) - - res := suite.EthereumTx(signer, stakingContract, value, pack) suite.Require().False(res.Failed(), res.VmError) suite.Commit() - resp, err := suite.DistributionQueryClient(suite.Ctx).DelegationRewards(suite.Ctx, &distrtypes.QueryDelegationRewardsRequest{DelegatorAddress: sdk.AccAddress(delAddr.Bytes()).String(), ValidatorAddress: val0.GetOperator()}) + resp, err := suite.DistributionQueryClient(suite.Ctx).DelegationRewards(suite.Ctx, + &distrtypes.QueryDelegationRewardsRequest{ + DelegatorAddress: sdk.AccAddress(delAddr.Bytes()).String(), + ValidatorAddress: operator0.String(), + }) suite.Require().NoError(err) - evmDenom := suite.App.EvmKeeper.GetParams(suite.Ctx).EvmDenom - args, errResult := tc.malleate(operator0, delAddr) - packData, err := suite.delegationRewardsMethod.PackInput(args) - suite.Require().NoError(err) + args, expectErr := tc.malleate(operator0, delAddr) - res, err = suite.App.EvmKeeper.CallEVMWithoutGas(suite.Ctx, suite.signer.Address(), &stakingContract, nil, packData, false) + rewards := suite.WithError(expectErr).DelegationRewards(suite.Ctx, args) if tc.result { suite.Require().NoError(err) - suite.Require().False(res.Failed(), res.VmError) - rewardsValue, err := suite.delegationRewardsMethod.UnpackOutput(res.Ret) - suite.Require().NoError(err) - suite.Require().EqualValues(rewardsValue.String(), resp.Rewards.AmountOf(evmDenom).TruncateInt().BigInt().String()) - } else { - suite.Error(res, errResult) + suite.Require().EqualValues(resp.Rewards[0].Amount.TruncateInt().String(), rewards.String()) } }) } diff --git a/x/staking/precompile/delegation_test.go b/x/staking/precompile/delegation_test.go index 1b534c21..156fa9a4 100644 --- a/x/staking/precompile/delegation_test.go +++ b/x/staking/precompile/delegation_test.go @@ -2,37 +2,35 @@ package precompile_test import ( "fmt" - "math/big" - "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" "github.com/functionx/fx-core/v8/x/staking/precompile" - "github.com/functionx/fx-core/v8/x/staking/types" ) func TestStakingDelegationABI(t *testing.T) { - delegationMethod := precompile.NewDelegationMethod(nil) + delegationABI := precompile.NewDelegationABI() - require.Len(t, delegationMethod.Method.Inputs, 2) - require.Len(t, delegationMethod.Method.Outputs, 2) + require.Len(t, delegationABI.Method.Inputs, 2) + require.Len(t, delegationABI.Method.Outputs, 2) } -func (suite *PrecompileTestSuite) TestDelegation() { +func (suite *StakingPrecompileTestSuite) TestDelegation() { testCases := []struct { name string - malleate func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) + malleate func(val sdk.ValAddress, del common.Address) (contract.DelegationArgs, error) error func(errArgs []string) string result bool }{ { name: "ok", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) { - return types.DelegationArgs{ + malleate: func(val sdk.ValAddress, del common.Address) (contract.DelegationArgs, error) { + return contract.DelegationArgs{ Validator: val.String(), Delegator: del, }, nil @@ -41,8 +39,8 @@ func (suite *PrecompileTestSuite) TestDelegation() { }, { name: "ok - zero", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) { - return types.DelegationArgs{ + malleate: func(val sdk.ValAddress, del common.Address) (contract.DelegationArgs, error) { + return contract.DelegationArgs{ Validator: val.String(), Delegator: del, }, nil @@ -51,9 +49,9 @@ func (suite *PrecompileTestSuite) TestDelegation() { }, { name: "failed - invalid validator address", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) { + malleate: func(val sdk.ValAddress, del common.Address) (contract.DelegationArgs, error) { newVal := val.String() + "1" - return types.DelegationArgs{ + return contract.DelegationArgs{ Validator: newVal, Delegator: del, }, fmt.Errorf("invalid validator address: %s", newVal) @@ -62,54 +60,10 @@ func (suite *PrecompileTestSuite) TestDelegation() { }, { name: "failed - validator not found", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) { + malleate: func(val sdk.ValAddress, del common.Address) (contract.DelegationArgs, error) { newVal := sdk.ValAddress(suite.signer.AccAddress()).String() - return types.DelegationArgs{ - Validator: newVal, - Delegator: del, - }, fmt.Errorf("validator does not exist") - }, - result: false, - }, - - { - name: "contract - ok", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) { - return types.DelegationArgs{ - Validator: val.String(), - Delegator: del, - }, nil - }, - result: true, - }, - { - name: "contract - ok - zero", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) { - return types.DelegationArgs{ - Validator: val.String(), - Delegator: del, - }, nil - }, - result: true, - }, - { - name: "contract - failed invalid validator address", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) { - newVal := val.String() + "1" - return types.DelegationArgs{ - Validator: newVal, - Delegator: del, - }, fmt.Errorf("invalid validator address: %s", newVal) - }, - result: false, - }, - { - name: "contract - failed validator not found", - malleate: func(val sdk.ValAddress, del common.Address) (types.DelegationArgs, error) { - newVal := sdk.ValAddress(suite.signer.AccAddress()).String() - - return types.DelegationArgs{ + return contract.DelegationArgs{ Validator: newVal, Delegator: del, }, fmt.Errorf("validator does not exist") @@ -120,48 +74,26 @@ func (suite *PrecompileTestSuite) TestDelegation() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val0 := suite.GetFirstValidator() - + operator0 := suite.GetFirstValAddr() delAmount := helpers.NewRandAmount() + delAddr := suite.GetDelAddr() - stakingContract := suite.stakingAddr - delAddr := suite.signer.Address() - value := big.NewInt(0) - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - delAddr = suite.stakingTestAddr - value = delAmount.BigInt() - } - - operator0, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val0.GetOperator()) - suite.Require().NoError(err) - - pack, err := suite.delegateV2Method.PackInput(types.DelegateV2Args{ - Validator: val0.GetOperator(), + res := suite.DelegateV2(suite.Ctx, contract.DelegateV2Args{ + Validator: operator0.String(), Amount: delAmount.BigInt(), }) - suite.Require().NoError(err) - - res := suite.EthereumTx(suite.signer, stakingContract, value, pack) suite.Require().False(res.Failed(), res.VmError) suite.Commit() - args, errResult := tc.malleate(operator0, delAddr) - packData, err := suite.delegationMethod.PackInput(args) - suite.Require().NoError(err) - delegation, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, delAddr.Bytes(), operator0) - suite.Require().NoError(err) + args, expectErr := tc.malleate(operator0, delAddr) + + delegation := suite.GetDelegation(delAddr.Bytes(), operator0) - res, _ = suite.App.EvmKeeper.CallEVMWithoutGas(suite.Ctx, suite.signer.Address(), &stakingContract, nil, packData, false) + delValue, _ := suite.WithError(expectErr).Delegation(suite.Ctx, args) if tc.result { - suite.Require().NoError(err) suite.Require().False(res.Failed(), res.VmError) - delValue, _, err := suite.delegationMethod.UnpackOutput(res.Ret) - suite.Require().NoError(err) suite.Require().Equal(delegation.GetShares().TruncateInt().String(), delValue.String()) - } else { - suite.Error(res, errResult) } }) } diff --git a/x/staking/precompile/keeper.go b/x/staking/precompile/keeper.go index 17395d82..28f85b7e 100644 --- a/x/staking/precompile/keeper.go +++ b/x/staking/precompile/keeper.go @@ -77,8 +77,8 @@ func (k Keeper) handlerTransferShares( return nil, nil, err } - withdrawMethod := NewWithdrawMethod(nil) - data, topic, err := withdrawMethod.NewWithdrawEvent(from, valAddr.String(), withdrawRewardRes.Amount.AmountOf(k.stakingDenom).BigInt()) + withdrawABI := NewWithdrawABI() + data, topic, err := withdrawABI.NewWithdrawEvent(from, valAddr.String(), withdrawRewardRes.Amount.AmountOf(k.stakingDenom).BigInt()) if err != nil { return nil, nil, err } @@ -105,7 +105,7 @@ func (k Keeper) handlerTransferShares( if err != nil { return nil, nil, err } - data, topic, err = withdrawMethod.NewWithdrawEvent(to, valAddr.String(), toWithdrawRewardsRes.Amount.AmountOf(k.stakingDenom).BigInt()) + data, topic, err = withdrawABI.NewWithdrawEvent(to, valAddr.String(), toWithdrawRewardsRes.Amount.AmountOf(k.stakingDenom).BigInt()) if err != nil { return nil, nil, err } diff --git a/x/staking/precompile/redelegate.go b/x/staking/precompile/redelegate.go index 7adfceeb..07b6ca0c 100644 --- a/x/staking/precompile/redelegate.go +++ b/x/staking/precompile/redelegate.go @@ -13,7 +13,6 @@ import ( fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type RedelegateV2Method struct { @@ -24,7 +23,7 @@ type RedelegateV2Method struct { func NewRedelegateV2Method(keeper *Keeper) *RedelegateV2Method { return &RedelegateV2Method{ Keeper: keeper, - RedelegateABI: NewRedelegateABI(), + RedelegateABI: NewRedelegateV2ABI(), } } @@ -79,7 +78,7 @@ type RedelegateABI struct { abi.Event } -func NewRedelegateABI() RedelegateABI { +func NewRedelegateV2ABI() RedelegateABI { return RedelegateABI{ Method: stakingABI.Methods["redelegateV2"], Event: stakingABI.Events["RedelegateV2"], @@ -94,7 +93,7 @@ func (m RedelegateABI) NewRedelegationEvent(sender common.Address, validatorSrc, return data, topic, nil } -func (m RedelegateABI) PackInput(args fxstakingtypes.RedelegateV2Args) ([]byte, error) { +func (m RedelegateABI) PackInput(args fxcontract.RedelegateV2Args) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.ValidatorSrc, args.ValidatorDst, args.Amount) if err != nil { return nil, err @@ -102,8 +101,8 @@ func (m RedelegateABI) PackInput(args fxstakingtypes.RedelegateV2Args) ([]byte, return append(m.Method.ID, arguments...), nil } -func (m RedelegateABI) UnpackInput(data []byte) (*fxstakingtypes.RedelegateV2Args, error) { - args := new(fxstakingtypes.RedelegateV2Args) +func (m RedelegateABI) UnpackInput(data []byte) (*fxcontract.RedelegateV2Args, error) { + args := new(fxcontract.RedelegateV2Args) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/redelegate_test.go b/x/staking/precompile/redelegate_test.go index ade2da1d..9e6f5dc0 100644 --- a/x/staking/precompile/redelegate_test.go +++ b/x/staking/precompile/redelegate_test.go @@ -4,26 +4,31 @@ import ( "fmt" "math/big" "strings" + "time" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" - "github.com/functionx/fx-core/v8/x/staking/types" + fxtypes "github.com/functionx/fx-core/v8/types" + "github.com/functionx/fx-core/v8/x/staking/precompile" ) -func (suite *PrecompileTestSuite) TestRedelegate() { +func (suite *StakingPrecompileTestSuite) TestRedelegate() { testCases := []struct { name string - malleate func(valSrc, valDst sdk.ValAddress, shares sdkmath.LegacyDec, delAmount sdkmath.Int) (interface{}, error) + malleate func(valSrc, valDst sdk.ValAddress, shares sdkmath.LegacyDec, delAmount sdkmath.Int) (contract.RedelegateV2Args, error) error func(errArgs []string) string result bool }{ { name: "ok v2", - malleate: func(valSrc, valDst sdk.ValAddress, shares sdkmath.LegacyDec, delAmount sdkmath.Int) (interface{}, error) { - return types.RedelegateV2Args{ + malleate: func(valSrc, valDst sdk.ValAddress, shares sdkmath.LegacyDec, delAmount sdkmath.Int) (contract.RedelegateV2Args, error) { + return contract.RedelegateV2Args{ ValidatorSrc: valSrc.String(), ValidatorDst: valDst.String(), Amount: delAmount.BigInt(), @@ -33,33 +38,9 @@ func (suite *PrecompileTestSuite) TestRedelegate() { }, { name: "failed - v2 invalid validator src", - malleate: func(_, valDst sdk.ValAddress, shares sdkmath.LegacyDec, delAmount sdkmath.Int) (interface{}, error) { + malleate: func(_, valDst sdk.ValAddress, shares sdkmath.LegacyDec, delAmount sdkmath.Int) (contract.RedelegateV2Args, error) { valSrc := sdk.ValAddress(suite.signer.Address().Bytes()) - return types.RedelegateV2Args{ - ValidatorSrc: valSrc.String(), - ValidatorDst: valDst.String(), - Amount: delAmount.BigInt(), - }, fmt.Errorf("validator does not exist") - }, - result: false, - }, - - { - name: "contract - ok v2", - malleate: func(valSrc, valDst sdk.ValAddress, shares sdkmath.LegacyDec, delAmount sdkmath.Int) (interface{}, error) { - return types.RedelegateV2Args{ - ValidatorSrc: valSrc.String(), - ValidatorDst: valDst.String(), - Amount: delAmount.BigInt(), - }, nil - }, - result: true, - }, - { - name: "contract - failed - v2 invalid validator src", - malleate: func(_, valDst sdk.ValAddress, shares sdkmath.LegacyDec, delAmount sdkmath.Int) (interface{}, error) { - valSrc := sdk.ValAddress(suite.signer.Address().Bytes()) - return types.RedelegateV2Args{ + return contract.RedelegateV2Args{ ValidatorSrc: valSrc.String(), ValidatorDst: valDst.String(), Amount: delAmount.BigInt(), @@ -70,54 +51,28 @@ func (suite *PrecompileTestSuite) TestRedelegate() { } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - vals := suite.GetValidators() - val0 := vals[0] - val1 := vals[1] - + operator0 := suite.GetFirstValAddr() + operator1 := suite.GetSecondValAddr() delAmt := helpers.NewRandAmount() + delAddr := suite.GetDelAddr() - stakingContract := suite.stakingAddr - delAddr := suite.signer.Address() - value := big.NewInt(0) - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - delAddr = suite.stakingTestAddr - value = delAmt.BigInt() - } - - operator0, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val0.GetOperator()) - suite.Require().NoError(err) - - operator1, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val1.GetOperator()) - suite.Require().NoError(err) - - // delegate to val0 - pack, err := suite.delegateV2Method.PackInput(types.DelegateV2Args{ - Validator: val0.GetOperator(), + res := suite.DelegateV2(suite.Ctx, contract.DelegateV2Args{ + Validator: operator0.String(), Amount: delAmt.BigInt(), }) - suite.Require().NoError(err) - - res := suite.EthereumTx(suite.signer, stakingContract, value, pack) suite.Require().False(res.Failed(), res.VmError) suite.Commit() - delegation0, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, delAddr.Bytes(), operator0) - suite.Require().NoError(err) - _, err = suite.App.StakingKeeper.GetDelegation(suite.Ctx, delAddr.Bytes(), operator1) + delegation0 := suite.GetDelegation(delAddr.Bytes(), operator0) + _, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, delAddr.Bytes(), operator1) suite.Require().ErrorIs(err, stakingtypes.ErrNoDelegation) - val0, err = suite.App.StakingKeeper.GetValidator(suite.Ctx, operator0) - suite.Require().NoError(err) + val0 := suite.GetValidator(operator0) - var packData []byte - args, errResult := tc.malleate(operator0, operator1, delegation0.Shares, delAmt) - packData, err = suite.redelegateV2Method.PackInput(args.(types.RedelegateV2Args)) - suite.Require().NoError(err) - - res = suite.EthereumTx(suite.signer, stakingContract, big.NewInt(0), packData) + args, expectErr := tc.malleate(operator0, operator1, delegation0.Shares, delAmt) + res = suite.WithError(expectErr).RedelegateV2(suite.Ctx, args) if tc.result { suite.Require().False(res.Failed(), res.VmError) @@ -127,20 +82,62 @@ func (suite *PrecompileTestSuite) TestRedelegate() { suite.Require().NoError(err) suite.Require().Equal(delegation0.Shares, delegation1New.Shares) - redelegates, err := suite.App.StakingKeeper.GetAllRedelegations(suite.Ctx, delAddr.Bytes(), operator0, operator1) + redelegations, err := suite.App.StakingKeeper.GetAllRedelegations(suite.Ctx, delAddr.Bytes(), operator0, operator1) suite.Require().NoError(err) - suite.Require().Equal(1, len(redelegates)) + suite.Require().Equal(1, len(redelegations)) - suite.CheckRedelegateLogs(res.Logs, delAddr, val0.GetOperator(), val1.GetOperator(), + suite.CheckRedelegateLogs(res.Logs, delAddr, val0.GetOperator(), operator1.String(), delegation0.Shares.TruncateInt().BigInt(), val0.TokensFromShares(delegation0.Shares).TruncateInt().BigInt(), - redelegates[0].Entries[0].CompletionTime.Unix()) + redelegations[0].Entries[0].CompletionTime.Unix()) - suite.CheckRedelegateEvents(suite.Ctx, val0.GetOperator(), val1.GetOperator(), + suite.CheckRedelegateEvents(suite.Ctx, val0.GetOperator(), operator1.String(), val0.TokensFromShares(delegation0.Shares).TruncateInt().BigInt(), - redelegates[0].Entries[0].CompletionTime) - } else { - suite.Error(res, errResult) + redelegations[0].Entries[0].CompletionTime) } }) } } + +func (suite *StakingPrecompileTestSuite) CheckRedelegateLogs(logs []*evmtypes.Log, delAddr common.Address, valSrc, valDst string, shares, amount *big.Int, completionTime int64) { + redelegateV2ABI := precompile.NewRedelegateV2ABI() + existLog := false + for _, log := range logs { + if log.Topics[0] == redelegateV2ABI.Event.ID.String() { + suite.Require().Equal(log.Address, contract.StakingAddress) + event, err := redelegateV2ABI.UnpackEvent(log.ToEthereum()) + suite.Require().NoError(err) + suite.Require().Equal(event.Sender, delAddr) + suite.Require().Equal(event.ValSrc, valSrc) + suite.Require().Equal(event.ValDst, valDst) + suite.Require().Equal(event.Amount.String(), amount.String()) + suite.Require().Equal(event.CompletionTime.Int64(), completionTime) + existLog = true + } + } + suite.Require().True(existLog) +} + +func (suite *StakingPrecompileTestSuite) CheckRedelegateEvents(ctx sdk.Context, valSrc, valDst string, amount *big.Int, completionTime time.Time) { + existEvent := false + for _, event := range ctx.EventManager().Events() { + if event.Type != stakingtypes.EventTypeRedelegate { + continue + } + for _, attr := range event.Attributes { + if attr.Key == stakingtypes.AttributeKeySrcValidator { + suite.Require().Equal(attr.Value, valSrc) + } + if attr.Key == stakingtypes.AttributeKeyDstValidator { + suite.Require().Equal(attr.Value, valDst) + } + if attr.Key == sdk.AttributeKeyAmount { + suite.Require().Equal(strings.TrimSuffix(attr.Value, fxtypes.DefaultDenom), amount.String()) + } + if attr.Key == stakingtypes.AttributeKeyCompletionTime { + suite.Require().Equal(attr.Value, completionTime.Format(time.RFC3339)) + } + } + existEvent = true + } + suite.Require().True(existEvent) +} diff --git a/x/staking/precompile/slashing_info.go b/x/staking/precompile/slashing_info.go index 9bcdc01d..db638aee 100644 --- a/x/staking/precompile/slashing_info.go +++ b/x/staking/precompile/slashing_info.go @@ -6,19 +6,19 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/core/vm" + fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type SlashingInfoMethod struct { *Keeper - SlashingABI + SlashingInfoABI } func NewSlashingInfoMethod(keeper *Keeper) *SlashingInfoMethod { return &SlashingInfoMethod{ - Keeper: keeper, - SlashingABI: NewSlashingABI(), + Keeper: keeper, + SlashingInfoABI: NewSlashingInfoABI(), } } @@ -60,17 +60,17 @@ func (m *SlashingInfoMethod) Run(evm *vm.EVM, contract *vm.Contract) ([]byte, er return m.PackOutput(validator.Jailed, signingInfo.MissedBlocksCounter) } -type SlashingABI struct { +type SlashingInfoABI struct { abi.Method } -func NewSlashingABI() SlashingABI { - return SlashingABI{ +func NewSlashingInfoABI() SlashingInfoABI { + return SlashingInfoABI{ Method: stakingABI.Methods["slashingInfo"], } } -func (m SlashingABI) PackInput(args fxstakingtypes.SlashingInfoArgs) ([]byte, error) { +func (m SlashingInfoABI) PackInput(args fxcontract.SlashingInfoArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator) if err != nil { return nil, err @@ -78,17 +78,17 @@ func (m SlashingABI) PackInput(args fxstakingtypes.SlashingInfoArgs) ([]byte, er return append(m.Method.ID, arguments...), nil } -func (m SlashingABI) UnpackInput(data []byte) (*fxstakingtypes.SlashingInfoArgs, error) { - args := new(fxstakingtypes.SlashingInfoArgs) +func (m SlashingInfoABI) UnpackInput(data []byte) (*fxcontract.SlashingInfoArgs, error) { + args := new(fxcontract.SlashingInfoArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } -func (m SlashingABI) PackOutput(jailed bool, missed int64) ([]byte, error) { +func (m SlashingInfoABI) PackOutput(jailed bool, missed int64) ([]byte, error) { return m.Method.Outputs.Pack(jailed, big.NewInt(missed)) } -func (m SlashingABI) UnpackOutput(data []byte) (bool, *big.Int, error) { +func (m SlashingInfoABI) UnpackOutput(data []byte) (bool, *big.Int, error) { unpack, err := m.Method.Outputs.Unpack(data) if err != nil { return false, nil, err diff --git a/x/staking/precompile/slashing_info_test.go b/x/staking/precompile/slashing_info_test.go index 1db2d3a0..2f5b6f3c 100644 --- a/x/staking/precompile/slashing_info_test.go +++ b/x/staking/precompile/slashing_info_test.go @@ -2,36 +2,33 @@ package precompile_test import ( "fmt" - "math/big" - "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" "github.com/functionx/fx-core/v8/x/staking/precompile" - "github.com/functionx/fx-core/v8/x/staking/types" ) func TestSlashingInfoABI(t *testing.T) { - slashingInfoMethod := precompile.NewSlashingInfoMethod(nil) + slashingInfoABI := precompile.NewSlashingInfoABI() - require.Len(t, slashingInfoMethod.Method.Inputs, 1) - require.Len(t, slashingInfoMethod.Method.Outputs, 2) + require.Len(t, slashingInfoABI.Method.Inputs, 1) + require.Len(t, slashingInfoABI.Method.Outputs, 2) } -func (suite *PrecompileTestSuite) TestSlashingInfo() { - slashingInfoMethod := precompile.NewSlashingInfoMethod(nil) +func (suite *StakingPrecompileTestSuite) TestSlashingInfo() { testCases := []struct { name string - malleate func(val sdk.ValAddress) (types.SlashingInfoArgs, error) + malleate func(val sdk.ValAddress) (contract.SlashingInfoArgs, error) result bool }{ { name: "ok", - malleate: func(val sdk.ValAddress) (types.SlashingInfoArgs, error) { - return types.SlashingInfoArgs{ + malleate: func(val sdk.ValAddress) (contract.SlashingInfoArgs, error) { + return contract.SlashingInfoArgs{ Validator: val.String(), }, nil }, @@ -39,29 +36,9 @@ func (suite *PrecompileTestSuite) TestSlashingInfo() { }, { name: "failed - invalid validator address", - malleate: func(val sdk.ValAddress) (types.SlashingInfoArgs, error) { + malleate: func(val sdk.ValAddress) (contract.SlashingInfoArgs, error) { valStr := val.String() + "1" - return types.SlashingInfoArgs{ - Validator: valStr, - }, fmt.Errorf("invalid validator address: %s", valStr) - }, - result: false, - }, - - { - name: "contract - ok", - malleate: func(val sdk.ValAddress) (types.SlashingInfoArgs, error) { - return types.SlashingInfoArgs{ - Validator: val.String(), - }, nil - }, - result: true, - }, - { - name: "contract - failed - invalid validator address", - malleate: func(val sdk.ValAddress) (types.SlashingInfoArgs, error) { - valStr := val.String() + "1" - return types.SlashingInfoArgs{ + return contract.SlashingInfoArgs{ Validator: valStr, }, fmt.Errorf("invalid validator address: %s", valStr) }, @@ -71,43 +48,25 @@ func (suite *PrecompileTestSuite) TestSlashingInfo() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val := suite.GetFirstValidator() - owner := suite.RandSigner() - spender := suite.RandSigner() + operator := suite.GetFirstValAddr() + spender := suite.NewSigner() allowanceAmt := helpers.NewRandAmount() - operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - suite.Require().NoError(err) - - // set allowance - suite.App.StakingKeeper.SetAllowance(suite.Ctx, operator, owner.AccAddress(), spender.AccAddress(), allowanceAmt.BigInt()) + suite.SetAllowance(operator, suite.signer.AccAddress(), spender.AccAddress(), allowanceAmt.BigInt()) - args, errResult := tc.malleate(operator) + args, expectErr := tc.malleate(operator) - packData, err := slashingInfoMethod.PackInput(args) - suite.Require().NoError(err) - stakingContract := suite.stakingAddr - - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - } - - res := suite.EthereumTx(owner, stakingContract, big.NewInt(0), packData) + jailed, missed := suite.WithError(expectErr).SlashingInfo(suite.Ctx, args) if tc.result { - suite.Require().False(res.Failed(), res.VmError) - jailed, missed, err := slashingInfoMethod.UnpackOutput(res.Ret) - suite.Require().NoError(err) validator, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, operator) suite.Require().NoError(err) - suite.Equal(validator.Jailed, jailed) + suite.Require().Equal(validator.Jailed, jailed) consAddr, err := validator.GetConsAddr() suite.Require().NoError(err) signingInfo, err := suite.App.SlashingKeeper.GetValidatorSigningInfo(suite.Ctx, consAddr) suite.Require().NoError(err) - suite.Equal(signingInfo.MissedBlocksCounter, missed.Int64()) - } else { - suite.Error(res, errResult) + suite.Require().Equal(signingInfo.MissedBlocksCounter, missed.Int64()) } }) } diff --git a/x/staking/precompile/transfer_shares.go b/x/staking/precompile/transfer_shares.go index 8c8efda8..d27561c6 100644 --- a/x/staking/precompile/transfer_shares.go +++ b/x/staking/precompile/transfer_shares.go @@ -13,7 +13,6 @@ import ( fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type TransferSharesMethod struct { @@ -89,7 +88,7 @@ func NewTransferSharesABI() TransferSharesABI { } } -func (m TransferSharesABI) PackInput(args fxstakingtypes.TransferSharesArgs) ([]byte, error) { +func (m TransferSharesABI) PackInput(args fxcontract.TransferSharesArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator, args.To, args.Shares) if err != nil { return nil, err @@ -97,8 +96,8 @@ func (m TransferSharesABI) PackInput(args fxstakingtypes.TransferSharesArgs) ([] return append(m.Method.ID, arguments...), nil } -func (m TransferSharesABI) UnpackInput(data []byte) (*fxstakingtypes.TransferSharesArgs, error) { - args := new(fxstakingtypes.TransferSharesArgs) +func (m TransferSharesABI) UnpackInput(data []byte) (*fxcontract.TransferSharesArgs, error) { + args := new(fxcontract.TransferSharesArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } @@ -182,7 +181,7 @@ func NewTransferFromSharesABI() TransferFromSharesABI { } } -func (m TransferFromSharesABI) PackInput(args fxstakingtypes.TransferFromSharesArgs) ([]byte, error) { +func (m TransferFromSharesABI) PackInput(args fxcontract.TransferFromSharesArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator, args.From, args.To, args.Shares) if err != nil { return nil, err @@ -190,8 +189,8 @@ func (m TransferFromSharesABI) PackInput(args fxstakingtypes.TransferFromSharesA return append(m.Method.ID, arguments...), nil } -func (m TransferFromSharesABI) UnpackInput(data []byte) (*fxstakingtypes.TransferFromSharesArgs, error) { - args := new(fxstakingtypes.TransferFromSharesArgs) +func (m TransferFromSharesABI) UnpackInput(data []byte) (*fxcontract.TransferFromSharesArgs, error) { + args := new(fxcontract.TransferFromSharesArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/transfer_shares_test.go b/x/staking/precompile/transfer_shares_test.go index 1c848b06..4513a3cb 100644 --- a/x/staking/precompile/transfer_shares_test.go +++ b/x/staking/precompile/transfer_shares_test.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "math/big" - "strings" "testing" sdkmath "cosmossdk.io/math" @@ -12,30 +11,31 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" fxtypes "github.com/functionx/fx-core/v8/types" "github.com/functionx/fx-core/v8/x/staking/precompile" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) func TestStakingTransferSharesABI(t *testing.T) { - transferSharesMethod := precompile.NewTransferSharesMethod(nil) + transferSharesABI := precompile.NewTransferSharesABI() - require.Len(t, transferSharesMethod.Method.Inputs, 3) - require.Len(t, transferSharesMethod.Method.Outputs, 2) + require.Len(t, transferSharesABI.Method.Inputs, 3) + require.Len(t, transferSharesABI.Method.Outputs, 2) - require.Len(t, transferSharesMethod.Event.Inputs, 5) + require.Len(t, transferSharesABI.Event.Inputs, 5) } -func (suite *PrecompileTestSuite) TestTransferShares() { +func (suite *StakingPrecompileTestSuite) TestTransferShares() { testCases := []struct { name string pretransfer func(val sdk.ValAddress, from, to common.Address, delAmount sdkmath.Int) - malleate func(val sdk.ValAddress, contract, to common.Address, shares *big.Int) ([]byte, *big.Int, []string) + malleate func(val sdk.ValAddress, to common.Address, shares *big.Int) (fxcontract.TransferSharesArgs, *big.Int, []string) suftransfer func(val sdk.ValAddress, from, to common.Address, delAmount sdkmath.Int) error func(errArgs []string) string result bool @@ -156,138 +156,21 @@ func (suite *PrecompileTestSuite) TestTransferShares() { suftransfer: suite.delegateToFromFunc, result: true, }, - - { - name: "contract - ok - from delegated", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferRand, - result: true, - }, - { - name: "contract - ok - from delegated - undelegate from and to", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferRand, - suftransfer: suite.undelegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from delegated - undelegate to and from", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferRand, - suftransfer: suite.undelegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from delegated - delegate from and to", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferRand, - suftransfer: suite.delegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from delegated - delegate to and from", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferRand, - suftransfer: suite.delegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from delegated - transfer all - undelegate to", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferAll, - suftransfer: suite.undelegateToFunc, - result: true, - }, - { - name: "contract - ok - from delegated - transfer all - delegate from and to", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferAll, - suftransfer: suite.delegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from delegated - transfer all - delegate to and from", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferAll, - suftransfer: suite.delegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferRand, - result: true, - }, - { - name: "contract - ok - from and to delegated - delegate from and to", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferRand, - suftransfer: suite.delegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - delegate to and from", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferRand, - suftransfer: suite.delegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - undelegate from and to", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferRand, - suftransfer: suite.undelegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - undelegate to and from", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferRand, - suftransfer: suite.undelegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - transfer all", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferAll, - result: true, - }, - { - name: "contract - ok - from and to delegated - transfer all - undelegate to", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferAll, - suftransfer: suite.undelegateToFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - transfer all - delegate from and to", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferAll, - suftransfer: suite.delegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - transfer all - delegate to and from", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferAll, - suftransfer: suite.delegateToFromFunc, - result: true, - }, } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { val := suite.GetFirstValidator() delAmt := sdkmath.NewInt(int64(tmrand.Intn(10000) + 1000)).Mul(sdkmath.NewInt(1e18)) - fromSigner := suite.RandSigner() - toSigner := suite.RandSigner() + suite.signer = suite.NewSigner() + toSigner := suite.NewSigner() - contract := suite.stakingAddr - delAddr := fromSigner.Address() - if strings.HasPrefix(tc.name, "contract") { - contract = suite.stakingTestAddr - delAddr = suite.stakingTestAddr - } + // contract := suite.stakingAddr + delAddr := suite.GetDelAddr() + // if strings.HasPrefix(tc.name, "contract") { + // contract = suite.stakingTestAddr + // delAddr = suite.stakingTestAddr + // } operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) suite.Require().NoError(err) @@ -324,8 +207,9 @@ func (suite *PrecompileTestSuite) TestTransferShares() { }) suite.Require().NoError(err) - pack, shares, _ := tc.malleate(operator, contract, toSigner.Address(), fromDelBefore.GetShares().TruncateInt().BigInt()) - res := suite.EthereumTx(fromSigner, contract, big.NewInt(0), pack) + args, shares, _ := tc.malleate(operator, toSigner.Address(), fromDelBefore.GetShares().TruncateInt().BigInt()) + suite.WithSigner(suite.signer) + res, _ := suite.TransferShares(suite.Ctx, args) if tc.result { suite.Require().False(res.Failed(), res.VmError) @@ -350,13 +234,14 @@ func (suite *PrecompileTestSuite) TestTransferShares() { } fromBalance = suite.App.BankKeeper.GetBalance(suite.Ctx, fromWithdrawAddr.Bytes(), fxtypes.DefaultDenom) - suite.Equal(fromBeforeRewards.Rewards.String(), sdk.NewDecCoinFromCoin(fromBalance).String()) + suite.Require().Equal(fromBeforeRewards.Rewards.String(), sdk.NewDecCoinFromCoin(fromBalance).String()) existLog := false for _, log := range res.Logs { - if log.Topics[0] == suite.transferSharesMethod.Event.ID.String() { + transferSharesABI := precompile.NewTransferSharesABI() + if log.Topics[0] == transferSharesABI.Event.ID.String() { suite.Require().Len(log.Topics, 3) - event, err := suite.transferSharesMethod.UnpackEvent(log.ToEthereum()) + event, err := transferSharesABI.UnpackEvent(log.ToEthereum()) suite.Require().NoError(err) suite.Require().Equal(event.From, delAddr) suite.Require().Equal(event.To, toSigner.Address()) @@ -373,39 +258,37 @@ func (suite *PrecompileTestSuite) TestTransferShares() { } } -func (suite *PrecompileTestSuite) packTransferRand(val sdk.ValAddress, contractAddr, to common.Address, shares *big.Int) ([]byte, *big.Int, []string) { +func (suite *StakingPrecompileTestSuite) packTransferRand(val sdk.ValAddress, to common.Address, shares *big.Int) (fxcontract.TransferSharesArgs, *big.Int, []string) { randShares := big.NewInt(0).Sub(shares, big.NewInt(0).Mul(big.NewInt(tmrand.Int63n(900)+100), big.NewInt(1e18))) - pack, err := suite.transferSharesMethod.PackInput(fxstakingtypes.TransferSharesArgs{ + args := fxcontract.TransferSharesArgs{ Validator: val.String(), To: to, Shares: randShares, - }) - suite.Require().NoError(err) - return pack, randShares, nil + } + return args, randShares, nil } -func (suite *PrecompileTestSuite) packTransferAll(val sdk.ValAddress, contractAddr, to common.Address, shares *big.Int) ([]byte, *big.Int, []string) { - pack, err := suite.transferSharesMethod.PackInput(fxstakingtypes.TransferSharesArgs{ +func (suite *StakingPrecompileTestSuite) packTransferAll(val sdk.ValAddress, to common.Address, shares *big.Int) (fxcontract.TransferSharesArgs, *big.Int, []string) { + args := fxcontract.TransferSharesArgs{ Validator: val.String(), To: to, Shares: shares, - }) - suite.Require().NoError(err) - return pack, shares, nil + } + return args, shares, nil } func TestStakingTransferFromSharesABI(t *testing.T) { - transferFromSharesMethod := precompile.NewTransferFromSharesMethod(nil) + transferFromSharesABI := precompile.NewTransferFromSharesABI() - require.Len(t, transferFromSharesMethod.Method.Inputs, 4) - require.Len(t, transferFromSharesMethod.Method.Outputs, 2) + require.Len(t, transferFromSharesABI.Method.Inputs, 4) + require.Len(t, transferFromSharesABI.Method.Outputs, 2) } -func (suite *PrecompileTestSuite) TestTransferFromShares() { +func (suite *StakingPrecompileTestSuite) TestTransferFromShares() { testCases := []struct { name string pretransfer func(val sdk.ValAddress, from, to common.Address, delAmount sdkmath.Int) - malleate func(val sdk.ValAddress, spedner, from, to common.Address, shares *big.Int) ([]byte, *big.Int, []string) + malleate func(val sdk.ValAddress, spender, from, to common.Address, shares *big.Int) (fxcontract.TransferFromSharesArgs, *big.Int, []string) suftransfer func(val sdk.ValAddress, from, to common.Address, delAmount sdkmath.Int) error func(errArgs []string) string result bool @@ -526,142 +409,20 @@ func (suite *PrecompileTestSuite) TestTransferFromShares() { suftransfer: suite.delegateToFromFunc, result: true, }, - - { - name: "contract - ok - from delegated", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferFromRand, - result: true, - }, - { - name: "contract - ok - from delegated - undelegate from and to", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferFromRand, - suftransfer: suite.undelegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from delegated - undelegate to and from", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferFromRand, - suftransfer: suite.undelegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from delegated - delegate from and to", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferFromRand, - suftransfer: suite.delegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from delegated - delegate to and from", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferFromRand, - suftransfer: suite.delegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from delegated - transfer all - undelegate to", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferFromAll, - suftransfer: suite.undelegateToFunc, - result: true, - }, - { - name: "contract - ok - from delegated - transfer all - delegate from and to", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferFromAll, - suftransfer: suite.delegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from delegated - transfer all - delegate to and from", - pretransfer: suite.delegateFromFunc, - malleate: suite.packTransferFromAll, - suftransfer: suite.delegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromRand, - result: true, - }, - { - name: "contract - ok - from and to delegated - delegate from and to", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromRand, - suftransfer: suite.delegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - delegate to and from", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromRand, - suftransfer: suite.delegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - undelegate from and to", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromRand, - suftransfer: suite.undelegateFromToFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - undelegate to and from", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromRand, - suftransfer: suite.undelegateToFromFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - transfer all", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromAll, - result: true, - }, - { - name: "contract - ok - from and to delegated - transfer all - undelegate to", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromAll, - suftransfer: suite.undelegateToFunc, - result: true, - }, - { - name: "contract - ok - from and to delegated - transfer all - delegate from and to", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromAll, - suftransfer: suite.delegateFromToFunc, - result: true, - }, - { - name: "ok - from and to delegated - transfer all - delegate to and from", - pretransfer: suite.delegateFromToFunc, - malleate: suite.packTransferFromAll, - suftransfer: suite.delegateToFromFunc, - result: true, - }, } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { val := suite.GetFirstValidator() delAmt := sdkmath.NewInt(int64(tmrand.Intn(10000) + 1000)).Mul(sdkmath.NewInt(1e18)) - fromSigner := suite.RandSigner() - toSigner := suite.RandSigner() - sender := suite.RandSigner() + fromSigner := suite.NewSigner() + toSigner := suite.NewSigner() + suite.signer = suite.NewSigner() // from delegate, approve sender, sender send tx, transferFrom to toSigner // from delegate, approve contract, sender call contract, transferFrom to toSigner - contract := suite.stakingAddr delAddr := fromSigner.Address() - spender := sender.Address() - if strings.HasPrefix(tc.name, "contract") { - contract = suite.stakingTestAddr - spender = suite.stakingTestAddr - } + spender := suite.GetDelAddr() operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) suite.Require().NoError(err) @@ -709,9 +470,10 @@ func (suite *PrecompileTestSuite) TestTransferFromShares() { } // NOTE: if contract test, spender is staking test contract - pack, shares, _ := tc.malleate(operator, spender, delAddr, toSigner.Address(), fromDelBefore.GetShares().TruncateInt().BigInt()) + args, shares, _ := tc.malleate(operator, spender, delAddr, toSigner.Address(), fromDelBefore.GetShares().TruncateInt().BigInt()) - res := suite.EthereumTx(sender, contract, big.NewInt(0), pack) + suite.WithSigner(suite.signer) + res, _ := suite.TransferFromShares(suite.Ctx, args) if tc.result { suite.Require().False(res.Failed(), res.VmError) @@ -745,9 +507,10 @@ func (suite *PrecompileTestSuite) TestTransferFromShares() { existLog := false for _, log := range res.Logs { - if log.Topics[0] == suite.transferFromSharesMethod.Event.ID.String() { + transferFromSharesABI := precompile.NewTransferFromSharesABI() + if log.Topics[0] == transferFromSharesABI.Event.ID.String() { suite.Require().Len(log.Topics, 3) - event, err := suite.transferFromSharesMethod.UnpackEvent(log.ToEthereum()) + event, err := transferFromSharesABI.UnpackEvent(log.ToEthereum()) suite.Require().NoError(err) suite.Require().Equal(event.From, delAddr) suite.Require().Equal(event.To, toSigner.Address()) @@ -764,38 +527,36 @@ func (suite *PrecompileTestSuite) TestTransferFromShares() { } } -func (suite *PrecompileTestSuite) packTransferFromRand(val sdk.ValAddress, spender, from, to common.Address, shares *big.Int) ([]byte, *big.Int, []string) { +func (suite *StakingPrecompileTestSuite) packTransferFromRand(val sdk.ValAddress, spender, from, to common.Address, shares *big.Int) (fxcontract.TransferFromSharesArgs, *big.Int, []string) { randShares := big.NewInt(0).Sub(shares, big.NewInt(0).Mul(big.NewInt(tmrand.Int63n(900)+100), big.NewInt(1e18))) suite.approveFunc(val, from, spender, randShares) - pack, err := suite.transferFromSharesMethod.PackInput(fxstakingtypes.TransferFromSharesArgs{ + return fxcontract.TransferFromSharesArgs{ Validator: val.String(), From: from, To: to, Shares: randShares, - }) - suite.Require().NoError(err) - return pack, randShares, nil + }, randShares, nil } -func (suite *PrecompileTestSuite) packTransferFromAll(val sdk.ValAddress, spender, from, to common.Address, shares *big.Int) ([]byte, *big.Int, []string) { +func (suite *StakingPrecompileTestSuite) packTransferFromAll(val sdk.ValAddress, spender, from, to common.Address, shares *big.Int) (fxcontract.TransferFromSharesArgs, *big.Int, []string) { suite.approveFunc(val, from, spender, shares) - pack, err := suite.transferFromSharesMethod.PackInput(fxstakingtypes.TransferFromSharesArgs{ + return fxcontract.TransferFromSharesArgs{ Validator: val.String(), From: from, To: to, Shares: shares, - }) - - suite.Require().NoError(err) - return pack, shares, nil + }, shares, nil } -func (suite *PrecompileTestSuite) TestTransferSharesCompare() { +func (suite *StakingPrecompileTestSuite) TestTransferSharesCompare() { + if !suite.IsCallPrecompile() { + suite.T().Skip() + } val := suite.GetFirstValidator() delAmount := sdkmath.NewInt(int64(tmrand.Int() + 100)).Mul(sdkmath.NewInt(1e18)) - signer1 := suite.RandSigner() - signer2 := suite.RandSigner() - signer3 := suite.RandSigner() + signer1 := suite.NewSigner() + signer2 := suite.NewSigner() + signer3 := suite.NewSigner() suite.MintToken(signer1.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) @@ -983,12 +744,15 @@ func (suite *PrecompileTestSuite) TestTransferSharesCompare() { suite.Require().EqualValues(0, startingInfo.PreviousPeriod) // transfer all shares, starting info removed } -func (suite *PrecompileTestSuite) TestPrecompileStakingSteps() { +func (suite *StakingPrecompileTestSuite) TestPrecompileStakingSteps() { + if !suite.IsCallPrecompile() { + suite.T().Skip() + } val := suite.GetFirstValidator() delAmount := sdkmath.NewInt(int64(tmrand.Int() + 100)).Mul(sdkmath.NewInt(1e18)) - signer1 := suite.RandSigner() - signer2 := suite.RandSigner() - signer3 := suite.RandSigner() + signer1 := suite.NewSigner() + signer2 := suite.NewSigner() + signer3 := suite.NewSigner() operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) suite.Require().NoError(err) @@ -1068,13 +832,13 @@ func (suite *PrecompileTestSuite) TestPrecompileStakingSteps() { suite.Commit() } -func (suite *PrecompileTestSuite) TestTransferSharesRedelegate() { +func (suite *StakingPrecompileTestSuite) TestTransferSharesRedelegate() { vals := suite.GetValidators() val := vals[0] valTmp := vals[1] delAmount := sdkmath.NewInt(int64(tmrand.Int() + 100)).Mul(sdkmath.NewInt(1e18)) - signer1 := suite.RandSigner() - signer2 := suite.RandSigner() + signer1 := suite.NewSigner() + signer2 := suite.NewSigner() operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) suite.Require().NoError(err) @@ -1090,7 +854,8 @@ func (suite *PrecompileTestSuite) TestTransferSharesRedelegate() { suite.Require().NoError(err) // redelegate - suite.Redelegate(operatorTmp, operator, signer1.AccAddress(), delegationTmp.Shares) + _, err = suite.App.StakingKeeper.BeginRedelegation(suite.Ctx, signer1.AccAddress(), operatorTmp, operator, delegationTmp.Shares) + suite.Require().NoError(err) suite.Commit() delegation, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, signer1.AccAddress(), operator) @@ -1098,13 +863,159 @@ func (suite *PrecompileTestSuite) TestTransferSharesRedelegate() { suite.Require().Equal(delegationTmp.Shares, delegation.Shares) // transfer shares - transferSharesMethod := precompile.NewTransferSharesMethod(nil) - pack, err := transferSharesMethod.PackInput(fxstakingtypes.TransferSharesArgs{ - Validator: val.GetOperator(), - To: signer2.Address(), - Shares: delegation.Shares.TruncateInt().BigInt(), + suite.WithSigner(signer1).WithContract(suite.stakingAddr) + _, _ = suite.WithError(errors.New("from has receiving redelegation")). + TransferShares(suite.Ctx, fxcontract.TransferSharesArgs{ + Validator: val.GetOperator(), + To: signer2.Address(), + Shares: delegation.Shares.TruncateInt().BigInt(), + }) +} + +func (suite *StakingPrecompileTestSuite) delegateFromFunc(val sdk.ValAddress, from, _ common.Address, delAmount sdkmath.Int) { + suite.MintToken(from.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) + _, err := stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ + DelegatorAddress: sdk.AccAddress(from.Bytes()).String(), + ValidatorAddress: val.String(), + Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), + }) + suite.Require().NoError(err) +} + +func (suite *StakingPrecompileTestSuite) undelegateToFunc(val sdk.ValAddress, _, to common.Address, _ sdkmath.Int) { + toDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, to.Bytes(), val) + suite.Require().NoError(err) + _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, to.Bytes(), val, toDel.Shares) + suite.Require().NoError(err) +} + +func (suite *StakingPrecompileTestSuite) delegateFromToFunc(val sdk.ValAddress, from, to common.Address, delAmount sdkmath.Int) { + suite.MintToken(from.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) + _, err := stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ + DelegatorAddress: sdk.AccAddress(from.Bytes()).String(), + ValidatorAddress: val.String(), + Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), + }) + suite.Require().NoError(err) + + suite.MintToken(to.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) + _, err = stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ + DelegatorAddress: sdk.AccAddress(to.Bytes()).String(), + ValidatorAddress: val.String(), + Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), }) suite.Require().NoError(err) - res := suite.EthereumTx(signer1, suite.stakingAddr, big.NewInt(0), pack) - suite.Error(res, errors.New("from has receiving redelegation")) +} + +func (suite *StakingPrecompileTestSuite) delegateToFromFunc(val sdk.ValAddress, from, to common.Address, delAmount sdkmath.Int) { + suite.MintToken(to.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) + _, err := stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ + DelegatorAddress: sdk.AccAddress(to.Bytes()).String(), + ValidatorAddress: val.String(), + Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), + }) + suite.Require().NoError(err) + + suite.MintToken(from.Bytes(), sdk.NewCoin(fxtypes.DefaultDenom, delAmount)) + _, err = stakingkeeper.NewMsgServerImpl(suite.App.StakingKeeper.Keeper).Delegate(suite.Ctx, &stakingtypes.MsgDelegate{ + DelegatorAddress: sdk.AccAddress(from.Bytes()).String(), + ValidatorAddress: val.String(), + Amount: sdk.NewCoin(fxtypes.DefaultDenom, delAmount), + }) + suite.Require().NoError(err) +} + +func (suite *StakingPrecompileTestSuite) undelegateFromToFunc(val sdk.ValAddress, from, to common.Address, _ sdkmath.Int) { + fromDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, from.Bytes(), val) + suite.Require().NoError(err) + _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, from.Bytes(), val, fromDel.Shares) + suite.Require().NoError(err) + + toDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, to.Bytes(), val) + suite.Require().NoError(err) + _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, to.Bytes(), val, toDel.Shares) + suite.Require().NoError(err) +} + +func (suite *StakingPrecompileTestSuite) undelegateToFromFunc(val sdk.ValAddress, from, to common.Address, _ sdkmath.Int) { + toDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, to.Bytes(), val) + suite.Require().NoError(err) + _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, to.Bytes(), val, toDel.Shares) + suite.Require().NoError(err) + + fromDel, err := suite.App.StakingKeeper.GetDelegation(suite.Ctx, from.Bytes(), val) + suite.Require().NoError(err) + _, _, err = suite.App.StakingKeeper.Undelegate(suite.Ctx, from.Bytes(), val, fromDel.Shares) + suite.Require().NoError(err) +} + +func (suite *StakingPrecompileTestSuite) approveFunc(val sdk.ValAddress, owner, spender common.Address, allowance *big.Int) { + suite.App.StakingKeeper.SetAllowance(suite.Ctx, val, owner.Bytes(), spender.Bytes(), allowance) +} + +func (suite *StakingPrecompileTestSuite) PrecompileStakingDelegation(val sdk.ValAddress, del common.Address) (*big.Int, *big.Int) { + return suite.Delegation(suite.Ctx, fxcontract.DelegationArgs{ + Validator: val.String(), + Delegator: del, + }) +} + +func (suite *StakingPrecompileTestSuite) PrecompileStakingTransferShares(signer *helpers.Signer, val sdk.ValAddress, receipt common.Address, shares *big.Int) (*big.Int, *big.Int) { + balanceBefore := suite.GetStakingBalance(signer.AccAddress()) + suite.WithSigner(signer) + res, _ := suite.TransferShares(suite.Ctx, fxcontract.TransferSharesArgs{ + Validator: val.String(), + To: receipt, + Shares: shares, + }) + suite.Require().False(res.Failed(), res.VmError) + + signerShares, _ := suite.PrecompileStakingDelegation(val, signer.Address()) + + balanceAfter := suite.GetStakingBalance(signer.AccAddress()) + rewards := balanceAfter.Sub(balanceBefore) + return signerShares, rewards.BigInt() +} + +func (suite *StakingPrecompileTestSuite) PrecompileStakingUndelegateV2(signer *helpers.Signer, val sdk.ValAddress, shares *big.Int) *big.Int { + balanceBefore := suite.GetStakingBalance(signer.AccAddress()) + suite.WithSigner(signer) + res := suite.UndelegateV2(suite.Ctx, fxcontract.UndelegateV2Args{ + Validator: val.String(), + Amount: shares, + }) + suite.Require().False(res.Failed(), res.VmError) + + balanceAfter := suite.GetStakingBalance(signer.AccAddress()) + rewards := balanceAfter.Sub(balanceBefore) + return rewards.BigInt() +} + +func (suite *StakingPrecompileTestSuite) PrecompileStakingApproveShares(signer *helpers.Signer, val sdk.ValAddress, spender common.Address, shares *big.Int) { + suite.WithSigner(signer) + suite.ApproveShares(suite.Ctx, fxcontract.ApproveSharesArgs{ + Validator: val.String(), + Spender: spender, + Shares: shares, + }) +} + +func (suite *StakingPrecompileTestSuite) PrecompileStakingTransferFromShares(signer *helpers.Signer, val sdk.ValAddress, from, receipt common.Address, shares *big.Int) { + suite.WithSigner(signer) + suite.TransferFromShares(suite.Ctx, fxcontract.TransferFromSharesArgs{ + Validator: val.String(), + From: from, + To: receipt, + Shares: shares, + }) +} + +func (suite *StakingPrecompileTestSuite) Delegate(val sdk.ValAddress, amount sdkmath.Int, dels ...sdk.AccAddress) { + for _, del := range dels { + suite.MintToken(del, sdk.NewCoin(fxtypes.DefaultDenom, amount)) + validator, err := suite.App.StakingKeeper.GetValidator(suite.Ctx, val) + suite.Require().NoError(err) + _, err = suite.App.StakingKeeper.Delegate(suite.Ctx, del, amount, stakingtypes.Unbonded, validator, true) + suite.Require().NoError(err) + } } diff --git a/x/staking/precompile/undelegate.go b/x/staking/precompile/undelegate.go index f219321f..0dd4013e 100644 --- a/x/staking/precompile/undelegate.go +++ b/x/staking/precompile/undelegate.go @@ -13,7 +13,6 @@ import ( fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type UndelegateV2Method struct { @@ -91,7 +90,7 @@ func NewUndelegateV2ABI() UndelegateABI { } } -func (m UndelegateABI) PackInput(args fxstakingtypes.UndelegateV2Args) ([]byte, error) { +func (m UndelegateABI) PackInput(args fxcontract.UndelegateV2Args) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator, args.Amount) if err != nil { return nil, err @@ -99,8 +98,8 @@ func (m UndelegateABI) PackInput(args fxstakingtypes.UndelegateV2Args) ([]byte, return append(m.Method.ID, arguments...), nil } -func (m UndelegateABI) UnpackInput(data []byte) (*fxstakingtypes.UndelegateV2Args, error) { - args := new(fxstakingtypes.UndelegateV2Args) +func (m UndelegateABI) UnpackInput(data []byte) (*fxcontract.UndelegateV2Args, error) { + args := new(fxcontract.UndelegateV2Args) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/undelegate_test.go b/x/staking/precompile/undelegate_test.go index 7864812d..c4853243 100644 --- a/x/staking/precompile/undelegate_test.go +++ b/x/staking/precompile/undelegate_test.go @@ -4,25 +4,31 @@ import ( "fmt" "math/big" "strings" + "time" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" - "github.com/functionx/fx-core/v8/x/staking/types" + fxtypes "github.com/functionx/fx-core/v8/types" + "github.com/functionx/fx-core/v8/x/staking/precompile" ) -func (suite *PrecompileTestSuite) TestUndelegate() { +func (suite *StakingPrecompileTestSuite) TestUndelegate() { testCases := []struct { name string - malleate func(val sdk.ValAddress, shares sdkmath.LegacyDec, delAmt sdkmath.Int) (interface{}, error) + malleate func(val sdk.ValAddress, shares sdkmath.LegacyDec, delAmt sdkmath.Int) (contract.UndelegateV2Args, error) error func(errArgs []string) string result bool }{ { name: "ok v2", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec, delAmt sdkmath.Int) (interface{}, error) { - return types.UndelegateV2Args{ + malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec, delAmt sdkmath.Int) (contract.UndelegateV2Args, error) { + return contract.UndelegateV2Args{ Validator: val.String(), Amount: delAmt.BigInt(), }, nil @@ -31,31 +37,9 @@ func (suite *PrecompileTestSuite) TestUndelegate() { }, { name: "failed - v2 invalid validator address", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec, delAmt sdkmath.Int) (interface{}, error) { + malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec, delAmt sdkmath.Int) (contract.UndelegateV2Args, error) { newVal := val.String() + "1" - return types.UndelegateV2Args{ - Validator: newVal, - Amount: delAmt.BigInt(), - }, fmt.Errorf("invalid validator address: %s", newVal) - }, - result: false, - }, - - { - name: "contract - ok v2", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec, delAmt sdkmath.Int) (interface{}, error) { - return types.UndelegateV2Args{ - Validator: val.String(), - Amount: delAmt.BigInt(), - }, nil - }, - result: true, - }, - { - name: "contract - failed - v2 invalid validator address", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec, delAmt sdkmath.Int) (interface{}, error) { - newVal := val.String() + "1" - return types.UndelegateV2Args{ + return contract.UndelegateV2Args{ Validator: newVal, Amount: delAmt.BigInt(), }, fmt.Errorf("invalid validator address: %s", newVal) @@ -66,28 +50,14 @@ func (suite *PrecompileTestSuite) TestUndelegate() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val := suite.GetFirstValidator() + operator := suite.GetFirstValAddr() delAmt := helpers.NewRandAmount() + delAddr := suite.GetDelAddr() - stakingContract := suite.stakingAddr - delAddr := suite.signer.Address() - value := big.NewInt(0) - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - delAddr = suite.stakingTestAddr - value = delAmt.BigInt() - } - - operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - suite.Require().NoError(err) - - pack, err := suite.delegateV2Method.PackInput(types.DelegateV2Args{ - Validator: val.GetOperator(), + res := suite.DelegateV2(suite.Ctx, contract.DelegateV2Args{ + Validator: operator.String(), Amount: delAmt.BigInt(), }) - suite.Require().NoError(err) - - res := suite.EthereumTx(suite.signer, stakingContract, value, pack) suite.Require().False(res.Failed(), res.VmError) suite.Commit() @@ -98,13 +68,9 @@ func (suite *PrecompileTestSuite) TestUndelegate() { suite.Require().NoError(err) suite.Require().Equal(0, len(undelegations)) - var packData []byte - args, errResult := tc.malleate(operator, delegation.Shares, delAmt) - packData, err = suite.undelegateV2Method.PackInput(args.(types.UndelegateV2Args)) - suite.Require().NoError(err) - - res = suite.EthereumTx(suite.signer, stakingContract, big.NewInt(0), packData) + args, expectErr := tc.malleate(operator, delegation.Shares, delAmt) + res = suite.WithError(expectErr).UndelegateV2(suite.Ctx, args) if tc.result { suite.Require().False(res.Failed(), res.VmError) @@ -113,17 +79,56 @@ func (suite *PrecompileTestSuite) TestUndelegate() { suite.Require().Equal(1, len(undelegations)) suite.Require().Equal(1, len(undelegations[0].Entries)) suite.Require().Equal(sdk.AccAddress(delAddr.Bytes()).String(), undelegations[0].DelegatorAddress) - suite.Require().Equal(val.GetOperator(), undelegations[0].ValidatorAddress) + suite.Require().Equal(operator.String(), undelegations[0].ValidatorAddress) suite.Require().Equal(delAmt, undelegations[0].Entries[0].Balance) - suite.CheckUndelegateLogs(res.Logs, delAddr, val.GetOperator(), delegation.Shares.TruncateInt().BigInt(), + suite.CheckUndelegateLogs(res.Logs, delAddr, operator.String(), delegation.Shares.TruncateInt().BigInt(), undelegations[0].Entries[0].Balance.BigInt(), undelegations[0].Entries[0].CompletionTime) - suite.CheckUndeledateEvents(suite.Ctx, val.GetOperator(), undelegations[0].Entries[0].Balance.BigInt(), + suite.CheckUndelegateEvents(suite.Ctx, operator.String(), undelegations[0].Entries[0].Balance.BigInt(), undelegations[0].Entries[0].CompletionTime) - } else { - suite.Error(res, errResult) } }) } } + +func (suite *StakingPrecompileTestSuite) CheckUndelegateLogs(logs []*evmtypes.Log, delAddr common.Address, valAddr string, shares, amount *big.Int, completionTime time.Time) { + undelegateV2ABI := precompile.NewUndelegateV2ABI() + existLog := false + for _, log := range logs { + if log.Topics[0] == undelegateV2ABI.Event.ID.String() { + suite.Require().Equal(log.Address, contract.StakingAddress) + event, err := undelegateV2ABI.UnpackEvent(log.ToEthereum()) + suite.Require().NoError(err) + suite.Require().Equal(event.Sender, delAddr) + suite.Require().Equal(event.Validator, valAddr) + suite.Require().Equal(event.Amount.String(), amount.String()) + suite.Require().Equal(event.CompletionTime.Int64(), completionTime.Unix()) + existLog = true + } + } + suite.Require().True(existLog) +} + +func (suite *StakingPrecompileTestSuite) CheckUndelegateEvents(ctx sdk.Context, valAddr string, amount *big.Int, completionTime time.Time) { + existEvent := false + for _, event := range ctx.EventManager().Events() { + if event.Type == stakingtypes.EventTypeUnbond { + for _, attr := range event.Attributes { + if attr.Key == stakingtypes.AttributeKeyValidator { + suite.Require().Equal(attr.Value, valAddr) + existEvent = true + } + if attr.Key == sdk.AttributeKeyAmount { + suite.Require().Equal(strings.TrimSuffix(attr.Value, fxtypes.DefaultDenom), amount.String()) + existEvent = true + } + if attr.Key == stakingtypes.AttributeKeyCompletionTime { + suite.Require().Equal(attr.Value, completionTime.Format(time.RFC3339)) + existEvent = true + } + } + } + } + suite.Require().True(existEvent) +} diff --git a/x/staking/precompile/validator_list.go b/x/staking/precompile/validator_list.go index 1d28be82..041221b5 100644 --- a/x/staking/precompile/validator_list.go +++ b/x/staking/precompile/validator_list.go @@ -4,8 +4,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/core/vm" + fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type ValidatorListMethod struct { @@ -48,9 +48,9 @@ func (m *ValidatorListMethod) Run(evm *vm.EVM, contract *vm.Contract) ([]byte, e valAddrs := make([]string, 0, len(bondedVals)) switch args.GetSortBy() { - case fxstakingtypes.ValidatorSortByPower: + case fxcontract.ValidatorSortByPower: valAddrs = validatorListPower(bondedVals) - case fxstakingtypes.ValidatorSortByMissed: + case fxcontract.ValidatorSortByMissed: valAddrs, err = m.ValidatorListMissedBlock(cacheCtx, bondedVals) if err != nil { return nil, err @@ -70,7 +70,7 @@ func NewValidatorListABI() ValidatorListABI { } } -func (m ValidatorListABI) PackInput(args fxstakingtypes.ValidatorListArgs) ([]byte, error) { +func (m ValidatorListABI) PackInput(args fxcontract.ValidatorListArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.SortBy) if err != nil { return nil, err @@ -78,8 +78,8 @@ func (m ValidatorListABI) PackInput(args fxstakingtypes.ValidatorListArgs) ([]by return append(m.Method.ID, arguments...), nil } -func (m ValidatorListABI) UnpackInput(data []byte) (*fxstakingtypes.ValidatorListArgs, error) { - args := new(fxstakingtypes.ValidatorListArgs) +func (m ValidatorListABI) UnpackInput(data []byte) (*fxcontract.ValidatorListArgs, error) { + args := new(fxcontract.ValidatorListArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/validator_list_test.go b/x/staking/precompile/validator_list_test.go index d8bac289..e9e58df8 100644 --- a/x/staking/precompile/validator_list_test.go +++ b/x/staking/precompile/validator_list_test.go @@ -2,124 +2,87 @@ package precompile_test import ( "fmt" - "math/big" "sort" - "strings" "testing" "github.com/stretchr/testify/require" + "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil/helpers" - "github.com/functionx/fx-core/v8/x/staking/precompile" - "github.com/functionx/fx-core/v8/x/staking/types" + precompile2 "github.com/functionx/fx-core/v8/x/staking/precompile" ) func TestValidatorListABI(t *testing.T) { - validatorListMethod := precompile.NewValidatorListMethod(nil) + validatorListABI := precompile2.NewValidatorListABI() - require.Len(t, validatorListMethod.Method.Inputs, 1) - require.Len(t, validatorListMethod.Method.Outputs, 1) + require.Len(t, validatorListABI.Method.Inputs, 1) + require.Len(t, validatorListABI.Method.Outputs, 1) } -func (suite *PrecompileTestSuite) TestValidatorList() { +func (suite *StakingPrecompileTestSuite) TestValidatorList() { testCases := []struct { name string - malleate func() (types.ValidatorListArgs, error) + malleate func() (contract.ValidatorListArgs, error) result bool }{ { name: "ok", - malleate: func() (types.ValidatorListArgs, error) { - return types.ValidatorListArgs{ - SortBy: uint8(types.ValidatorSortByPower), + malleate: func() (contract.ValidatorListArgs, error) { + return contract.ValidatorListArgs{ + SortBy: uint8(contract.ValidatorSortByPower), }, nil }, result: true, }, { name: "ok - missed", - malleate: func() (types.ValidatorListArgs, error) { - return types.ValidatorListArgs{ - SortBy: uint8(types.ValidatorSortByMissed), + malleate: func() (contract.ValidatorListArgs, error) { + return contract.ValidatorListArgs{ + SortBy: uint8(contract.ValidatorSortByMissed), }, nil }, result: true, }, { name: "failed - invalid order value", - malleate: func() (types.ValidatorListArgs, error) { - return types.ValidatorListArgs{ + malleate: func() (contract.ValidatorListArgs, error) { + return contract.ValidatorListArgs{ SortBy: 100, }, fmt.Errorf("over the sort by limit") }, result: false, }, - { - name: "contract - ok", - malleate: func() (types.ValidatorListArgs, error) { - return types.ValidatorListArgs{ - SortBy: uint8(types.ValidatorSortByPower), - }, nil - }, - result: true, - }, - { - name: "contract - ok - missed", - malleate: func() (types.ValidatorListArgs, error) { - return types.ValidatorListArgs{ - SortBy: uint8(types.ValidatorSortByMissed), - }, nil - }, - result: true, - }, } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val := suite.GetFirstValidator() - owner := suite.RandSigner() - spender := suite.RandSigner() + operator := suite.GetFirstValAddr() + spender := suite.NewSigner() allowanceAmt := helpers.NewRandAmount() - operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - suite.Require().NoError(err) - - // set allowance - suite.App.StakingKeeper.SetAllowance(suite.Ctx, operator, owner.AccAddress(), spender.AccAddress(), allowanceAmt.BigInt()) - - args, errResult := tc.malleate() + suite.SetAllowance(operator, suite.signer.AccAddress(), spender.AccAddress(), allowanceAmt.BigInt()) - packData, err := suite.validatorListMethod.PackInput(args) - suite.Require().NoError(err) - stakingContract := suite.stakingAddr - - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - } - - res := suite.EthereumTx(owner, stakingContract, big.NewInt(0), packData) + args, expectErr := tc.malleate() + valAddrs := suite.WithError(expectErr).ValidatorList(suite.Ctx, args) if tc.result { - suite.Require().False(res.Failed(), res.VmError) - valAddrs, err := suite.validatorListMethod.UnpackOutput(res.Ret) - suite.Require().NoError(err) valsByPower, err := suite.App.StakingKeeper.GetBondedValidatorsByPower(suite.Ctx) suite.Require().NoError(err) - suite.Equal(len(valAddrs), len(valsByPower)) + suite.Require().Equal(len(valAddrs), len(valsByPower)) - if args.GetSortBy() == types.ValidatorSortByPower { + if args.GetSortBy() == contract.ValidatorSortByPower { for index, addr := range valAddrs { - suite.Equal(addr, valsByPower[index].OperatorAddress) + suite.Require().Equal(addr, valsByPower[index].OperatorAddress) } } - if args.GetSortBy() == types.ValidatorSortByMissed { - valList := make([]precompile.Validator, 0, len(valsByPower)) + if args.GetSortBy() == contract.ValidatorSortByMissed { + valList := make([]precompile2.Validator, 0, len(valsByPower)) for _, validator := range valsByPower { consAddr, err := validator.GetConsAddr() suite.Require().NoError(err) info, err := suite.App.SlashingKeeper.GetValidatorSigningInfo(suite.Ctx, consAddr) suite.Require().NoError(err) - valList = append(valList, precompile.Validator{ + valList = append(valList, precompile2.Validator{ ValAddr: validator.OperatorAddress, MissedBlocks: info.MissedBlocksCounter, }) @@ -128,11 +91,9 @@ func (suite *PrecompileTestSuite) TestValidatorList() { return valList[i].MissedBlocks > valList[j].MissedBlocks }) for index, addr := range valAddrs { - suite.Equal(addr, valList[index].ValAddr) + suite.Require().Equal(addr, valList[index].ValAddr) } } - } else { - suite.Error(res, errResult) } }) } diff --git a/x/staking/precompile/withdraw.go b/x/staking/precompile/withdraw.go index a6e0e2aa..8678b712 100644 --- a/x/staking/precompile/withdraw.go +++ b/x/staking/precompile/withdraw.go @@ -13,7 +13,6 @@ import ( fxcontract "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/x/evm/types" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" ) type WithdrawMethod struct { @@ -94,7 +93,7 @@ func (m WithdrawABI) NewWithdrawEvent(sender common.Address, validator string, r return data, topic, nil } -func (m WithdrawABI) PackInput(args fxstakingtypes.WithdrawArgs) ([]byte, error) { +func (m WithdrawABI) PackInput(args fxcontract.WithdrawArgs) ([]byte, error) { arguments, err := m.Method.Inputs.Pack(args.Validator) if err != nil { return nil, err @@ -102,8 +101,8 @@ func (m WithdrawABI) PackInput(args fxstakingtypes.WithdrawArgs) ([]byte, error) return append(m.Method.ID, arguments...), nil } -func (m WithdrawABI) UnpackInput(data []byte) (*fxstakingtypes.WithdrawArgs, error) { - args := new(fxstakingtypes.WithdrawArgs) +func (m WithdrawABI) UnpackInput(data []byte) (*fxcontract.WithdrawArgs, error) { + args := new(fxcontract.WithdrawArgs) err := types.ParseMethodArgs(m.Method, args, data[4:]) return args, err } diff --git a/x/staking/precompile/withdraw_test.go b/x/staking/precompile/withdraw_test.go index e758e24e..c02f3edd 100644 --- a/x/staking/precompile/withdraw_test.go +++ b/x/staking/precompile/withdraw_test.go @@ -2,8 +2,6 @@ package precompile_test import ( "fmt" - "math/big" - "strings" "testing" sdkmath "cosmossdk.io/math" @@ -12,32 +10,31 @@ import ( distritypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/stretchr/testify/require" - "github.com/functionx/fx-core/v8/testutil/helpers" + "github.com/functionx/fx-core/v8/contract" fxtypes "github.com/functionx/fx-core/v8/types" "github.com/functionx/fx-core/v8/x/staking/precompile" - "github.com/functionx/fx-core/v8/x/staking/types" ) func TestStakingWithdrawABI(t *testing.T) { - withdrawMethod := precompile.NewWithdrawMethod(nil) + withdrawABI := precompile.NewWithdrawABI() - require.Len(t, withdrawMethod.Method.Inputs, 1) - require.Len(t, withdrawMethod.Method.Outputs, 1) + require.Len(t, withdrawABI.Method.Inputs, 1) + require.Len(t, withdrawABI.Method.Outputs, 1) - require.Len(t, withdrawMethod.Event.Inputs, 3) + require.Len(t, withdrawABI.Event.Inputs, 3) } -func (suite *PrecompileTestSuite) TestWithdraw() { +func (suite *StakingPrecompileTestSuite) TestWithdraw() { testCases := []struct { name string - malleate func(val sdk.ValAddress, shares sdkmath.LegacyDec) (types.WithdrawArgs, error) + malleate func(val sdk.ValAddress, shares sdkmath.LegacyDec) (contract.WithdrawArgs, error) error func(errArgs []string) string result bool }{ { name: "ok", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (types.WithdrawArgs, error) { - return types.WithdrawArgs{ + malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (contract.WithdrawArgs, error) { + return contract.WithdrawArgs{ Validator: val.String(), }, nil }, @@ -45,9 +42,9 @@ func (suite *PrecompileTestSuite) TestWithdraw() { }, { name: "failed invalid validator address", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (types.WithdrawArgs, error) { + malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (contract.WithdrawArgs, error) { newVal := val.String() + "1" - return types.WithdrawArgs{ + return contract.WithdrawArgs{ Validator: newVal, }, fmt.Errorf("invalid validator address: %s", newVal) }, @@ -55,40 +52,10 @@ func (suite *PrecompileTestSuite) TestWithdraw() { }, { name: "failed validator not found", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (types.WithdrawArgs, error) { + malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (contract.WithdrawArgs, error) { newVal := sdk.ValAddress(suite.signer.Address().Bytes()).String() - return types.WithdrawArgs{ - Validator: newVal, - }, fmt.Errorf("validator does not exist") - }, - result: false, - }, - { - name: "contract - ok", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (types.WithdrawArgs, error) { - return types.WithdrawArgs{ - Validator: val.String(), - }, nil - }, - result: true, - }, - { - name: "contract - failed invalid validator address", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (types.WithdrawArgs, error) { - newVal := val.String() + "1" - return types.WithdrawArgs{ - Validator: newVal, - }, fmt.Errorf("invalid validator address: %s", newVal) - }, - result: false, - }, - { - name: "contract - failed validator not found", - malleate: func(val sdk.ValAddress, shares sdkmath.LegacyDec) (types.WithdrawArgs, error) { - newVal := sdk.ValAddress(suite.signer.Address().Bytes()).String() - - return types.WithdrawArgs{ + return contract.WithdrawArgs{ Validator: newVal, }, fmt.Errorf("validator does not exist") }, @@ -98,31 +65,14 @@ func (suite *PrecompileTestSuite) TestWithdraw() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - val := suite.GetFirstValidator() - - delAmt := helpers.NewRandAmount() - signer := suite.RandSigner() - suite.MintToken(signer.AccAddress(), sdk.NewCoin(fxtypes.DefaultDenom, delAmt)) - - stakingContract := suite.stakingAddr - delAddr := signer.Address() - value := big.NewInt(0) - if strings.HasPrefix(tc.name, "contract") { - stakingContract = suite.stakingTestAddr - delAddr = suite.stakingTestAddr - value = delAmt.BigInt() - } - - operator, err := suite.App.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - suite.Require().NoError(err) + operator := suite.GetFirstValAddr() + delAddr := suite.GetDelAddr() + delAmt := suite.GetStakingBalance(delAddr.Bytes()) - pack, err := suite.delegateV2Method.PackInput(types.DelegateV2Args{ - Validator: val.GetOperator(), + res := suite.DelegateV2(suite.Ctx, contract.DelegateV2Args{ + Validator: operator.String(), Amount: delAmt.BigInt(), }) - suite.Require().NoError(err) - - res := suite.EthereumTx(signer, stakingContract, value, pack) suite.Require().False(res.Failed(), res.VmError) suite.Commit() @@ -134,11 +84,9 @@ func (suite *PrecompileTestSuite) TestWithdraw() { delegation := suite.GetDelegation(delAddr.Bytes(), operator) - args, errResult := tc.malleate(operator, delegation.Shares) - packData, err := suite.withdrawMethod.PackInput(args) - suite.Require().NoError(err) - res = suite.EthereumTx(signer, stakingContract, big.NewInt(0), packData) + args, expectErr := tc.malleate(operator, delegation.Shares) + res, _ = suite.WithError(expectErr).Withdraw(suite.Ctx, args) if tc.result { suite.Require().False(res.Failed(), res.VmError) @@ -146,20 +94,22 @@ func (suite *PrecompileTestSuite) TestWithdraw() { suite.Require().NoError(err) suite.Require().Equal(totalAfter, totalBefore) - reward, err := suite.withdrawMethod.UnpackOutput(res.Ret) + withdrawABI := precompile.NewWithdrawABI() + reward, err := withdrawABI.UnpackOutput(res.Ret) suite.Require().NoError(err) + chainBalances := suite.App.BankKeeper.GetAllBalances(suite.Ctx, delAddr.Bytes()) suite.Require().True(chainBalances.AmountOf(fxtypes.DefaultDenom).Equal(sdkmath.NewIntFromBigInt(reward)), chainBalances.String()) existLog := false for _, log := range res.Logs { - if log.Topics[0] == suite.withdrawMethod.Event.ID.String() { - suite.Require().Equal(log.Address, suite.stakingAddr.String()) + if log.Topics[0] == withdrawABI.Event.ID.String() { + suite.Require().Equal(contract.StakingAddress, log.Address) - event, err := suite.withdrawMethod.UnpackEvent(log.ToEthereum()) + event, err := withdrawABI.UnpackEvent(log.ToEthereum()) suite.Require().NoError(err) suite.Require().Equal(event.Sender, delAddr) - suite.Require().Equal(event.Validator, val.GetOperator()) + suite.Require().Equal(event.Validator, operator.String()) suite.Require().Equal(event.Reward.String(), chainBalances.AmountOf(fxtypes.DefaultDenom).BigInt().String()) existLog = true } @@ -171,7 +121,7 @@ func (suite *PrecompileTestSuite) TestWithdraw() { if event.Type == distritypes.EventTypeWithdrawRewards { for _, attr := range event.Attributes { if attr.Key == distritypes.AttributeKeyValidator { - suite.Require().Equal(attr.Value, val.GetOperator()) + suite.Require().Equal(attr.Value, operator.String()) existEvent = true } if attr.Key == sdk.AttributeKeyAmount { @@ -186,8 +136,6 @@ func (suite *PrecompileTestSuite) TestWithdraw() { } } suite.Require().True(existEvent) - } else { - suite.Error(res, errResult) } }) } diff --git a/x/staking/testutil/staking.go b/x/staking/testutil/staking.go deleted file mode 100644 index d20cd438..00000000 --- a/x/staking/testutil/staking.go +++ /dev/null @@ -1,69 +0,0 @@ -package testutil - -import ( - sdkmath "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/stretchr/testify/require" - - fxstakingkeeper "github.com/functionx/fx-core/v8/x/staking/keeper" -) - -type StakingSuite struct { - *require.Assertions - ctx sdk.Context - stakingKeeper *fxstakingkeeper.Keeper -} - -func (s *StakingSuite) Init(ass *require.Assertions, ctx sdk.Context, stakingKeeper *fxstakingkeeper.Keeper) *StakingSuite { - s.Assertions = ass - s.ctx = ctx - s.stakingKeeper = stakingKeeper - return s -} - -func (s *StakingSuite) GetFirstValidator() stakingtypes.Validator { - validators, err := s.stakingKeeper.GetValidators(s.ctx, 1) - s.NoError(err) - s.NotEmpty(validators) - return validators[0] -} - -func (s *StakingSuite) GetValidators() []stakingtypes.Validator { - validators, err := s.stakingKeeper.GetValidators(s.ctx, 10) - s.NoError(err) - return validators -} - -func (s *StakingSuite) GetValidator(valAddr sdk.ValAddress) stakingtypes.Validator { - validator, err := s.stakingKeeper.GetValidator(s.ctx, valAddr) - s.NoError(err) - return validator -} - -func (s *StakingSuite) Delegate(delAddr sdk.AccAddress, delAmount sdkmath.Int, val sdk.ValAddress) { - validator, err := s.stakingKeeper.GetValidator(s.ctx, val) - s.NoError(err) - _, err = s.stakingKeeper.Delegate(s.ctx, delAddr, delAmount, stakingtypes.Unbonded, validator, true) - s.NoError(err) -} - -func (s *StakingSuite) GetDelegation(delAddr sdk.AccAddress, val sdk.ValAddress) stakingtypes.Delegation { - delegation, err := s.stakingKeeper.GetDelegation(s.ctx, delAddr, val) - s.NoError(err) - return delegation -} - -func (s *StakingSuite) Undelegate(delAddr sdk.AccAddress, val sdk.ValAddress) { - delegation, err := s.stakingKeeper.GetDelegation(s.ctx, delAddr, val) - s.NoError(err) - _, _, err = s.stakingKeeper.Undelegate(s.ctx, delAddr, val, delegation.Shares) - s.NoError(err) -} - -func (s *StakingSuite) Redelegate(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) { - delegation, err := s.stakingKeeper.GetDelegation(s.ctx, delAddr, valSrcAddr) - s.NoError(err) - _, err = s.stakingKeeper.BeginRedelegation(s.ctx, delAddr, valSrcAddr, valDstAddr, delegation.Shares) - s.NoError(err) -} diff --git a/x/staking/testutil/staking_precompile.go b/x/staking/testutil/staking_precompile.go deleted file mode 100644 index 90e019c3..00000000 --- a/x/staking/testutil/staking_precompile.go +++ /dev/null @@ -1,135 +0,0 @@ -package testutil - -import ( - "math/big" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" - evmtypes "github.com/evmos/ethermint/x/evm/types" - "github.com/stretchr/testify/require" - - "github.com/functionx/fx-core/v8/x/staking/precompile" - fxstakingtypes "github.com/functionx/fx-core/v8/x/staking/types" -) - -type StakingPrecompileSuite struct { - *require.Assertions -} - -func (s *StakingPrecompileSuite) EthereumTx(data []byte, value *big.Int, gasLimit uint64, success bool) *evmtypes.MsgEthereumTxResponse { - return nil -} - -func (s *StakingPrecompileSuite) Allowance(validator sdk.ValAddress, owner, spender common.Address) *big.Int { - method := precompile.NewAllowanceSharesMethod(nil) - data, err := method.PackInput(fxstakingtypes.AllowanceSharesArgs{ - Validator: validator.String(), - Owner: owner, - Spender: spender, - }) - s.NoError(err) - tx := s.EthereumTx(data, nil, method.RequiredGas(), true) - output, err := method.UnpackOutput(tx.Ret) - s.NoError(err) - return output -} - -func (s *StakingPrecompileSuite) Delegation(validator sdk.ValAddress, delegator common.Address) (*big.Int, *big.Int) { - method := precompile.NewDelegationMethod(nil) - data, err := method.PackInput(fxstakingtypes.DelegationArgs{ - Validator: validator.String(), - Delegator: delegator, - }) - s.NoError(err) - tx := s.EthereumTx(data, nil, method.RequiredGas(), true) - shares, amount, err := method.UnpackOutput(tx.Ret) - s.NoError(err) - return shares, amount -} - -func (s *StakingPrecompileSuite) DelegationRewards(validator sdk.ValAddress, delegator common.Address) *big.Int { - method := precompile.NewDelegationRewardsMethod(nil) - data, err := method.PackInput(fxstakingtypes.DelegationRewardsArgs{ - Validator: validator.String(), - Delegator: delegator, - }) - s.NoError(err) - tx := s.EthereumTx(data, nil, method.RequiredGas(), true) - rewards, err := method.UnpackOutput(tx.Ret) - s.NoError(err) - return rewards -} - -func (s *StakingPrecompileSuite) Approve(validator sdk.ValAddress, spender common.Address, shares *big.Int, success bool) *evmtypes.MsgEthereumTxResponse { - method := precompile.NewApproveSharesMethod(nil) - data, err := method.PackInput(fxstakingtypes.ApproveSharesArgs{ - Validator: validator.String(), - Spender: spender, - Shares: shares, - }) - s.NoError(err) - return s.EthereumTx(data, nil, method.RequiredGas(), success) -} - -func (s *StakingPrecompileSuite) TransferShares(validator sdk.ValAddress, to common.Address, shares *big.Int, success bool) *evmtypes.MsgEthereumTxResponse { - method := precompile.NewTransferSharesMethod(nil) - data, err := method.PackInput(fxstakingtypes.TransferSharesArgs{ - Validator: validator.String(), - To: to, - Shares: shares, - }) - s.NoError(err) - return s.EthereumTx(data, nil, method.RequiredGas(), success) -} - -func (s *StakingPrecompileSuite) TransferFromShares(validator sdk.ValAddress, from, to common.Address, shares *big.Int, success bool) *evmtypes.MsgEthereumTxResponse { - method := precompile.NewTransferFromSharesMethod(nil) - data, err := method.PackInput(fxstakingtypes.TransferFromSharesArgs{ - Validator: validator.String(), - From: from, - To: to, - Shares: shares, - }) - s.NoError(err) - return s.EthereumTx(data, nil, method.RequiredGas(), success) -} - -func (s *StakingPrecompileSuite) Withdraw(validator sdk.ValAddress, success bool) *evmtypes.MsgEthereumTxResponse { - method := precompile.NewWithdrawMethod(nil) - data, err := method.PackInput(fxstakingtypes.WithdrawArgs{ - Validator: validator.String(), - }) - s.NoError(err) - return s.EthereumTx(data, nil, method.RequiredGas(), success) -} - -func (s *StakingPrecompileSuite) DelegateV2(validator sdk.ValAddress, amount *big.Int, success bool) *evmtypes.MsgEthereumTxResponse { - method := precompile.NewDelegateV2Method(nil) - data, err := method.PackInput(fxstakingtypes.DelegateV2Args{ - Validator: validator.String(), - Amount: amount, - }) - s.NoError(err) - return s.EthereumTx(data, nil, method.RequiredGas(), success) -} - -func (s *StakingPrecompileSuite) RedelegateV2(validatorSrc, validatorDst sdk.ValAddress, amount *big.Int, success bool) *evmtypes.MsgEthereumTxResponse { - method := precompile.NewRedelegateV2Method(nil) - data, err := method.PackInput(fxstakingtypes.RedelegateV2Args{ - ValidatorSrc: validatorSrc.String(), - ValidatorDst: validatorDst.String(), - Amount: amount, - }) - s.NoError(err) - return s.EthereumTx(data, nil, method.RequiredGas(), success) -} - -func (s *StakingPrecompileSuite) UndelegateV2(validator sdk.ValAddress, amount *big.Int, success bool) *evmtypes.MsgEthereumTxResponse { - method := precompile.NewUndelegateV2Method(nil) - data, err := method.PackInput(fxstakingtypes.UndelegateV2Args{ - Validator: validator.String(), - Amount: amount, - }) - s.NoError(err) - return s.EthereumTx(data, nil, method.RequiredGas(), success) -}