Merge pull request #84 from 0glabs/staking-precompile

feat: staking precompile
This commit is contained in:
MiniFrenchBread 2024-11-11 13:42:13 +08:00 committed by GitHub
commit 76eebc57c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 8956 additions and 28 deletions

5
.gitignore vendored
View File

@ -49,3 +49,8 @@ go.work.sum
# runtime # runtime
run run
# contracts
precompiles/interfaces/build
precompiles/interfaces/node_modules
precompiles/interfaces/abis

View File

@ -110,6 +110,7 @@ import (
chainparams "github.com/0glabs/0g-chain/app/params" chainparams "github.com/0glabs/0g-chain/app/params"
"github.com/0glabs/0g-chain/chaincfg" "github.com/0glabs/0g-chain/chaincfg"
dasignersprecompile "github.com/0glabs/0g-chain/precompiles/dasigners" dasignersprecompile "github.com/0glabs/0g-chain/precompiles/dasigners"
stakingprecompile "github.com/0glabs/0g-chain/precompiles/staking"
"github.com/0glabs/0g-chain/x/bep3" "github.com/0glabs/0g-chain/x/bep3"
bep3keeper "github.com/0glabs/0g-chain/x/bep3/keeper" bep3keeper "github.com/0glabs/0g-chain/x/bep3/keeper"
@ -499,11 +500,18 @@ func NewApp(
app.dasignersKeeper = dasignerskeeper.NewKeeper(keys[dasignerstypes.StoreKey], appCodec, app.stakingKeeper, govAuthAddrStr) app.dasignersKeeper = dasignerskeeper.NewKeeper(keys[dasignerstypes.StoreKey], appCodec, app.stakingKeeper, govAuthAddrStr)
// precopmiles // precopmiles
precompiles := make(map[common.Address]vm.PrecompiledContract) precompiles := make(map[common.Address]vm.PrecompiledContract)
// dasigners
daSignersPrecompile, err := dasignersprecompile.NewDASignersPrecompile(app.dasignersKeeper) daSignersPrecompile, err := dasignersprecompile.NewDASignersPrecompile(app.dasignersKeeper)
if err != nil { if err != nil {
panic("initialize precompile failed") panic(fmt.Sprintf("initialize dasigners precompile failed: %v", err))
} }
precompiles[daSignersPrecompile.Address()] = daSignersPrecompile precompiles[daSignersPrecompile.Address()] = daSignersPrecompile
// staking
stakingPrecompile, err := stakingprecompile.NewStakingPrecompile(app.stakingKeeper)
if err != nil {
panic(fmt.Sprintf("initialize staking precompile failed: %v", err))
}
precompiles[stakingPrecompile.Address()] = stakingPrecompile
app.evmKeeper = evmkeeper.NewKeeper( app.evmKeeper = evmkeeper.NewKeeper(
appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey],

View File

@ -3,4 +3,5 @@ package common
const ( const (
ErrGetStateDB = "get EVM StateDB failed" ErrGetStateDB = "get EVM StateDB failed"
ErrInvalidNumberOfArgs = "invalid number of arguments; expected %d; got: %d" ErrInvalidNumberOfArgs = "invalid number of arguments; expected %d; got: %d"
ErrSenderNotOrigin = "msg.sender is not from tx origin"
) )

View File

@ -0,0 +1,18 @@
package common
import (
"math/big"
"strings"
"cosmossdk.io/math"
"github.com/ethereum/go-ethereum/common"
)
func ToLowerHexWithoutPrefix(addr common.Address) string {
return strings.ToLower(addr.Hex()[2:])
}
// BigIntToLegacyDec converts a uint number (18 decimals) to math.LegacyDec (18 decimals)
func BigIntToLegacyDec(x *big.Int) math.LegacyDec {
return math.LegacyNewDecFromBigIntWithPrec(x, math.LegacyPrecision)
}

View File

@ -139,11 +139,11 @@ func (d *DASignersPrecompile) Run(evm *vm.EVM, contract *vm.Contract, readonly b
bz, err = d.RegisteredEpoch(ctx, evm, method, args) bz, err = d.RegisteredEpoch(ctx, evm, method, args)
// txs // txs
case DASignersFunctionRegisterSigner: case DASignersFunctionRegisterSigner:
bz, err = d.RegisterSigner(ctx, evm, stateDB, method, args) bz, err = d.RegisterSigner(ctx, evm, stateDB, contract, method, args)
case DASignersFunctionRegisterNextEpoch: case DASignersFunctionRegisterNextEpoch:
bz, err = d.RegisterNextEpoch(ctx, evm, stateDB, method, args) bz, err = d.RegisterNextEpoch(ctx, evm, stateDB, contract, method, args)
case DASignersFunctionUpdateSocket: case DASignersFunctionUpdateSocket:
bz, err = d.UpdateSocket(ctx, evm, stateDB, method, args) bz, err = d.UpdateSocket(ctx, evm, stateDB, contract, method, args)
} }
if err != nil { if err != nil {

View File

@ -14,6 +14,7 @@ import (
"github.com/0glabs/0g-chain/x/dasigners/v1/types" "github.com/0glabs/0g-chain/x/dasigners/v1/types"
abci "github.com/cometbft/cometbft/abci/types" abci "github.com/cometbft/cometbft/abci/types"
"github.com/consensys/gnark-crypto/ecc/bn254" "github.com/consensys/gnark-crypto/ecc/bn254"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -23,7 +24,6 @@ import (
"cosmossdk.io/math" "cosmossdk.io/math"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
) )
type DASignersTestSuite struct { type DASignersTestSuite struct {
@ -44,8 +44,7 @@ func (suite *DASignersTestSuite) AddDelegation(from string, to string, amount ma
suite.Require().NoError(err) suite.Require().NoError(err)
validator, found := suite.StakingKeeper.GetValidator(suite.Ctx, valAddr) validator, found := suite.StakingKeeper.GetValidator(suite.Ctx, valAddr)
if !found { if !found {
consPriv, err := ethsecp256k1.GenerateKey() consPriv := ed25519.GenPrivKey()
suite.Require().NoError(err)
newValidator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{}) newValidator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{})
suite.Require().NoError(err) suite.Require().NoError(err)
validator = newValidator validator = newValidator
@ -73,8 +72,8 @@ func (suite *DASignersTestSuite) SetupTest() {
suite.Assert().EqualValues(ok, true) suite.Assert().EqualValues(ok, true)
suite.dasigners = precompile.(*dasignersprecompile.DASignersPrecompile) suite.dasigners = precompile.(*dasignersprecompile.DASignersPrecompile)
suite.signerOne = testutil.GenSigner() suite.signerOne = suite.GenSigner()
suite.signerTwo = testutil.GenSigner() suite.signerTwo = suite.GenSigner()
abi, err := abi.JSON(strings.NewReader(dasignersprecompile.DASignersABI)) abi, err := abi.JSON(strings.NewReader(dasignersprecompile.DASignersABI))
suite.Assert().NoError(err) suite.Assert().NoError(err)
suite.abi = abi suite.abi = abi

View File

@ -62,7 +62,7 @@ func (d *DASignersPrecompile) IsSigner(ctx sdk.Context, _ *vm.EVM, method *abi.M
if len(args) != 1 { if len(args) != 1 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 1, len(args)) return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 1, len(args))
} }
account := ToLowerHexWithoutPrefix(args[0].(common.Address)) account := precopmiles_common.ToLowerHexWithoutPrefix(args[0].(common.Address))
_, found, err := d.dasignersKeeper.GetSigner(ctx, account) _, found, err := d.dasignersKeeper.GetSigner(ctx, account)
if err != nil { if err != nil {
return nil, err return nil, err
@ -74,7 +74,7 @@ func (d *DASignersPrecompile) RegisteredEpoch(ctx sdk.Context, _ *vm.EVM, method
if len(args) != 2 { if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args)) return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
} }
account := ToLowerHexWithoutPrefix(args[0].(common.Address)) account := precopmiles_common.ToLowerHexWithoutPrefix(args[0].(common.Address))
epoch := args[1].(*big.Int).Uint64() epoch := args[1].(*big.Int).Uint64()
_, found, err := d.dasignersKeeper.GetRegistration(ctx, epoch, account) _, found, err := d.dasignersKeeper.GetRegistration(ctx, epoch, account)
if err != nil { if err != nil {

View File

@ -7,18 +7,30 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/statedb"
precopmiles_common "github.com/0glabs/0g-chain/precompiles/common"
) )
func (d *DASignersPrecompile) RegisterSigner(ctx sdk.Context, evm *vm.EVM, stateDB *statedb.StateDB, method *abi.Method, args []interface{}) ([]byte, error) { func (d *DASignersPrecompile) RegisterSigner(
ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgRegisterSigner(args) msg, err := NewMsgRegisterSigner(args)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// validation // validation
sender := ToLowerHexWithoutPrefix(evm.Origin) sender := precopmiles_common.ToLowerHexWithoutPrefix(evm.Origin)
if sender != msg.Signer.Account { if sender != msg.Signer.Account {
return nil, fmt.Errorf(ErrInvalidSender, sender, msg.Signer.Account) return nil, fmt.Errorf(ErrInvalidSender, sender, msg.Signer.Account)
} }
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute // execute
_, err = d.dasignersKeeper.RegisterSigner(sdk.WrapSDKContext(ctx), msg) _, err = d.dasignersKeeper.RegisterSigner(sdk.WrapSDKContext(ctx), msg)
if err != nil { if err != nil {
@ -32,11 +44,22 @@ func (d *DASignersPrecompile) RegisterSigner(ctx sdk.Context, evm *vm.EVM, state
return method.Outputs.Pack() return method.Outputs.Pack()
} }
func (d *DASignersPrecompile) RegisterNextEpoch(ctx sdk.Context, evm *vm.EVM, stateDB *statedb.StateDB, method *abi.Method, args []interface{}) ([]byte, error) { func (d *DASignersPrecompile) RegisterNextEpoch(
msg, err := NewMsgRegisterNextEpoch(args, ToLowerHexWithoutPrefix(evm.Origin)) ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgRegisterNextEpoch(args, precopmiles_common.ToLowerHexWithoutPrefix(evm.Origin))
if err != nil { if err != nil {
return nil, err return nil, err
} }
// validation
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute // execute
_, err = d.dasignersKeeper.RegisterNextEpoch(sdk.WrapSDKContext(ctx), msg) _, err = d.dasignersKeeper.RegisterNextEpoch(sdk.WrapSDKContext(ctx), msg)
if err != nil { if err != nil {
@ -45,11 +68,22 @@ func (d *DASignersPrecompile) RegisterNextEpoch(ctx sdk.Context, evm *vm.EVM, st
return method.Outputs.Pack() return method.Outputs.Pack()
} }
func (d *DASignersPrecompile) UpdateSocket(ctx sdk.Context, evm *vm.EVM, stateDB *statedb.StateDB, method *abi.Method, args []interface{}) ([]byte, error) { func (d *DASignersPrecompile) UpdateSocket(
msg, err := NewMsgUpdateSocket(args, ToLowerHexWithoutPrefix(evm.Origin)) ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgUpdateSocket(args, precopmiles_common.ToLowerHexWithoutPrefix(evm.Origin))
if err != nil { if err != nil {
return nil, err return nil, err
} }
// validation
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute // execute
_, err = d.dasignersKeeper.UpdateSocket(sdk.WrapSDKContext(ctx), msg) _, err = d.dasignersKeeper.UpdateSocket(sdk.WrapSDKContext(ctx), msg)
if err != nil { if err != nil {

View File

@ -3,7 +3,6 @@ package dasigners
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"strings"
precopmiles_common "github.com/0glabs/0g-chain/precompiles/common" precopmiles_common "github.com/0glabs/0g-chain/precompiles/common"
dasignerstypes "github.com/0glabs/0g-chain/x/dasigners/v1/types" dasignerstypes "github.com/0glabs/0g-chain/x/dasigners/v1/types"
@ -90,7 +89,7 @@ func NewQuerySignerRequest(args []interface{}) (*dasignerstypes.QuerySignerReque
Accounts: make([]string, len(accounts)), Accounts: make([]string, len(accounts)),
} }
for i, account := range accounts { for i, account := range accounts {
req.Accounts[i] = ToLowerHexWithoutPrefix(account) req.Accounts[i] = precopmiles_common.ToLowerHexWithoutPrefix(account)
} }
return &req, nil return &req, nil
} }
@ -139,10 +138,6 @@ func NewIDASignersSignerDetail(signer *dasignerstypes.Signer) IDASignersSignerDe
} }
} }
func ToLowerHexWithoutPrefix(addr common.Address) string {
return strings.ToLower(addr.Hex()[2:])
}
func NewMsgRegisterSigner(args []interface{}) (*dasignerstypes.MsgRegisterSigner, error) { func NewMsgRegisterSigner(args []interface{}) (*dasignerstypes.MsgRegisterSigner, error) {
if len(args) != 2 { if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args)) return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
@ -151,7 +146,7 @@ func NewMsgRegisterSigner(args []interface{}) (*dasignerstypes.MsgRegisterSigner
signer := args[0].(IDASignersSignerDetail) signer := args[0].(IDASignersSignerDetail)
return &dasignerstypes.MsgRegisterSigner{ return &dasignerstypes.MsgRegisterSigner{
Signer: &dasignerstypes.Signer{ Signer: &dasignerstypes.Signer{
Account: ToLowerHexWithoutPrefix(signer.Signer), Account: precopmiles_common.ToLowerHexWithoutPrefix(signer.Signer),
Socket: signer.Socket, Socket: signer.Socket,
PubkeyG1: SerializeG1(signer.PkG1), PubkeyG1: SerializeG1(signer.PkG1),
PubkeyG2: SerializeG2(signer.PkG2), PubkeyG2: SerializeG2(signer.PkG2),

View File

@ -0,0 +1,18 @@
{
"extends": "solhint:recommended",
"plugins": ["prettier"],
"rules": {
"avoid-low-level-calls": "off",
"compiler-version": "off",
"gas-custom-errors": "off",
"explicit-types": ["warn", "implicit"],
"func-visibility": ["warn", { "ignoreConstructors": true }],
"max-states-count": "off",
"no-empty-blocks": "off",
"no-global-import": "off",
"no-inline-assembly": "off",
"not-rely-on-time": "off",
"prettier/prettier": "error",
"reason-string": "off"
}
}

View File

@ -0,0 +1,88 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.0;
library BN254 {
struct G1Point {
uint X;
uint Y;
}
// Encoding of field elements is: X[1] * i + X[0]
struct G2Point {
uint[2] X;
uint[2] Y;
}
}
interface IDASigners {
/*=== struct ===*/
struct SignerDetail {
address signer;
string socket;
BN254.G1Point pkG1;
BN254.G2Point pkG2;
}
struct Params {
uint tokensPerVote;
uint maxVotesPerSigner;
uint maxQuorums;
uint epochBlocks;
uint encodedSlices;
}
/*=== event ===*/
event NewSigner(
address indexed signer,
BN254.G1Point pkG1,
BN254.G2Point pkG2
);
event SocketUpdated(address indexed signer, string socket);
/*=== function ===*/
function params() external view returns (Params memory);
function epochNumber() external view returns (uint);
function quorumCount(uint _epoch) external view returns (uint);
function isSigner(address _account) external view returns (bool);
function getSigner(
address[] memory _account
) external view returns (SignerDetail[] memory);
function getQuorum(
uint _epoch,
uint _quorumId
) external view returns (address[] memory);
function getQuorumRow(
uint _epoch,
uint _quorumId,
uint32 _rowIndex
) external view returns (address);
function registerSigner(
SignerDetail memory _signer,
BN254.G1Point memory _signature
) external;
function updateSocket(string memory _socket) external;
function registeredEpoch(
address _account,
uint _epoch
) external view returns (bool);
function registerNextEpoch(BN254.G1Point memory _signature) external;
function getAggPkG1(
uint _epoch,
uint _quorumId,
bytes memory _quorumBitmap
)
external
view
returns (BN254.G1Point memory aggPkG1, uint total, uint hit);
}

View File

@ -0,0 +1,415 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.0;
/**
* @dev Description defines a validator description
*/
struct Description {
string moniker;
string identity;
string website;
string securityContact;
string details;
}
/**
* @dev CommissionRates defines the initial commission rates to be used for creating
* a validator.
*/
struct CommissionRates {
uint rate; // 18 decimals
uint maxRate; // 18 decimals
uint maxChangeRate; // 18 decimals
}
/**
* @dev Commission defines the commission parameters.
*/
struct Commission {
CommissionRates commissionRates;
uint updateTime;
}
/**
* @dev Validator defines a validator.
*/
struct Validator {
string operatorAddress;
string consensusPubkey;
bool jailed;
BondStatus status;
uint tokens;
uint delegatorShares; // 18 decimals
Description description;
int64 unbondingHeight;
int64 unbondingTime;
Commission commission;
uint minSelfDelegation;
int64 unbondingOnHoldRefCount;
uint64[] unbondingIds;
}
/**
* @dev Delegation represents the bond with tokens held by an account.
*/
struct Delegation {
string delegatorAddress;
string validatorAddress;
uint shares; // 18 decimals
}
/**
* @dev RedelegationResponse is equivalent to a Redelegation except that its entries
* contain a balance in addition to shares which is more suitable for client
* responses.
*/
struct DelegationResponse {
Delegation delegation;
uint balance;
}
/**
* @dev UnbondingDelegationEntry defines an unbonding object with relevant metadata.
*/
struct UnbondingDelegationEntry {
int64 creationHeight;
int64 completionTime;
uint initialBalance;
uint balance;
uint64 unbondingId;
int64 unbondingOnHoldRefCount;
}
/**
* @dev UnbondingDelegation stores all of a single delegator's unbonding bonds
* for a single validator in an time-ordered list.
*/
struct UnbondingDelegation {
string delegatorAddress;
string validatorAddress;
UnbondingDelegationEntry[] entries;
}
/**
* @dev RedelegationResponse is equivalent to a Redelegation except that its entries
* contain a balance in addition to shares which is more suitable for client
* responses.
*/
struct RedelegationResponse {
Redelegation redelegation;
RedelegationEntryResponse[] entries;
}
/**
* @dev Redelegation contains the list of a particular delegator's redelegating bonds
* from a particular source validator to a particular destination validator.
*/
struct Redelegation {
string delegatorAddress;
string validatorSrcAddress;
string validatorDstAddress;
RedelegationEntry[] entries;
}
/**
* @dev RedelegationEntry defines a redelegation object with relevant metadata.
*/
struct RedelegationEntry {
int64 creationHeight;
int64 completionTime;
uint initialBalance;
uint sharesDst; // 18 decimals
uint64 unbondingId;
int64 unbondingOnHoldRefCount;
}
/**
* @dev RedelegationEntryResponse is equivalent to a RedelegationEntry except that it
* contains a balance in addition to shares which is more suitable for client
* responses.
*/
struct RedelegationEntryResponse {
RedelegationEntry redelegationEntry;
uint balance;
}
/**
* @dev Params defines the parameters for the x/staking module.
*/
struct Params {
int64 unbondingTime;
uint32 maxValidators;
uint32 maxEntries;
uint32 historicalEntries;
string bondDenom;
uint minCommissionRate; // 18 decimals
}
/**
* @dev BondStatus is the status of a validator.
*/
enum BondStatus {
Unspecified,
Unbonded,
Unbonding,
Bonded
}
struct NullableUint {
bool isNull;
uint value;
}
struct PageRequest {
bytes key;
uint64 offset;
uint64 limit;
bool countTotal;
bool reverse;
}
struct PageResponse {
bytes nextKey;
uint64 total;
}
interface IStaking {
/*=== cosmos tx ===*/
/**
* @dev CreateValidator defines a method for creating a new validator for tx sender.
* cosmos grpc: rpc CreateValidator(MsgCreateValidator) returns (MsgCreateValidatorResponse);
*/
function createValidator(
Description memory description,
CommissionRates memory commission,
uint minSelfDelegation,
string memory pubkey, // 0gchaind tendermint show-validator
uint value
) external;
/**
* @dev EditValidator defines a method for editing an existing validator (tx sender).
* cosmos grpc: rpc EditValidator(MsgEditValidator) returns (MsgEditValidatorResponse);
*/
function editValidator(
Description memory description,
NullableUint memory commissionRate,
NullableUint memory minSelfDelegation
) external;
/**
* @dev Delegate defines a method for performing a delegation of coins from a delegator to a validator.abi
* The delegator is tx sender.
* cosmos grpc: rpc Delegate(MsgDelegate) returns (MsgDelegateResponse);
*/
function delegate(
string memory validatorAddress,
uint amount // in bond denom
) external;
/**
* @dev BeginRedelegate defines a method for performing a redelegationA
* of coins from a delegator and source validator to a destination validator.
* The delegator is tx sender.
* cosmos grpc: rpc BeginRedelegate(MsgBeginRedelegate) returns (MsgBeginRedelegateResponse);
*/
function beginRedelegate(
string memory validatorSrcAddress,
string memory validatorDstAddress,
uint amount // in bond denom
) external returns (uint completionTime);
/**
* @dev Undelegate defines a method for performing an undelegation from a
* delegate and a validator.
* The delegator is tx sender.
* cosmos grpc: rpc Undelegate(MsgUndelegate) returns (MsgUndelegateResponse);
*/
function undelegate(
string memory validatorAddress,
uint amount // in bond denom
) external returns (uint completionTime);
/**
* @dev CancelUnbondingDelegation defines a method for performing canceling the unbonding delegation
* and delegate back to previous validator.
* The delegator is tx sender.
* Since: cosmos-sdk 0.46
* cosmos grpc: rpc CancelUnbondingDelegation(MsgCancelUnbondingDelegation) returns (MsgCancelUnbondingDelegationResponse);
*/
function cancelUnbondingDelegation(
string memory validatorAddress,
uint amount, // in bond denom
uint creationHeight
) external;
/**
* @dev UpdateParams defines an operation for updating the x/staking module parameters.
* Since: cosmos-sdk 0.47
* grpc: rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
*/
// Skipped. This function is controlled by governance module.
/*=== cosmos query ===*/
/**
* @dev Validators queries all validators that match the given status.
* cosmos grpc: rpc Validators(QueryValidatorsRequest) returns (QueryValidatorsResponse);
*/
function validators(
string memory status,
PageRequest memory pagination
)
external
view
returns (
Validator[] memory validators,
PageResponse memory paginationResult
);
/**
* @dev Validator queries validator info for given validator address.
* cosmos grpc: rpc Validator(QueryValidatorRequest) returns (QueryValidatorResponse);
*/
function validator(
string memory validatorAddress
) external view returns (Validator memory validator);
/**
* @dev ValidatorDelegations queries delegate info for given validator.
* cosmos grpc: rpc ValidatorDelegations(QueryValidatorDelegationsRequest) returns (QueryValidatorDelegationsResponse);
*/
function validatorDelegations(
string memory validatorAddr,
PageRequest memory pagination
)
external
view
returns (
DelegationResponse[] memory delegationResponses,
PageResponse memory paginationResult
);
/**
* @dev ValidatorUnbondingDelegations queries unbonding delegations of a validator.
* cosmos grpc: rpc ValidatorUnbondingDelegations(QueryValidatorUnbondingDelegationsRequest) returns (QueryValidatorUnbondingDelegationsResponse);
*/
//
function validatorUnbondingDelegations(
string memory validatorAddr,
PageRequest memory pagination
)
external
view
returns (
UnbondingDelegation[] memory unbondingResponses,
PageResponse memory paginationResult
);
/**
* @dev Delegation queries delegate info for given validator delegator pair.
* cosmos grpc: rpc Delegation(QueryDelegationRequest) returns (QueryDelegationResponse);
*/
function delegation(
string memory delegatorAddr,
string memory validatorAddr
) external view returns (Delegation memory delegation, uint balance);
/**
* @dev UnbondingDelegation queries unbonding info for given validator delegator pair.
* cosmos grpc: rpc UnbondingDelegation(QueryUnbondingDelegationRequest) returns (QueryUnbondingDelegationResponse);
*/
function unbondingDelegation(
string memory delegatorAddr,
string memory validatorAddr
) external view returns (UnbondingDelegation memory unbond);
/**
* @dev DelegatorDelegations queries all delegations of a given delegator address.
*
* cosmos grpc: rpc DelegatorDelegations(QueryDelegatorDelegationsRequest) returns (QueryDelegatorDelegationsResponse);
*/
function delegatorDelegations(
string memory delegatorAddr,
PageRequest memory pagination
)
external
view
returns (
DelegationResponse[] memory delegationResponses,
PageResponse memory paginationResult
);
/**
* @dev DelegatorUnbondingDelegations queries all unbonding delegations of a given delegator address.
* cosmos grpc: rpc DelegatorUnbondingDelegations(QueryDelegatorUnbondingDelegationsRequest)
*/
function delegatorUnbondingDelegations(
string memory delegatorAddr,
PageRequest memory pagination
)
external
view
returns (
UnbondingDelegation[] memory unbondingResponses,
PageResponse memory paginationResult
);
/**
* @dev Redelegations queries redelegations of given address.
*
* grpc: rpc Redelegations(QueryRedelegationsRequest) returns (QueryRedelegationsResponse);
*/
function redelegations(
string memory delegatorAddress,
string memory srcValidatorAddress,
string memory dstValidatorAddress,
PageRequest calldata pageRequest
)
external
view
returns (
RedelegationResponse[] calldata redelegationResponses,
PageResponse calldata paginationResult
);
/**
* @dev DelegatorValidators queries all validators info for given delegator address.
* cosmos grpc: rpc DelegatorValidators(QueryDelegatorValidatorsRequest) returns (QueryDelegatorValidatorsResponse);
*/
function delegatorValidators(
string memory delegatorAddr,
PageRequest memory pagination
)
external
view
returns (
Validator[] memory validators,
PageResponse memory paginationResult
);
/**
* @dev DelegatorValidator queries validator info for given delegator validator pair.
* cosmos grpc: rpc DelegatorValidator(QueryDelegatorValidatorRequest) returns (QueryDelegatorValidatorResponse);
*/
function delegatorValidator(
string memory delegatorAddr,
string memory validatorAddr
) external view returns (Validator memory validator);
/**
* @dev Pool queries the pool info.
* cosmos grpc: rpc Pool(QueryPoolRequest) returns (QueryPoolResponse);
*/
function pool()
external
view
returns (uint notBondedTokens, uint bondedTokens);
/**
* @dev Parameters queries the staking parameters.
* cosmos grpc: rpc Params(QueryParamsRequest) returns (QueryParamsResponse);
*/
function params() external view returns (Params memory params);
}

View File

@ -0,0 +1,33 @@
import "hardhat-abi-exporter";
import { HardhatUserConfig } from "hardhat/types";
const config: HardhatUserConfig = {
paths: {
artifacts: "build/artifacts",
cache: "build/cache",
sources: "contracts",
},
solidity: {
compilers: [
{
version: "0.8.20",
settings: {
evmVersion: "istanbul",
optimizer: {
enabled: true,
runs: 200,
},
},
},
],
},
abiExporter: {
path: "./abis",
runOnCompile: true,
clear: true,
flat: true,
format: "json",
},
};
export default config;

View File

@ -0,0 +1,27 @@
{
"name": "precompile-contracts",
"version": "1.0.0",
"license": "MIT",
"scripts": {
"build": "hardhat compile",
"fmt:sol": "prettier 'contracts/**/*.sol' -w"
},
"devDependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.5",
"@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"eslint": "8.34.0",
"eslint-config-prettier": "8.6.0",
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-prettier": "4.2.1",
"hardhat": "^2.22.2",
"hardhat-abi-exporter": "^2.10.1",
"prettier": "2.8.4",
"prettier-plugin-organize-imports": "3.2.4",
"prettier-plugin-solidity": "1.1.2",
"solhint": "^4.5.4",
"solhint-plugin-prettier": "0.0.5",
"ts-node": "^10.9.2",
"typescript": "4.9.5"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
package staking
const (
ErrPubKeyInvalidLength = "public key with invalid length"
)

View File

@ -0,0 +1,223 @@
package staking
import (
sdk "github.com/cosmos/cosmos-sdk/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/core/vm"
)
func (s *StakingPrecompile) Validators(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryValidatorsRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.Validators(ctx, req)
if err != nil {
return nil, err
}
validators := make([]Validator, len(response.Validators))
for i, v := range response.Validators {
validators[i] = convertValidator(v)
}
paginationResult := convertPageResponse(response.Pagination)
return method.Outputs.Pack(validators, paginationResult)
}
func (s *StakingPrecompile) Validator(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryValidatorRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.Validator(ctx, req)
if err != nil {
return nil, err
}
return method.Outputs.Pack(convertValidator(response.Validator))
}
func (s *StakingPrecompile) ValidatorDelegations(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryValidatorDelegationsRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.ValidatorDelegations(ctx, req)
if err != nil {
return nil, err
}
delegationResponses := make([]DelegationResponse, len(response.DelegationResponses))
for i, v := range response.DelegationResponses {
delegationResponses[i] = convertDelegationResponse(v)
}
paginationResult := convertPageResponse(response.Pagination)
return method.Outputs.Pack(delegationResponses, paginationResult)
}
func (s *StakingPrecompile) ValidatorUnbondingDelegations(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryValidatorUnbondingDelegationsRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.ValidatorUnbondingDelegations(ctx, req)
if err != nil {
return nil, err
}
unbondingResponses := make([]UnbondingDelegation, len(response.UnbondingResponses))
for i, v := range response.UnbondingResponses {
unbondingResponses[i] = convertUnbondingDelegation(v)
}
paginationResult := convertPageResponse(response.Pagination)
return method.Outputs.Pack(unbondingResponses, paginationResult)
}
func (s *StakingPrecompile) Delegation(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryDelegationRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.Delegation(ctx, req)
if err != nil {
return nil, err
}
delegation := convertDelegation(response.DelegationResponse.Delegation)
balance := response.DelegationResponse.Balance.Amount.BigInt()
return method.Outputs.Pack(delegation, balance)
}
func (s *StakingPrecompile) UnbondingDelegation(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryUnbondingDelegationRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.UnbondingDelegation(ctx, req)
if err != nil {
return nil, err
}
return method.Outputs.Pack(convertUnbondingDelegation(response.Unbond))
}
func (s *StakingPrecompile) DelegatorDelegations(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryDelegatorDelegationsRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.DelegatorDelegations(ctx, req)
if err != nil {
return nil, err
}
delegationResponses := make([]DelegationResponse, len(response.DelegationResponses))
for i, v := range response.DelegationResponses {
delegationResponses[i] = convertDelegationResponse(v)
}
paginationResult := convertPageResponse(response.Pagination)
return method.Outputs.Pack(delegationResponses, paginationResult)
}
func (s *StakingPrecompile) DelegatorUnbondingDelegations(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryDelegatorUnbondingDelegationsRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.DelegatorUnbondingDelegations(ctx, req)
if err != nil {
return nil, err
}
unbondingResponses := make([]UnbondingDelegation, len(response.UnbondingResponses))
for i, v := range response.UnbondingResponses {
unbondingResponses[i] = convertUnbondingDelegation(v)
}
paginationResult := convertPageResponse(response.Pagination)
return method.Outputs.Pack(unbondingResponses, paginationResult)
}
func (s *StakingPrecompile) Redelegations(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryRedelegationsRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.Redelegations(ctx, req)
if err != nil {
return nil, err
}
redelegationResponses := make([]RedelegationResponse, len(response.RedelegationResponses))
for i, v := range response.RedelegationResponses {
redelegationResponses[i] = convertRedelegationResponse(v)
}
paginationResult := convertPageResponse(response.Pagination)
return method.Outputs.Pack(redelegationResponses, paginationResult)
}
func (s *StakingPrecompile) DelegatorValidators(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryDelegatorValidatorsRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.DelegatorValidators(ctx, req)
if err != nil {
return nil, err
}
validators := make([]Validator, len(response.Validators))
for i, v := range response.Validators {
validators[i] = convertValidator(v)
}
paginationResult := convertPageResponse(response.Pagination)
return method.Outputs.Pack(validators, paginationResult)
}
func (s *StakingPrecompile) DelegatorValidator(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryDelegatorValidatorRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.DelegatorValidator(ctx, req)
if err != nil {
return nil, err
}
return method.Outputs.Pack(convertValidator(response.Validator))
}
func (s *StakingPrecompile) Pool(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryPoolRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.Pool(ctx, req)
if err != nil {
return nil, err
}
notBondedTokens := response.Pool.NotBondedTokens.BigInt()
bondedTokens := response.Pool.BondedTokens.BigInt()
return method.Outputs.Pack(notBondedTokens, bondedTokens)
}
func (s *StakingPrecompile) Params(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) {
req, err := NewQueryParamsRequest(args)
if err != nil {
return nil, err
}
response, err := stakingkeeper.Querier{Keeper: s.stakingKeeper}.Params(ctx, req)
if err != nil {
return nil, err
}
return method.Outputs.Pack(convertParams(response.Params))
}

View File

@ -0,0 +1,801 @@
package staking_test
import (
"math/big"
stakingprecompile "github.com/0glabs/0g-chain/precompiles/staking"
sdk "github.com/cosmos/cosmos-sdk/types"
query "github.com/cosmos/cosmos-sdk/types/query"
"github.com/ethereum/go-ethereum/common"
)
func (s *StakingTestSuite) TestValidators() {
method := stakingprecompile.StakingFunctionValidators
testCases := []struct {
name string
malleate func() []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func() []byte {
input, err := s.abi.Pack(
method,
"",
query.PageRequest{
Limit: 10,
CountTotal: true,
},
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
validators := out[0].([]stakingprecompile.Validator)
paginationResult := out[1].(stakingprecompile.PageResponse)
s.Assert().EqualValues(3, len(validators))
s.Assert().EqualValues(3, paginationResult.Total)
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
s.AddDelegation(s.signerOne.HexAddr, s.signerTwo.HexAddr, sdk.NewIntFromUint64(1000000))
bz, err := s.runTx(tc.malleate(), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestValidator() {
method := stakingprecompile.StakingFunctionValidator
testCases := []struct {
name string
malleate func(operatorAddress string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(operatorAddress string) []byte {
input, err := s.abi.Pack(
method,
operatorAddress,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
validator := out[0].(stakingprecompile.Validator)
s.Require().EqualValues(common.HexToAddress(validator.OperatorAddress), common.BytesToAddress(operatorAddress.Bytes()))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(operatorAddress.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestValidatorDelegations() {
method := stakingprecompile.StakingFunctionValidatorDelegations
testCases := []struct {
name string
malleate func(operatorAddress string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(operatorAddress string) []byte {
input, err := s.abi.Pack(
method,
operatorAddress,
query.PageRequest{
Limit: 10,
CountTotal: true,
},
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
delegations := out[0].([]stakingprecompile.DelegationResponse)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
s.Require().EqualValues(len(delegations), len(d))
// jsonData, _ := json.MarshalIndent(delegations, "", " ")
// fmt.Printf("delegations: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(operatorAddress.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestValidatorUnbondingDelegations() {
method := stakingprecompile.StakingFunctionValidatorUnbondingDelegations
testCases := []struct {
name string
malleate func(operatorAddress string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(operatorAddress string) []byte {
input, err := s.abi.Pack(
method,
operatorAddress,
query.PageRequest{
Limit: 10,
CountTotal: true,
},
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
unbonding := out[0].([]stakingprecompile.UnbondingDelegation)
s.Require().EqualValues(len(unbonding), 1)
// jsonData, _ := json.MarshalIndent(unbonding, "", " ")
// fmt.Printf("delegations: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
delAddr, err := sdk.AccAddressFromBech32(d[0].DelegatorAddress)
s.Require().NoError(err)
_, err = s.stakingKeeper.Undelegate(s.Ctx, delAddr, operatorAddress, sdk.NewDec(1))
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(operatorAddress.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestDelegation() {
method := stakingprecompile.StakingFunctionDelegation
testCases := []struct {
name string
malleate func(delAddr, valAddr string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(delAddr, valAddr string) []byte {
input, err := s.abi.Pack(
method,
delAddr,
valAddr,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
d := out[0].(stakingprecompile.Delegation)
b := out[1].(*big.Int)
_ = d
_ = b
/*
jsonData, _ := json.MarshalIndent(d, "", " ")
fmt.Printf("delegation: %s\n", string(jsonData))
fmt.Printf("balance: %v\n", b)
*/
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
delAddr, err := sdk.AccAddressFromBech32(d[0].DelegatorAddress)
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(delAddr.String(), operatorAddress.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestUnbondingDelegation() {
method := stakingprecompile.StakingFunctionUnbondingDelegation
testCases := []struct {
name string
malleate func(delAddr, valAddr string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(delAddr, valAddr string) []byte {
input, err := s.abi.Pack(
method,
delAddr,
valAddr,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
u := out[0].(stakingprecompile.UnbondingDelegation)
_ = u
// jsonData, _ := json.MarshalIndent(u, "", " ")
// fmt.Printf("delegation: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
delAddr, err := sdk.AccAddressFromBech32(d[0].DelegatorAddress)
s.Require().NoError(err)
_, err = s.stakingKeeper.Undelegate(s.Ctx, delAddr, operatorAddress, sdk.NewDec(1))
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(delAddr.String(), operatorAddress.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestDelegatorDelegations() {
method := stakingprecompile.StakingFunctionDelegatorDelegations
testCases := []struct {
name string
malleate func(delAddr string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(delAddr string) []byte {
input, err := s.abi.Pack(
method,
delAddr,
query.PageRequest{
Limit: 10,
CountTotal: true,
},
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
d := out[0].([]stakingprecompile.DelegationResponse)
paginationResult := out[1].(stakingprecompile.PageResponse)
s.Assert().EqualValues(1, len(d))
s.Assert().EqualValues(1, paginationResult.Total)
// jsonData, _ := json.MarshalIndent(d, "", " ")
// fmt.Printf("delegation: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
delAddr, err := sdk.AccAddressFromBech32(d[0].DelegatorAddress)
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(delAddr.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestDelegatorUnbondingDelegations() {
method := stakingprecompile.StakingFunctionDelegatorUnbondingDelegations
testCases := []struct {
name string
malleate func(delAddr string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(delAddr string) []byte {
input, err := s.abi.Pack(
method,
delAddr,
query.PageRequest{
Limit: 10,
CountTotal: true,
},
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
d := out[0].([]stakingprecompile.UnbondingDelegation)
paginationResult := out[1].(stakingprecompile.PageResponse)
s.Assert().EqualValues(1, len(d))
s.Assert().EqualValues(1, paginationResult.Total)
// jsonData, _ := json.MarshalIndent(d, "", " ")
// fmt.Printf("delegation: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
delAddr, err := sdk.AccAddressFromBech32(d[0].DelegatorAddress)
s.Require().NoError(err)
_, err = s.stakingKeeper.Undelegate(s.Ctx, delAddr, operatorAddress, sdk.NewDec(1))
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(delAddr.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestRedelegations() {
method := stakingprecompile.StakingFunctionRedelegations
testCases := []struct {
name string
malleate func(delAddr, srcValAddr, dstValAddr string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(delAddr, srcValAddr, dstValAddr string) []byte {
input, err := s.abi.Pack(
method,
delAddr,
srcValAddr,
dstValAddr,
query.PageRequest{
Limit: 10,
CountTotal: true,
},
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
d := out[0].([]stakingprecompile.RedelegationResponse)
paginationResult := out[1].(stakingprecompile.PageResponse)
s.Assert().EqualValues(1, len(d))
s.Assert().EqualValues(1, paginationResult.Total)
// jsonData, _ := json.MarshalIndent(d, "", " ")
// fmt.Printf("redelegations: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
delAddr, err := sdk.AccAddressFromBech32(d[0].DelegatorAddress)
s.Require().NoError(err)
// setup redelegations
s.setupValidator(s.signerOne)
_, err = s.stakingKeeper.BeginRedelegation(s.Ctx, delAddr, operatorAddress, s.signerOne.ValAddr, sdk.NewDec(1))
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(delAddr.String(), operatorAddress.String(), s.signerOne.ValAddr.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestDelegatorValidators() {
method := stakingprecompile.StakingFunctionDelegatorValidators
testCases := []struct {
name string
malleate func(delAddr string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(delAddr string) []byte {
input, err := s.abi.Pack(
method,
delAddr,
query.PageRequest{
Limit: 10,
CountTotal: true,
},
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
v := out[0].([]stakingprecompile.Validator)
paginationResult := out[1].(stakingprecompile.PageResponse)
s.Assert().EqualValues(1, len(v))
s.Assert().EqualValues(1, paginationResult.Total)
// jsonData, _ := json.MarshalIndent(v, "", " ")
// fmt.Printf("validators: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
delAddr, err := sdk.AccAddressFromBech32(d[0].DelegatorAddress)
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(delAddr.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestDelegatorValidator() {
method := stakingprecompile.StakingFunctionDelegatorValidator
testCases := []struct {
name string
malleate func(delAddr, valAddr string) []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func(delAddr, valAddr string) []byte {
input, err := s.abi.Pack(
method,
delAddr,
valAddr,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
v := out[0].(stakingprecompile.Validator)
_ = v
// jsonData, _ := json.MarshalIndent(v, "", " ")
// fmt.Printf("validators: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
d := s.stakingKeeper.GetValidatorDelegations(s.Ctx, operatorAddress)
delAddr, err := sdk.AccAddressFromBech32(d[0].DelegatorAddress)
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(delAddr.String(), operatorAddress.String()), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestPool() {
method := stakingprecompile.StakingFunctionPool
testCases := []struct {
name string
malleate func() []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func() []byte {
input, err := s.abi.Pack(
method,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
bonded := out[0].(*big.Int)
unbonded := out[0].(*big.Int)
s.Assert().Equal(bonded.Int64(), int64(0))
s.Assert().Equal(unbonded.Int64(), int64(0))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
bz, err := s.runTx(tc.malleate(), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}
func (s *StakingTestSuite) TestParams() {
method := stakingprecompile.StakingFunctionParams
testCases := []struct {
name string
malleate func() []byte
postCheck func(bz []byte)
gas uint64
expErr bool
errContains string
}{
{
"success",
func() []byte {
input, err := s.abi.Pack(
method,
)
s.Assert().NoError(err)
return input
},
func(data []byte) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
params := out[0].(stakingprecompile.Params)
_ = params
// jsonData, _ := json.MarshalIndent(params, "", " ")
// fmt.Printf("params: %s\n", string(jsonData))
},
100000,
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
bz, err := s.runTx(tc.malleate(), s.signerOne, 10000000)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.errContains)
} else {
s.Require().NoError(err)
s.Require().NotNil(bz)
tc.postCheck(bz)
}
})
}
}

View File

@ -0,0 +1,148 @@
package staking
import (
"fmt"
"strings"
precopmiles_common "github.com/0glabs/0g-chain/precompiles/common"
"github.com/cosmos/cosmos-sdk/store/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/evmos/ethermint/x/evm/statedb"
)
const (
PrecompileAddress = "0x0000000000000000000000000000000000001001"
// txs
StakingFunctionCreateValidator = "createValidator"
StakingFunctionEditValidator = "editValidator"
StakingFunctionDelegate = "delegate"
StakingFunctionBeginRedelegate = "beginRedelegate"
StakingFunctionUndelegate = "undelegate"
StakingFunctionCancelUnbondingDelegation = "cancelUnbondingDelegation"
// queries
StakingFunctionValidators = "validators"
StakingFunctionValidator = "validator"
StakingFunctionValidatorDelegations = "validatorDelegations"
StakingFunctionValidatorUnbondingDelegations = "validatorUnbondingDelegations"
StakingFunctionDelegation = "delegation"
StakingFunctionUnbondingDelegation = "unbondingDelegation"
StakingFunctionDelegatorDelegations = "delegatorDelegations"
StakingFunctionDelegatorUnbondingDelegations = "delegatorUnbondingDelegations"
StakingFunctionRedelegations = "redelegations"
StakingFunctionDelegatorValidators = "delegatorValidators"
StakingFunctionDelegatorValidator = "delegatorValidator"
StakingFunctionPool = "pool"
StakingFunctionParams = "params"
)
var _ vm.PrecompiledContract = &StakingPrecompile{}
type StakingPrecompile struct {
abi abi.ABI
stakingKeeper *stakingkeeper.Keeper
}
func NewStakingPrecompile(stakingKeeper *stakingkeeper.Keeper) (*StakingPrecompile, error) {
abi, err := abi.JSON(strings.NewReader(StakingABI))
if err != nil {
return nil, err
}
return &StakingPrecompile{
abi: abi,
stakingKeeper: stakingKeeper,
}, nil
}
// Address implements vm.PrecompiledContract.
func (s *StakingPrecompile) Address() common.Address {
return common.HexToAddress(PrecompileAddress)
}
// RequiredGas implements vm.PrecompiledContract.
func (s *StakingPrecompile) RequiredGas(input []byte) uint64 {
return 0
}
// Run implements vm.PrecompiledContract.
func (s *StakingPrecompile) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([]byte, error) {
// parse input
if len(contract.Input) < 4 {
return nil, vm.ErrExecutionReverted
}
method, err := s.abi.MethodById(contract.Input[:4])
if err != nil {
return nil, vm.ErrExecutionReverted
}
args, err := method.Inputs.Unpack(contract.Input[4:])
if err != nil {
return nil, err
}
// get state db and context
stateDB, ok := evm.StateDB.(*statedb.StateDB)
if !ok {
return nil, fmt.Errorf(precopmiles_common.ErrGetStateDB)
}
ctx := stateDB.GetContext()
// reset gas config
ctx = ctx.WithKVGasConfig(types.KVGasConfig())
initialGas := ctx.GasMeter().GasConsumed()
var bz []byte
switch method.Name {
// queries
case StakingFunctionValidators:
bz, err = s.Validators(ctx, evm, method, args)
case StakingFunctionValidator:
bz, err = s.Validator(ctx, evm, method, args)
case StakingFunctionValidatorDelegations:
bz, err = s.ValidatorDelegations(ctx, evm, method, args)
case StakingFunctionValidatorUnbondingDelegations:
bz, err = s.ValidatorUnbondingDelegations(ctx, evm, method, args)
case StakingFunctionDelegation:
bz, err = s.Delegation(ctx, evm, method, args)
case StakingFunctionUnbondingDelegation:
bz, err = s.UnbondingDelegation(ctx, evm, method, args)
case StakingFunctionDelegatorDelegations:
bz, err = s.DelegatorDelegations(ctx, evm, method, args)
case StakingFunctionDelegatorUnbondingDelegations:
bz, err = s.DelegatorUnbondingDelegations(ctx, evm, method, args)
case StakingFunctionRedelegations:
bz, err = s.Redelegations(ctx, evm, method, args)
case StakingFunctionDelegatorValidators:
bz, err = s.DelegatorValidators(ctx, evm, method, args)
case StakingFunctionDelegatorValidator:
bz, err = s.DelegatorValidator(ctx, evm, method, args)
case StakingFunctionPool:
bz, err = s.Pool(ctx, evm, method, args)
case StakingFunctionParams:
bz, err = s.Params(ctx, evm, method, args)
// txs
case StakingFunctionCreateValidator:
bz, err = s.CreateValidator(ctx, evm, stateDB, contract, method, args)
case StakingFunctionEditValidator:
bz, err = s.EditValidator(ctx, evm, stateDB, contract, method, args)
case StakingFunctionDelegate:
bz, err = s.Delegate(ctx, evm, stateDB, contract, method, args)
case StakingFunctionBeginRedelegate:
bz, err = s.BeginRedelegate(ctx, evm, stateDB, contract, method, args)
case StakingFunctionUndelegate:
bz, err = s.Undelegate(ctx, evm, stateDB, contract, method, args)
case StakingFunctionCancelUnbondingDelegation:
bz, err = s.CancelUnbondingDelegation(ctx, evm, stateDB, contract, method, args)
}
if err != nil {
return nil, err
}
cost := ctx.GasMeter().GasConsumed() - initialGas
if !contract.UseGas(cost) {
return nil, vm.ErrOutOfGas
}
return bz, nil
}

View File

@ -0,0 +1,145 @@
package staking_test
import (
"errors"
"math/big"
"strings"
"testing"
"cosmossdk.io/math"
stakingprecompile "github.com/0glabs/0g-chain/precompiles/staking"
"github.com/0glabs/0g-chain/precompiles/testutil"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
sdk "github.com/cosmos/cosmos-sdk/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"
"github.com/ethereum/go-ethereum/core/vm"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"github.com/stretchr/testify/suite"
)
type StakingTestSuite struct {
testutil.PrecompileTestSuite
abi abi.ABI
addr common.Address
staking *stakingprecompile.StakingPrecompile
stakingKeeper *stakingkeeper.Keeper
signerOne *testutil.TestSigner
signerTwo *testutil.TestSigner
}
func (suite *StakingTestSuite) SetupTest() {
suite.PrecompileTestSuite.SetupTest()
suite.stakingKeeper = suite.App.GetStakingKeeper()
suite.addr = common.HexToAddress(stakingprecompile.PrecompileAddress)
precompiles := suite.EvmKeeper.GetPrecompiles()
precompile, ok := precompiles[suite.addr]
suite.Assert().EqualValues(ok, true)
suite.staking = precompile.(*stakingprecompile.StakingPrecompile)
suite.signerOne = suite.GenSigner()
suite.signerTwo = suite.GenSigner()
abi, err := abi.JSON(strings.NewReader(stakingprecompile.StakingABI))
suite.Assert().NoError(err)
suite.abi = abi
}
func (suite *StakingTestSuite) AddDelegation(from string, to string, amount math.Int) {
accAddr, err := sdk.AccAddressFromHexUnsafe(from)
suite.Require().NoError(err)
valAddr, err := sdk.ValAddressFromHex(to)
suite.Require().NoError(err)
validator, found := suite.StakingKeeper.GetValidator(suite.Ctx, valAddr)
if !found {
consPriv := ed25519.GenPrivKey()
newValidator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{})
suite.Require().NoError(err)
validator = newValidator
}
validator.Tokens = validator.Tokens.Add(amount)
validator.DelegatorShares = validator.DelegatorShares.Add(amount.ToLegacyDec())
suite.StakingKeeper.SetValidator(suite.Ctx, validator)
bonded := suite.stakingKeeper.GetDelegatorBonded(suite.Ctx, accAddr)
suite.StakingKeeper.SetDelegation(suite.Ctx, stakingtypes.Delegation{
DelegatorAddress: accAddr.String(),
ValidatorAddress: valAddr.String(),
Shares: bonded.Add(amount).ToLegacyDec(),
})
}
func (suite *StakingTestSuite) setupValidator(signer *testutil.TestSigner) {
method := stakingprecompile.StakingFunctionCreateValidator
description := stakingprecompile.Description{
Moniker: "test node",
Identity: "test node identity",
Website: "http://test.node.com",
SecurityContact: "test node security contract",
Details: "test node details",
}
commission := stakingprecompile.CommissionRates{
Rate: math.LegacyOneDec().BigInt(),
MaxRate: math.LegacyOneDec().BigInt(),
MaxChangeRate: math.LegacyOneDec().BigInt(),
}
minSelfDelegation := big.NewInt(1)
pubkey := "eh/aR8BGUBIYI/Ust0NVBxZafLDAm7344F9dKzZU+7g="
value := big.NewInt(100000000)
input, err := suite.abi.Pack(
method,
description,
commission,
minSelfDelegation,
pubkey,
value,
)
suite.Assert().NoError(err)
_, err = suite.runTx(input, signer, 10000000)
suite.Assert().NoError(err)
_, err = suite.stakingKeeper.ApplyAndReturnValidatorSetUpdates(suite.Ctx)
suite.Assert().NoError(err)
}
func (suite *StakingTestSuite) firstBondedValidator() (sdk.ValAddress, error) {
validators := suite.stakingKeeper.GetValidators(suite.Ctx, 10)
for _, v := range validators {
if v.IsBonded() {
return sdk.ValAddressFromBech32(v.OperatorAddress)
}
}
return nil, errors.New("no bonded validator")
}
func (suite *StakingTestSuite) runTx(input []byte, signer *testutil.TestSigner, gas uint64) ([]byte, error) {
contract := vm.NewPrecompile(vm.AccountRef(signer.Addr), vm.AccountRef(suite.addr), big.NewInt(0), gas)
contract.Input = input
msgEthereumTx := evmtypes.NewTx(suite.EvmKeeper.ChainID(), 0, &suite.addr, big.NewInt(0), gas, big.NewInt(0), big.NewInt(0), big.NewInt(0), input, nil)
msgEthereumTx.From = signer.HexAddr
err := msgEthereumTx.Sign(suite.EthSigner, signer.Signer)
suite.Assert().NoError(err, "failed to sign Ethereum message")
proposerAddress := suite.Ctx.BlockHeader().ProposerAddress
cfg, err := suite.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, suite.EvmKeeper.ChainID())
suite.Assert().NoError(err, "failed to instantiate EVM config")
msg, err := msgEthereumTx.AsMessage(suite.EthSigner, big.NewInt(0))
suite.Assert().NoError(err, "failed to instantiate Ethereum message")
evm := suite.EvmKeeper.NewEVM(suite.Ctx, msg, cfg, nil, suite.Statedb)
precompiles := suite.EvmKeeper.GetPrecompiles()
evm.WithPrecompiles(precompiles, []common.Address{suite.addr})
return suite.staking.Run(evm, contract, false)
}
func TestKeeperSuite(t *testing.T) {
suite.Run(t, new(StakingTestSuite))
}

163
precompiles/staking/tx.go Normal file
View File

@ -0,0 +1,163 @@
package staking
import (
"fmt"
"math/big"
precopmiles_common "github.com/0glabs/0g-chain/precompiles/common"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/evmos/ethermint/x/evm/statedb"
)
func (s *StakingPrecompile) CreateValidator(
ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgCreateValidator(args, evm.Origin, s.stakingKeeper.BondDenom(ctx))
if err != nil {
return nil, err
}
// validation
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute
_, err = stakingkeeper.NewMsgServerImpl(s.stakingKeeper).CreateValidator(ctx, msg)
if err != nil {
return nil, err
}
// emit events
return method.Outputs.Pack()
}
func (s *StakingPrecompile) EditValidator(
ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgEditValidator(args, evm.Origin)
if err != nil {
return nil, err
}
// validation
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute
_, err = stakingkeeper.NewMsgServerImpl(s.stakingKeeper).EditValidator(ctx, msg)
if err != nil {
return nil, err
}
// emit events
return method.Outputs.Pack()
}
func (s *StakingPrecompile) Delegate(
ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgDelegate(args, evm.Origin, s.stakingKeeper.BondDenom(ctx))
if err != nil {
return nil, err
}
// validation
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute
_, err = stakingkeeper.NewMsgServerImpl(s.stakingKeeper).Delegate(ctx, msg)
if err != nil {
return nil, err
}
// emit events
return method.Outputs.Pack()
}
func (s *StakingPrecompile) BeginRedelegate(
ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgBeginRedelegate(args, evm.Origin, s.stakingKeeper.BondDenom(ctx))
if err != nil {
return nil, err
}
// validation
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute
response, err := stakingkeeper.NewMsgServerImpl(s.stakingKeeper).BeginRedelegate(ctx, msg)
if err != nil {
return nil, err
}
// emit events
return method.Outputs.Pack(big.NewInt(response.CompletionTime.UTC().Unix()))
}
func (s *StakingPrecompile) Undelegate(
ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgUndelegate(args, evm.Origin, s.stakingKeeper.BondDenom(ctx))
if err != nil {
return nil, err
}
// validation
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute
response, err := stakingkeeper.NewMsgServerImpl(s.stakingKeeper).Undelegate(ctx, msg)
if err != nil {
return nil, err
}
// emit events
return method.Outputs.Pack(big.NewInt(response.CompletionTime.UTC().Unix()))
}
func (s *StakingPrecompile) CancelUnbondingDelegation(
ctx sdk.Context,
evm *vm.EVM,
stateDB *statedb.StateDB,
contract *vm.Contract,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, err := NewMsgCancelUnbondingDelegation(args, evm.Origin, s.stakingKeeper.BondDenom(ctx))
if err != nil {
return nil, err
}
// validation
if contract.CallerAddress != evm.Origin {
return nil, fmt.Errorf(precopmiles_common.ErrSenderNotOrigin)
}
// execute
_, err = stakingkeeper.NewMsgServerImpl(s.stakingKeeper).CancelUnbondingDelegation(ctx, msg)
if err != nil {
return nil, err
}
// emit events
return method.Outputs.Pack()
}

View File

@ -0,0 +1,485 @@
package staking_test
import (
"encoding/base64"
"math/big"
"time"
"cosmossdk.io/math"
stakingprecompile "github.com/0glabs/0g-chain/precompiles/staking"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/evmos/ethermint/x/evm/statedb"
)
func (s *StakingTestSuite) TestCreateValidator() {
method := stakingprecompile.StakingFunctionCreateValidator
description := stakingprecompile.Description{
Moniker: "test node",
Identity: "test node identity",
Website: "http://test.node.com",
SecurityContact: "test node security contract",
Details: "test node details",
}
commission := stakingprecompile.CommissionRates{
Rate: math.LegacyOneDec().BigInt(),
MaxRate: math.LegacyOneDec().BigInt(),
MaxChangeRate: math.LegacyOneDec().BigInt(),
}
minSelfDelegation := big.NewInt(1)
pubkey := "eh/aR8BGUBIYI/Ust0NVBxZafLDAm7344F9dKzZU+7g="
value := big.NewInt(100000000)
testCases := []struct {
name string
malleate func() []byte
gas uint64
callerAddress *common.Address
postCheck func(data []byte)
expError bool
errContains string
}{
{
"fail - ErrPubKeyInvalidLength",
func() []byte {
input, err := s.abi.Pack(
method,
description,
commission,
minSelfDelegation,
s.signerOne.HexAddr,
value,
)
s.Assert().NoError(err)
return input
},
200000,
nil,
func([]byte) {},
true,
stakingprecompile.ErrPubKeyInvalidLength,
},
{
"success",
func() []byte {
input, err := s.abi.Pack(
method,
description,
commission,
minSelfDelegation,
pubkey,
value,
)
s.Assert().NoError(err)
return input
},
200000,
nil,
func(data []byte) {},
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
bz, err := s.runTx(tc.malleate(), s.signerOne, 10000000)
s.stakingKeeper.ApplyAndReturnValidatorSetUpdates(s.Ctx)
if tc.expError {
s.Require().ErrorContains(err, tc.errContains)
s.Require().Empty(bz)
} else {
s.Require().NoError(err)
// query the validator in the staking keeper
validator := s.StakingKeeper.Validator(s.Ctx, s.signerOne.ValAddr)
s.Require().NoError(err)
s.Require().NotNil(validator, "expected validator not to be nil")
tc.postCheck(bz)
isBonded := validator.IsBonded()
s.Require().Equal(true, isBonded, "expected validator bonded to be %t; got %t", true, isBonded)
consPubKey, err := validator.ConsPubKey()
s.Require().NoError(err)
consPubKeyBase64 := base64.StdEncoding.EncodeToString(consPubKey.Bytes())
s.Require().Equal(pubkey, consPubKeyBase64, "expected validator pubkey to be %s; got %s", pubkey, consPubKeyBase64)
operator := validator.GetOperator()
s.Require().Equal(s.signerOne.ValAddr, operator, "expected validator operator to be %s; got %s", s.signerOne.ValAddr, operator)
commissionRate := validator.GetCommission()
s.Require().Equal(commission.Rate.String(), commissionRate.BigInt().String(), "expected validator commission rate to be %s; got %s", commission.Rate.String(), commissionRate.String())
valMinSelfDelegation := validator.GetMinSelfDelegation()
s.Require().Equal(minSelfDelegation.String(), valMinSelfDelegation.String(), "expected validator min self delegation to be %s; got %s", minSelfDelegation.String(), valMinSelfDelegation.String())
moniker := validator.GetMoniker()
s.Require().Equal(description.Moniker, moniker, "expected validator moniker to be %s; got %s", description.Moniker, moniker)
jailed := validator.IsJailed()
s.Require().Equal(false, jailed, "expected validator jailed to be %t; got %t", false, jailed)
}
})
}
}
func (s *StakingTestSuite) TestEditValidator() {
method := stakingprecompile.StakingFunctionEditValidator
description := stakingprecompile.Description{
Moniker: "test node",
Identity: "test node identity",
Website: "http://test.node.com",
SecurityContact: "test node security contract",
Details: "test node details",
}
newRate := math.LegacyOneDec().BigInt()
newRate.Div(newRate, big.NewInt(2))
minSelfDelegation := big.NewInt(2)
testCases := []struct {
name string
malleate func() []byte
gas uint64
callerAddress *common.Address
postCheck func(data []byte)
expError bool
errContains string
}{
{
"success",
func() []byte {
input, err := s.abi.Pack(
method,
description,
stakingprecompile.NullableUint{
IsNull: false,
Value: newRate,
},
stakingprecompile.NullableUint{
IsNull: true,
Value: math.LegacyOneDec().BigInt(),
},
)
s.Assert().NoError(err)
return input
},
200000,
nil,
func(data []byte) {},
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
s.setupValidator(s.signerOne)
// move block time forward
s.Ctx = s.Ctx.WithBlockTime(time.Now().Add(time.Hour * 100))
s.Statedb = statedb.New(s.Ctx, s.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(s.Ctx.HeaderHash().Bytes())))
bz, err := s.runTx(tc.malleate(), s.signerOne, 10000000)
if tc.expError {
s.Require().ErrorContains(err, tc.errContains)
s.Require().Empty(bz)
} else {
s.Require().NoError(err)
// query the validator in the staking keeper
validator := s.StakingKeeper.Validator(s.Ctx, s.signerOne.ValAddr)
s.Require().NoError(err)
s.Require().NotNil(validator, "expected validator not to be nil")
tc.postCheck(bz)
isBonded := validator.IsBonded()
s.Require().Equal(true, isBonded, "expected validator bonded to be %t; got %t", false, isBonded)
operator := validator.GetOperator()
s.Require().Equal(s.signerOne.ValAddr, operator, "expected validator operator to be %s; got %s", s.signerOne.ValAddr, operator)
commissionRate := validator.GetCommission()
s.Require().Equal(newRate.String(), commissionRate.BigInt().String(), "expected validator commission rate to be %s; got %s", newRate.String(), commissionRate.String())
valMinSelfDelegation := validator.GetMinSelfDelegation()
s.Require().Equal(big.NewInt(1).String(), valMinSelfDelegation.String(), "expected validator min self delegation to be %s; got %s", minSelfDelegation.String(), valMinSelfDelegation.String())
moniker := validator.GetMoniker()
s.Require().Equal(description.Moniker, moniker, "expected validator moniker to be %s; got %s", description.Moniker, moniker)
jailed := validator.IsJailed()
s.Require().Equal(false, jailed, "expected validator jailed to be %t; got %t", false, jailed)
}
})
}
}
func (s *StakingTestSuite) TestDelegate() {
method := stakingprecompile.StakingFunctionDelegate
testCases := []struct {
name string
malleate func(valAddr string) []byte
gas uint64
callerAddress *common.Address
postCheck func(valAddr sdk.ValAddress)
expError bool
errContains string
}{
{
"success",
func(valAddr string) []byte {
input, err := s.abi.Pack(
method,
valAddr,
big.NewInt(1000000),
)
s.Assert().NoError(err)
return input
},
200000,
nil,
func(valAddr sdk.ValAddress) {
d, found := s.stakingKeeper.GetDelegation(s.Ctx, s.signerOne.AccAddr, valAddr)
s.Assert().EqualValues(found, true)
s.Assert().EqualValues(d.ValidatorAddress, valAddr.String())
s.Assert().EqualValues(d.DelegatorAddress, s.signerOne.AccAddr.String())
// jsonData, _ := json.MarshalIndent(d, "", " ")
// fmt.Printf("delegation: %s\n", string(jsonData))
},
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
bz, err := s.runTx(tc.malleate(operatorAddress.String()), s.signerOne, 10000000)
if tc.expError {
s.Require().ErrorContains(err, tc.errContains)
s.Require().Empty(bz)
} else {
s.Require().NoError(err)
tc.postCheck(operatorAddress)
}
})
}
}
func (s *StakingTestSuite) TestBeginRedelegate() {
method := stakingprecompile.StakingFunctionBeginRedelegate
testCases := []struct {
name string
malleate func(srcAddr, dstAddr string) []byte
gas uint64
callerAddress *common.Address
postCheck func(data []byte, srcAddr, dstAddr sdk.ValAddress)
expError bool
errContains string
}{
{
"success",
func(srcAddr, dstAddr string) []byte {
input, err := s.abi.Pack(
method,
srcAddr,
dstAddr,
big.NewInt(1000000),
)
s.Assert().NoError(err)
return input
},
200000,
nil,
func(data []byte, srcAddr, dstAddr sdk.ValAddress) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
d, found := s.stakingKeeper.GetRedelegation(s.Ctx, s.signerOne.AccAddr, srcAddr, dstAddr)
s.Assert().EqualValues(found, true)
s.Assert().EqualValues(d.DelegatorAddress, s.signerOne.AccAddr.String())
s.Assert().EqualValues(d.ValidatorSrcAddress, srcAddr.String())
s.Assert().EqualValues(d.ValidatorDstAddress, dstAddr.String())
completionTime := out[0].(*big.Int)
params := s.stakingKeeper.GetParams(s.Ctx)
s.Assert().EqualValues(completionTime.Int64(), s.Ctx.BlockHeader().Time.Add(params.UnbondingTime).UTC().Unix())
// jsonData, _ := json.MarshalIndent(d, "", " ")
// fmt.Printf("redelegation: %s\n", string(jsonData))
},
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
operatorAddress, err := s.firstBondedValidator()
s.Require().NoError(err)
// move block time forward
s.Ctx = s.Ctx.WithBlockTime(time.Now().Add(time.Hour * 100))
s.Statedb = statedb.New(s.Ctx, s.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(s.Ctx.HeaderHash().Bytes())))
s.setupValidator(s.signerOne)
bz, err := s.runTx(tc.malleate(s.signerOne.ValAddr.String(), operatorAddress.String()), s.signerOne, 10000000)
if tc.expError {
s.Require().ErrorContains(err, tc.errContains)
s.Require().Empty(bz)
} else {
s.Require().NoError(err)
tc.postCheck(bz, s.signerOne.ValAddr, operatorAddress)
}
})
}
}
func (s *StakingTestSuite) TestUndelegate() {
method := stakingprecompile.StakingFunctionUndelegate
testCases := []struct {
name string
malleate func(valAddr string) []byte
gas uint64
callerAddress *common.Address
postCheck func(data []byte, valAddr sdk.ValAddress)
expError bool
errContains string
}{
{
"success",
func(valAddr string) []byte {
input, err := s.abi.Pack(
method,
valAddr,
big.NewInt(1000000),
)
s.Assert().NoError(err)
return input
},
200000,
nil,
func(data []byte, valAddr sdk.ValAddress) {
out, err := s.abi.Methods[method].Outputs.Unpack(data)
s.Require().NoError(err, "failed to unpack output")
d, found := s.stakingKeeper.GetUnbondingDelegation(s.Ctx, s.signerOne.AccAddr, valAddr)
s.Assert().EqualValues(found, true)
s.Assert().EqualValues(d.DelegatorAddress, s.signerOne.AccAddr.String())
s.Assert().EqualValues(d.ValidatorAddress, valAddr.String())
completionTime := out[0].(*big.Int)
params := s.stakingKeeper.GetParams(s.Ctx)
s.Assert().EqualValues(completionTime.Int64(), s.Ctx.BlockHeader().Time.Add(params.UnbondingTime).UTC().Unix())
// jsonData, _ := json.MarshalIndent(d, "", " ")
// fmt.Printf("redelegation: %s\n", string(jsonData))
},
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
// move block time forward
s.Ctx = s.Ctx.WithBlockTime(time.Now().Add(time.Hour * 100))
s.Statedb = statedb.New(s.Ctx, s.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(s.Ctx.HeaderHash().Bytes())))
s.setupValidator(s.signerOne)
bz, err := s.runTx(tc.malleate(s.signerOne.ValAddr.String()), s.signerOne, 10000000)
if tc.expError {
s.Require().ErrorContains(err, tc.errContains)
s.Require().Empty(bz)
} else {
s.Require().NoError(err)
tc.postCheck(bz, s.signerOne.ValAddr)
}
})
}
}
func (s *StakingTestSuite) TestCancelUnbondingDelegation() {
method := stakingprecompile.StakingFunctionCancelUnbondingDelegation
testCases := []struct {
name string
malleate func(valAddr string, height *big.Int) []byte
gas uint64
callerAddress *common.Address
postCheck func(valAddr sdk.ValAddress)
expError bool
errContains string
}{
{
"success",
func(valAddr string, height *big.Int) []byte {
input, err := s.abi.Pack(
method,
valAddr,
big.NewInt(1),
height,
)
s.Assert().NoError(err)
return input
},
200000,
nil,
func(valAddr sdk.ValAddress) {
_, found := s.stakingKeeper.GetUnbondingDelegation(s.Ctx, s.signerOne.AccAddr, valAddr)
s.Assert().EqualValues(found, false)
// jsonData, _ := json.MarshalIndent(d, "", " ")
// fmt.Printf("redelegation: %s\n", string(jsonData))
},
false,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTest()
// move block time forward
s.Ctx = s.Ctx.WithBlockTime(time.Now().Add(time.Hour * 100))
s.Statedb = statedb.New(s.Ctx, s.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(s.Ctx.HeaderHash().Bytes())))
s.setupValidator(s.signerOne)
// unbond
_, err := s.stakingKeeper.Undelegate(s.Ctx, s.signerOne.AccAddr, s.signerOne.ValAddr, sdk.NewDec(1))
s.Require().NoError(err)
u, _ := s.stakingKeeper.GetUnbondingDelegation(s.Ctx, s.signerOne.AccAddr, s.signerOne.ValAddr)
height := u.Entries[0].CreationHeight
bz, err := s.runTx(tc.malleate(s.signerOne.ValAddr.String(), big.NewInt(height)), s.signerOne, 10000000)
if tc.expError {
s.Require().ErrorContains(err, tc.errContains)
s.Require().Empty(bz)
} else {
s.Require().NoError(err)
tc.postCheck(s.signerOne.ValAddr)
}
})
}
}

View File

@ -0,0 +1,602 @@
package staking
import (
"encoding/base64"
"errors"
"fmt"
"math/big"
"cosmossdk.io/math"
precopmiles_common "github.com/0glabs/0g-chain/precompiles/common"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
query "github.com/cosmos/cosmos-sdk/types/query"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/ethereum/go-ethereum/common"
)
type Commission = struct {
CommissionRates CommissionRates `json:"commissionRates"`
UpdateTime *big.Int `json:"updateTime"`
}
type CommissionRates = struct {
Rate *big.Int `json:"rate"`
MaxRate *big.Int `json:"maxRate"`
MaxChangeRate *big.Int `json:"maxChangeRate"`
}
type Delegation = struct {
DelegatorAddress string `json:"delegatorAddress"`
ValidatorAddress string `json:"validatorAddress"`
Shares *big.Int `json:"shares"`
}
type DelegationResponse = struct {
Delegation Delegation `json:"delegation"`
Balance *big.Int `json:"balance"`
}
type Description = struct {
Moniker string `json:"moniker"`
Identity string `json:"identity"`
Website string `json:"website"`
SecurityContact string `json:"securityContact"`
Details string `json:"details"`
}
type NullableUint = struct {
IsNull bool `json:"isNull"`
Value *big.Int `json:"value"`
}
type PageRequest = struct {
Key []byte `json:"key"`
Offset uint64 `json:"offset"`
Limit uint64 `json:"limit"`
CountTotal bool `json:"countTotal"`
Reverse bool `json:"reverse"`
}
type PageResponse = struct {
NextKey []byte `json:"nextKey"`
Total uint64 `json:"total"`
}
type Params = struct {
UnbondingTime int64 `json:"unbondingTime"`
MaxValidators uint32 `json:"maxValidators"`
MaxEntries uint32 `json:"maxEntries"`
HistoricalEntries uint32 `json:"historicalEntries"`
BondDenom string `json:"bondDenom"`
MinCommissionRate *big.Int `json:"minCommissionRate"`
}
type Redelegation = struct {
DelegatorAddress string `json:"delegatorAddress"`
ValidatorSrcAddress string `json:"validatorSrcAddress"`
ValidatorDstAddress string `json:"validatorDstAddress"`
Entries []RedelegationEntry `json:"entries"`
}
type RedelegationEntry = struct {
CreationHeight int64 `json:"creationHeight"`
CompletionTime int64 `json:"completionTime"`
InitialBalance *big.Int `json:"initialBalance"`
SharesDst *big.Int `json:"sharesDst"`
UnbondingId uint64 `json:"unbondingId"`
UnbondingOnHoldRefCount int64 `json:"unbondingOnHoldRefCount"`
}
type RedelegationEntryResponse = struct {
RedelegationEntry RedelegationEntry `json:"redelegationEntry"`
Balance *big.Int `json:"balance"`
}
type RedelegationResponse = struct {
Redelegation Redelegation `json:"redelegation"`
Entries []RedelegationEntryResponse `json:"entries"`
}
type UnbondingDelegation = struct {
DelegatorAddress string `json:"delegatorAddress"`
ValidatorAddress string `json:"validatorAddress"`
Entries []UnbondingDelegationEntry `json:"entries"`
}
type UnbondingDelegationEntry = struct {
CreationHeight int64 `json:"creationHeight"`
CompletionTime int64 `json:"completionTime"`
InitialBalance *big.Int `json:"initialBalance"`
Balance *big.Int `json:"balance"`
UnbondingId uint64 `json:"unbondingId"`
UnbondingOnHoldRefCount int64 `json:"unbondingOnHoldRefCount"`
}
type Validator = struct {
OperatorAddress string `json:"operatorAddress"`
ConsensusPubkey string `json:"consensusPubkey"`
Jailed bool `json:"jailed"`
Status uint8 `json:"status"`
Tokens *big.Int `json:"tokens"`
DelegatorShares *big.Int `json:"delegatorShares"`
Description Description `json:"description"`
UnbondingHeight int64 `json:"unbondingHeight"`
UnbondingTime int64 `json:"unbondingTime"`
Commission Commission `json:"commission"`
MinSelfDelegation *big.Int `json:"minSelfDelegation"`
UnbondingOnHoldRefCount int64 `json:"unbondingOnHoldRefCount"`
UnbondingIds []uint64 `json:"unbondingIds"`
}
func convertValidator(v stakingtypes.Validator) Validator {
validator := Validator{}
operatorAddress, err := sdk.ValAddressFromBech32(v.OperatorAddress)
if err != nil {
validator.OperatorAddress = v.OperatorAddress
} else {
validator.OperatorAddress = common.BytesToAddress(operatorAddress.Bytes()).String()
}
ed25519pk, ok := v.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey)
if !ok {
validator.ConsensusPubkey = v.ConsensusPubkey.String()
} else {
validator.ConsensusPubkey = base64.StdEncoding.EncodeToString(ed25519pk.Bytes())
}
validator.Jailed = v.Jailed
validator.Status = uint8(v.Status)
validator.Tokens = v.Tokens.BigInt()
validator.DelegatorShares = v.DelegatorShares.BigInt()
validator.Description = Description{
Moniker: v.Description.Moniker,
Identity: v.Description.Identity,
Website: v.Description.Website,
SecurityContact: v.Description.SecurityContact,
Details: v.Description.Details,
}
validator.UnbondingHeight = v.UnbondingHeight
validator.UnbondingTime = v.UnbondingTime.UTC().Unix()
validator.Commission = Commission{
CommissionRates: convertCommissionRates(v.Commission.CommissionRates),
UpdateTime: big.NewInt(v.Commission.UpdateTime.UTC().Unix()),
}
validator.MinSelfDelegation = v.MinSelfDelegation.BigInt()
validator.UnbondingOnHoldRefCount = v.UnbondingOnHoldRefCount
validator.UnbondingIds = v.UnbondingIds
return validator
}
func convertQueryPageRequest(pagination PageRequest) *query.PageRequest {
return &query.PageRequest{
Key: pagination.Key,
Offset: pagination.Offset,
Limit: pagination.Limit,
CountTotal: pagination.CountTotal,
Reverse: pagination.Reverse,
}
}
func convertPageResponse(pagination *query.PageResponse) PageResponse {
if pagination == nil {
return PageResponse{
NextKey: make([]byte, 0),
Total: 1,
}
}
return PageResponse{
NextKey: pagination.NextKey,
Total: pagination.Total,
}
}
func convertStakingDescription(description Description) stakingtypes.Description {
return stakingtypes.Description{
Moniker: description.Moniker,
Identity: description.Identity,
Website: description.Website,
SecurityContact: description.SecurityContact,
Details: description.Details,
}
}
func convertStakingCommissionRates(commission CommissionRates) stakingtypes.CommissionRates {
return stakingtypes.CommissionRates{
Rate: precopmiles_common.BigIntToLegacyDec(commission.Rate),
MaxRate: precopmiles_common.BigIntToLegacyDec(commission.MaxRate),
MaxChangeRate: precopmiles_common.BigIntToLegacyDec(commission.MaxChangeRate),
}
}
func convertCommissionRates(commission stakingtypes.CommissionRates) CommissionRates {
return CommissionRates{
Rate: commission.Rate.BigInt(),
MaxRate: commission.MaxRate.BigInt(),
MaxChangeRate: commission.MaxChangeRate.BigInt(),
}
}
func convertDelegation(delegation stakingtypes.Delegation) Delegation {
return Delegation{
DelegatorAddress: delegation.DelegatorAddress,
ValidatorAddress: delegation.ValidatorAddress,
Shares: delegation.Shares.BigInt(),
}
}
func convertDelegationResponse(response stakingtypes.DelegationResponse) DelegationResponse {
return DelegationResponse{
Delegation: convertDelegation(response.Delegation),
Balance: response.Balance.Amount.BigInt(),
}
}
func convertUnbondingDelegationEntry(entry stakingtypes.UnbondingDelegationEntry) UnbondingDelegationEntry {
return UnbondingDelegationEntry{
CreationHeight: entry.CreationHeight,
CompletionTime: entry.CompletionTime.UTC().Unix(),
InitialBalance: entry.InitialBalance.BigInt(),
Balance: entry.Balance.BigInt(),
UnbondingId: entry.UnbondingId,
UnbondingOnHoldRefCount: entry.UnbondingOnHoldRefCount,
}
}
func convertUnbondingDelegation(response stakingtypes.UnbondingDelegation) UnbondingDelegation {
entries := make([]UnbondingDelegationEntry, len(response.Entries))
for i, v := range response.Entries {
entries[i] = convertUnbondingDelegationEntry(v)
}
return UnbondingDelegation{
DelegatorAddress: response.DelegatorAddress,
ValidatorAddress: response.ValidatorAddress,
Entries: entries,
}
}
func convertRedelegationEntry(entry stakingtypes.RedelegationEntry) RedelegationEntry {
return RedelegationEntry{
CreationHeight: entry.CreationHeight,
CompletionTime: entry.CompletionTime.UTC().Unix(),
InitialBalance: entry.InitialBalance.BigInt(),
SharesDst: entry.SharesDst.BigInt(),
UnbondingId: entry.UnbondingId,
UnbondingOnHoldRefCount: entry.UnbondingOnHoldRefCount,
}
}
func convertRedelegation(redelegation stakingtypes.Redelegation) Redelegation {
entries := make([]RedelegationEntry, len(redelegation.Entries))
for i, v := range redelegation.Entries {
entries[i] = convertRedelegationEntry(v)
}
return Redelegation{
DelegatorAddress: redelegation.DelegatorAddress,
ValidatorSrcAddress: redelegation.ValidatorSrcAddress,
ValidatorDstAddress: redelegation.ValidatorDstAddress,
Entries: entries,
}
}
func convertRedelegationEntryResponse(response stakingtypes.RedelegationEntryResponse) RedelegationEntryResponse {
return RedelegationEntryResponse{
RedelegationEntry: convertRedelegationEntry(response.RedelegationEntry),
Balance: response.Balance.BigInt(),
}
}
func convertRedelegationResponse(response stakingtypes.RedelegationResponse) RedelegationResponse {
entries := make([]RedelegationEntryResponse, len(response.Entries))
for i, v := range response.Entries {
entries[i] = convertRedelegationEntryResponse(v)
}
return RedelegationResponse{
Redelegation: convertRedelegation(response.Redelegation),
Entries: entries,
}
}
func convertParams(params stakingtypes.Params) Params {
return Params{
UnbondingTime: int64(params.UnbondingTime.Seconds()),
MaxValidators: params.MaxValidators,
MaxEntries: params.MaxEntries,
HistoricalEntries: params.HistoricalEntries,
BondDenom: params.BondDenom,
MinCommissionRate: params.MinCommissionRate.BigInt(),
}
}
func NewMsgCreateValidator(args []interface{}, sender common.Address, denom string) (*stakingtypes.MsgCreateValidator, error) {
if len(args) != 5 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 5, len(args))
}
description := args[0].(Description)
commission := args[1].(CommissionRates)
minSelfDelegation := args[2].(*big.Int)
pkstr := args[3].(string)
bz, err := base64.StdEncoding.DecodeString(pkstr)
if err != nil {
return nil, err
}
var pk cryptotypes.PubKey
if len(bz) == ed25519.PubKeySize {
pk = &ed25519.PubKey{Key: bz}
} else {
return nil, errors.New(ErrPubKeyInvalidLength)
}
pkAny, err := codectypes.NewAnyWithValue(pk)
if err != nil {
return nil, err
}
value := args[4].(*big.Int)
msg := &stakingtypes.MsgCreateValidator{
Description: convertStakingDescription(description),
Commission: convertStakingCommissionRates(commission),
MinSelfDelegation: math.NewIntFromBigInt(minSelfDelegation),
DelegatorAddress: sdk.AccAddress(sender.Bytes()).String(),
ValidatorAddress: sdk.ValAddress(sender.Bytes()).String(),
Pubkey: pkAny,
Value: sdk.Coin{Denom: denom, Amount: math.NewIntFromBigInt(value)},
}
return msg, msg.ValidateBasic()
}
func NewMsgEditValidator(args []interface{}, sender common.Address) (*stakingtypes.MsgEditValidator, error) {
if len(args) != 3 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 3, len(args))
}
description := args[0].(Description)
commissionRateNullable := args[1].(NullableUint)
var commissionRate *sdk.Dec
if !commissionRateNullable.IsNull {
value := precopmiles_common.BigIntToLegacyDec(commissionRateNullable.Value)
commissionRate = &value
}
minSelfDelegationNullable := args[2].(NullableUint)
var minSelfDelegation *sdk.Int
if !minSelfDelegationNullable.IsNull {
value := math.NewIntFromBigInt(minSelfDelegationNullable.Value)
minSelfDelegation = &value
}
msg := &stakingtypes.MsgEditValidator{
Description: convertStakingDescription(description),
CommissionRate: commissionRate,
ValidatorAddress: sdk.ValAddress(sender.Bytes()).String(),
MinSelfDelegation: minSelfDelegation,
}
return msg, msg.ValidateBasic()
}
func NewMsgDelegate(args []interface{}, sender common.Address, denom string) (*stakingtypes.MsgDelegate, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
validatorAddress := args[0].(string)
amount := args[1].(*big.Int)
msg := &stakingtypes.MsgDelegate{
DelegatorAddress: sdk.AccAddress(sender.Bytes()).String(),
ValidatorAddress: validatorAddress,
Amount: sdk.Coin{Denom: denom, Amount: math.NewIntFromBigInt(amount)},
}
return msg, msg.ValidateBasic()
}
func NewMsgBeginRedelegate(args []interface{}, sender common.Address, denom string) (*stakingtypes.MsgBeginRedelegate, error) {
if len(args) != 3 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 3, len(args))
}
validatorSrcAddress := args[0].(string)
validatorDstAddress := args[1].(string)
amount := args[2].(*big.Int)
msg := &stakingtypes.MsgBeginRedelegate{
DelegatorAddress: sdk.AccAddress(sender.Bytes()).String(),
ValidatorSrcAddress: validatorSrcAddress,
ValidatorDstAddress: validatorDstAddress,
Amount: sdk.Coin{Denom: denom, Amount: math.NewIntFromBigInt(amount)},
}
return msg, msg.ValidateBasic()
}
func NewMsgUndelegate(args []interface{}, sender common.Address, denom string) (*stakingtypes.MsgUndelegate, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
validatorAddress := args[0].(string)
amount := args[1].(*big.Int)
msg := &stakingtypes.MsgUndelegate{
DelegatorAddress: sdk.AccAddress(sender.Bytes()).String(),
ValidatorAddress: validatorAddress,
Amount: sdk.Coin{Denom: denom, Amount: math.NewIntFromBigInt(amount)},
}
return msg, msg.ValidateBasic()
}
func NewMsgCancelUnbondingDelegation(args []interface{}, sender common.Address, denom string) (*stakingtypes.MsgCancelUnbondingDelegation, error) {
if len(args) != 3 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 3, len(args))
}
validatorAddress := args[0].(string)
amount := args[1].(*big.Int)
creationHeight := args[2].(*big.Int)
msg := &stakingtypes.MsgCancelUnbondingDelegation{
DelegatorAddress: sdk.AccAddress(sender.Bytes()).String(),
ValidatorAddress: validatorAddress,
Amount: sdk.Coin{Denom: denom, Amount: math.NewIntFromBigInt(amount)},
CreationHeight: creationHeight.Int64(),
}
return msg, msg.ValidateBasic()
}
func NewQueryValidatorsRequest(args []interface{}) (*stakingtypes.QueryValidatorsRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
status := args[0].(string)
pagination := args[1].(PageRequest)
return &stakingtypes.QueryValidatorsRequest{
Status: status,
Pagination: convertQueryPageRequest(pagination),
}, nil
}
func NewQueryValidatorRequest(args []interface{}) (*stakingtypes.QueryValidatorRequest, error) {
if len(args) != 1 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 1, len(args))
}
validatorAddress := args[0].(string)
return &stakingtypes.QueryValidatorRequest{
ValidatorAddr: validatorAddress,
}, nil
}
func NewQueryValidatorDelegationsRequest(args []interface{}) (*stakingtypes.QueryValidatorDelegationsRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
validatorAddr := args[0].(string)
pagination := args[1].(PageRequest)
return &stakingtypes.QueryValidatorDelegationsRequest{
ValidatorAddr: validatorAddr,
Pagination: convertQueryPageRequest(pagination),
}, nil
}
func NewQueryValidatorUnbondingDelegationsRequest(args []interface{}) (*stakingtypes.QueryValidatorUnbondingDelegationsRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
validatorAddr := args[0].(string)
pagination := args[1].(PageRequest)
return &stakingtypes.QueryValidatorUnbondingDelegationsRequest{
ValidatorAddr: validatorAddr,
Pagination: convertQueryPageRequest(pagination),
}, nil
}
func NewQueryDelegationRequest(args []interface{}) (*stakingtypes.QueryDelegationRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
delegatorAddr := args[0].(string)
validatorAddr := args[1].(string)
return &stakingtypes.QueryDelegationRequest{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
}, nil
}
func NewQueryUnbondingDelegationRequest(args []interface{}) (*stakingtypes.QueryUnbondingDelegationRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
delegatorAddr := args[0].(string)
validatorAddr := args[1].(string)
return &stakingtypes.QueryUnbondingDelegationRequest{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
}, nil
}
func NewQueryDelegatorDelegationsRequest(args []interface{}) (*stakingtypes.QueryDelegatorDelegationsRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
delegatorAddr := args[0].(string)
pagination := args[1].(PageRequest)
return &stakingtypes.QueryDelegatorDelegationsRequest{
DelegatorAddr: delegatorAddr,
Pagination: convertQueryPageRequest(pagination),
}, nil
}
func NewQueryDelegatorUnbondingDelegationsRequest(args []interface{}) (*stakingtypes.QueryDelegatorUnbondingDelegationsRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
delegatorAddr := args[0].(string)
pagination := args[1].(PageRequest)
return &stakingtypes.QueryDelegatorUnbondingDelegationsRequest{
DelegatorAddr: delegatorAddr,
Pagination: convertQueryPageRequest(pagination),
}, nil
}
func NewQueryRedelegationsRequest(args []interface{}) (*stakingtypes.QueryRedelegationsRequest, error) {
if len(args) != 4 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 4, len(args))
}
delegatorAddress := args[0].(string)
validatorSrcAddress := args[1].(string)
validatorDstAddress := args[2].(string)
pagination := args[3].(PageRequest)
return &stakingtypes.QueryRedelegationsRequest{
DelegatorAddr: delegatorAddress,
SrcValidatorAddr: validatorSrcAddress,
DstValidatorAddr: validatorDstAddress,
Pagination: convertQueryPageRequest(pagination),
}, nil
}
func NewQueryDelegatorValidatorsRequest(args []interface{}) (*stakingtypes.QueryDelegatorValidatorsRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
delegatorAddr := args[0].(string)
pagination := args[1].(PageRequest)
return &stakingtypes.QueryDelegatorValidatorsRequest{
DelegatorAddr: delegatorAddr,
Pagination: convertQueryPageRequest(pagination),
}, nil
}
func NewQueryDelegatorValidatorRequest(args []interface{}) (*stakingtypes.QueryDelegatorValidatorRequest, error) {
if len(args) != 2 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 2, len(args))
}
delegatorAddr := args[0].(string)
validatorAddr := args[1].(string)
return &stakingtypes.QueryDelegatorValidatorRequest{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
}, nil
}
func NewQueryPoolRequest(args []interface{}) (*stakingtypes.QueryPoolRequest, error) {
if len(args) != 0 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 0, len(args))
}
return &stakingtypes.QueryPoolRequest{}, nil
}
func NewQueryParamsRequest(args []interface{}) (*stakingtypes.QueryParamsRequest, error) {
if len(args) != 0 {
return nil, fmt.Errorf(precopmiles_common.ErrInvalidNumberOfArgs, 0, len(args))
}
return &stakingtypes.QueryParamsRequest{}, nil
}

View File

@ -1,11 +1,13 @@
package testutil package testutil
import ( import (
"math/big"
"strings" "strings"
"time"
"github.com/0glabs/0g-chain/app" "github.com/0glabs/0g-chain/app"
"github.com/0glabs/0g-chain/chaincfg" "github.com/0glabs/0g-chain/chaincfg"
dasignersprecompile "github.com/0glabs/0g-chain/precompiles/dasigners" precopmiles_common "github.com/0glabs/0g-chain/precompiles/common"
"github.com/0glabs/0g-chain/x/bep3/types" "github.com/0glabs/0g-chain/x/bep3/types"
"github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -44,15 +46,25 @@ type TestSigner struct {
HexAddr string HexAddr string
PrivKey cryptotypes.PrivKey PrivKey cryptotypes.PrivKey
Signer keyring.Signer Signer keyring.Signer
ValAddr sdk.ValAddress
AccAddr sdk.AccAddress
} }
func GenSigner() *TestSigner { func (suite *PrecompileTestSuite) GenSigner() *TestSigner {
var s TestSigner var s TestSigner
addr, priv := emtests.NewAddrKey() addr, priv := emtests.NewAddrKey()
s.PrivKey = priv s.PrivKey = priv
s.Addr = addr s.Addr = addr
s.HexAddr = dasignersprecompile.ToLowerHexWithoutPrefix(s.Addr) s.HexAddr = precopmiles_common.ToLowerHexWithoutPrefix(s.Addr)
s.Signer = emtests.NewSigner(priv) s.Signer = emtests.NewSigner(priv)
valAddr, _ := sdk.ValAddressFromHex(s.HexAddr)
accAddr, _ := sdk.AccAddressFromHexUnsafe(s.HexAddr)
s.ValAddr = valAddr
s.AccAddr = accAddr
// 10000 a0gi for test
suite.App.GetBankKeeper().MintCoins(suite.Ctx, "mint", sdk.NewCoins(chaincfg.MakeCoinForGasDenom(big.NewInt(10000000000))))
suite.App.GetBankKeeper().SendCoinsFromModuleToAccount(suite.Ctx, "mint", accAddr, sdk.NewCoins(chaincfg.MakeCoinForGasDenom(big.NewInt(10000000000))))
return &s return &s
} }
@ -70,7 +82,7 @@ func (suite *PrecompileTestSuite) SetupTest() {
hexAddr := strings.ToLower(crypto.PubkeyToAddress(key.PublicKey).Hex()[2:]) hexAddr := strings.ToLower(crypto.PubkeyToAddress(key.PublicKey).Hex()[2:])
valAddr, err := sdk.ValAddressFromHex(hexAddr) valAddr, err := sdk.ValAddressFromHex(hexAddr)
suite.Assert().NoError(err) suite.Assert().NoError(err)
suite.Ctx = suite.App.NewContext(true, tmproto.Header{Height: 1, ChainID: app.TestChainId, ProposerAddress: consAddress}) suite.Ctx = suite.App.NewContext(true, tmproto.Header{Height: 1, ChainID: app.TestChainId, ProposerAddress: consAddress, Time: time.Now()})
newValidator, err := stakingtypes.NewValidator(valAddr, privkey.PubKey(), stakingtypes.Description{}) newValidator, err := stakingtypes.NewValidator(valAddr, privkey.PubKey(), stakingtypes.Description{})
suite.Assert().NoError(err) suite.Assert().NoError(err)
err = suite.StakingKeeper.SetValidatorByConsAddr(suite.Ctx, newValidator) err = suite.StakingKeeper.SetValidatorByConsAddr(suite.Ctx, newValidator)