Skip to content

Commit 3991c2c

Browse files
cloudgrayzsystmvladjdkAlex | Interchain Labs
authored
tests: add revert error e2e tests for contract and precompile calls (cosmos#476)
* update launch script path & remove duplicate We should use only one launch script. Managing both is extra un-necessary overhead * chore: add private keys info in local_node.sh * add bech32 and bank e2e tests * add undelegate test * faster local node and udpate delegate test * add create validator tc * update delegate tc include event checking also * update test codes added event checking to undelegate tc receipt has hash field instead of transactionHash * chore: remove un-used variable * add edit validator tc * add cancel unbonding tc * chore: format test files * add redelegate tc * skip gas estimation for faster tests * add redelegations query test * chore: refactor variable names * chore: change filename * add validators query test and fix gov interface * add set withdraw address tc * order test sequences and add withdraw delegator reward tc * add claim rewards tc * add withdraw validator commission * add fund community pool tc * add deposit validator rewards pool tc * add validator queries tc * chore: unify convention * add erc20 tc * update local node script it should contains every precompiles by default * fix slashing query and add e2e tests for it * fix: decode bech32 consensus address before converting to bytes The consensus address was previously used in its bech32-encoded form (a 52-character string), which is incorrect. This led to attempts to interpret a bech32 string directly as a 20-byte address, resulting in invalid conversions and data loss. This fix ensures the bech32 consensus address is properly decoded into its original 20-byte form before further processing, preserving the correct address representation expected in EVM-compatible byte format. * add gov tc * add more tcs to gov precompile (should fix cancel) * fix cancel proposal tc * add p256 tc * remove un-used variables * add werc20 tc * more timeout and verbose log * fix local_node.sh * add edgecase test for staking precompile and lint local node script * revert solidity test script change * chore: trim comments * p256 happy case * refactoring * refactor: make findEvent as common * check delegation shares and balance also * add checking user balance for withdraw delegator reward test * add checking user balance for claim rewards tc * strict balance check * add user balance check for fund community pool tc * add user balance check for deposit validator rewards pool tc * should use owner, not contract address itself * add event checks for erc20 precompile tc * add balance checks for werc20 tc * add balance check for gov deposit tc * add balance check for gov cancel proposal tc * add revert e2e test cases * lint: unused variable * chore(tests): set default evm chain id - 262144 * WIP: test: enhance revert test * WIP: test: enhance revert test * WIP: test: enhance revert test * test: enhance revert test * chore(tests): refine code * chore: update changelog * chore: add semi-colon --------- Co-authored-by: zsystm <[email protected]> Co-authored-by: Vlad J <[email protected]> Co-authored-by: Hyunwoo Lee <[email protected]> Co-authored-by: Alex | Interchain Labs <[email protected]>
1 parent befde4f commit 3991c2c

File tree

11 files changed

+1349
-145
lines changed

11 files changed

+1349
-145
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
- [\#398](https://github.com/cosmos/evm/pull/398) Post-audit security fixes (batch 4)
6363
- [\#442](https://github.com/cosmos/evm/pull/442) Prevent nil pointer by checking error in gov precompile FromResponse.
6464
- [\#387](https://github.com/cosmos/evm/pull/387) (Experimental) EVM-compatible appside mempool
65+
- [\#476](https://github.com/cosmos/evm/pull/476) Add revert error e2e tests for contract and precompile calls
6566

6667
### FEATURES
6768

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/solidity/init-node.sh

Lines changed: 0 additions & 145 deletions
This file was deleted.
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import "./cosmos/staking/StakingI.sol";
5+
import "./cosmos/distribution/DistributionI.sol";
6+
import "./cosmos/bank/IBank.sol";
7+
import "./cosmos/common/Types.sol";
8+
9+
/**
10+
* @title RevertTestContract
11+
* @dev Contract for testing Cosmos precompile revert scenarios and error message handling
12+
* Focuses specifically on precompile calls and interactions with Cosmos SDK modules
13+
*/
14+
contract RevertTestContract {
15+
uint256 public counter = 0;
16+
17+
// Events to track what operations are performed
18+
event PrecompileCallMade(string precompileName, bool success);
19+
event OutOfGasSimulated(uint256 gasLeft);
20+
21+
constructor() payable {}
22+
23+
// ============ DIRECT PRECOMPILE CALL REVERTS ============
24+
25+
/**
26+
* @dev Direct staking precompile call that will revert
27+
*/
28+
function directStakingRevert(string calldata invalidValidator) external {
29+
counter++;
30+
emit PrecompileCallMade("staking", false);
31+
// This should revert with invalid validator address
32+
STAKING_CONTRACT.delegate(address(this), invalidValidator, 1);
33+
}
34+
35+
/**
36+
* @dev Direct distribution precompile call that will revert
37+
*/
38+
function directDistributionRevert(string calldata invalidValidator) external {
39+
counter++;
40+
emit PrecompileCallMade("distribution", false);
41+
// This should revert with invalid validator address
42+
DISTRIBUTION_CONTRACT.withdrawDelegatorRewards(address(this), invalidValidator);
43+
}
44+
45+
/**
46+
* @dev Direct bank precompile call that will revert
47+
*/
48+
function directBankRevert() external pure {
49+
revert("intended revert");
50+
}
51+
52+
// ============ PRECOMPILE CALL VIA CONTRACT REVERTS ============
53+
54+
/**
55+
* @dev Precompile call via contract that reverts
56+
*/
57+
function precompileViaContractRevert(string calldata invalidValidator) external {
58+
counter++;
59+
try this.internalStakingCall(invalidValidator) {
60+
// Should not reach here
61+
} catch (bytes memory reason) {
62+
// Re-throw the error to maintain the revert
63+
assembly {
64+
revert(add(reason, 0x20), mload(reason))
65+
}
66+
}
67+
}
68+
69+
/**
70+
* @dev Internal function for precompile call via contract
71+
*/
72+
function internalStakingCall(string calldata validatorAddress) external {
73+
require(msg.sender == address(this), "Only self can call");
74+
emit PrecompileCallMade("staking_internal", false);
75+
STAKING_CONTRACT.delegate(address(this), validatorAddress, 1);
76+
}
77+
78+
/**
79+
* @dev Complex scenario: multiple precompile calls with revert
80+
*/
81+
function multiplePrecompileCallsWithRevert(string calldata validatorAddress) external {
82+
counter++;
83+
84+
// First, make a successful call
85+
try IBANK_CONTRACT.balances(address(this)) returns (Balance[] memory) {
86+
emit PrecompileCallMade("bank", true);
87+
} catch {
88+
emit PrecompileCallMade("bank", false);
89+
}
90+
91+
// Then make a call that will revert
92+
emit PrecompileCallMade("staking_multi", false);
93+
STAKING_CONTRACT.delegate(address(this), validatorAddress, 1);
94+
}
95+
96+
// ============ PRECOMPILE OUT OF GAS ERROR CASES ============
97+
98+
/**
99+
* @dev Direct precompile call that runs out of gas
100+
*/
101+
function directStakingOutOfGas(string calldata validatorAddress) external {
102+
counter++;
103+
emit OutOfGasSimulated(gasleft());
104+
105+
// First consume most gas
106+
for (uint256 i = 0; i < 1000000; i++) {
107+
counter++;
108+
}
109+
110+
// Then try precompile call with remaining gas
111+
STAKING_CONTRACT.delegate(address(this), validatorAddress, 1);
112+
}
113+
114+
/**
115+
* @dev Precompile call via contract that runs out of gas
116+
*/
117+
function precompileViaContractOutOfGas(string calldata validatorAddress) external {
118+
counter++;
119+
emit OutOfGasSimulated(gasleft());
120+
121+
// Consume most gas first
122+
for (uint256 i = 0; i < 1000000; i++) {
123+
counter++;
124+
}
125+
126+
// Then try internal precompile call
127+
this.internalStakingCall(validatorAddress);
128+
}
129+
130+
/**
131+
* @dev Wrapper precompile call that runs out of gas
132+
*/
133+
function wrappedPrecompileOutOfGas(string calldata validatorAddress) external {
134+
counter++;
135+
emit OutOfGasSimulated(gasleft());
136+
137+
// Consume most gas in expensive operations
138+
for (uint256 i = 0; i < 500000; i++) {
139+
keccak256(abi.encode(i, block.timestamp, msg.sender));
140+
counter++;
141+
}
142+
143+
// Then try multiple precompile calls
144+
STAKING_CONTRACT.delegate(address(this), validatorAddress, 1);
145+
DISTRIBUTION_CONTRACT.withdrawDelegatorRewards(address(this), validatorAddress);
146+
}
147+
148+
// ============ UTILITY FUNCTIONS ============
149+
150+
/**
151+
* @dev Get current counter value
152+
*/
153+
function getCounter() external view returns (uint256) {
154+
return counter;
155+
}
156+
157+
/**
158+
* @dev Reset counter (for testing)
159+
*/
160+
function resetCounter() external {
161+
counter = 0;
162+
}
163+
164+
/**
165+
* @dev Fund contract with native tokens
166+
*/
167+
receive() external payable {}
168+
169+
/**
170+
* @dev Withdraw funds (for testing)
171+
*/
172+
function withdraw() external {
173+
payable(msg.sender).transfer(address(this).balance);
174+
}
175+
}
176+
177+
/**
178+
* @title PrecompileWrapper
179+
* @dev Helper contract for testing precompile calls via external contracts
180+
*/
181+
contract PrecompileWrapper {
182+
event WrapperCall(string operation, bool success);
183+
184+
constructor() payable {}
185+
186+
/**
187+
* @dev Wrapper function that calls staking precompile and reverts
188+
*/
189+
function wrappedStakingCall(string calldata validatorAddress, uint256 amount) external {
190+
emit WrapperCall("staking", false);
191+
STAKING_CONTRACT.delegate(address(this), validatorAddress, amount);
192+
revert("Wrapper intentional revert");
193+
}
194+
195+
/**
196+
* @dev Wrapper function that calls distribution precompile and reverts
197+
*/
198+
function wrappedDistributionCall(string calldata validatorAddress) external {
199+
emit WrapperCall("distribution", false);
200+
DISTRIBUTION_CONTRACT.withdrawDelegatorRewards(address(this), validatorAddress);
201+
revert("Wrapper intentional revert");
202+
}
203+
204+
/**
205+
* @dev Wrapper function that runs out of gas
206+
*/
207+
function wrappedOutOfGasCall(string calldata validatorAddress) external {
208+
// Consume all gas
209+
for (uint256 i = 0; i < 1000000; i++) {
210+
// Gas consuming operation
211+
keccak256(abi.encode(i));
212+
}
213+
214+
STAKING_CONTRACT.delegate(address(this), validatorAddress, 1);
215+
}
216+
217+
/**
218+
* @dev Fund wrapper contract
219+
*/
220+
receive() external payable {}
221+
}

0 commit comments

Comments
 (0)