Skip to content

Commit 89bb381

Browse files
authored
fix: EoA-CA Identification logic (#710)
1 parent e36b7da commit 89bb381

File tree

22 files changed

+224
-34
lines changed

22 files changed

+224
-34
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
- [\#701](https://github.com/cosmos/evm/pull/701) Add address codec support to ERC20 IBC callbacks to handle hex addresses in addition to bech32 addresses.
5555
- [\#702](https://github.com/cosmos/evm/pull/702) Fix mempool e2e test
5656
- [\#704](https://github.com/cosmos/evm/pull/704) Fix EIP-7702 test cases
57+
- [\#706](https://github.com/cosmos/evm/pull/706) Fix EoA-CA Identification logic
58+
5759

5860
### FEATURES
5961

ante/evm/06_account_verification.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func VerifyAccountBalance(
2929
ethTx *ethtypes.Transaction,
3030
) error {
3131
// Only EOA are allowed to send transactions.
32-
if account != nil && account.IsContract() {
32+
if account != nil && account.HasCodeHash() {
3333
// check eip-7702
3434
code := evmKeeper.GetCode(ctx, common.BytesToHash(account.CodeHash))
3535
_, delegated := ethtypes.ParseDelegation(code)

precompiles/staking/tx.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66

77
"github.com/ethereum/go-ethereum/accounts/abi"
8+
ethtypes "github.com/ethereum/go-ethereum/core/types"
89
"github.com/ethereum/go-ethereum/core/vm"
910

1011
cmn "github.com/cosmos/evm/precompiles/common"
@@ -59,10 +60,16 @@ func (p Precompile) CreateValidator(
5960
)
6061

6162
msgSender := contract.Caller()
62-
// we won't allow calls from smart contracts
63-
if hasCode := stateDB.GetCode(msgSender) != nil; hasCode {
63+
64+
// We won't allow calls from smart contracts
65+
// unless they are EIP-7702 delegated accounts
66+
code := stateDB.GetCode(msgSender)
67+
_, delegated := ethtypes.ParseDelegation(code)
68+
if len(code) > 0 && !delegated {
69+
// call by contract method
6470
return nil, errors.New(ErrCannotCallFromContract)
6571
}
72+
6673
if msgSender != validatorHexAddr {
6774
return nil, fmt.Errorf(cmn.ErrRequesterIsNotMsgSender, msgSender.String(), validatorHexAddr.String())
6875
}
@@ -105,10 +112,16 @@ func (p Precompile) EditValidator(
105112
)
106113

107114
msgSender := contract.Caller()
108-
// we won't allow calls from smart contracts
109-
if hasCode := stateDB.GetCode(msgSender) != nil; hasCode {
115+
116+
// We won't allow calls from smart contracts
117+
// unless they are EIP-7702 delegated accounts
118+
code := stateDB.GetCode(msgSender)
119+
_, delegated := ethtypes.ParseDelegation(code)
120+
if len(code) > 0 && !delegated {
121+
// call by contract method
110122
return nil, errors.New(ErrCannotCallFromContract)
111123
}
124+
112125
if msgSender != validatorHexAddr {
113126
return nil, fmt.Errorf(cmn.ErrRequesterIsNotMsgSender, msgSender.String(), validatorHexAddr.String())
114127
}

tests/integration/precompiles/distribution/test_integration.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1343,7 +1343,8 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp
13431343
// check contract was correctly deployed
13441344
cAcc := s.network.App.GetEVMKeeper().GetAccount(s.network.GetContext(), contractAddr)
13451345
Expect(cAcc).ToNot(BeNil(), "contract account should exist")
1346-
Expect(cAcc.IsContract()).To(BeTrue(), "account should be a contract")
1346+
isContract := s.network.App.GetEVMKeeper().IsContract(s.network.GetContext(), contractAddr)
1347+
Expect(isContract).To(BeTrue(), "account should be a contract")
13471348

13481349
// Contract delegate
13491350
stkPrecompile := s.getStakingPrecompile()

tests/integration/precompiles/gov/test_integration.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,8 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp
10521052

10531053
cAcc := s.network.App.GetEVMKeeper().GetAccount(s.network.GetContext(), contractAddr)
10541054
Expect(cAcc).ToNot(BeNil(), "failed to get contract account")
1055-
Expect(cAcc.IsContract()).To(BeTrue(), "expected contract account")
1055+
isContract := s.network.App.GetEVMKeeper().IsContract(s.network.GetContext(), contractAddr)
1056+
Expect(isContract).To(BeTrue(), "expected contract account")
10561057

10571058
contractAddrDupe, err = s.factory.DeployContract(
10581059
txSenderKey,
@@ -1067,7 +1068,8 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp
10671068

10681069
cAccDupe := s.network.App.GetEVMKeeper().GetAccount(s.network.GetContext(), contractAddrDupe)
10691070
Expect(cAccDupe).ToNot(BeNil(), "failed to get dupe contract account")
1070-
Expect(cAccDupe.IsContract()).To(BeTrue(), "expected dupe contract account")
1071+
isContract = s.network.App.GetEVMKeeper().IsContract(s.network.GetContext(), contractAddrDupe)
1072+
Expect(isContract).To(BeTrue(), "expected dupe contract account")
10711073

10721074
callArgs = testutiltypes.CallArgs{
10731075
ContractABI: govCallerContract.ABI,

tests/integration/precompiles/slashing/test_integration.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp
8585
// check contract was correctly deployed
8686
cAcc := s.network.App.GetEVMKeeper().GetAccount(s.network.GetContext(), contractAddr)
8787
Expect(cAcc).ToNot(BeNil(), "contract account should exist")
88-
Expect(cAcc.IsContract()).To(BeTrue(), "account should be a contract")
88+
isContract := s.network.App.GetEVMKeeper().IsContract(s.network.GetContext(), contractAddr)
89+
Expect(isContract).To(BeTrue(), "account should be a contract")
8990

9091
// populate default call args
9192
callArgs = testutiltypes.CallArgs{

tests/integration/precompiles/staking/test_integration.go

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1440,7 +1440,8 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp
14401440
// check contract was correctly deployed
14411441
cAcc := s.network.App.GetEVMKeeper().GetAccount(s.network.GetContext(), contractAddr)
14421442
Expect(cAcc).ToNot(BeNil(), "contract account should exist")
1443-
Expect(cAcc.IsContract()).To(BeTrue(), "account should be a contract")
1443+
isContract := s.network.App.GetEVMKeeper().IsContract(s.network.GetContext(), contractAddr)
1444+
Expect(isContract).To(BeTrue(), "account should be a contract")
14441445

14451446
// populate default TxArgs
14461447
txArgs.To = &contractAddr
@@ -1565,6 +1566,36 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp
15651566
Expect(err).NotTo(BeNil(), "expected validator NOT to be found")
15661567
Expect(err.Error()).To(ContainSubstring("not found"), "expected validator NOT to be found")
15671568
})
1569+
1570+
It("tx from validator operator with delegated code - should create a validator", func() {
1571+
s.delegateAccountToContract(valPriv, valHexAddr, contractTwoAddr)
1572+
1573+
callArgs = testutiltypes.CallArgs{
1574+
ContractABI: stakingCallerTwoContract.ABI,
1575+
MethodName: "testCreateValidatorWithTransfer",
1576+
Args: []interface{}{
1577+
defaultDescription, defaultCommission, defaultMinSelfDelegation, valHexAddr, defaultPubkeyBase64Str, false, false,
1578+
},
1579+
}
1580+
1581+
txArgs = evmtypes.EvmTxArgs{
1582+
To: &valHexAddr,
1583+
GasLimit: 500_000,
1584+
Amount: new(big.Int).Set(defaultValue),
1585+
}
1586+
1587+
_, _, err = s.factory.CallContractAndCheckLogs(
1588+
valPriv,
1589+
txArgs, callArgs,
1590+
passCheck.WithExpEvents(staking.EventTypeCreateValidator),
1591+
)
1592+
Expect(err).To(BeNil(), "error while calling the smart contract")
1593+
Expect(s.network.NextBlock()).To(BeNil())
1594+
1595+
qc := s.network.GetStakingClient()
1596+
_, err := qc.Validator(s.network.GetContext(), &stakingtypes.QueryValidatorRequest{ValidatorAddr: sdk.ValAddress(valAddr).String()})
1597+
Expect(err).To(BeNil(), "expected validator NOT to be found")
1598+
})
15681599
})
15691600

15701601
Context("to edit a validator", func() {
@@ -1683,6 +1714,33 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp
16831714
Expect(validator.Description.Moniker).To(Equal("original moniker"), "expected validator moniker is updated")
16841715
Expect(validator.Commission.Rate.BigInt().String()).To(Equal("100000000000000000"), "expected validator commission rate remain unchanged")
16851716
})
1717+
1718+
It("with tx from validator operator using delegated code - should NOT edit a validator", func() {
1719+
s.delegateAccountToContract(valPriv, valHexAddr, contractAddr)
1720+
callArgs.Args = []interface{}{
1721+
defaultDescription, valHexAddr,
1722+
defaultCommissionRate, defaultMinSelfDelegation,
1723+
}
1724+
1725+
txArgs.To = &valHexAddr
1726+
1727+
_, _, err = s.factory.CallContractAndCheckLogs(
1728+
valPriv,
1729+
txArgs,
1730+
callArgs,
1731+
execRevertedCheck,
1732+
)
1733+
Expect(err).To(BeNil(), "error while calling the smart contract")
1734+
Expect(s.network.NextBlock()).To(BeNil())
1735+
1736+
qc := s.network.GetStakingClient()
1737+
qRes, err := qc.Validator(s.network.GetContext(), &stakingtypes.QueryValidatorRequest{ValidatorAddr: sdk.ValAddress(valAddr).String()})
1738+
Expect(err).To(BeNil())
1739+
Expect(qRes).NotTo(BeNil())
1740+
1741+
validator := qRes.Validator
1742+
Expect(validator.Description.Moniker).NotTo(Equal(defaultDescription.Moniker), "expected validator moniker NOT to be updated")
1743+
})
16861744
})
16871745

16881746
Context("delegating", func() {

tests/integration/precompiles/staking/test_tx.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,25 @@ func (s *PrecompileTestSuite) TestCreateValidator() {
245245
true,
246246
"does not match the requester address",
247247
},
248+
{
249+
"fail - cannot be called from account with code (if it is not EIP-7702 delegated account)",
250+
func() []interface{} {
251+
stDB.SetCode(validatorAddress, []byte{0x60, 0x00})
252+
return []interface{}{
253+
description,
254+
commission,
255+
minSelfDelegation,
256+
validatorAddress,
257+
pubkey,
258+
value,
259+
}
260+
},
261+
200000,
262+
nil,
263+
func([]byte) {},
264+
true,
265+
staking.ErrCannotCallFromContract,
266+
},
248267
{
249268
"success",
250269
func() []interface{} {
@@ -522,7 +541,7 @@ func (s *PrecompileTestSuite) TestEditValidator() {
522541
"minimum self delegation must be a positive integer",
523542
},
524543
{
525-
"fail - calling precompile from a different address than validator (smart contract call)",
544+
"fail - cannot be called from account with code (if it is not EIP-7702 delegated account)",
526545
func() []interface{} {
527546
return []interface{}{
528547
description,
@@ -540,6 +559,23 @@ func (s *PrecompileTestSuite) TestEditValidator() {
540559
true,
541560
"does not match the requester address",
542561
},
562+
{
563+
"fail - cannot be called from smart contract",
564+
func() []interface{} {
565+
stDB.SetCode(validatorAddress, []byte{0x60, 0x00})
566+
return []interface{}{
567+
description,
568+
validatorAddress,
569+
commissionRate,
570+
minSelfDelegation,
571+
}
572+
},
573+
200000,
574+
nil,
575+
func([]byte) {},
576+
true,
577+
staking.ErrCannotCallFromContract,
578+
},
543579
{
544580
"success",
545581
func() []interface{} {

tests/integration/precompiles/staking/test_utils.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,20 @@ import (
77
"time"
88

99
"github.com/ethereum/go-ethereum/common"
10+
gethtypes "github.com/ethereum/go-ethereum/core/types"
11+
"github.com/holiman/uint256"
1012

1113
//nolint:revive // dot imports are fine for Ginkgo
1214
. "github.com/onsi/gomega"
1315

16+
"github.com/cosmos/evm/crypto/ethsecp256k1"
1417
"github.com/cosmos/evm/precompiles/staking"
18+
evmtypes "github.com/cosmos/evm/x/vm/types"
1519

1620
"cosmossdk.io/math"
1721

1822
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
23+
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
1924
sdk "github.com/cosmos/cosmos-sdk/types"
2025
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
2126
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
@@ -153,3 +158,44 @@ func GenerateBase64PubKey() string {
153158
pubKey := privKey.PubKey().(*ed25519.PubKey)
154159
return base64.StdEncoding.EncodeToString(pubKey.Bytes())
155160
}
161+
162+
// delegateAccountToContract delegates the given account to the given contract address using
163+
// the EIP-7702 SetCodeTx.
164+
func (s *PrecompileTestSuite) delegateAccountToContract(privKey cryptotypes.PrivKey, accountAddr, contractAddr common.Address) {
165+
ethPriv, ok := privKey.(*ethsecp256k1.PrivKey)
166+
Expect(ok).To(BeTrue(), "expected ethsecp256k1 private key")
167+
ecdsaPriv, err := ethPriv.ToECDSA()
168+
Expect(err).To(BeNil(), "error converting to ECDSA private key")
169+
170+
chainID := evmtypes.GetChainConfig().GetChainId()
171+
accResp, err := s.grpcHandler.GetEvmAccount(accountAddr)
172+
Expect(err).To(BeNil(), "error while getting the EVM account")
173+
174+
nonce := accResp.GetNonce()
175+
176+
authorization := gethtypes.SetCodeAuthorization{
177+
ChainID: *uint256.NewInt(chainID),
178+
Address: contractAddr,
179+
Nonce: nonce + 1,
180+
}
181+
182+
signedAuth, err := gethtypes.SignSetCode(ecdsaPriv, authorization)
183+
Expect(err).To(BeNil(), "error while signing the SetCodeAuthorization")
184+
185+
txArgs := evmtypes.EvmTxArgs{
186+
To: &common.Address{},
187+
GasLimit: 500_000,
188+
AuthorizationList: []gethtypes.SetCodeAuthorization{
189+
signedAuth,
190+
},
191+
}
192+
193+
_, err = s.factory.ExecuteEthTx(privKey, txArgs)
194+
Expect(err).To(BeNil(), "error while executing the SetCode transaction")
195+
Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block")
196+
197+
codeHash := s.network.App.GetEVMKeeper().GetCodeHash(s.network.GetContext(), accountAddr)
198+
code := s.network.App.GetEVMKeeper().GetCode(s.network.GetContext(), codeHash)
199+
_, delegated := gethtypes.ParseDelegation(code)
200+
Expect(delegated).To(BeTrue(), "expected account to be delegated to the contract address")
201+
}

tests/integration/x/erc20/test_ibc_callback.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ func (s *KeeperTestSuite) TestOnRecvPacketRegistered() {
233233
CodeHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
234234
})
235235
s.Require().NoError(err)
236-
s.Require().True(s.network.App.GetEVMKeeper().IsContract(ctx, collidedAddr))
236+
acct := s.network.App.GetEVMKeeper().GetAccount(ctx, collidedAddr)
237+
s.Require().True(acct.HasCodeHash())
237238
},
238239
ackSuccess: false,
239240
receiver: secpAddr,

0 commit comments

Comments
 (0)