Skip to content

Commit 217e905

Browse files
authored
Add blockhash cheatcode
1 parent 65bf28c commit 217e905

File tree

6 files changed

+96
-2
lines changed

6 files changed

+96
-2
lines changed

.github/workflows/nextest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
test:
4444
name: test ${{ matrix.name }}
4545
runs-on: ${{ matrix.runner_label }}
46-
timeout-minutes: 90
46+
timeout-minutes: 120
4747
needs: [matrices]
4848
strategy:
4949
fail-fast: false

crates/forge/tests/it/revive/cheats_individual.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ macro_rules! revive_cheat_test_original {
4141

4242
revive_cheat_test!(test_custom_nonce, "Nonce");
4343
revive_cheat_test_original!(test_nonce, "Nonce");
44+
revive_cheat_test!(test_coinbase, "CoinBase");
45+
revive_cheat_test!(test_set_custom_blockhash, "SetBlockhash");
46+
revive_cheat_test_original!(test_set_blockhash, "SetBlockhash");

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use foundry_cheatcodes::{
1010
Vm::{
1111
chainIdCall, coinbaseCall, dealCall, etchCall, getNonce_0Call, loadCall, polkadotSkipCall,
1212
pvmCall, resetNonceCall, revertToStateAndDeleteCall, revertToStateCall, rollCall,
13-
setNonceCall, setNonceUnsafeCall, snapshotStateCall, storeCall, warpCall,
13+
setBlockhashCall, setNonceCall, setNonceUnsafeCall, snapshotStateCall, storeCall, warpCall,
1414
},
1515
journaled_account, precompile_error,
1616
};
@@ -380,6 +380,25 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {
380380

381381
cheatcode.dyn_apply(ccx, executor)
382382
}
383+
t if is::<setBlockhashCall>(t) => {
384+
let &setBlockhashCall { blockNumber, blockHash } =
385+
cheatcode.as_any().downcast_ref().unwrap();
386+
387+
tracing::info!(cheatcode = ?cheatcode.as_debug(), using_pvm = ?using_pvm);
388+
389+
// Validate blockNumber is not in the future
390+
let current_block = ctx.externalities.get_block_number();
391+
if blockNumber > current_block {
392+
return Err(foundry_cheatcodes::Error::from(
393+
"block number must be less than or equal to the current block number",
394+
));
395+
}
396+
397+
let block_num_u64 = blockNumber.to::<u64>();
398+
ctx.externalities.set_blockhash(block_num_u64, blockHash);
399+
400+
cheatcode.dyn_apply(ccx, executor)
401+
}
383402
t if using_pvm && is::<etchCall>(t) => {
384403
let etchCall { target, newRuntimeBytecode } =
385404
cheatcode.as_any().downcast_ref().unwrap();

crates/revive-strategy/src/state.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ impl TestEnv {
109109
});
110110
}
111111

112+
pub fn get_block_number(&mut self) -> U256 {
113+
// Get block number in pallet-revive runtime.
114+
self.0.lock().unwrap().externalities.execute_with(|| U256::from(System::block_number()))
115+
}
116+
112117
pub fn set_timestamp(&mut self, new_timestamp: U256) {
113118
// Set timestamp in pallet-revive runtime (milliseconds).
114119
self.0.lock().unwrap().externalities.execute_with(|| {
@@ -239,4 +244,19 @@ impl TestEnv {
239244
BlockAuthor::set(&account_id32);
240245
});
241246
}
247+
248+
pub fn set_blockhash(&mut self, block_number: u64, block_hash: FixedBytes<32>) {
249+
self.0.lock().unwrap().externalities.execute_with(|| {
250+
use polkadot_sdk::frame_system::BlockHash;
251+
252+
let hash = sp_core::H256::from_slice(block_hash.as_slice());
253+
BlockHash::<Runtime>::insert(block_number, hash);
254+
});
255+
}
256+
257+
pub fn is_contract(&self, address: Address) -> bool {
258+
self.0.lock().unwrap().externalities.execute_with(|| {
259+
AccountInfo::<Runtime>::load_contract(&H160::from_slice(address.as_slice())).is_some()
260+
})
261+
}
242262
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
pragma solidity ^0.8.18;
3+
4+
import "ds-test/test.sol";
5+
import "../../cheats/Vm.sol";
6+
7+
contract BlockCoinBase {
8+
function coinbase() public view returns (address) {
9+
return block.coinbase;
10+
}
11+
}
12+
13+
contract CoinbaseTest is DSTest {
14+
Vm constant vm = Vm(HEVM_ADDRESS);
15+
16+
function testCoinbase() public {
17+
vm.pvm(true);
18+
BlockCoinBase coinbase = new BlockCoinBase();
19+
vm.coinbase(0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8);
20+
assertEq(coinbase.coinbase(), 0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8, "coinbase failed");
21+
}
22+
23+
function testCoinbaseFuzzed(address who) public {
24+
vm.coinbase(who);
25+
BlockCoinBase coinbase = new BlockCoinBase();
26+
assertEq(coinbase.coinbase(), who, "coinbase failed");
27+
}
28+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
pragma solidity ^0.8.18;
3+
4+
import "ds-test/test.sol";
5+
import "cheats/Vm.sol";
6+
7+
contract BlockHash {
8+
function getBlockhash(uint256 blockNumber) public view returns (bytes32) {
9+
return blockhash(blockNumber);
10+
}
11+
}
12+
13+
contract SetBlockhash is DSTest {
14+
Vm constant vm = Vm(HEVM_ADDRESS);
15+
16+
function testSetBlockhash() public {
17+
vm.pvm(true);
18+
vm.roll(10);
19+
BlockHash blockHash = new BlockHash();
20+
bytes32 expectedHash = 0x1234567890123456789012345678901234567890123456789012345678901234;
21+
vm.setBlockhash(9, expectedHash);
22+
assertEq(blockHash.getBlockhash(9), expectedHash);
23+
}
24+
}

0 commit comments

Comments
 (0)