Skip to content

Commit a03e347

Browse files
authored
Add warning when overflow happens (#503)
1 parent e3ea87e commit a03e347

File tree

4 files changed

+186
-92
lines changed

4 files changed

+186
-92
lines changed

crates/anvil-polkadot/tests/it/state_injector.rs

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -804,12 +804,9 @@ async fn test_set_immutable_storage() {
804804
));
805805

806806
// Verify initial immutable value
807-
let call_tx = TransactionRequest::default()
808-
.from(alith_addr)
809-
.to(contract_address)
810-
.input(TransactionInput::both(
811-
ImmutableStorage::getImmutableValueCall {}.abi_encode().into(),
812-
));
807+
let call_tx = TransactionRequest::default().from(alith_addr).to(contract_address).input(
808+
TransactionInput::both(ImmutableStorage::getImmutableValueCall {}.abi_encode().into()),
809+
);
813810
let result = unwrap_response::<Bytes>(
814811
node.eth_rpc(EthRequest::EthCall(call_tx.into(), None, None, None)).await.unwrap(),
815812
)
@@ -820,12 +817,9 @@ async fn test_set_immutable_storage() {
820817
);
821818

822819
// Verify initial immutable address
823-
let call_tx = TransactionRequest::default()
824-
.from(alith_addr)
825-
.to(contract_address)
826-
.input(TransactionInput::both(
827-
ImmutableStorage::getImmutableAddressCall {}.abi_encode().into(),
828-
));
820+
let call_tx = TransactionRequest::default().from(alith_addr).to(contract_address).input(
821+
TransactionInput::both(ImmutableStorage::getImmutableAddressCall {}.abi_encode().into()),
822+
);
829823
let result = unwrap_response::<Bytes>(
830824
node.eth_rpc(EthRequest::EthCall(call_tx.into(), None, None, None)).await.unwrap(),
831825
)
@@ -842,28 +836,20 @@ async fn test_set_immutable_storage() {
842836
Account::from(subxt_signer::eth::dev::charleth()).address(),
843837
));
844838

845-
let immutables = vec![
846-
Bytes::from(new_value.abi_encode()),
847-
Bytes::from(new_address.abi_encode()),
848-
];
839+
let immutables =
840+
vec![Bytes::from(new_value.abi_encode()), Bytes::from(new_address.abi_encode())];
849841

850842
unwrap_response::<()>(
851-
node.eth_rpc(EthRequest::SetImmutableStorageAt(
852-
contract_address,
853-
immutables,
854-
))
855-
.await
856-
.unwrap(),
843+
node.eth_rpc(EthRequest::SetImmutableStorageAt(contract_address, immutables))
844+
.await
845+
.unwrap(),
857846
)
858847
.unwrap();
859848

860849
// Verify new immutable value
861-
let call_tx = TransactionRequest::default()
862-
.from(alith_addr)
863-
.to(contract_address)
864-
.input(TransactionInput::both(
865-
ImmutableStorage::getImmutableValueCall {}.abi_encode().into(),
866-
));
850+
let call_tx = TransactionRequest::default().from(alith_addr).to(contract_address).input(
851+
TransactionInput::both(ImmutableStorage::getImmutableValueCall {}.abi_encode().into()),
852+
);
867853
let result = unwrap_response::<Bytes>(
868854
node.eth_rpc(EthRequest::EthCall(call_tx.into(), None, None, None)).await.unwrap(),
869855
)
@@ -874,12 +860,9 @@ async fn test_set_immutable_storage() {
874860
);
875861

876862
// Verify new immutable address
877-
let call_tx = TransactionRequest::default()
878-
.from(alith_addr)
879-
.to(contract_address)
880-
.input(TransactionInput::both(
881-
ImmutableStorage::getImmutableAddressCall {}.abi_encode().into(),
882-
));
863+
let call_tx = TransactionRequest::default().from(alith_addr).to(contract_address).input(
864+
TransactionInput::both(ImmutableStorage::getImmutableAddressCall {}.abi_encode().into()),
865+
);
883866
let result = unwrap_response::<Bytes>(
884867
node.eth_rpc(EthRequest::EthCall(call_tx.into(), None, None, None)).await.unwrap(),
885868
)

crates/anvil-polkadot/tests/it/utils.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -465,16 +465,12 @@ fn load_contract_json(name: &str) -> Value {
465465
let contract_path =
466466
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("test-data/{name}.json"));
467467

468-
serde_json::from_reader(std::io::BufReader::new(
469-
std::fs::File::open(contract_path).unwrap(),
470-
))
471-
.unwrap()
468+
serde_json::from_reader(std::io::BufReader::new(std::fs::File::open(contract_path).unwrap()))
469+
.unwrap()
472470
}
473471

474472
fn decode_hex_field(json: &Value, field: &str) -> Option<Vec<u8>> {
475-
json.get(field)
476-
.and_then(|v| v.as_str())
477-
.map(|s| hex::decode(s).unwrap())
473+
json.get(field).and_then(|v| v.as_str()).map(|s| hex::decode(s).unwrap())
478474
}
479475

