From 4aaaeeb344d7dfa7eb852873018c1550eae03668 Mon Sep 17 00:00:00 2001 From: devin Date: Thu, 6 Feb 2025 22:57:45 +0800 Subject: [PATCH] fix issuse about contract Stack too deep --- precompiles/avs/IAVSManager.sol | 50 ++++------ precompiles/avs/abi.json | 146 +++++++++++++++------------ precompiles/avs/avs_test.go | 151 ++++++++++------------------ precompiles/avs/tx.go | 5 +- precompiles/avs/types.go | 170 +++++++++++++++++++------------- precompiles/common/error.go | 3 + 6 files changed, 265 insertions(+), 260 deletions(-) diff --git a/precompiles/avs/IAVSManager.sol b/precompiles/avs/IAVSManager.sol index 273806423..375797098 100644 --- a/precompiles/avs/IAVSManager.sol +++ b/precompiles/avs/IAVSManager.sol @@ -9,7 +9,24 @@ IAVSManager constant AVSMANAGER_CONTRACT = IAVSManager(AVSMANAGER_PRECOMPILE_ADD /// @title AVS-Manager Precompile Contract /// @dev The interface through which solidity contracts will interact with AVS-Manager /// @custom:address 0x0000000000000000000000000000000000000901 - + struct AVSParams { + address sender; + string avsName; + uint64 minStakeAmount; + address taskAddr; + address slashAddr; + address rewardAddr; + address[] avsOwnerAddress; + address[] whitelistAddress; + string[] assetIds; + uint64 avsUnbondingPeriod; + uint64 minSelfDelegation; + string epochIdentifier; + uint64 miniOptInOperators; + uint64 minTotalStakeAmount; + uint64 avsRewardProportion; + uint64 avsSlashProportion; + } interface IAVSManager { // note:string and bytes will be hashed. address / uintX will not be hashed when using indexed. event AVSRegistered(address indexed avsAddr, string sender, string avsName); @@ -42,36 +59,9 @@ interface IAVSManager { ); /// @dev Register AVS contract to EXO. - /// @param sender The external address for calling this method. - /// @param avsName The name of AVS. - /// @param minStakeAmount The minimum amount of funds staked by each operator. - /// @param taskAddr The task address of AVS. - /// @param slashAddr The slash address of AVS. - /// @param rewardAddr The reward address of AVS. - /// @param avsOwnerAddress The owners who have permission for AVS. - /// @param whitelistAddress The whitelist address of the operator. - /// @param assetIds The basic asset information of AVS. - /// @param avsUnbondingPeriod The unbonding duration of AVS. - /// @param minSelfDelegation The minimum delegation amount for an operator. - /// @param epochIdentifier The AVS epoch identifier. - /// @param params 1.miniOptInOperators The minimum number of opt-in operators. - ///2.minTotalStakeAmount The minimum total amount of stake by all operators. - ///3.avsReward The proportion of reward for AVS. - ///4.avsSlash The proportion of slash for AVS. + /// @param params The params of AVS. function registerAVS( - address sender, - string memory avsName, - uint64 minStakeAmount, - address taskAddr, - address slashAddr, - address rewardAddr, - address[] memory avsOwnerAddress, - address[] memory whitelistAddress, - string[] memory assetIds, - uint64 avsUnbondingPeriod, - uint64 minSelfDelegation, - string memory epochIdentifier, - uint64[] memory params + AVSParams calldata params ) external returns (bool success); /// @dev Update AVS info to EXO. diff --git a/precompiles/avs/abi.json b/precompiles/avs/abi.json index 9510a8d11..66d5745df 100644 --- a/precompiles/avs/abi.json +++ b/precompiles/avs/abi.json @@ -617,69 +617,91 @@ { "inputs": [ { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "string", - "name": "avsName", - "type": "string" - }, - { - "internalType": "uint64", - "name": "minStakeAmount", - "type": "uint64" - }, - { - "internalType": "address", - "name": "taskAddr", - "type": "address" - }, - { - "internalType": "address", - "name": "slashAddr", - "type": "address" - }, - { - "internalType": "address", - "name": "rewardAddr", - "type": "address" - }, - { - "internalType": "address[]", - "name": "avsOwnerAddress", - "type": "address[]" - }, - { - "internalType": "address[]", - "name": "whitelistAddress", - "type": "address[]" - }, - { - "internalType": "string[]", - "name": "assetIds", - "type": "string[]" - }, - { - "internalType": "uint64", - "name": "avsUnbondingPeriod", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "minSelfDelegation", - "type": "uint64" - }, - { - "internalType": "string", - "name": "epochIdentifier", - "type": "string" - }, - { - "internalType": "uint64[]", + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "string", + "name": "avsName", + "type": "string" + }, + { + "internalType": "uint64", + "name": "minStakeAmount", + "type": "uint64" + }, + { + "internalType": "address", + "name": "taskAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "slashAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "rewardAddr", + "type": "address" + }, + { + "internalType": "address[]", + "name": "avsOwnerAddress", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "whitelistAddress", + "type": "address[]" + }, + { + "internalType": "string[]", + "name": "assetIds", + "type": "string[]" + }, + { + "internalType": "uint64", + "name": "avsUnbondingPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "minSelfDelegation", + "type": "uint64" + }, + { + "internalType": "string", + "name": "epochIdentifier", + "type": "string" + }, + { + "internalType": "uint64", + "name": "miniOptInOperators", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "minTotalStakeAmount", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "avsRewardProportion", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "avsSlashProportion", + "type": "uint64" + } + ], + "internalType": "struct AVSParams", "name": "params", - "type": "uint64[]" + "type": "tuple" } ], "name": "registerAVS", diff --git a/precompiles/avs/avs_test.go b/precompiles/avs/avs_test.go index d5bfd754e..15544d530 100644 --- a/precompiles/avs/avs_test.go +++ b/precompiles/avs/avs_test.go @@ -85,11 +85,13 @@ func (suite *AVSManagerPrecompileSuite) TestIsTransaction() { }) } } - -func (suite *AVSManagerPrecompileSuite) TestRegisterAVS() { +func (s *AVSManagerPrecompileSuite) TestRegisterAVS() { + // Default variables used during tests. + gas := uint64(2_000) + senderAddress := utiltx.GenerateAddress() avsName, slashAddress, rewardAddress := "avsTest", "0xDF907c29719154eb9872f021d21CAE6E5025d7aB", "0xDF907c29719154eb9872f021d21CAE6E5025d7aB" avsOwnerAddress := []common.Address{ - suite.Address, + s.Address, utiltx.GenerateAddress(), utiltx.GenerateAddress(), } @@ -97,114 +99,67 @@ func (suite *AVSManagerPrecompileSuite) TestRegisterAVS() { utiltx.GenerateAddress(), utiltx.GenerateAddress(), } - assetID := suite.AssetIDs + assetID := s.AssetIDs minStakeAmount, taskAddr := uint64(3), "0xDF907c29719154eb9872f021d21CAE6E5025d7aB" avsUnbondingPeriod, minSelfDelegation := uint64(3), uint64(3) epochIdentifier := epochstypes.DayEpochID - params := []uint64{2, 3, 4, 4} - commonMalleate := func() (common.Address, []byte) { - input, err := suite.precompile.Pack( - avs.MethodRegisterAVS, - suite.Address, - avsName, - minStakeAmount, - common.HexToAddress(taskAddr), - common.HexToAddress(slashAddress), - common.HexToAddress(rewardAddress), - avsOwnerAddress, - exoWhiteListAddress, - assetID, - avsUnbondingPeriod, - minSelfDelegation, - epochIdentifier, - params, - ) - suite.Require().NoError(err, "failed to pack input") - return common.HexToAddress("0x3e108c058e8066DA635321Dc3018294cA82ddEdf"), input - } - - successRet, err := suite.precompile.Methods[avs.MethodRegisterAVS].Outputs.Pack(true) - suite.Require().NoError(err) - - testcases := []struct { + method := s.precompile.Methods[avs.MethodRegisterAVS] + testCases := []struct { name string - malleate func() (common.Address, []byte) - readOnly bool - expPass bool + sender common.Address + origin common.Address + malleate func() []interface{} + ibcSetup bool + expError bool errContains string - returnBytes []byte }{ { - name: "pass for avs-registered", - malleate: func() (common.Address, []byte) { - return commonMalleate() + name: "pass for avs-registered", + sender: senderAddress, + origin: senderAddress, + malleate: func() []interface{} { + return []interface{}{ + avs.AVSParams{ + Sender: senderAddress, + AvsName: avsName, + MinStakeAmount: minStakeAmount, + TaskAddr: common.HexToAddress(taskAddr), + SlashAddr: common.HexToAddress(slashAddress), + RewardAddr: common.HexToAddress(rewardAddress), + AvsOwnerAddresses: avsOwnerAddress, + WhitelistAddresses: exoWhiteListAddress, + AssetIds: assetID, + AvsUnbondingPeriod: avsUnbondingPeriod, + MinSelfDelegation: minSelfDelegation, + EpochIdentifier: epochIdentifier, + MiniOptInOperators: 1, + MinTotalStakeAmount: 1, + AvsRewardProportion: 5, + AvsSlashProportion: 5, + }, + } }, - readOnly: false, - expPass: true, - returnBytes: successRet, + expError: false, + ibcSetup: true, }, } - for _, tc := range testcases { - tc := tc - suite.Run(tc.name, func() { - baseFee := suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx) - - // malleate testcase - caller, input := tc.malleate() - - contract := vm.NewPrecompile(vm.AccountRef(caller), suite.precompile, big.NewInt(0), uint64(1e6)) - contract.Input = input - - contractAddr := contract.Address() - // Build and sign Ethereum transaction - txArgs := evmtypes.EvmTxArgs{ - ChainID: suite.App.EvmKeeper.ChainID(), - Nonce: 0, - To: &contractAddr, - Amount: nil, - GasLimit: 100000, - GasPrice: app.MainnetMinGasPrices.BigInt(), - GasFeeCap: baseFee, - GasTipCap: big.NewInt(1), - Accesses: ðtypes.AccessList{}, - } - msgEthereumTx := evmtypes.NewTx(&txArgs) - - msgEthereumTx.From = suite.Address.String() - err := msgEthereumTx.Sign(suite.EthSigner, suite.Signer) - suite.Require().NoError(err, "failed to sign Ethereum message") - - // Instantiate config - proposerAddress := suite.Ctx.BlockHeader().ProposerAddress - cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, suite.App.EvmKeeper.ChainID()) - suite.Require().NoError(err, "failed to instantiate EVM config") - - msg, err := msgEthereumTx.AsMessage(suite.EthSigner, baseFee) - suite.Require().NoError(err, "failed to instantiate Ethereum message") - - // Instantiate EVM - evm := suite.App.EvmKeeper.NewEVM( - suite.Ctx, msg, cfg, nil, suite.StateDB, + for _, tc := range testCases { + s.Run(tc.name, func() { + s.SetupTest() + contract := vm.NewContract(vm.AccountRef(tc.sender), s.precompile, big.NewInt(0), gas) + _, err := s.precompile.RegisterAVS( + s.Ctx, + tc.origin, + contract, + s.StateDB, + &method, + tc.malleate(), ) - - params := suite.App.EvmKeeper.GetParams(suite.Ctx) - activePrecompiles := params.GetActivePrecompilesAddrs() - precompileMap := suite.App.EvmKeeper.Precompiles(activePrecompiles...) - err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) - suite.Require().NoError(err, "invalid precompiles", activePrecompiles) - evm.WithPrecompiles(precompileMap, activePrecompiles) - - // Run precompiled contract - bz, err := suite.precompile.Run(evm, contract, tc.readOnly) - // Check results - if tc.expPass { - suite.Require().NoError(err, "expected no error when running the precompile") - suite.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") + if tc.expError { + s.Require().ErrorContains(err, tc.errContains) } else { - suite.Require().Error(err, "expected error to be returned when running the precompile") - suite.Require().Nil(bz, "expected returned bytes to be nil") - suite.Require().ErrorContains(err, tc.errContains) + s.Require().NoError(err) } }) } diff --git a/precompiles/avs/tx.go b/precompiles/avs/tx.go index 98c6b1de4..21cf95ddf 100644 --- a/precompiles/avs/tx.go +++ b/precompiles/avs/tx.go @@ -31,17 +31,18 @@ const ( // RegisterAVS AVSInfoRegister register the avs related information and change the state in avs keeper module. func (p Precompile) RegisterAVS( ctx sdk.Context, - _ common.Address, + origin common.Address, contract *vm.Contract, stateDB vm.StateDB, method *abi.Method, args []interface{}, ) ([]byte, error) { // parse the avs input params first. - avsParams, err := p.GetAVSParamsFromInputs(ctx, args) + avsParams, err := p.GetAVSParamsFromInputs(contract, origin, method, args) if err != nil { return nil, errorsmod.Wrap(err, "parse args error") } + // verification of the calling address to ensure it is avs contract owner if !slices.Contains(avsParams.AvsOwnerAddress, avsParams.CallerAddress.String()) { return nil, errorsmod.Wrap(err, "not qualified to registerOrDeregister") diff --git a/precompiles/avs/types.go b/precompiles/avs/types.go index 41bd5094d..7625b4ef2 100644 --- a/precompiles/avs/types.go +++ b/precompiles/avs/types.go @@ -2,6 +2,8 @@ package avs import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/core/vm" exocmn "github.com/ExocoreNetwork/exocore/precompiles/common" avstypes "github.com/ExocoreNetwork/exocore/x/avs/types" @@ -11,114 +13,97 @@ import ( "golang.org/x/xerrors" ) -func (p Precompile) GetAVSParamsFromInputs(_ sdk.Context, args []interface{}) (*avstypes.AVSRegisterOrDeregisterParams, error) { +func (p Precompile) GetAVSParamsFromInputs(contract *vm.Contract, origin common.Address, method *abi.Method, args []interface{}) (*avstypes.AVSRegisterOrDeregisterParams, error) { if len(args) != len(p.ABI.Methods[MethodRegisterAVS].Inputs) { return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodRegisterAVS].Inputs), len(args)) } + var avsPayload AVSPayload + if err := method.Inputs.Copy(&avsPayload, args); err != nil { + return nil, fmt.Errorf("error while unpacking args to AVSPayload struct: %s", err) + } avsParams := &avstypes.AVSRegisterOrDeregisterParams{} // we'd better not use evm.Origin but let the precompile caller pass in the sender address, // since tx.origin has some security issue and might not be supported // in a long term: https://docs.soliditylang.org/en/latest/security-considerations.html#tx-origin - callerAddress, ok := args[0].(common.Address) - if !ok || (callerAddress == common.Address{}) { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 0, "common.Address", callerAddress) + if !common.IsHexAddress(avsPayload.AVSParams.Sender.String()) { + return nil, fmt.Errorf("the contract input parameter sender error,value:%v", avsPayload.AVSParams.Sender) } - avsParams.CallerAddress = callerAddress[:] - avsName, ok := args[1].(string) - if !ok || avsName == "" { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 1, "string", avsName) + // The provided sender address should always be equal to the origin address. + // In case the contract caller address is the same as the sender address provided, + // update the sender address to be equal to the origin address. + // Otherwise, if the provided sender address is different from the origin address, + // return an error because is a forbidden operation + _, err := CheckOriginAndSender(contract, origin, avsPayload.AVSParams.Sender) + if err != nil { + return nil, err } - avsParams.AvsName = avsName + + avsParams.CallerAddress = avsPayload.AVSParams.Sender[:] + avsParams.AvsName = avsPayload.AVSParams.AvsName // When creating tasks in AVS, check the minimum requirements,minStakeAmount at least greater than 0 - minStakeAmount, ok := args[2].(uint64) - if !ok || minStakeAmount == 0 { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 2, "uint64", minStakeAmount) + if avsPayload.AVSParams.MinStakeAmount == 0 { + return nil, fmt.Errorf("the contract input parameter MinStakeAmount error,value:%v", avsPayload.AVSParams.MinStakeAmount) } - avsParams.MinStakeAmount = minStakeAmount + avsParams.MinStakeAmount = avsPayload.AVSParams.MinStakeAmount - taskAddr, ok := args[3].(common.Address) - if !ok || taskAddr == (common.Address{}) { - return nil, xerrors.Errorf(exocmn.ErrContractInputParamOrType, 3, "common.Address", taskAddr) + if !common.IsHexAddress(avsPayload.AVSParams.TaskAddr.String()) { + return nil, fmt.Errorf("the contract input parameter TaskAddr error,value:%v", avsPayload.AVSParams.TaskAddr) } - avsParams.TaskAddr = taskAddr + avsParams.TaskAddr = avsPayload.AVSParams.TaskAddr - slashContractAddr, ok := args[4].(common.Address) - if !ok || (slashContractAddr == common.Address{}) { - return nil, xerrors.Errorf(exocmn.ErrContractInputParamOrType, 4, "common.Address", slashContractAddr) + if !common.IsHexAddress(avsPayload.AVSParams.SlashAddr.String()) { + return nil, fmt.Errorf("the contract input parameter SlashAddr error,value:%v", avsPayload.AVSParams.SlashAddr) } - avsParams.SlashContractAddr = slashContractAddr + avsParams.SlashContractAddr = avsPayload.AVSParams.SlashAddr - rewardContractAddr, ok := args[5].(common.Address) - if !ok || (rewardContractAddr == common.Address{}) { - return nil, xerrors.Errorf(exocmn.ErrContractInputParamOrType, 5, "common.Address", rewardContractAddr) + if !common.IsHexAddress(avsPayload.AVSParams.RewardAddr.String()) { + return nil, fmt.Errorf("the contract input parameter RewardAddr error,value:%v", avsPayload.AVSParams.RewardAddr) } - avsParams.RewardContractAddr = rewardContractAddr + avsParams.RewardContractAddr = avsPayload.AVSParams.RewardAddr // bech32 - avsOwnerAddress, ok := args[6].([]common.Address) - if !ok || avsOwnerAddress == nil { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 6, "[]common.Address", avsOwnerAddress) + if avsPayload.AVSParams.AvsOwnerAddresses == nil { + return nil, fmt.Errorf("the contract input parameter AvsOwnerAddresses error,value:%v", avsPayload.AVSParams.AvsOwnerAddresses) } - exoAddresses := make([]string, len(avsOwnerAddress)) - for i, addr := range avsOwnerAddress { + exoAddresses := make([]string, len(avsPayload.AVSParams.AvsOwnerAddresses)) + for i, addr := range avsPayload.AVSParams.AvsOwnerAddresses { var accAddr sdk.AccAddress = addr[:] exoAddresses[i] = accAddr.String() } avsParams.AvsOwnerAddress = exoAddresses // bech32 - whitelistAddress, ok := args[7].([]common.Address) - if !ok || whitelistAddress == nil { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 7, "[]common.Address", whitelistAddress) + + if avsPayload.AVSParams.WhitelistAddresses == nil { + return nil, fmt.Errorf("the contract input parameter WhitelistAddresses error,value:%v", avsPayload.AVSParams.WhitelistAddresses) } - exoWhiteAddresses := make([]string, len(whitelistAddress)) - for i, addr := range whitelistAddress { + exoWhiteAddresses := make([]string, len(avsPayload.AVSParams.WhitelistAddresses)) + for i, addr := range avsPayload.AVSParams.WhitelistAddresses { var accAddr sdk.AccAddress = addr[:] exoWhiteAddresses[i] = accAddr.String() } avsParams.WhitelistAddress = exoWhiteAddresses // string, since it is the address_id representation - assetID, ok := args[8].([]string) - if !ok || len(assetID) == 0 { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 8, "[]string", assetID) + if avsPayload.AVSParams.AssetIds == nil { + return nil, fmt.Errorf("the contract input parameter AssetIds error,value:%v", avsPayload.AVSParams.AssetIds) } - avsParams.AssetID = assetID + avsParams.AssetID = avsPayload.AVSParams.AssetIds - unbondingPeriod, ok := args[9].(uint64) - if !ok || unbondingPeriod == 0 { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 9, "uint64", unbondingPeriod) - } - avsParams.UnbondingPeriod = unbondingPeriod + avsParams.UnbondingPeriod = avsPayload.AVSParams.AvsUnbondingPeriod - minSelfDelegation, ok := args[10].(uint64) - if !ok { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 10, "uint64", minSelfDelegation) - } - avsParams.MinSelfDelegation = minSelfDelegation + avsParams.MinSelfDelegation = avsPayload.AVSParams.MinSelfDelegation - epochIdentifier, ok := args[11].(string) - if !ok || epochIdentifier == "" { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 11, "string", epochIdentifier) + if avsPayload.AVSParams.EpochIdentifier == "" { + return nil, fmt.Errorf("the contract input parameter EpochIdentifier error,value:%v", avsPayload.AVSParams.EpochIdentifier) } - avsParams.EpochIdentifier = epochIdentifier + avsParams.EpochIdentifier = avsPayload.AVSParams.EpochIdentifier - // The parameters below are used when creating tasks, to ensure that the minimum criteria are met by the set - // of operators. + avsParams.MinOptInOperators = avsPayload.AVSParams.MiniOptInOperators - taskParam, ok := args[12].([]uint64) - if !ok || taskParam == nil || len(taskParam) != 4 { - return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 12, "[]string", taskParam) - } - minOptInOperators := taskParam[0] - avsParams.MinOptInOperators = minOptInOperators + avsParams.MinTotalStakeAmount = avsPayload.AVSParams.MinTotalStakeAmount - minTotalStakeAmount := taskParam[1] - avsParams.MinTotalStakeAmount = minTotalStakeAmount + avsParams.AvsReward = avsPayload.AVSParams.AvsRewardProportion - avsReward := taskParam[2] - avsParams.AvsReward = avsReward - - avsSlash := taskParam[3] - avsParams.AvsSlash = avsSlash + avsParams.AvsSlash = avsPayload.AVSParams.AvsSlashProportion return avsParams, nil } @@ -280,3 +265,52 @@ func (p Precompile) GetTaskParamsFromInputs(_ sdk.Context, args []interface{}) ( taskParams.TaskStatisticalPeriod = taskStatisticalPeriod return taskParams, nil } + +// AVSParams is a utility structure used to wrap args received by the +// Solidity interface of the avs function. +type AVSParams struct { + Sender common.Address `abi:"sender"` // the sender of the transaction + AvsName string `abi:"avsName"` // the name of AVS + MinStakeAmount uint64 `abi:"minStakeAmount"` // the minimum amount of funds staked by each operator + TaskAddr common.Address `abi:"taskAddr"` // the task address of AVS + SlashAddr common.Address `abi:"slashAddr"` // the slash address of AVS + RewardAddr common.Address `abi:"rewardAddr"` // the reward address of AVS + AvsOwnerAddresses []common.Address `abi:"avsOwnerAddresses"` // the owners who have permission for AVS + WhitelistAddresses []common.Address `abi:"whitelistAddresses"` // the whitelist address of the operator + AssetIds []string `abi:"assetIds"` // the basic asset information of AVS + AvsUnbondingPeriod uint64 `abi:"avsUnbondingPeriod"` // the unbonding duration of AVS + MinSelfDelegation uint64 `abi:"minSelfDelegation"` // the minimum delegation amount for an operator + EpochIdentifier string `abi:"epochIdentifier"` // the AVS epoch identifier + MiniOptInOperators uint64 `abi:"miniOptInOperators"` // the minimum number of opt-in operators + MinTotalStakeAmount uint64 `abi:"minTotalStakeAmount"` // the minimum total amount of stake by all operators + AvsRewardProportion uint64 `abi:"avsRewardProportion"` // the proportion of reward for AVS + AvsSlashProportion uint64 `abi:"avsSlashProportion"` // the proportion of slash for AVS +} + +// AVSPayload is the same as the expected input of the avs function in the Solidity interface. +type AVSPayload struct { + AVSParams AVSParams +} + +// ParseAVStData parses the packet data from the outpost precompiled contract. +func ParseAVStData(method *abi.Method, args []interface{}) (AVSParams, error) { + if len(args) != 1 { + return AVSParams{}, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 1, len(args)) + } + + var avsPayload AVSPayload + if err := method.Inputs.Copy(&avsPayload, args); err != nil { + return AVSParams{}, fmt.Errorf("error while unpacking args to AVSPayload struct: %s", err) + } + return avsPayload.AVSParams, nil +} + +// CheckOriginAndSender ensures the correct sender is being used. +func CheckOriginAndSender(contract *vm.Contract, origin common.Address, sender common.Address) (common.Address, error) { + if contract.CallerAddress == sender { + return origin, nil + } else if origin != sender { + return common.Address{}, fmt.Errorf(exocmn.ErrDifferentOriginFromSender, origin.String(), sender.String()) + } + return sender, nil +} diff --git a/precompiles/common/error.go b/precompiles/common/error.go index 0401a51f0..6c72d6d5f 100644 --- a/precompiles/common/error.go +++ b/precompiles/common/error.go @@ -22,4 +22,7 @@ const ( ErrEmptyGateways = "the gateways is empty" ErrIndexOutOfRange = "index out of range, index:%d, length:%d" + + // ErrDifferentOriginFromSender is raised when the origin address is not the same as the sender address. + ErrDifferentOriginFromSender = "origin address %s is not the same as sender address %s" )