From 676868449bb89e5fc922a08c520257c3a53ea767 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Tue, 31 Oct 2023 16:03:14 -0300 Subject: [PATCH] Implement scale-info for all primitive types --- Cargo.lock | 40 +-- chains/ethereum/playground/Cargo.toml | 28 ++ chains/ethereum/playground/res/Example.sol | 15 + .../ethereum/playground/res/TestContract.yul | 330 ++++++++++++++++++ chains/ethereum/playground/src/compiler.rs | 54 +++ chains/ethereum/playground/src/main.rs | 116 ++++++ chains/ethereum/primitives/Cargo.toml | 40 ++- chains/ethereum/primitives/src/block.rs | 13 +- chains/ethereum/primitives/src/bytes.rs | 2 +- chains/ethereum/primitives/src/eth_hash.rs | 54 +++ chains/ethereum/primitives/src/eth_uint.rs | 102 ++++++ chains/ethereum/primitives/src/lib.rs | 26 +- chains/ethereum/primitives/src/log.rs | 12 +- .../ethereum/primitives/src/storage_proof.rs | 12 +- chains/ethereum/primitives/src/tx_receipt.rs | 13 +- 15 files changed, 805 insertions(+), 52 deletions(-) create mode 100644 chains/ethereum/playground/Cargo.toml create mode 100644 chains/ethereum/playground/res/Example.sol create mode 100644 chains/ethereum/playground/res/TestContract.yul create mode 100644 chains/ethereum/playground/src/compiler.rs create mode 100644 chains/ethereum/playground/src/main.rs create mode 100644 chains/ethereum/primitives/src/eth_hash.rs create mode 100644 chains/ethereum/primitives/src/eth_uint.rs diff --git a/Cargo.lock b/Cargo.lock index d0a13f07..9886420a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1476,9 +1476,9 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", @@ -2033,24 +2033,6 @@ dependencies = [ "triehash", ] -[[package]] -name = "ethereum" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e04d24d20b8ff2235cffbf242d5092de3aa45f77c5270ddbfadd2778ca13fea" -dependencies = [ - "bytes", - "ethereum-types", - "hash-db 0.16.0", - "hash256-std-hasher", - "parity-scale-codec", - "rlp", - "scale-info", - "serde", - "sha3", - "trie-root", -] - [[package]] name = "ethereum-types" version = "0.14.1" @@ -2339,7 +2321,7 @@ checksum = "d9aa9791c426a506274390c425afcbbd6f620ce7daa7d30f3f99a0d706f8f29b" dependencies = [ "auto_impl", "environmental", - "ethereum 0.14.0", + "ethereum", "evm-core", "evm-gasometer", "evm-runtime", @@ -5087,7 +5069,7 @@ dependencies = [ "anyhow", "bytes", "const-hex", - "ethereum 0.14.0", + "ethereum", "ethereum-types", "parity-scale-codec", "rosetta-config-astar", @@ -5207,12 +5189,18 @@ version = "0.1.0" dependencies = [ "bytes", "const-hex", - "ethereum 0.15.0", - "ethereum-types", + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-num-traits", + "impl-rlp", + "impl-serde", "parity-scale-codec", + "primitive-types", "scale-info", "serde", "thiserror", + "uint", ] [[package]] @@ -5387,9 +5375,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +checksum = "724fd11728a3804e9944b14cab63825024c40bf42f8af87c8b5d97c4bbacf426" dependencies = [ "alloy-rlp", "proptest", diff --git a/chains/ethereum/playground/Cargo.toml b/chains/ethereum/playground/Cargo.toml new file mode 100644 index 00000000..509df692 --- /dev/null +++ b/chains/ethereum/playground/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "playground" +version = "0.1.0" +edition = "2021" +license = "MIT" +repository = "https://github.com/analog-labs/chain-connectors" +description = "Ethereum integration tests." + +[features] +default = ["sputnik-vm"] +sputnik-vm = ["rosetta-ethereum-executor/sputnik-evm"] +rust-vm = ["rosetta-ethereum-executor/rust-evm"] + +[dependencies] +async-trait = "0.1" +ethers-solc = { version = "2.0", features = ["async", "project-util"] } +eyre = "0.6" +hex-literal = "0.4" +inkwell = { version = "0.2.0", features = ["target-webassembly", "no-libffi-linking", "llvm15-0"] } +jsonrpsee = { version = "0.20", default-features = false, features = ["macros", "client"] } +rosetta-ethereum-executor = { workspace = true, features = ["jsonrpsee"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1.32", features = ["rt-multi-thread", "macros"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["tracing-log", "parking_lot"] } +url = "2.4.0" diff --git a/chains/ethereum/playground/res/Example.sol b/chains/ethereum/playground/res/Example.sol new file mode 100644 index 00000000..8f007eb3 --- /dev/null +++ b/chains/ethereum/playground/res/Example.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.5.0 <0.9.0; + +contract Example { + uint256 private _value; + + function value() public view returns (uint256) { + return _value; + } + + function setValue(uint256 newValue) public { + _value = newValue; + } +} diff --git a/chains/ethereum/playground/res/TestContract.yul b/chains/ethereum/playground/res/TestContract.yul new file mode 100644 index 00000000..e208832d --- /dev/null +++ b/chains/ethereum/playground/res/TestContract.yul @@ -0,0 +1,330 @@ +object "TestContract" { + code { + // Store the creator in slot zero. + sstore(0, caller()) + + // Deploy the contract + datacopy(0, dataoffset("Runtime"), datasize("Runtime")) + return(0, datasize("Runtime")) + } + object "Runtime" { + code { + // Dispatcher + switch selector() + case 0x185c38a4 /* "revertWithMessage()" */ { + revertWithMessage() + } + case 0xc06a97cb /* "revertWithoutMessage()" */ { + revertWithoutMessage() + } + case 0xb1ae6db0 /* "invalidOpcode()" */ { + invalidOpcode() + } + case 0x8c192bc9 /* "stackUnderflow()" */ { + stackUnderflow() + } + case 0xddf91d0c /* stackOverflow() */ { + stackOverflow() + } + case 0xd502dc8e /* invalidJumpDest() */ { + invalidJumpDest() + } + case 0x31fe52e8 /* outOfGas() */ { + outOfGas() + } + case 0x71663476 /* resultTest() */ { + resultTest() + } + case 0xfe3fb5c7 /* sha256Precompiled() */ { + sha256Precompiled() + } + case 0x4fae1f41 /* ripemd160Precompiled() */ { + ripemd160Precompiled() + } + case 0x6cd5c39b /* deployContract() */ { + let addr := deployContract() + mstore(0, addr) + return(12, 20) + } + case 0xa90bc19e /* deployCopy() */ { + let addr := deployCopy() + mstore(0, addr) + return(12, 20) + } + case 0x800d8e2b /* deployCopy(uint256) */ { + let salt := calldataload(4) + let addr := deployCopy2(salt) + mstore(0, addr) + return(12, 20) + } + case 0xea879634 /* getCode() */ { + let size := getCode(0) + return(0, size) + } + case 0xf509c2d2 /* getCodeWithConstructor() */ { + let size := getCodeWithConstructor(0) + return(0, size) + } + case 0x677342ce /* sqrt(uint256) */ { + let result := sqrt(calldataload(4)) + mstore(0, result) + return(0, 32) + } + default { + revert(0, 0) + } + + /* ---------- calldata decoding functions ----------- */ + function selector() -> s { + s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000) + } + + // Revert with message "something is wrong" + function revertWithMessage() { + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Function selector for Error(string) + mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) // Data offset + mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000012) // String length + mstore(0x44, 0x736f6d657468696e672069732077726f6e670000000000000000000000000000) // String data "something is wrong" + revert(0, 0x60) + } + + // Revert with empty message + function revertWithoutMessage() { + revert(0, 0) + } + + // Trigger Invalid OPCODE error + function invalidOpcode() { + // https://ethereum.org/en/developers/docs/evm/opcodes/ + verbatim_0i_0o(hex"A5") // Doesn't exists A5 opcode + return(0, 0x0) + } + + // Trigger Stack Underflow error + function stackUnderflow() { + verbatim_1i_0o(hex"56", dataoffset("UnderflowCode")) // JUMP + return(0, 0) + } + + // Trigger Stack Overflow error + function stackOverflow() { + verbatim_1i_0o(hex"56", dataoffset("OverflowCode")) // JUMP + return(0, 0) + } + + // Trigger Invalid JUMPDEST error + function invalidJumpDest() { + verbatim_1i_0o(hex"56", dataoffset("InvalidJumpCode")) // JUMP + return(0, 0) + } + + // Trigger out of gas error + function outOfGas() { + verbatim_1i_0o(hex"56", dataoffset("OutOfGasCode")) // JUMP + return(0, 0) + } + + // Calls the SHA256 precompiled contract + function sha256Precompiled() { + let size := sub(calldatasize(), 4) + calldatacopy( + 0, // out-memory ptr + 4, // calldata offset + size // calldata size + ) + let result := staticcall( + gas(), // Gas available + 0x02, // contract address (sha256 precompiled) + 0, // in-memory ptr + size, // in-memory size + 0, // out-memory ptr + 32 // out-memory size + ) + if iszero(result) { + revert(0, 0x20) + } + return(0, 0x20) + } + + // Calls the RIPEMD-160 precompiled contract + function ripemd160Precompiled() { + let size := sub(calldatasize(), 4) + calldatacopy( + 0, // out-memory ptr + 4, // calldata offset + size // calldata size + ) + let result := staticcall( + gas(), // Gas the call can use + 0x03, // contract address (RIPEMD-160 precompiled) + 0, // in-memory ptr + size, // in-memory size + 0, // out-memory ptr + 32 // out-memory size + ) + if iszero(result) { + revert(0, 0x20) + } + return(12, 20) + } + + // Deploy a copy of self a contract + function deployCopy() -> addr { + // Copy the constructor to memory + let size := getCodeWithConstructor(0) + addr := create( + callvalue(), // value to transfer to the contract + 0, // in-memory ptr + size // in-memory size + ) + if iszero(addr) { + revert(0, size) + } + } + + // Create a contract using CREATE2 opcode + function deployCopy2(salt) -> addr { + // Copy the constructor to memory + let size := getCodeWithConstructor(0) + addr := create2( + callvalue(),// value to transfer to the contract + 0, // in-memory ptr + size, // in-memory size + salt // salt + ) + if iszero(addr) { + revert(0, size) + } + } + + // Deploy a contract based on calldata parameters + function deployContract() -> addr { + // read codesize + let size := sub(calldatasize(), 4) + + // Store constructor + let constructor := or( + // PUSH2 [codesize] PUSH1 0 DUP2 CALLER DUP3 SSTORE PUSH1 0x0e DUP3 CODECOPY RETURN + 0x610000600081338255600e8239f3000000000000000000000000000000000000, + shl(232, size) + ) + + // Store bytecode + calldatacopy(0, 14, size) + + // Deploy contract + addr := create( + callvalue(), // value to transfer to the contract + 0, // in-memory ptr + add(size, 14) // in-memory size + ) + if iszero(addr) { + revert(0, size) + } + } + + // Return self bytecode + function getCode(ptr) -> size { + // Copy the code to memory + codecopy( + ptr, // out-memory ptr + 0, // code offset + codesize() // code size + ) + size := codesize() + } + + // Return the constructor for deploying a copy of self. + function getCodeWithConstructor(ptr) -> size { + // Copy the code to memory + size := getCode(add(ptr, 14)) + + // Load code size into constructor + let constructor := or( + // PUSH2 [codesize] PUSH1 0 DUP2 CALLER DUP3 SSTORE PUSH1 0x0e DUP3 CODECOPY RETURN + 0x610000600081338255600e8239f3000000000000000000000000000000000000, + shl(232, size) + ) + + // Store constructor in memory between bytes [ptr, ..ptr + 13] + mstore(0, or(constructor, mload(ptr))) + + // Add constructor size to final bytecode size + size := add(size, 13) + } + + /// Returns the maximum result size possible given the available gas + /// + /// # Description + /// Calculate the gas cost of memory expansion given the number of 256-bit words `x`: + /// f(x) -> 3 * x + (x^2 / 512) + /// + /// Calculate the number of 256-bit words given the available gas `y`: + /// f(y) -> sqrt(512 * (y + 1152)) - 768 + /// + /// reference: https://ethereum.github.io/yellowpaper/paper.pdf Appendix H + function resultTest() { + // Current available GAS minus the gas necessary to calculate the sqrt (~664 gas units). + let g := sub(gas(), 664) + // Calculate the number of 256-bit words given the available gas `g` + let words := sub(sqrt(shl(9, add(g, 1152))), 768) + // Convert 256-bit words to bytes by multiplying it by 32 + let size := shl(5, words) + return(0, size) + } + + // Square Root (worst case gas cost is 555) + // https://ethereum-magicians.org/t/eip-7054-gas-efficient-square-root-calculation-with-binary-search-approach/14539 + function sqrt(x) -> res { + let xx := x + let r := 1 + if lt(0x100000000000000000000000000000000, xx) { + xx := shr(128, xx) + r := shl(64, r) + } + if lt(0x10000000000000000, xx) { + xx := shr(64, xx) + r := shl(32, r) + } + if lt(0x100000000, xx) { + xx := shr(32, xx) + r := shl(16, r) + } + if lt(0x10000, xx) { + xx := shr(16, xx) + r := shl(8, r) + } + if lt(0x100, xx) { + xx := shr(8, xx) + r := shl(4, r) + } + if lt(0x10, xx) { + xx := shr(4, xx) + r := shl(2, r) + } + if lt(0x8, xx) { + r := shl(1, r) + } + r := shr(1, add(r, div(x, r))) + r := shr(1, add(r, div(x, r))) + r := shr(1, add(r, div(x, r))) + r := shr(1, add(r, div(x, r))) + r := shr(1, add(r, div(x, r))) + r := shr(1, add(r, div(x, r))) + r := shr(1, add(r, div(x, r))) + res := div(x, r) + if lt(r, res) { + res := r + } + } + + function require(condition) { + if iszero(condition) { revert(0, 0) } + } + } + data "InvalidJumpCode" hex"00" // STOP + data "OutOfGasCode" hex"5B6003580356" // JUMPDEST PUSH1 0x03 PC SUB JUMP + data "UnderflowCode" hex"5B010101010101" // JUMPDEST ADD ADD ADD ADD ADD ADD + data "OverflowCode" hex"5B456004580356" // JUMPDEST GASLIMIT PUSH1 0x04 PC SUB JUMP + } +} diff --git a/chains/ethereum/playground/src/compiler.rs b/chains/ethereum/playground/src/compiler.rs new file mode 100644 index 00000000..7a8ad733 --- /dev/null +++ b/chains/ethereum/playground/src/compiler.rs @@ -0,0 +1,54 @@ +use ethers_solc::{ + artifacts::{Optimizer, OptimizerDetails, YulDetails}, + ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, SolcConfig, +}; +use std::path::Path; + +// #[allow(clippy::unwrap_used)] +pub fn compile(dirname: &Path) -> eyre::Result<()> { + let paths = ProjectPathsConfig::builder().sources(dirname).build()?; + + // Solidity compiler settings + let mut solc_config = SolcConfig::builder().build(); + solc_config.settings.metadata = None; + solc_config.settings.remappings = Vec::with_capacity(0); + solc_config.settings.evm_version = Some(EvmVersion::Berlin); + solc_config.settings.optimizer = Optimizer { + enabled: Some(true), + runs: Some(1000), + details: Some(OptimizerDetails { + peephole: Some(true), + inliner: Some(true), + jumpdest_remover: Some(true), + order_literals: Some(true), + deduplicate: Some(true), + cse: Some(true), + constant_optimizer: Some(true), + yul: Some(true), + yul_details: Some(YulDetails { stack_allocation: Some(true), optimizer_steps: None }), + }), + }; + + // Configure the project with all its paths, solc, cache etc. + let project = Project::builder() + .solc_config(solc_config) + .paths(paths) + .ephemeral() + .no_artifacts() + .build()?; + + // Confile the project + let output = project.compile()?; + + for (id, artifact) in output.compiled_artifacts().artifacts::() { + println!("\n{}", id.identifier()); + + if let Some(bytes) = artifact.bytecode.as_ref().map(|bytecode| bytecode.clone().object) { + println!("bytecode: {bytes:?}"); + } + } + + Ok(()) +} +// 0x608060405234801561001057600080fd5b5060c68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80633fa4f24514603757806355241077146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506087565b005b60008054905090565b806000819055505056fea265627a7a723158209b607a766a9486da8fcc06a7966ee6dff7bbcc4ec2b30a30213214eefff2f44364736f6c63430005110032 +// 0x608060405234801561000f575f80fd5b506101438061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80633fa4f245146100385780635524107714610056575b5f80fd5b610040610072565b60405161004d919061009b565b60405180910390f35b610070600480360381019061006b91906100e2565b61007a565b005b5f8054905090565b805f8190555050565b5f819050919050565b61009581610083565b82525050565b5f6020820190506100ae5f83018461008c565b92915050565b5f80fd5b6100c181610083565b81146100cb575f80fd5b50565b5f813590506100dc816100b8565b92915050565b5f602082840312156100f7576100f66100b4565b5b5f610104848285016100ce565b9150509291505056fea2646970667358221220457ec1632ab7440f507c91944cbe7c552ae70eca4af7799552d11c077e98168c64736f6c63430008160033 diff --git a/chains/ethereum/playground/src/main.rs b/chains/ethereum/playground/src/main.rs new file mode 100644 index 00000000..79b80ea3 --- /dev/null +++ b/chains/ethereum/playground/src/main.rs @@ -0,0 +1,116 @@ +mod compiler; + +use std::path::PathBuf; + +use jsonrpsee::ws_client::WsClientBuilder; +use rosetta_ethereum_executor::{ + backend::{jsonrpsee::Adapter, AtBlock, EthereumRpc, TransactionCall}, + primitives::{Address, Bytes, H256, U256, U64}, +}; + +#[cfg(all(feature = "sputnik-vm", not(feature = "rust-vm")))] +use rosetta_ethereum_executor::vms::SputnikEVM as EVM; + +#[cfg(feature = "rust-vm")] +use rosetta_ethereum_executor::vms::RustEVM as EVM; + +#[allow(clippy::too_many_lines)] +#[tokio::main] +async fn main() -> eyre::Result<()> { + tracing_subscriber::FmtSubscriber::builder() + .with_level(true) + .with_max_level(tracing::Level::TRACE) + .init(); + + let solidity_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("res"); + println!("solidity_dir: {solidity_dir:?}"); + let _ = compiler::compile(&solidity_dir); + + let uri = url::Url::parse("ws://127.0.0.1:8545")?; + let client = Adapter(WsClientBuilder::default().build(uri).await?); + + let chain_id = client.chain_id().await?; + println!("chain_id: {chain_id:?}\n"); + let chain_id = u64::try_from(chain_id).map_err(|error| eyre::format_err!("{error}"))?; + + let result = client + .get_proof( + Address::from(hex_literal::hex!("34964a63A099F8DE44AcD5318374c6395c052734")), + &[], + AtBlock::Latest, + ) + .await?; + println!("account: {}\n", serde_json::to_string_pretty(&result)?); + + let result = client + .storage( + Address::from(hex_literal::hex!("34964a63A099F8DE44AcD5318374c6395c052734")), + H256::zero(), + AtBlock::Latest, + ) + .await?; + println!("storage: {}\n", serde_json::to_string_pretty(&result)?); + + let result = client + .create_access_list( + &TransactionCall { + from: Some(Address::zero()), + to: Some(Address::from(hex_literal::hex!( + "99d6b5638DD27BC2fE201FB6854C61F6b698C403" + ))), + gas_limit: Some(U64::MAX), + gas_price: None, + value: Some(U256::zero()), + data: Some(Bytes::from(hex_literal::hex!( + "ee58e99d0000000000000000000000000000000000000000000000000000000000000001" + ))), + chain_id: Some(chain_id.into()), + ..TransactionCall::default() + }, + AtBlock::Latest, + ) + .await?; + println!("access list: {}\n", serde_json::to_string_pretty(&result)?); + + let result = client.block(AtBlock::Latest).await?; + println!("block: {}\n", serde_json::to_string_pretty(&result)?); + + // Contract Addr 21030 23630 + // let contract_addr = hex_literal::hex!("34964a63A099F8DE44AcD5318374c6395c052734"); + // let data = hex_literal::hex!("fe3fb5c7"); + + let contract_addr = hex_literal::hex!("e0607131aBEE20c0d351EE83AB981091798FdA4C"); + let data = hex_literal::hex!( + "ee58e99d0000000000000000000000000000000000000000000000000000000000000001" + ); + let gas_limit = 0x0000_0fff_ffff_u64; + + let tx = TransactionCall { + from: Some(Address::zero()), + to: Some(Address::from(contract_addr)), + gas_limit: Some(U64::from(gas_limit)), + gas_price: None, + value: Some(U256::zero()), + data: Some(Bytes::from(data)), + chain_id: Some(chain_id.into()), + ..TransactionCall::default() + }; + + let result = client.call(&tx, AtBlock::Latest).await; + let gas_used = client.estimate_gas(&tx, AtBlock::Latest).await?; + if let Ok(result) = result { + println!("\n\ngas: {gas_used}\n{}\n", serde_json::to_string_pretty(&result)?); + } else { + println!("\n\ngas: {gas_used}\n{result:?}\n"); + } + + #[cfg(feature = "rust-vm")] + let mut evm = EVM::new(client).await?; + + #[cfg(all(feature = "sputnik-vm", not(feature = "rust-vm")))] + let mut evm = EVM::new(client); + + let res = evm.call(&tx, AtBlock::Latest).await; + println!("{res:?}"); + Ok(()) +} diff --git a/chains/ethereum/primitives/Cargo.toml b/chains/ethereum/primitives/Cargo.toml index 8ae5ad1d..cce47709 100644 --- a/chains/ethereum/primitives/Cargo.toml +++ b/chains/ethereum/primitives/Cargo.toml @@ -9,8 +9,15 @@ description = "Ethereum Connector Primitives" [dependencies] bytes = { package = "bytes", version = "1.5", default-features = false } const-hex = { version = "1.9", default-features = false, features = ["alloc"] } -ethereum = { version = "0.15", default-features = false } -ethereum-types = { version = "0.14", default-features = false, features = ["num-traits"] } +ethbloom = { version = "0.13", default-features = false } +primitive-types = { version = "0.12", default-features = false, features = ["byteorder", "rustc-hex", "num-traits"] } +uint = { version = "0.9", default-features = false } + +fixed-hash = { version = "0.8", default-features = false, features = ["byteorder", "rustc-hex"] } +impl-codec-macro = { package = "impl-codec", version = "0.6", default-features = false, optional = true } +impl-num-traits = { version = "0.1", default-features = false } +impl-rlp-macro = { package = "impl-rlp", version = "0.3", default-features = false, optional = true } +impl-serde-macro = { package = "impl-serde", version = "0.4", default-features = false, optional = true } parity-scale-codec = { workspace = true, features = ["derive", "bytes"], optional = true } scale-info = { version = "2.9", default-features = false, features = ["derive"], optional = true } @@ -20,18 +27,31 @@ thiserror = { version = "1.0", optional = true } [features] default = ["std"] with-codec = [ - "parity-scale-codec", - "scale-info", - "ethereum/with-codec", - "ethereum-types/codec", - "ethereum-types/scale-info", + "dep:parity-scale-codec", + "dep:scale-info", + "dep:impl-codec-macro", + "ethbloom/codec", + "primitive-types/codec", + "primitive-types/scale-info", +] +with-serde = [ + "dep:serde", + "dep:impl-serde-macro", + "ethbloom/serialize", + "primitive-types/serde_no_std", +] +with-rlp = [ + "dep:impl-rlp-macro", + "ethbloom/rlp", + "primitive-types/rlp", ] -with-serde = ["serde", "ethereum/with-serde", "ethereum-types/serialize"] std = [ "bytes/std", "const-hex/std", - "ethereum/std", - "ethereum-types/std", + "ethbloom/std", + "primitive-types/std", + "uint/std", + "fixed-hash/std", "parity-scale-codec/std", "scale-info/std", "serde/std", diff --git a/chains/ethereum/primitives/src/block.rs b/chains/ethereum/primitives/src/block.rs index 04b49c14..a288c956 100644 --- a/chains/ethereum/primitives/src/block.rs +++ b/chains/ethereum/primitives/src/block.rs @@ -1,13 +1,20 @@ -use crate::bytes::Bytes; +use crate::{ + bytes::Bytes, + eth_hash::{Address, H256, H64}, + eth_uint::{U256, U64}, +}; use alloc::vec::Vec; -use ethereum_types::{Address, Bloom, H256, H64, U256, U64}; +use ethbloom::Bloom; /// The block type returned from RPC calls. /// /// This is generic over a `TX` type which will be either the hash or the full transaction, /// i.e. `Block` or `Block`. #[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "with-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "with-codec", + derive(parity_scale_codec::Encode, parity_scale_codec::Decode, scale_info::TypeInfo) +)] #[cfg_attr( feature = "with-serde", derive(serde::Serialize, serde::Deserialize), diff --git a/chains/ethereum/primitives/src/bytes.rs b/chains/ethereum/primitives/src/bytes.rs index 74a03a20..4d9d0559 100644 --- a/chains/ethereum/primitives/src/bytes.rs +++ b/chains/ethereum/primitives/src/bytes.rs @@ -27,7 +27,7 @@ impl scale_info::TypeInfo for Bytes { .path(scale_info::Path::new("Bytes", module_path!())) .composite( scale_info::build::FieldsBuilder::<_, scale_info::build::UnnamedFields>::default() - .field(|f| f.ty::>().type_name("Vec")), + .field(|f| f.ty::<[u8]>().type_name("Vec")), ) } } diff --git a/chains/ethereum/primitives/src/eth_hash.rs b/chains/ethereum/primitives/src/eth_hash.rs new file mode 100644 index 00000000..d232dfd0 --- /dev/null +++ b/chains/ethereum/primitives/src/eth_hash.rs @@ -0,0 +1,54 @@ +// ignore clippy warnings in `construct_fixed_hash!` macro. +#![allow( + clippy::pedantic, + clippy::reversed_empty_ranges, + clippy::assign_op_pattern, + clippy::incorrect_clone_impl_on_copy_type +)] +use fixed_hash::*; +#[cfg(feature = "with-codec")] +use impl_codec_macro::impl_fixed_hash_codec; +#[cfg(feature = "with-rlp")] +use impl_rlp_macro::impl_fixed_hash_rlp; +#[cfg(feature = "with-serde")] +use impl_serde_macro::impl_fixed_hash_serde; +pub use primitive_types::{H128, H160, H256, H384, H512, H768}; + +// Aliases for Ethereum types. +pub type Address = H160; +pub type TxHash = H256; +pub type Secret = H256; +pub type Public = H512; +pub type Signature = H520; + +macro_rules! impl_hash { + ($hash: ident, $n_bytes: expr) => { + construct_fixed_hash! { pub struct $hash($n_bytes); } + + #[cfg(feature = "with-codec")] + impl_fixed_hash_codec!($hash, $n_bytes); + #[cfg(feature = "with-rlp")] + impl_fixed_hash_rlp!($hash, $n_bytes); + #[cfg(feature = "with-serde")] + impl_fixed_hash_serde!($hash, $n_bytes); + + #[cfg(feature = "with-codec")] + impl ::scale_info::TypeInfo for $hash { + type Identity = Self; + fn type_info() -> ::scale_info::Type { + use ::scale_info::{build::Fields, Path, Type}; + + Type::builder() + .path(Path::new(stringify!($hash), module_path!())) + .type_params(Vec::new()) + .composite(Fields::unnamed().field(|f| { + f.ty::<[u8; $n_bytes]>().type_name(concat!("[u8; ", $n_bytes, "]")) + })) + } + } + }; +} + +impl_hash!(H32, 4); +impl_hash!(H64, 8); +impl_hash!(H520, 65); diff --git a/chains/ethereum/primitives/src/eth_uint.rs b/chains/ethereum/primitives/src/eth_uint.rs new file mode 100644 index 00000000..38167db2 --- /dev/null +++ b/chains/ethereum/primitives/src/eth_uint.rs @@ -0,0 +1,102 @@ +// ignore clippy warnings in `construct_uint!` macro. +#![allow( + clippy::pedantic, + clippy::reversed_empty_ranges, + clippy::assign_op_pattern, + clippy::incorrect_clone_impl_on_copy_type +)] + +#[cfg(feature = "with-codec")] +use impl_codec_macro::impl_uint_codec; +use impl_num_traits::impl_uint_num_traits; +#[cfg(feature = "with-rlp")] +use impl_rlp_macro::impl_uint_rlp; +#[cfg(feature = "with-serde")] +use impl_serde_macro::impl_uint_serde; +pub use primitive_types::{Error, U128, U256, U512}; +pub use uint::{FromDecStrErr, FromStrRadixErr, FromStrRadixErrKind}; + +uint::construct_uint! { + /// Unsigned 64-bit integer. + pub struct U64(1); +} + +impl U64 { + /// Multiplies two 64-bit integers to produce full 128-bit integer. + /// Overflow is not possible. + #[inline(always)] + pub fn full_mul(self, other: Self) -> primitive_types::U128 { + primitive_types::U128(uint::uint_full_mul_reg!(U64, 1, self, other)) + } +} + +impl_uint_num_traits!(U64, 1); +#[cfg(feature = "with-codec")] +impl_uint_codec!(U64, 1); +#[cfg(feature = "with-rlp")] +impl_uint_rlp!(U64, 1); +#[cfg(feature = "with-serde")] +impl_uint_serde!(U64, 1); + +#[cfg(feature = "with-codec")] +impl ::scale_info::TypeInfo for U64 { + type Identity = ::Identity; + fn type_info() -> ::scale_info::Type { + // Alias to u64 primitive type + ::type_info() + } +} + +impl From for U128 { + fn from(value: U64) -> Self { + let U64(ref arr) = value; + let mut ret = [0; 2]; + ret[0] = arr[0]; + Self(ret) + } +} + +impl<'a> From<&'a U64> for U128 { + fn from(value: &'a U64) -> Self { + let U64(ref arr) = value; + let mut ret = [0; 2]; + ret[0] = arr[0]; + Self(ret) + } +} + +impl From for U256 { + fn from(value: U64) -> Self { + let U64(ref arr) = value; + let mut ret = [0; 4]; + ret[0] = arr[0]; + Self(ret) + } +} + +impl<'a> From<&'a U64> for U256 { + fn from(value: &'a U64) -> Self { + let U64(ref arr) = value; + let mut ret = [0; 4]; + ret[0] = arr[0]; + Self(ret) + } +} + +impl From for U512 { + fn from(value: U64) -> Self { + let U64(ref arr) = value; + let mut ret = [0; 8]; + ret[0] = arr[0]; + Self(ret) + } +} + +impl<'a> From<&'a U64> for U512 { + fn from(value: &'a U64) -> Self { + let U64(ref arr) = value; + let mut ret = [0; 8]; + ret[0] = arr[0]; + Self(ret) + } +} diff --git a/chains/ethereum/primitives/src/lib.rs b/chains/ethereum/primitives/src/lib.rs index 27efa3d7..f4611006 100644 --- a/chains/ethereum/primitives/src/lib.rs +++ b/chains/ethereum/primitives/src/lib.rs @@ -4,19 +4,21 @@ extern crate alloc; mod block; mod bytes; +mod eth_hash; +mod eth_uint; mod log; mod storage_proof; mod tx_receipt; pub use block::Block; pub use bytes::Bytes; -pub use ethereum_types::{Address, Bloom, H256, H64, U128, U256, U64}; +pub use eth_hash::{Address, Public, Secret, Signature, TxHash, H128, H256, H384, H512, H520}; +pub use eth_uint::{U128, U256, U512, U64}; +pub use ethbloom::{Bloom, BloomRef, Input as BloomInput}; pub use log::Log; pub use storage_proof::{EIP1186ProofResponse, StorageProof}; pub use tx_receipt::TransactionReceipt; -pub type TxHash = H256; - #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr( feature = "with-codec", @@ -27,6 +29,24 @@ pub enum BlockIdentifier { Number(u64), } +impl From for BlockIdentifier { + fn from(value: u64) -> Self { + Self::Number(value) + } +} + +impl From for BlockIdentifier { + fn from(value: U64) -> Self { + Self::Number(value.as_u64()) + } +} + +impl From for BlockIdentifier { + fn from(hash: H256) -> Self { + Self::Hash(hash) + } +} + #[cfg(feature = "with-serde")] impl serde::Serialize for BlockIdentifier { fn serialize(&self, serializer: S) -> Result diff --git a/chains/ethereum/primitives/src/log.rs b/chains/ethereum/primitives/src/log.rs index 16c75cdb..cad9f7f0 100644 --- a/chains/ethereum/primitives/src/log.rs +++ b/chains/ethereum/primitives/src/log.rs @@ -1,10 +1,16 @@ -use crate::bytes::Bytes; +use crate::{ + bytes::Bytes, + eth_hash::{Address, H256}, + eth_uint::{U256, U64}, +}; use alloc::{string::String, vec::Vec}; -use ethereum_types::{Address, H256, U256, U64}; /// A log produced by a transaction. #[derive(Debug, Clone, Default, PartialEq, Eq)] -#[cfg_attr(feature = "with-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "with-codec", + derive(parity_scale_codec::Encode, parity_scale_codec::Decode, scale_info::TypeInfo) +)] #[cfg_attr( feature = "with-serde", derive(serde::Serialize, serde::Deserialize), diff --git a/chains/ethereum/primitives/src/storage_proof.rs b/chains/ethereum/primitives/src/storage_proof.rs index cbf8b7c1..05ebbc59 100644 --- a/chains/ethereum/primitives/src/storage_proof.rs +++ b/chains/ethereum/primitives/src/storage_proof.rs @@ -1,6 +1,9 @@ -use crate::bytes::Bytes; +use crate::{ + bytes::Bytes, + eth_hash::{Address, H256}, + eth_uint::{U256, U64}, +}; use alloc::vec::Vec; -use ethereum_types::{Address, H256, U256, U64}; #[derive(Debug, Default, Clone, PartialEq, Eq)] #[cfg_attr( @@ -15,7 +18,10 @@ pub struct StorageProof { } #[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "with-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "with-codec", + derive(parity_scale_codec::Encode, parity_scale_codec::Decode, scale_info::TypeInfo) +)] #[cfg_attr( feature = "with-serde", derive(serde::Serialize, serde::Deserialize), diff --git a/chains/ethereum/primitives/src/tx_receipt.rs b/chains/ethereum/primitives/src/tx_receipt.rs index e10c9e74..274cbdfd 100644 --- a/chains/ethereum/primitives/src/tx_receipt.rs +++ b/chains/ethereum/primitives/src/tx_receipt.rs @@ -1,11 +1,18 @@ -use crate::log::Log; +use crate::{ + eth_hash::{Address, H256}, + eth_uint::{U256, U64}, + log::Log, +}; use alloc::vec::Vec; use core::cmp::Ordering; -use ethereum_types::{Address, Bloom, H256, U256, U64}; +use ethbloom::Bloom; /// "Receipt" of an executed transaction: details of its execution. #[derive(Debug, Default, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "with-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "with-codec", + derive(parity_scale_codec::Encode, parity_scale_codec::Decode, scale_info::TypeInfo) +)] #[cfg_attr( feature = "with-serde", derive(serde::Serialize, serde::Deserialize),