480476
pub fn get_contract_code(name: &str) -> ContractCode {

crates/revive-strategy/src/cheatcodes/mod.rs

Lines changed: 92 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use alloy_sol_types::SolValue;
66
use foundry_cheatcodes::{
77
Broadcast, BroadcastableTransactions, CheatcodeInspectorStrategy,
88
CheatcodeInspectorStrategyContext, CheatcodeInspectorStrategyRunner, CheatsConfig, CheatsCtxt,
9-
CommonCreateInput, Ecx, EvmCheatcodeInspectorStrategyRunner, Result,
9+
CommonCreateInput, DynCheatcode, Ecx, EvmCheatcodeInspectorStrategyRunner, Result,
1010
Vm::{
1111
AccountAccessKind, chainIdCall, coinbaseCall, dealCall, etchCall, getNonce_0Call, loadCall,
1212
polkadot_0Call, polkadot_1Call, polkadotSkipCall, resetNonceCall,
@@ -318,8 +318,9 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {
318318
tracing::info!(cheatcode = ?cheatcode.as_debug() , using_revive = ?using_revive);
319319
let dealCall { account, newBalance } = cheatcode.as_any().downcast_ref().unwrap();
320320

321-
ctx.externalities.set_balance(*account, *newBalance);
322-
cheatcode.dyn_apply(ccx, executor)
321+
let clamped_balance = ctx.externalities.set_balance(*account, *newBalance);
322+
let clamped_deal = dealCall { account: *account, newBalance: clamped_balance };
323+
clamped_deal.dyn_apply(ccx, executor)
323324
}
324325
t if using_revive && is::<setNonceCall>(t) => {
325326
tracing::info!(cheatcode = ?cheatcode.as_debug() , using_revive = ?using_revive);
@@ -354,29 +355,10 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {
354355
}
355356
t if using_revive && is::<rollCall>(t) => {
356357
let &rollCall { newHeight } = cheatcode.as_any().downcast_ref().unwrap();
357-
let new_block_number: u64 = newHeight.saturating_to();
358-
359-
// blockhash should be the same on both revive and revm sides, so fetch it before
360-
// changing the block number.
361-
let prev_new_height_hash = ccx
362-
.ecx
363-
.journaled_state
364-
.database
365-
.block_hash(new_block_number.saturating_sub(1))
366-
.expect("Should not fail");
367-
let new_height_hash = ccx
368-
.ecx
369-
.journaled_state
370-
.database
371-
.block_hash(new_block_number)
372-
.expect("Should not fail");
373-
ctx.externalities.set_block_number(
374-
newHeight,
375-
prev_new_height_hash,
376-
new_height_hash,
377-
);
358+
let clamped_height =
359+
ctx.externalities.roll(newHeight, &mut *ccx.ecx.journaled_state.database);
378360

379-
cheatcode.dyn_apply(ccx, executor)
361+
rollCall { newHeight: clamped_height }.dyn_apply(ccx, executor)
380362
}
381363
t if using_revive && is::<snapshotStateCall>(t) => {
382364
ctx.externalities.start_snapshotting();
@@ -398,10 +380,9 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {
398380
t if using_revive && is::<warpCall>(t) => {
399381
let &warpCall { newTimestamp } = cheatcode.as_any().downcast_ref().unwrap();
400382

401-
tracing::info!(cheatcode = ?cheatcode.as_debug() , using_revive = ?using_revive);
402-
ctx.externalities.set_timestamp(newTimestamp);
383+
let clamped_timestamp = ctx.externalities.set_timestamp(newTimestamp);
403384

404-
cheatcode.dyn_apply(ccx, executor)
385+
warpCall { newTimestamp: clamped_timestamp }.dyn_apply(ccx, executor)
405386
}
406387

407388
t if using_revive && is::<chainIdCall>(t) => {
@@ -425,19 +406,30 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {
425406
cheatcode.as_any().downcast_ref().unwrap();
426407

427408
tracing::info!(cheatcode = ?cheatcode.as_debug(), using_revive = ?using_revive);
409+
let u64_max: U256 = U256::from(u64::MAX);
410+
let clamped_block_number = if blockNumber > u64_max {
411+
tracing::warn!(
412+
blockNumber = ?blockNumber,
413+
max = ?u64_max,
414+
"Block number exceeds u64::MAX. Clamping to u64::MAX."
415+
);
416+
u64_max
417+
} else {
418+
blockNumber
419+
};
428420

429421
// Validate blockNumber is not in the future
430422
let current_block = ctx.externalities.get_block_number();
431-
if blockNumber > current_block {
423+
if clamped_block_number > current_block {
432424
return Err(foundry_cheatcodes::Error::from(
433425
"block number must be less than or equal to the current block number",
434426
));
435427
}
436428

437-
let block_num_u64 = blockNumber.to::<u64>();
438-
ctx.externalities.set_blockhash(block_num_u64, blockHash);
429+
ctx.externalities.set_blockhash(clamped_block_number.to(), blockHash);
439430

440-
cheatcode.dyn_apply(ccx, executor)
431+
setBlockhashCall { blockNumber: clamped_block_number, blockHash }
432+
.dyn_apply(ccx, executor)
441433
}
442434
t if using_revive && is::<etchCall>(t) => {
443435
let etchCall { target, newRuntimeBytecode } =
@@ -669,15 +661,15 @@ fn select_revive(
669661

670662
let block_number = data.block.number;
671663
let timestamp = data.block.timestamp;
664+
ctx.externalities.set_timestamp(timestamp);
665+
ctx.externalities.roll(block_number, &mut *data.journaled_state.database);
672666

673667
ctx.externalities.execute_with(||{
674668
// Enable debug mode to bypass EIP-170 size checks during testing
675669
if data.cfg.limit_contract_code_size == Some(usize::MAX) {
676670
let debug_settings = DebugSettings::new(true, true, true);
677671
debug_settings.write_to_storage::<Runtime>();
678672
}
679-
System::set_block_number(block_number.saturating_to());
680-
Timestamp::set_timestamp(timestamp.saturating_to::<u64>() * 1000);
681673
<revive_env::Runtime as polkadot_sdk::pallet_revive::Config>::ChainId::set(
682674
&data.cfg.chain_id,
683675
);
@@ -703,7 +695,22 @@ fn select_revive(
703695
let account = H160::from_slice(address.as_slice());
704696
let account_id =
705697
AccountId32Mapper::<Runtime>::to_fallback_account_id(&account);
706-
let amount_pvm = sp_core::U256::from_little_endian(&amount.as_le_bytes()).min(u128::MAX.into());
698+
699+
let u128_max: U256 = U256::from(u128::MAX);
700+
let clamped_amount = if amount > u128_max {
701+
tracing::info!(
702+
address = ?address,
703+
requested = ?amount,
704+
actual = ?u128_max,
705+
"Migration: balance exceeds u128::MAX, clamping to u128::MAX. \
706+
pallet-revive uses u128 for balances."
707+
);
708+
u128_max
709+
} else {
710+
amount
711+
};
712+
713+
let amount_pvm = sp_core::U256::from_little_endian(&clamped_amount.as_le_bytes());
707714
Pallet::<Runtime>::set_evm_balance(&account, amount_pvm)
708715
.expect("failed to set evm balance");
709716

@@ -1045,6 +1052,30 @@ impl foundry_cheatcodes::CheatcodeInspectorStrategyExt for PvmCheatcodeInspector
10451052
}
10461053
};
10471054

1055+
let u128_max: U256 = U256::from(u128::MAX);
1056+
if input.value() > u128_max {
1057+
tracing::warn!(
1058+
caller = ?input.caller(),
1059+
value = ?input.value(),
1060+
max = ?u128_max,
1061+
"Create value exceeds u128::MAX. pallet-revive uses u128 for balances."
1062+
);
1063+
return Some(CreateOutcome {
1064+
result: InterpreterResult {
1065+
result: InstructionResult::Revert,
1066+
output: Bytes::from_iter(
1067+
format!(
1068+
"Create value {} exceeds u128::MAX ({}). pallet-revive uses u128 for balances.",
1069+
input.value(),
1070+
u128::MAX
1071+
).as_bytes(),
1072+
),
1073+
gas: Gas::new(input.gas_limit()),
1074+
},
1075+
address: None,
1076+
});
1077+
}
1078+
10481079
let gas_price_pvm =
10491080
sp_core::U256::from_little_endian(&U256::from(ecx.tx.gas_price).as_le_bytes());
10501081
let mut tracer = Tracer::new(state.expected_calls.clone(), state.expected_creates.clone());
@@ -1217,6 +1248,32 @@ impl foundry_cheatcodes::CheatcodeInspectorStrategyExt for PvmCheatcodeInspector
12171248

12181249
tracing::info!("running call on pallet-revive with {} {:#?}", ctx.runtime_mode, call);
12191250

1251+
let u128_max: U256 = U256::from(u128::MAX);
1252+
let call_value = call.call_value();
1253+
if call_value > u128_max {
1254+
tracing::warn!(
1255+
caller = ?call.caller,
1256+
target = ?call.target_address,
1257+
value = ?call_value,
1258+
max = ?u128_max,
1259+
"Call value exceeds u128::MAX. pallet-revive uses u128 for balances."
1260+
);
1261+
return Some(CallOutcome {
1262+
result: InterpreterResult {
1263+
result: InstructionResult::Revert,
1264+
output: Bytes::from_iter(
1265+
format!(
1266+
"Call value {} exceeds u128::MAX ({}). pallet-revive uses u128 for balances.",
1267+
call_value,
1268+
u128::MAX
1269+
).as_bytes(),
1270+
),
1271+
gas: Gas::new(call.gas_limit),
1272+
},
1273+
memory_offset: call.return_memory_offset.clone(),
1274+
});
1275+
}
1276+
12201277
let gas_price_pvm =
12211278
sp_core::U256::from_little_endian(&U256::from(ecx.tx.gas_price).as_le_bytes());
12221279
let mock_handler = MockHandlerImpl::new(

0 commit comments

Comments
 (0)