Skip to content

Commit

Permalink
imp(staking): implement the CreateValidator function for staking pr…
Browse files Browse the repository at this point in the history
…ecompiled contract (#2030)

* imp(staking): add create validator

* imp(precompile): implement the createValidator function of precompiled contract staking

* chore: update CHANGELOG.md

* imp(staking): remove pubkey from CreateValidator event

* imp(staking): update CreateValidator event

* imp(staking): use abi arguments pack events

* imp(staking): use json marshal unmarshal validator description and commission

* imp(staking): only allow users to directly call the StakingI contract's address which is 0x0000000000000000000000000000000000000800 to create a new validator

* imp(staking): update create validator for precompiles

* imp(staking): add CreateValidatorMethod to IsTransaction

* Update precompiles/staking/StakingI.sol

* Update precompiles/staking/abi.json

* imp(staking): update event CreateValidator change string indexed validatorAddress to address indexed validatorAddress

* imp(staking): update precompiles NewMsgCreateValidator

* imp(staking): update CreateValidator event and some optimizations

* imp(staking): update precompiles common errors

* imp(staking): update precompiles staking tx.go

---------

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
  • Loading branch information
luchenqun and fedekunze committed Nov 16, 2023
1 parent 11ba20c commit 3cdc57d
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
- (osmosis-outpost) [#2017](https://github.com/evmos/evmos/pull/2017) Refactor types, errors and precompile struct.
- (erc20) [#2023](https://github.com/evmos/evmos/pull/2023) Add tests for ERC20 precompile queries.
- (osmosis-outpost) [#2025](https://github.com/evmos/evmos/pull/2025) Use a struct to wrap parsed parameters from Solidity.
- (staking) [#2030](https://github.com/evmos/evmos/pull/2030) Implement the `CreateValidator` function for staking precompiled contract.
- (erc20) [#2037](https://github.com/evmos/evmos/pull/2037) Add IsTransactions and RequiredGas tests for the ERC20 extension.
- (bank) [#2040](https://github.com/evmos/evmos/pull/2040) Add bank extension unit tests for queries.
- (bank) [#2041](https://github.com/evmos/evmos/pull/2041) Add `supplyOf` query to bank extension.
Expand Down
4 changes: 4 additions & 0 deletions precompiles/common/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ const (
ErrNegativeAmount = "negative amount when decreasing allowance"
// ErrInvalidType is raised when the provided type is different than the expected.
ErrInvalidType = "invalid type for %s: expected %T, received %T"
// ErrInvalidDescription is raised when the input description cannot be cast to stakingtypes.Description{}.
ErrInvalidDescription = "invalid description: %v"
// ErrInvalidCommission is raised when the input commission cannot be cast to stakingtypes.CommissionRates{}.
ErrInvalidCommission = "invalid commission: %v"
)
40 changes: 40 additions & 0 deletions precompiles/staking/StakingI.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,22 @@ address constant STAKING_PRECOMPILE_ADDRESS = 0x00000000000000000000000000000000
StakingI constant STAKING_CONTRACT = StakingI(STAKING_PRECOMPILE_ADDRESS);

/// @dev Define all the available staking methods.
string constant MSG_CREATE_VALIDATOR = "/cosmos.staking.v1beta1.MsgCreateValidator";
string constant MSG_DELEGATE = "/cosmos.staking.v1beta1.MsgDelegate";
string constant MSG_UNDELEGATE = "/cosmos.staking.v1beta1.MsgUndelegate";
string constant MSG_REDELEGATE = "/cosmos.staking.v1beta1.MsgBeginRedelegate";
string constant MSG_CANCEL_UNDELEGATION = "/cosmos.staking.v1beta1.MsgCancelUnbondingDelegation";

/// @dev Defines the initial description to be used for creating
/// a validator.
struct Description {
string moniker;
string identity;
string website;
string securityContact;
string details;
}

/// @dev Defines the initial commission rates to be used for creating
/// a validator.
struct CommissionRates {
Expand Down Expand Up @@ -114,6 +125,25 @@ enum BondStatus {
/// wraps the pallet.
/// @custom:address 0x0000000000000000000000000000000000000800
interface StakingI is authorization.AuthorizationI {
/// @dev Defines a method for creating a new validator.
/// @param description The initial description
/// @param commissionRates The initial commissionRates
/// @param minSelfDelegation The validator's self declared minimum self delegation
/// @param delegatorAddress The delegator address
/// @param validatorAddress The validator address
/// @param pubkey The consensus public key of the validator
/// @param value The amount of the coin to be self delegated to the validator
/// @return success Whether or not the create validator was successful
function createValidator(
Description calldata description,
CommissionRates calldata commissionRates,
uint256 minSelfDelegation,
address delegatorAddress,
string memory validatorAddress,
string memory pubkey,
uint256 value
) external returns (bool success);

/// @dev Defines a method for performing a delegation of coins from a delegator to a validator.
/// @param delegatorAddress The address of the delegator
/// @param validatorAddress The address of the validator
Expand Down Expand Up @@ -238,6 +268,16 @@ interface StakingI is authorization.AuthorizationI {
PageResponse calldata pageResponse
);

/// @dev CreateValidator defines an Event emitted when a create a new validator.
/// @param delegatorAddress The address of the delegator
/// @param validatorAddress The address of the validator
/// @param value The amount of coin being self delegated
event CreateValidator(
address indexed delegatorAddress,
address indexed validatorAddress,
uint256 value
);

/// @dev Delegate defines an Event emitted when a given amount of tokens are delegated from the
/// delegator address to the validator address.
/// @param delegatorAddress The address of the delegator
Expand Down
118 changes: 118 additions & 0 deletions precompiles/staking/abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,31 @@
"name": "CancelUnbondingDelegation",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "delegatorAddress",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "validatorAddress",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "CreateValidator",
"type": "event"
},
{
"anonymous": false,
"inputs": [
Expand Down Expand Up @@ -308,6 +333,99 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "moniker",
"type": "string"
},
{
"internalType": "string",
"name": "identity",
"type": "string"
},
{
"internalType": "string",
"name": "website",
"type": "string"
},
{
"internalType": "string",
"name": "securityContact",
"type": "string"
},
{
"internalType": "string",
"name": "details",
"type": "string"
}
],
"internalType": "struct Description",
"name": "description",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "rate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxRate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxChangeRate",
"type": "uint256"
}
],
"internalType": "struct CommissionRates",
"name": "commissionRates",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "minSelfDelegation",
"type": "uint256"
},
{
"internalType": "address",
"name": "delegatorAddress",
"type": "address"
},
{
"internalType": "string",
"name": "validatorAddress",
"type": "string"
},
{
"internalType": "string",
"name": "pubkey",
"type": "string"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "createValidator",
"outputs": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down
54 changes: 54 additions & 0 deletions precompiles/staking/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
)

const (
// EventTypeCreateValidator defines the event type for the staking CreateValidator transaction.
EventTypeCreateValidator = "CreateValidator"
// EventTypeDelegate defines the event type for the staking Delegate transaction.
EventTypeDelegate = "Delegate"
// EventTypeUnbond defines the event type for the staking Undelegate transaction.
Expand Down Expand Up @@ -122,6 +124,35 @@ func (p Precompile) EmitAllowanceChangeEvent(ctx sdk.Context, stateDB vm.StateDB
return nil
}

// EmitCreateValidatorEvent creates a new ctreate valdator event emitted on a CreateValidator transaction.
func (p Precompile) EmitCreateValidatorEvent(ctx sdk.Context, stateDB vm.StateDB, msg *stakingtypes.MsgCreateValidator, delegatorAddr common.Address) error {
// Prepare the event topics
event := p.ABI.Events[EventTypeCreateValidator]

valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if err != nil {
return err
}

topics, err := p.createCreateValidatorTxTopics(3, event, delegatorAddr, common.BytesToAddress(valAddr.Bytes()))
if err != nil {
return err
}

// Prepare the event data
var b bytes.Buffer
b.Write(cmn.PackNum(reflect.ValueOf(msg.Value.Amount.BigInt())))

stateDB.AddLog(&ethtypes.Log{
Address: p.Address(),
Topics: topics,
Data: b.Bytes(),
BlockNumber: uint64(ctx.BlockHeight()),
})

return nil
}

// EmitDelegateEvent creates a new delegate event emitted on a Delegate transaction.
func (p Precompile) EmitDelegateEvent(ctx sdk.Context, stateDB vm.StateDB, msg *stakingtypes.MsgDelegate, delegatorAddr common.Address) error {
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
Expand Down Expand Up @@ -260,3 +291,26 @@ func (p Precompile) createStakingTxTopics(topicsLen uint64, event abi.Event, del

return topics, nil
}

// createCreateValidatorTxTopics creates the topics for staking transactions CreateValidator.
func (p Precompile) createCreateValidatorTxTopics(topicsLen uint64, event abi.Event, delegatorAddr common.Address, validatorAddr common.Address) ([]common.Hash, error) {
topics := make([]common.Hash, topicsLen)
// NOTE: If your solidity event contains indexed event types, then they become a topic rather than part of the data property of the log.
// In solidity you may only have up to 4 topics but only 3 indexed event types. The first topic is always the signature of the event.

// The first topic is always the signature of the event.
topics[0] = event.ID

var err error
topics[1], err = cmn.MakeTopic(delegatorAddr)
if err != nil {
return nil, err
}

topics[2], err = cmn.MakeTopic(validatorAddr)
if err != nil {
return nil, err
}

return topics, nil
}
6 changes: 5 additions & 1 deletion precompiles/staking/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz [
case authorization.DecreaseAllowanceMethod:
bz, err = p.DecreaseAllowance(ctx, evm.Origin, stateDB, method, args)
// Staking transactions
case CreateValidatorMethod:
bz, err = p.CreateValidator(ctx, evm.Origin, contract, stateDB, method, args)
case DelegateMethod:
bz, err = p.Delegate(ctx, evm.Origin, contract, stateDB, method, args)
case UndelegateMethod:
Expand Down Expand Up @@ -151,6 +153,7 @@ func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz [
// IsTransaction checks if the given method name corresponds to a transaction or query.
//
// Available staking transactions are:
// - CreateValidator
// - Delegate
// - Undelegate
// - Redelegate
Expand All @@ -163,7 +166,8 @@ func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz [
// - DecreaseAllowance
func (Precompile) IsTransaction(method string) bool {
switch method {
case DelegateMethod,
case CreateValidatorMethod,
DelegateMethod,
UndelegateMethod,
RedelegateMethod,
CancelUnbondingDelegationMethod,
Expand Down
46 changes: 46 additions & 0 deletions precompiles/staking/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
)

const (
// CreateValidatorMethod defines the ABI method name for the staking create validator transaction
CreateValidatorMethod = "createValidator"
// DelegateMethod defines the ABI method name for the staking Delegate
// transaction.
DelegateMethod = "delegate"
Expand All @@ -43,6 +45,50 @@ const (
CancelUnbondingDelegationAuthz = stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION
)

// CreateValidator performs create validator.
func (p Precompile) CreateValidator(
ctx sdk.Context,
origin common.Address,
_ *vm.Contract,
stateDB vm.StateDB,
method *abi.Method,
args []interface{},
) ([]byte, error) {
msg, delegatorHexAddr, err := NewMsgCreateValidator(args, p.stakingKeeper.BondDenom(ctx))
if err != nil {
return nil, err
}

p.Logger(ctx).Debug(
"tx called",
"method", method.Name,
"commission", msg.Commission.String(),
"min_self_delegation", msg.MinSelfDelegation.String(),
"delegator_address", delegatorHexAddr.String(),
"validator_address", msg.ValidatorAddress,
"pubkey", msg.Pubkey.String(),
"value", msg.Value.Amount.String(),
)

// we only allow the tx signer "origin" to create their own validator.
if origin != delegatorHexAddr {
return nil, fmt.Errorf(ErrDifferentOriginFromDelegator, origin.String(), delegatorHexAddr.String())
}

// Execute the transaction using the message server
msgSrv := stakingkeeper.NewMsgServerImpl(&p.stakingKeeper)
if _, err = msgSrv.CreateValidator(sdk.WrapSDKContext(ctx), msg); err != nil {
return nil, err
}

// Emit the event for the delegate transaction
if err = p.EmitCreateValidatorEvent(ctx, stateDB, msg, delegatorHexAddr); err != nil {
return nil, err
}

return method.Outputs.Pack(true)
}

// Delegate performs a delegation of coins from a delegator to a validator.
func (p Precompile) Delegate(
ctx sdk.Context,
Expand Down

0 comments on commit 3cdc57d

Please sign in to comment.