Skip to content

Commit ca6b8b0

Browse files
qiweiii0xrusowskygrandizzy
authored
feat(cheats): RLP (#11232)
* add rlp cheats * fmt * test: use in real block * style: fmt --------- Co-authored-by: 0xrusowsky <[email protected]> Co-authored-by: 0xrusowsky <[email protected]> Co-authored-by: grandizzy <[email protected]>
1 parent 5a6266a commit ca6b8b0

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed

crates/cheatcodes/assets/cheatcodes.json

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

crates/cheatcodes/spec/src/vm.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2997,6 +2997,13 @@ interface Vm {
29972997
/// Generates a ready-to-sign digest of human-readable typed data following the EIP-712 standard.
29982998
#[cheatcode(group = Utilities)]
29992999
function eip712HashTypedData(string calldata jsonData) external pure returns (bytes32 digest);
3000+
3001+
/// RLP encodes a list of bytes into an RLP payload.
3002+
#[cheatcode(group = Utilities)]
3003+
function toRlp(bytes[] calldata data) external pure returns (bytes memory);
3004+
/// RLP decodes an RLP payload into a list of bytes.
3005+
#[cheatcode(group = Utilities)]
3006+
function fromRlp(bytes calldata rlp) external pure returns (bytes[] memory data);
30003007
}
30013008
}
30023009

crates/cheatcodes/src/utils.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*
44
use alloy_dyn_abi::{DynSolType, DynSolValue, Resolver, TypedData, eip712_parser::EncodeType};
55
use alloy_ens::namehash;
66
use alloy_primitives::{B64, Bytes, I256, U256, aliases::B32, keccak256, map::HashMap};
7+
use alloy_rlp::{Decodable, Encodable};
78
use alloy_sol_types::SolValue;
89
use foundry_common::{TYPE_BINDING_PREFIX, fs};
910
use foundry_config::fs_permissions::FsAccessKind;
@@ -491,3 +492,25 @@ fn get_struct_hash(primary: &str, type_def: &String, abi_encoded_data: &Bytes) -
491492

492493
Ok(keccak256(&bytes_to_hash).to_vec())
493494
}
495+
496+
impl Cheatcode for toRlpCall {
497+
fn apply(&self, _state: &mut Cheatcodes) -> Result {
498+
let Self { data } = self;
499+
500+
let mut buf = Vec::new();
501+
data.encode(&mut buf);
502+
503+
Ok(Bytes::from(buf).abi_encode())
504+
}
505+
}
506+
507+
impl Cheatcode for fromRlpCall {
508+
fn apply(&self, _state: &mut Cheatcodes) -> Result {
509+
let Self { rlp } = self;
510+
511+
let decoded: Vec<Bytes> = Vec::<Bytes>::decode(&mut rlp.as_ref())
512+
.map_err(|e| fmt_err!("Failed to decode RLP: {e}"))?;
513+
514+
Ok(decoded.abi_encode())
515+
}
516+
}

testdata/cheats/Vm.sol

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

testdata/default/cheats/Rlp.t.sol

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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 Rlp is DSTest {
8+
Vm constant vm = Vm(HEVM_ADDRESS);
9+
10+
function testToRlp() public {
11+
bytes[] memory data = new bytes[](2);
12+
data[0] = hex"01";
13+
data[1] = hex"02";
14+
15+
bytes memory rlp = vm.toRlp(data);
16+
17+
// Assert the expected RLP encoding for [0x01, 0x02]
18+
// 0xc2 = list with 2 bytes total length
19+
// 0x01 = first byte
20+
// 0x02 = second byte
21+
assertEq(rlp, hex"c20102");
22+
}
23+
24+
function testFromRlp() public {
25+
// RLP encoded [0x01, 0x02]
26+
bytes memory rlp = hex"c20102";
27+
28+
bytes[] memory decoded = vm.fromRlp(rlp);
29+
assertEq(decoded.length, 2);
30+
assertEq(decoded[0], hex"01");
31+
assertEq(decoded[1], hex"02");
32+
}
33+
34+
function testRoundTrip() public {
35+
bytes[] memory original = new bytes[](3);
36+
original[0] = hex"deadbeef";
37+
original[1] = hex"cafebabe";
38+
original[2] = hex"01020304";
39+
40+
bytes memory rlp = vm.toRlp(original);
41+
bytes[] memory decoded = vm.fromRlp(rlp);
42+
43+
assertEq(decoded.length, original.length);
44+
for (uint256 i = 0; i < original.length; i++) {
45+
assertEq(decoded[i], original[i]);
46+
}
47+
}
48+
49+
function testDecodeBlockHeader() public {
50+
// cast block 23270177 --raw
51+
bytes memory blockHeader =
52+
hex"f9027da01581f4448b16694d5a728161cd65f8c80b88f5352a6f5bd2d2315b970582958da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794dadb0d80178819f2319190d340ce9a924f783711a010d2afa5dabcf2dbfe3aa82b758427938e07880bd6fef3c82c404d0dd7c3f0f3a0f81230c715a462c827898bf2e337982907a7af90e5be20f911785bda05dab93ca0740f11bc75cf25e40d78d892d2e03083eaa573e5b4c26913fcc1b833db854c94b9010085f734fb06ea8fe377abbcb2e27f9ac99751ba817dc327327db101fd76f964ed0b7ca161f148fc165b9e5b575dc7473f17f4b8ebbf4a7b02b3e1e642197f27b2af54680834449abaf833619ac7d18afb50b19d5f6944dca0dc952edfdd9837573783c339ee6a36353ce6e536eaaf29fcd569c426091d4e24568dc353347f98c74fb6f8c91d68d358467c437563f66566377fe6c3f9e8301dbeb5fc7e7adee7a85ef5f8fa905cedbaf26601e21ba91646cac4034601e51d889d49739ee6990943a6a41927660f68e1f50b9f9209ee29551a7dae478d88e0547eefc83334ea770bb6fbac620fc47479c2c59389622bf32f55e36a75e56a5fc47c38bf8ef211fc0e8084016313218402af50e883fc53b78468b5ea9b974275696c6465724e657420284e65746865726d696e6429a0580ca94e91c0e7aef26ffb0c86f6ae48ef40df6dd1629f203a1930e0ce0be9d188000000000000000084479c1e2aa00345740e1b79edb2fbb3a20220e1a497ea9bb82aaba7dc7a881f7f3cae8a8ea38080a06675ad2a40134499a753924a04b75898ae09efc6fba6b3d7a506203042cb7611a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
53+
54+
bytes[] memory decoded = vm.fromRlp(blockHeader);
55+
56+
// Verify key fields against known values from block 23270177
57+
assertEq(decoded.length, 21);
58+
assertEq(decoded[0], hex"1581f4448b16694d5a728161cd65f8c80b88f5352a6f5bd2d2315b970582958d", "parentHash");
59+
assertEq(decoded[1], hex"1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "uncleHash");
60+
assertEq(decoded[2], hex"dadb0d80178819f2319190d340ce9a924f783711", "coinbase");
61+
assertEq(decoded[3], hex"10d2afa5dabcf2dbfe3aa82b758427938e07880bd6fef3c82c404d0dd7c3f0f3", "stateRoot");
62+
assertEq(decoded[4], hex"f81230c715a462c827898bf2e337982907a7af90e5be20f911785bda05dab93c", "transactionsRoot");
63+
assertEq(decoded[5], hex"740f11bc75cf25e40d78d892d2e03083eaa573e5b4c26913fcc1b833db854c94", "receiptsRoot");
64+
65+
{
66+
// Verify logsBloom (256 bytes)
67+
bytes memory logsBloom = decoded[6];
68+
assertEq(logsBloom.length, 256, "logsBloom length");
69+
_checkLogsBloom(logsBloom, 0, 0x85f734fb06ea8fe377abbcb2e27f9ac99751ba817dc327327db101fd76f964ed);
70+
_checkLogsBloom(logsBloom, 1, 0x0b7ca161f148fc165b9e5b575dc7473f17f4b8ebbf4a7b02b3e1e642197f27b2);
71+
_checkLogsBloom(logsBloom, 2, 0xaf54680834449abaf833619ac7d18afb50b19d5f6944dca0dc952edfdd983757);
72+
_checkLogsBloom(logsBloom, 3, 0x3783c339ee6a36353ce6e536eaaf29fcd569c426091d4e24568dc353347f98c7);
73+
_checkLogsBloom(logsBloom, 4, 0x4fb6f8c91d68d358467c437563f66566377fe6c3f9e8301dbeb5fc7e7adee7a8);
74+
_checkLogsBloom(logsBloom, 5, 0x5ef5f8fa905cedbaf26601e21ba91646cac4034601e51d889d49739ee6990943);
75+
_checkLogsBloom(logsBloom, 6, 0xa6a41927660f68e1f50b9f9209ee29551a7dae478d88e0547eefc83334ea770b);
76+
_checkLogsBloom(logsBloom, 7, 0xb6fbac620fc47479c2c59389622bf32f55e36a75e56a5fc47c38bf8ef211fc0e);
77+
}
78+
79+
assertEq(decoded[7], hex"", "difficulty");
80+
assertEq(decoded[8], hex"01631321", "number");
81+
assertEq(decoded[9], hex"02af50e8", "gasLimit");
82+
assertEq(decoded[10], hex"fc53b7", "gasUsed");
83+
assertEq(decoded[11], hex"68b5ea9b", "timestamp");
84+
assertEq(decoded[12], hex"4275696c6465724e657420284e65746865726d696e6429", "extraData");
85+
assertEq(decoded[13], hex"580ca94e91c0e7aef26ffb0c86f6ae48ef40df6dd1629f203a1930e0ce0be9d1", "mixHash");
86+
assertEq(decoded[14], hex"0000000000000000", "nonce");
87+
assertEq(decoded[15], hex"479c1e2a", "baseFee");
88+
assertEq(decoded[16], hex"0345740e1b79edb2fbb3a20220e1a497ea9bb82aaba7dc7a881f7f3cae8a8ea3", "withdrawalsHash");
89+
assertEq(decoded[17], hex"", "blobGasUsed");
90+
assertEq(decoded[18], hex"", "excessBlobGas");
91+
assertEq(decoded[19], hex"6675ad2a40134499a753924a04b75898ae09efc6fba6b3d7a506203042cb7611", "parentBeaconRoot");
92+
assertEq(decoded[20], hex"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "requestsHash");
93+
}
94+
95+
function _checkLogsBloom(bytes memory data, uint256 n, uint256 expected) internal {
96+
uint256 offset = (n + 1) * 32;
97+
bytes32 result;
98+
assembly {
99+
result := mload(add(data, offset))
100+
}
101+
102+
assertEq(result, bytes32(expected), string.concat("logsBloom[", vm.toString(n), "]"));
103+
}
104+
}

0 commit comments

Comments
 (0)