From baf6288459417e1bbc8cf0d54c52c1bf3a92a4d4 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Thu, 18 Jan 2024 11:24:20 -0300 Subject: [PATCH 01/26] Remove bitcoin connector --- .github/workflows/ci.yaml | 4 +- Cargo.lock | 193 +++++---------------------- Cargo.toml | 4 - README.md | 15 +-- chains/bitcoin/config/Cargo.toml | 11 -- chains/bitcoin/config/src/lib.rs | 48 ------- chains/bitcoin/server/Cargo.toml | 21 --- chains/bitcoin/server/src/lib.rs | 209 ------------------------------ chains/ethereum/server/src/lib.rs | 2 +- chains/polkadot/server/src/lib.rs | 2 +- docker-compose.yml | 28 +--- rosetta-client/Cargo.toml | 4 +- rosetta-client/src/client.rs | 29 +---- rosetta-client/src/lib.rs | 3 - rosetta-client/src/tx_builder.rs | 2 +- rosetta-client/src/wallet.rs | 7 +- rosetta-server/build.rs | 7 - scripts/check.sh | 3 - scripts/pull_nodes.sh | 1 - scripts/push_connectors.sh | 25 ---- 20 files changed, 40 insertions(+), 578 deletions(-) delete mode 100644 chains/bitcoin/config/Cargo.toml delete mode 100644 chains/bitcoin/config/src/lib.rs delete mode 100644 chains/bitcoin/server/Cargo.toml delete mode 100644 chains/bitcoin/server/src/lib.rs delete mode 100644 rosetta-server/build.rs delete mode 100755 scripts/push_connectors.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c8658892..32ceb73d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -53,7 +53,7 @@ jobs: needs: [rustfmt] strategy: matrix: - crate: [rosetta-server-astar, rosetta-server-bitcoin, rosetta-server-ethereum, rosetta-server-polkadot, rosetta-client] + crate: [rosetta-server-astar, rosetta-server-ethereum, rosetta-server-polkadot, rosetta-client] name: ${{ matrix.crate }} runs-on: self-hosted steps: @@ -116,7 +116,6 @@ jobs: run: | cargo clippy --locked --workspace --examples --tests --all-features \ --exclude rosetta-server-astar \ - --exclude rosetta-server-bitcoin \ --exclude rosetta-server-ethereum \ --exclude rosetta-server-polkadot \ --exclude rosetta-client \ @@ -138,7 +137,6 @@ jobs: run: | cargo test --locked --workspace --all-features \ --exclude rosetta-server-astar \ - --exclude rosetta-server-bitcoin \ --exclude rosetta-server-ethereum \ --exclude rosetta-server-polkadot \ --exclude rosetta-client diff --git a/Cargo.lock b/Cargo.lock index 2f920a98..f7e80d78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -869,27 +869,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "base64-compat" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" -dependencies = [ - "byteorder", -] - [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bech32" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" - [[package]] name = "bech32" version = "0.9.1" @@ -920,7 +905,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ - "bitcoin_hashes 0.11.0", + "bitcoin_hashes", "rand 0.8.5", "rand_core 0.6.4", "serde", @@ -942,47 +927,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bitcoin_hashes" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1" -dependencies = [ - "serde", -] - [[package]] name = "bitcoin_hashes" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" -[[package]] -name = "bitcoincore-rpc-async" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a36d64c8fc538867644983cb3af416595d0e70ab6a06a98cbee5cd60bdc1625d" -dependencies = [ - "async-trait", - "bitcoincore-rpc-json-async", - "jsonrpc-async", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "bitcoincore-rpc-json-async" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2703b13da6811c55e51e3dcb39b55f06081dbc95f063c141120be7564fd09be4" -dependencies = [ - "hex 0.3.2", - "sapio-bitcoin", - "serde", - "serde_json", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -1306,11 +1256,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ "base64 0.21.7", - "bech32 0.9.1", + "bech32", "bs58 0.5.0", "digest 0.10.7", "generic-array 0.14.7", - "hex 0.4.3", + "hex", "ripemd", "serde", "serde_derive", @@ -1347,7 +1297,7 @@ checksum = "a5104de16b218eddf8e34ffe2f86f74bfa4e61e95a1b89732fccf6325efd0557" dependencies = [ "cfg-if", "cpufeatures", - "hex 0.4.3", + "hex", "proptest", "serde", ] @@ -2042,7 +1992,7 @@ checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek 3.2.0", "hashbrown 0.12.3", - "hex 0.4.3", + "hex", "rand_core 0.6.4", "sha2 0.9.9", "zeroize", @@ -2057,7 +2007,7 @@ dependencies = [ "curve25519-dalek 4.1.1", "ed25519 2.2.3", "hashbrown 0.14.3", - "hex 0.4.3", + "hex", "rand_core 0.6.4", "sha2 0.10.8", "zeroize", @@ -2115,7 +2065,7 @@ checksum = "fe81b5c06ecfdbc71dd845216f225f53b62a10cb8a16c946836a3467f701d05b" dependencies = [ "base64 0.21.7", "bytes", - "hex 0.4.3", + "hex", "k256", "log", "rand 0.8.5", @@ -2165,7 +2115,7 @@ dependencies = [ "aes 0.8.3", "ctr 0.9.2", "digest 0.10.7", - "hex 0.4.3", + "hex", "hmac 0.12.1", "pbkdf2 0.11.0", "rand 0.8.5", @@ -2185,7 +2135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" dependencies = [ "ethereum-types", - "hex 0.4.3", + "hex", "once_cell", "regex", "serde", @@ -2673,9 +2623,9 @@ dependencies = [ [[package]] name = "fraction" -version = "0.13.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" +checksum = "864d054a996544d325470be9db9ff3be18319e568ad90b3b59f1b8bea046ab2f" dependencies = [ "lazy_static", "num", @@ -3060,12 +3010,6 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" - [[package]] name = "hex" version = "0.4.3" @@ -3267,7 +3211,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" dependencies = [ "futures-util", - "hex 0.4.3", + "hex", "hyper", "pin-project", "tokio", @@ -3497,20 +3441,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonrpc-async" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20e8e4ed08ee58717113cbf277b1ecef5cd9554d3e48c114de338289727d466" -dependencies = [ - "async-trait", - "base64-compat", - "serde", - "serde_derive", - "serde_json", - "tokio", -] - [[package]] name = "jsonrpsee" version = "0.20.3" @@ -5175,20 +5105,18 @@ dependencies = [ "fraction", "futures", "getrandom 0.2.12", - "hex 0.4.3", + "hex", "js-sys", "log", "num-traits", "rosetta-core", "rosetta-server-astar", - "rosetta-server-bitcoin", "rosetta-server-ethereum", "rosetta-server-polkadot", "rosetta-tx-ethereum", "rosetta-tx-polkadot", "serde", "serde_json", - "void", "wasm-bindgen", "web-sys", ] @@ -5202,14 +5130,6 @@ dependencies = [ "subxt", ] -[[package]] -name = "rosetta-config-bitcoin" -version = "0.5.0" -dependencies = [ - "anyhow", - "rosetta-core", -] - [[package]] name = "rosetta-config-ethereum" version = "0.5.0" @@ -5257,13 +5177,13 @@ name = "rosetta-crypto" version = "0.5.0" dependencies = [ "anyhow", - "bech32 0.9.1", + "bech32", "blake2-rfc", "bs58 0.5.0", "ecdsa", "ed25519-dalek 1.0.1", "ethers", - "hex 0.4.3", + "hex", "hmac 0.12.1", "k256", "p256", @@ -5288,7 +5208,7 @@ dependencies = [ "docker-api", "futures", "getrandom 0.2.12", - "hex 0.4.3", + "hex", "log", "nanoid", "rosetta-client", @@ -5326,7 +5246,7 @@ dependencies = [ "futures", "futures-timer", "futures-util", - "hex 0.4.3", + "hex", "jsonrpsee 0.21.0", "log", "nanoid", @@ -5353,7 +5273,7 @@ dependencies = [ "ethers", "ethers-solc", "futures", - "hex 0.4.3", + "hex", "log", "parity-scale-codec", "rosetta-client", @@ -5371,22 +5291,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "rosetta-server-bitcoin" -version = "0.5.0" -dependencies = [ - "anyhow", - "async-trait", - "bitcoincore-rpc-async", - "hex 0.4.3", - "rosetta-config-bitcoin", - "rosetta-core", - "rosetta-docker", - "serde_json", - "tokio", - "void", -] - [[package]] name = "rosetta-server-ethereum" version = "0.5.0" @@ -5400,7 +5304,7 @@ dependencies = [ "ethers-solc", "futures-timer", "futures-util", - "hex 0.4.3", + "hex", "rosetta-client", "rosetta-config-ethereum", "rosetta-core", @@ -5421,7 +5325,7 @@ version = "0.5.0" dependencies = [ "anyhow", "async-trait", - "hex 0.4.3", + "hex", "parity-scale-codec", "rosetta-config-polkadot", "rosetta-core", @@ -5506,7 +5410,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f868fd508789837c62557c8eb3c2ce6891ed3b4961e96e14f068d35299c270f" dependencies = [ - "bitcoin_hashes 0.11.0", + "bitcoin_hashes", "rand_core 0.6.4", "serde", "unicode-normalization", @@ -5748,28 +5652,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "sapio-bitcoin" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60df4fbc4dc3173998ec341b611c38c15ef5ce93c7d7405673aea3a911016d7a" -dependencies = [ - "bech32 0.7.3", - "bitcoin_hashes 0.9.7", - "sapio-secp256k1", - "serde", -] - -[[package]] -name = "sapio-secp256k1" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7830cd189705b505d3c291fd41a6ab852085d5024feffdc15e59aff64f1024d2" -dependencies = [ - "secp256k1-sys 0.4.2", - "serde", -] - [[package]] name = "scale-bits" version = "0.4.0" @@ -6005,16 +5887,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ - "secp256k1-sys 0.6.1", -] - -[[package]] -name = "secp256k1-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" -dependencies = [ - "cc", + "secp256k1-sys", ] [[package]] @@ -6201,7 +6074,7 @@ checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" dependencies = [ "base64 0.13.1", "chrono", - "hex 0.4.3", + "hex", "indexmap 1.9.3", "serde", "serde_json", @@ -6429,7 +6302,7 @@ dependencies = [ "futures-lite 2.2.0", "futures-util", "hashbrown 0.14.3", - "hex 0.4.3", + "hex", "hmac 0.12.1", "itertools 0.11.0", "libm", @@ -6479,7 +6352,7 @@ dependencies = [ "futures-lite 2.2.0", "futures-util", "hashbrown 0.14.3", - "hex 0.4.3", + "hex", "itertools 0.11.0", "log", "lru", @@ -7256,7 +7129,7 @@ dependencies = [ "either", "frame-metadata 16.0.0", "futures", - "hex 0.4.3", + "hex", "impl-serde", "jsonrpsee 0.20.3", "parity-scale-codec", @@ -7286,7 +7159,7 @@ checksum = "12800ad6128b4bfc93d2af89b7d368bff7ea2f6604add35f96f6a8c06c7f9abf" dependencies = [ "frame-metadata 16.0.0", "heck", - "hex 0.4.3", + "hex", "jsonrpsee 0.20.3", "parity-scale-codec", "proc-macro2", @@ -7448,7 +7321,7 @@ checksum = "20689c7d03b6461b502d0b95d6c24874c7d24dea2688af80486a130a06af3b07" dependencies = [ "dirs", "fs2", - "hex 0.4.3", + "hex", "once_cell", "reqwest", "semver 1.0.21", @@ -7727,9 +7600,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", @@ -8070,7 +7941,7 @@ checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", - "hex 0.4.3", + "hex", "static_assertions", ] @@ -8242,12 +8113,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "w3f-bls" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 9ac48aa1..47d20bca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,6 @@ members = [ "chains/astar/config", "chains/astar/server", - "chains/bitcoin/config", - "chains/bitcoin/server", "chains/ethereum/config", "chains/ethereum/rpc-client", "chains/ethereum/server", @@ -26,8 +24,6 @@ resolver = "2" [workspace.dependencies] rosetta-config-astar = { path = "chains/astar/config", default-features = false } rosetta-server-astar = { path = "chains/astar/server" } -rosetta-config-bitcoin = { path = "chains/bitcoin/config" } -rosetta-server-bitcoin = { path = "chains/bitcoin/server" } rosetta-config-ethereum = { path = "chains/ethereum/config" } rosetta-server-ethereum = { path = "chains/ethereum/server" } rosetta-ethereum-rpc-client = { path = "chains/ethereum/rpc-client" } diff --git a/README.md b/README.md index a45e96b1..c76201ce 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ rosetta-wallet [command] ## Reference wallet implementations -To help you get started with wallets on specific chains, we have developed complete Rosetta API reference implementations for Bitcoin (deprioritized for now), Ethereum, and Substrate-based chains. +To help you get started with wallets on specific chains, we have developed complete Rosetta API reference implementations for Ethereum, and Substrate-based chains. ### Ethereum example @@ -92,18 +92,6 @@ rosetta-wallet --chain dot --keyfile /tmp/alice transfer bob_acc_key 15000000000 rosetta-wallet --chain dot --keyfile /tmp/bob balance ``` -### Bitcoin example - -To use this repository, you need to fork it and start playing with the code. For example, running these commands will help you learn more about Rosetta API implementation for Bitcoin wallets: - -``` -rosetta-wallet --chain btc --keyfile /tmp/alice faucet 1000 -rosetta-wallet --chain btc --keyfile /tmp/bob account -rosetta-wallet --chain btc --keyfile /tmp/alice transfer bob_acc_key 1000 -rosetta-wallet --chain btc --keyfile /tmp/alice faucet 1 -rosetta-wallet --chain btc --keyfile /tmp/bob balance -``` - ## Reference CLI implementation To help you get started with rosetta-cli, we have developed a standard indexer endpoint that you can leverage to integrate external blockchains automatically. The indexer endpoint complements the existing Data and Construction API endpoints in Rosetta API specifications, allowing developers to fully support asset integration. @@ -127,7 +115,6 @@ http://rosetta.analog.one:3000 Running a local testnet with docker compose up initiates a number of containers, including: -- bitcoin: http://127.0.0.1:8080 - ethereum: http://127.0.0.1:8081 - polkadot: http://127.0.0.1:8082 - block explorer: [http://127.0.0.1:3000](http://127.0.0.1:3000) diff --git a/chains/bitcoin/config/Cargo.toml b/chains/bitcoin/config/Cargo.toml deleted file mode 100644 index 982a0d9f..00000000 --- a/chains/bitcoin/config/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "rosetta-config-bitcoin" -version = "0.5.0" -edition = "2021" -license = "MIT" -repository = "https://github.com/analog-labs/chain-connectors" -description = "Bitcoin configuration." - -[dependencies] -anyhow = "1.0" -rosetta-core.workspace = true diff --git a/chains/bitcoin/config/src/lib.rs b/chains/bitcoin/config/src/lib.rs deleted file mode 100644 index c6763705..00000000 --- a/chains/bitcoin/config/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -use anyhow::Result; -use rosetta_core::{ - crypto::{address::AddressFormat, Algorithm}, - BlockchainConfig, NodeUri, -}; -use std::sync::Arc; - -/// Retrieve the [`BlockchainConfig`] from the provided `network` -/// -/// # Errors -/// Returns `Err` if the network is not supported -pub fn config(network: &str) -> Result { - let (network, symbol, bip44_id) = match network { - "regtest" => ("regtest", "tBTC", 1), - "mainnet" => ("mainnet", "BTC", 0), - _ => anyhow::bail!("unsupported network: {}", network), - }; - Ok(BlockchainConfig { - blockchain: "bitcoin", - network, - algorithm: Algorithm::EcdsaSecp256k1, - address_format: AddressFormat::Bech32("bcrt"), - coin: bip44_id, - bip44: true, - utxo: true, - currency_unit: "satoshi", - currency_symbol: symbol, - currency_decimals: 8, - node_uri: NodeUri::parse("http://127.0.0.1:18443")?, - node_image: "ruimarinho/bitcoin-core:23", - node_command: Arc::new(|network, port| { - let mut params: Vec = vec![ - "-rpcbind=0.0.0.0".into(), - format!("-rpcport={port}"), - "-rpcallowip=0.0.0.0/0".into(), - "-rpcuser=rosetta".into(), - "-rpcpassword=rosetta".into(), - ]; - if network == "regtest" { - params.push("-regtest=1".into()); - } - params - }), - node_additional_ports: &[], - connector_port: 8080, - testnet: network == "regtest", - }) -} diff --git a/chains/bitcoin/server/Cargo.toml b/chains/bitcoin/server/Cargo.toml deleted file mode 100644 index ec1ae79d..00000000 --- a/chains/bitcoin/server/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "rosetta-server-bitcoin" -version = "0.5.0" -edition = "2021" -license = "MIT" -repository = "https://github.com/analog-labs/chain-connectors" -description = "Bitcoin rosetta server." - -[dependencies] -anyhow = "1.0" -async-trait = "0.1" -bitcoincore-rpc-async = "3.0" -hex = "0.4" -rosetta-config-bitcoin.workspace = true -rosetta-core.workspace = true -serde_json = "1.0" -tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } -void = "1.0" - -[dev-dependencies] -rosetta-docker = { workspace = true, features = ["tests"] } diff --git a/chains/bitcoin/server/src/lib.rs b/chains/bitcoin/server/src/lib.rs deleted file mode 100644 index 72634ed9..00000000 --- a/chains/bitcoin/server/src/lib.rs +++ /dev/null @@ -1,209 +0,0 @@ -use anyhow::{Context, Result}; -use bitcoincore_rpc_async::{bitcoin::BlockHash, Auth, Client, RpcApi}; -use rosetta_core::{ - crypto::{address::Address, PublicKey}, - types::{ - Block, BlockIdentifier, Coin, PartialBlockIdentifier, Transaction, TransactionIdentifier, - }, - BlockchainClient, BlockchainConfig, -}; -use std::str::FromStr; -use void::{unreachable, Void}; - -pub type BitcoinMetadataParams = (); -pub type BitcoinMetadata = (); - -pub struct BitcoinClient { - config: BlockchainConfig, - client: Client, - genesis_block: BlockIdentifier, -} - -impl BitcoinClient { - /// Creates a new bitcoin client from `network`and `addr` - /// - /// # Errors - /// Will return `Err` when the network is invalid, or when the provided `addr` is unreacheable. - pub async fn new(network: &str, addr: &str) -> Result { - let config = rosetta_config_bitcoin::config(network)?; - Self::from_config(config, addr).await - } - - /// Creates a new bitcoin client from `config` and `addr` - /// - /// # Errors - /// Will return `Err` when the network is invalid, or when the provided `addr` is unreacheable. - pub async fn from_config(config: BlockchainConfig, addr: &str) -> Result { - let client = - Client::new(addr.to_string(), Auth::UserPass("rosetta".into(), "rosetta".into())) - .await?; - let genesis = client.get_block_hash(0).await?; - let genesis_block = BlockIdentifier { index: 0, hash: genesis.to_string() }; - - Ok(Self { config, client, genesis_block }) - } -} - -/// Bitcoin community has adopted 6 blocks as a standard confirmation period. -/// That is, once a transaction is included in a block in the blockchain which is followed up by at -/// least 6 additional blocks the transaction is called “confirmed.” While this was chosen somewhat -/// arbitrarily, it is a reasonably safe value in practice as the only time this would have left -/// users vulnerable to double-spending was the atypical March 2013 fork. -const CONFIRMATION_PERIOD: u64 = 6; - -#[async_trait::async_trait] -impl BlockchainClient for BitcoinClient { - type MetadataParams = BitcoinMetadataParams; - type Metadata = BitcoinMetadata; - type EventStream<'a> = rosetta_core::EmptyEventStream; - type Call = Void; - type CallResult = (); - - fn config(&self) -> &BlockchainConfig { - &self.config - } - - fn genesis_block(&self) -> &BlockIdentifier { - &self.genesis_block - } - - async fn node_version(&self) -> Result { - let info = self.client.get_network_info().await?; - let major = info.version / 10000; - let rest = info.version % 10000; - let minor = rest / 100; - let patch = rest % 100; - Ok(format!("{major}.{minor}.{patch}")) - } - - async fn current_block(&self) -> Result { - let hash = self.client.get_best_block_hash().await?; - let info = self.client.get_block_info(&hash).await?; - Ok(BlockIdentifier { index: info.height as u64, hash: hash.to_string() }) - } - - async fn finalized_block(&self) -> Result { - let index = self.client.get_block_count().await?.saturating_sub(CONFIRMATION_PERIOD); - let hash = self.client.get_block_hash(index).await?; - Ok(BlockIdentifier { index, hash: hash.to_string() }) - } - - async fn balance(&self, _address: &Address, _block: &BlockIdentifier) -> Result { - todo!() - } - - async fn coins(&self, _address: &Address, _block: &BlockIdentifier) -> Result> { - todo!() - } - - async fn faucet(&self, _address: &Address, _value: u128) -> Result> { - todo!() - } - - async fn metadata( - &self, - _public_key: &PublicKey, - _options: &Self::MetadataParams, - ) -> Result { - Ok(()) - } - - async fn submit(&self, _transaction: &[u8]) -> Result> { - todo!() - } - - async fn block(&self, block: &PartialBlockIdentifier) -> Result { - let block = match (block.hash.as_ref(), block.index) { - (Some(block_hash), _) => { - let hash = BlockHash::from_str(block_hash).context("Invalid block hash")?; - self.client.get_block(&hash).await? - }, - (None, Some(height)) => { - let block_bash = - self.client.get_block_hash(height).await.context("cannot find by index")?; - self.client.get_block(&block_bash).await? - }, - (None, None) => anyhow::bail!("the block hash or index must be specified"), - }; - - let block_height = if let Ok(height) = block.bip34_block_height() { - height - } else { - let info = self - .client - .get_block_info(&block.block_hash()) - .await - .context("Cannot find block height")?; - info.height as u64 - }; - - let transactions = block - .txdata - .iter() - .map(|tx| Transaction { - transaction_identifier: TransactionIdentifier::new(tx.txid().as_hash().to_string()), - operations: vec![], - related_transactions: None, - metadata: serde_json::to_value(tx.clone()).ok(), - }) - .collect::>(); - - Ok(Block { - block_identifier: BlockIdentifier { - index: block_height, - hash: block.block_hash().to_string(), - }, - parent_block_identifier: BlockIdentifier { - index: block_height.saturating_sub(1), - hash: block.header.prev_blockhash.to_string(), - }, - timestamp: i64::from(block.header.time) * 1000, - transactions, - metadata: None, - }) - } - - async fn block_transaction( - &self, - _block: &BlockIdentifier, - _tx: &TransactionIdentifier, - ) -> Result { - anyhow::bail!("not implemented") - } - - async fn call(&self, req: &Void) -> Result<()> { - unreachable(*req) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - pub async fn client_from_config(config: BlockchainConfig) -> Result { - let network = config.network.to_string(); - let url = config.node_uri.to_string(); - BitcoinClient::new(network.as_str(), url.as_str()).await - } - - #[tokio::test] - async fn test_network_status() -> Result<()> { - let config = rosetta_config_bitcoin::config("regtest")?; - rosetta_docker::tests::network_status::(client_from_config, config) - .await - } - - #[tokio::test] - #[ignore] - async fn test_account() -> Result<()> { - let config = rosetta_config_bitcoin::config("regtest")?; - rosetta_docker::tests::account::(client_from_config, config).await - } - - #[tokio::test] - #[ignore] - async fn test_construction() -> Result<()> { - let config = rosetta_config_bitcoin::config("regtest")?; - rosetta_docker::tests::construction::(client_from_config, config).await - } -} diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index 44e35452..29a2785a 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -54,7 +54,7 @@ impl MaybeWsEthereumClient { Self::from_config(config, addr).await } - /// Creates a new bitcoin client from `config` and `addr` + /// Creates a new ethereum client from `config` and `addr` /// /// # Errors /// Will return `Err` when the network is invalid, or when the provided `addr` is unreacheable. diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index 32031011..e22eb6a0 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -49,7 +49,7 @@ impl PolkadotClient { Self::from_config(config, addr).await } - /// Creates a new bitcoin client using the provided `config` and connets to `addr` + /// Creates a new substrate client using the provided `config` and connets to `addr` /// /// # Errors /// Will return `Err` when the network is invalid, or when the provided `addr` is unreacheable. diff --git a/docker-compose.yml b/docker-compose.yml index ebe1f15d..830c7334 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,28 +1,11 @@ version: "3.9" volumes: - bitcoin-volume: ethereum-volume: polkadot-volume: astar-volume: services: - bitcoin: - image: "ruimarinho/bitcoin-core:23" - command: "-regtest=1 -rpcbind=0.0.0.0 -rpcport=18443 -rpcallowip=0.0.0.0/0 -rpcuser=rosetta -rpcpassword=rosetta" - expose: - - "18443" - ulimits: - nofile: - soft: 10000 - hard: 10000 - deploy: - resources: - reservations: - memory: 1g - volumes: - - "bitcoin-volume:/home/bitcoin/.bitcoin" - ethereum: image: "ethereum/client-go:v1.12.2" command: "--dev --ipcdisable --http --http.addr 0.0.0.0 --http.vhosts * --http.api eth,debug,admin,txpool,web3" @@ -41,7 +24,7 @@ services: polkadot: image: "parity/polkadot:v1.5.0" - command: "--dev --rpc-external --rpc-port=9944 --alice --blocks-pruning archive --state-pruning archive --base-path /polkadot" + command: "--chain=westend-dev --dev --rpc-external --rpc-port=9944 --alice --blocks-pruning archive --state-pruning archive --base-path /polkadot" expose: - "9944" user: root @@ -73,15 +56,6 @@ services: volumes: - "astar-volume:/astar" - - connector-bitcoin: - image: "analoglabs/connector-bitcoin" - command: "--network regtest --addr 0.0.0.0:8080 --node-addr http://bitcoin:18443" - ports: - - "8080:8080" - depends_on: - - bitcoin - connector-ethereum: image: "analoglabs/connector-ethereum" command: "--network dev --addr 0.0.0.0:8081 --node-addr http://ethereum:8545" diff --git a/rosetta-client/Cargo.toml b/rosetta-client/Cargo.toml index 17ec72e4..56dfc7b8 100644 --- a/rosetta-client/Cargo.toml +++ b/rosetta-client/Cargo.toml @@ -11,7 +11,7 @@ anyhow = "1.0" async-trait = "0.1" derive_more = "0.99" dirs-next = "2.0" -fraction = { version = "0.13", default-features = false, features = ["with-bigint", "with-decimal"] } +fraction = { version = "0.15", default-features = false, features = ["with-bigint", "with-decimal"] } futures = "0.3" getrandom = "0.2" hex = "0.4" @@ -19,14 +19,12 @@ log = "0.4" num-traits = "0.2" rosetta-core.workspace = true rosetta-server-astar.workspace = true -rosetta-server-bitcoin.workspace = true rosetta-server-ethereum.workspace = true rosetta-server-polkadot.workspace = true rosetta-tx-ethereum.workspace = true rosetta-tx-polkadot.workspace = true serde.workspace = true serde_json.workspace = true -void = "1.0" [target.'cfg(target_family = "wasm")'.dependencies] getrandom = { version = "0.2", features = ["js"] } diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index 0dd8ffb5..17bb9257 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -12,7 +12,6 @@ use derive_more::From; use futures::Stream; use rosetta_core::{BlockchainClient, ClientEvent}; use rosetta_server_astar::{AstarClient, AstarMetadata, AstarMetadataParams}; -use rosetta_server_bitcoin::{BitcoinClient, BitcoinMetadata, BitcoinMetadataParams}; use rosetta_server_ethereum::{ config::{Query as EthQuery, QueryResult as EthQueryResult}, EthereumMetadata, EthereumMetadataParams, MaybeWsEthereumClient as EthereumClient, @@ -21,13 +20,11 @@ use rosetta_server_polkadot::{PolkadotClient, PolkadotMetadata, PolkadotMetadata use serde::{Deserialize, Serialize}; use serde_json::Value; use std::{pin::Pin, str::FromStr}; -use void::Void; // TODO: Use #[allow(clippy::large_enum_variant)] /// Generic Client pub enum GenericClient { - Bitcoin(BitcoinClient), Ethereum(EthereumClient), Astar(AstarClient), Polkadot(PolkadotClient), @@ -37,10 +34,6 @@ pub enum GenericClient { impl GenericClient { pub async fn new(blockchain: Blockchain, network: &str, url: &str) -> Result { Ok(match blockchain { - Blockchain::Bitcoin => { - let client = BitcoinClient::new(network, url).await?; - Self::Bitcoin(client) - }, Blockchain::Ethereum => { let client = EthereumClient::new("ethereum", network, url).await?; Self::Ethereum(client) @@ -70,10 +63,6 @@ impl GenericClient { pub async fn from_config(config: BlockchainConfig, url: &str) -> Result { let blockchain = Blockchain::from_str(config.blockchain)?; Ok(match blockchain { - Blockchain::Bitcoin => { - let client = BitcoinClient::from_config(config, url).await?; - Self::Bitcoin(client) - }, Blockchain::Ethereum | Blockchain::Polygon | Blockchain::Arbitrum => { let client = EthereumClient::from_config(config, url).await?; Self::Ethereum(client) @@ -96,7 +85,6 @@ impl GenericClient { /// Generic Blockchain Params #[derive(Deserialize, Serialize, From)] pub enum GenericMetadataParams { - Bitcoin(BitcoinMetadataParams), Ethereum(EthereumMetadataParams), Astar(AstarMetadataParams), Polkadot(PolkadotMetadataParams), @@ -105,21 +93,18 @@ pub enum GenericMetadataParams { /// Generic Blockchain Metadata #[derive(Deserialize, Serialize, From)] pub enum GenericMetadata { - Bitcoin(BitcoinMetadata), Ethereum(EthereumMetadata), Astar(AstarMetadata), Polkadot(PolkadotMetadata), } pub enum GenericCall { - Bitcoin(Void), Ethereum(EthQuery), Polkadot(CallRequest), } #[allow(clippy::large_enum_variant)] pub enum GenericCallResult { - Bitcoin(()), Ethereum(EthQueryResult), Polkadot(Value), } @@ -127,7 +112,6 @@ pub enum GenericCallResult { macro_rules! dispatch { ($self:tt$($method:tt)+) => { match $self { - Self::Bitcoin(client) => client$($method)*, Self::Ethereum(client) => client$($method)*, Self::Astar(client) => client$($method)*, Self::Polkadot(client) => client$($method)*, @@ -181,9 +165,6 @@ impl BlockchainClient for GenericClient { params: &Self::MetadataParams, ) -> Result { Ok(match (self, params) { - (Self::Bitcoin(client), GenericMetadataParams::Bitcoin(params)) => { - client.metadata(public_key, params).await?.into() - }, (Self::Ethereum(client), GenericMetadataParams::Ethereum(params)) => { client.metadata(public_key, params).await?.into() }, @@ -215,27 +196,23 @@ impl BlockchainClient for GenericClient { async fn call(&self, req: &GenericCall) -> Result { let result = match self { - Self::Bitcoin(client) => match req { - GenericCall::Bitcoin(args) => GenericCallResult::Bitcoin(client.call(args).await?), - _ => anyhow::bail!("invalid call"), - }, Self::Ethereum(client) => match req { GenericCall::Ethereum(args) => { GenericCallResult::Ethereum(client.call(args).await?) }, - _ => anyhow::bail!("invalid call"), + GenericCall::Polkadot(_) => anyhow::bail!("invalid call"), }, Self::Astar(client) => match req { GenericCall::Ethereum(args) => { GenericCallResult::Ethereum(client.call(args).await?) }, - _ => anyhow::bail!("invalid call"), + GenericCall::Polkadot(_) => anyhow::bail!("invalid call"), }, Self::Polkadot(client) => match req { GenericCall::Polkadot(args) => { GenericCallResult::Polkadot(client.call(args).await?) }, - _ => anyhow::bail!("invalid call"), + GenericCall::Ethereum(_) => anyhow::bail!("invalid call"), }, }; Ok(result) diff --git a/rosetta-client/src/lib.rs b/rosetta-client/src/lib.rs index 1207bc72..1610041d 100644 --- a/rosetta-client/src/lib.rs +++ b/rosetta-client/src/lib.rs @@ -17,8 +17,6 @@ pub use signer::Signer; /// Supported chains. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Blockchain { - /// Bitcoin - Bitcoin, /// Ethereum Ethereum, /// Astar @@ -44,7 +42,6 @@ impl std::str::FromStr for Blockchain { fn from_str(blockchain: &str) -> Result { Ok(match blockchain { - "bitcoin" => Self::Bitcoin, "ethereum" => Self::Ethereum, "astar" => Self::Astar, "polkadot" => Self::Polkadot, diff --git a/rosetta-client/src/tx_builder.rs b/rosetta-client/src/tx_builder.rs index e722e0d9..bed506a2 100644 --- a/rosetta-client/src/tx_builder.rs +++ b/rosetta-client/src/tx_builder.rs @@ -23,7 +23,7 @@ impl GenericTransactionBuilder { "polkadot" | "westend" | "rococo" => { Self::Polkadot(rosetta_tx_polkadot::PolkadotTransactionBuilder) }, - _ => anyhow::bail!("unsupported blockchain"), + _ => anyhow::bail!("unsupported blockchain: {}", config.blockchain), }) } diff --git a/rosetta-client/src/wallet.rs b/rosetta-client/src/wallet.rs index 868ec867..84efa8c3 100644 --- a/rosetta-client/src/wallet.rs +++ b/rosetta-client/src/wallet.rs @@ -249,7 +249,7 @@ impl Wallet { match self.metadata(&metadata_params).await? { GenericMetadata::Ethereum(metadata) => metadata, GenericMetadata::Astar(metadata) => metadata.0, - _ => anyhow::bail!("unsupported op"), + GenericMetadata::Polkadot(_) => anyhow::bail!("unsupported op"), }; Ok(rosetta_tx_ethereum::U256(metadata.gas_limit).as_u128()) } @@ -274,7 +274,6 @@ impl Wallet { GenericClient::Ethereum(client) => client.call(&EthQuery::CallContract(call)).await?, GenericClient::Astar(client) => client.call(&EthQuery::CallContract(call)).await?, GenericClient::Polkadot(_) => anyhow::bail!("polkadot doesn't support eth_view_call"), - GenericClient::Bitcoin(_) => anyhow::bail!("bitcoin doesn't support eth_view_call"), }; let EthQueryResult::CallContract(exit_reason) = result else { anyhow::bail!("[this is a bug] invalid result type"); @@ -302,7 +301,6 @@ impl Wallet { client.call(&EthQuery::GetStorageAt(get_storage)).await? }, GenericClient::Polkadot(_) => anyhow::bail!("polkadot doesn't support eth_storage"), - GenericClient::Bitcoin(_) => anyhow::bail!("bitcoin doesn't support eth_storage"), }; let EthQueryResult::GetStorageAt(value) = result else { anyhow::bail!("[this is a bug] invalid result type"); @@ -329,7 +327,6 @@ impl Wallet { GenericClient::Ethereum(client) => client.call(&EthQuery::GetProof(get_proof)).await?, GenericClient::Astar(client) => client.call(&EthQuery::GetProof(get_proof)).await?, GenericClient::Polkadot(_) => anyhow::bail!("polkadot doesn't support eth_storage"), - GenericClient::Bitcoin(_) => anyhow::bail!("bitcoin doesn't support eth_storage"), }; let EthQueryResult::GetProof(proof) = result else { anyhow::bail!("[this is a bug] invalid result type"); @@ -353,7 +350,6 @@ impl Wallet { client.call(&EthQuery::GetTransactionReceipt(get_tx_receipt)).await? }, GenericClient::Polkadot(_) => anyhow::bail!("polkadot doesn't support eth_storage"), - GenericClient::Bitcoin(_) => anyhow::bail!("bitcoin doesn't support eth_storage"), }; let EthQueryResult::GetTransactionReceipt(maybe_receipt) = result else { anyhow::bail!("[this is a bug] invalid result type"); @@ -371,7 +367,6 @@ impl Wallet { GenericClient::Ethereum(client) => client.call(&EthQuery::ChainId).await?, GenericClient::Astar(client) => client.call(&EthQuery::ChainId).await?, GenericClient::Polkadot(_) => anyhow::bail!("polkadot doesn't support eth_chainId"), - GenericClient::Bitcoin(_) => anyhow::bail!("bitcoin doesn't support eth_chainId"), }; let EthQueryResult::ChainId(value) = result else { anyhow::bail!("[this is a bug] invalid result type"); diff --git a/rosetta-server/build.rs b/rosetta-server/build.rs deleted file mode 100644 index 88e78920..00000000 --- a/rosetta-server/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -use anyhow::Result; -use vergen::EmitBuilder; - -fn main() -> Result<()> { - EmitBuilder::builder().all_git().emit()?; - Ok(()) -} diff --git a/scripts/check.sh b/scripts/check.sh index 88b8a2b8..78dc6985 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -73,7 +73,6 @@ exec_cmd 'dprint check' 'dprint check' exec_cmd 'cargo deny' 'cargo deny check' # exec_cmd 'clippy rosetta-server-astar' 'cargo clippy --locked -p rosetta-server-astar --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' -# exec_cmd 'clippy rosetta-server-bitcoin' 'cargo clippy --locked -p rosetta-server-bitcoin --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' # exec_cmd 'clippy rosetta-server-ethereum' 'cargo clippy --locked -p rosetta-server-ethereum --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' # exec_cmd 'clippy rosetta-server-polkadot' 'cargo clippy --locked -p rosetta-server-polkadot --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' # exec_cmd 'clippy rosetta-client' 'cargo clippy --locked -p rosetta-client --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' @@ -85,11 +84,9 @@ if [[ "${RUN_TESTS}" == "1" ]]; then cargo test --locked -p rosetta-server-ethereum cargo test --locked -p rosetta-server-astar cargo test --locked -p rosetta-server-polkadot - cargo test --locked -p rosetta-server-bitcoin cargo test --locked -p rosetta-client cargo test --locked --workspace --all-features \ --exclude rosetta-server-astar \ - --exclude rosetta-server-bitcoin \ --exclude rosetta-server-ethereum \ --exclude rosetta-server-polkadot \ --exclude rosetta-client diff --git a/scripts/pull_nodes.sh b/scripts/pull_nodes.sh index 2f8b8a25..5498c419 100755 --- a/scripts/pull_nodes.sh +++ b/scripts/pull_nodes.sh @@ -1,7 +1,6 @@ #!/bin/bash set -e -docker image pull ruimarinho/bitcoin-core:23 docker image pull ethereum/client-go:v1.12.2 docker image pull parity/polkadot:v1.5.0 docker image pull staketechnologies/astar-collator:v5.28.0-rerun diff --git a/scripts/push_connectors.sh b/scripts/push_connectors.sh deleted file mode 100755 index 149bd0d6..00000000 --- a/scripts/push_connectors.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -set -e - -# First arg is the Docker image tag on all images -tag=$1 - -docker push analoglabs/connector-bitcoin:latest -docker push analoglabs/connector-ethereum:latest -docker push analoglabs/connector-polkadot:latest -docker push analoglabs/connector-astar:latest - -if [[ -n "${tag}" ]]; then - echo "Tagging all images: ${tag}"; - docker tag analoglabs/connector-bitcoin:latest "analoglabs/connector-bitcoin:${tag}" - docker push "analoglabs/connector-bitcoin:${tag}" - - docker tag analoglabs/connector-ethereum:latest "analoglabs/connector-ethereum:${tag}" - docker push "analoglabs/connector-ethereum:${tag}" - - docker tag analoglabs/connector-polkadot:latest "analoglabs/connector-polkadot:${tag}" - docker push "analoglabs/connector-polkadot:${tag}" - - docker tag analoglabs/connector-astar:latest "analoglabs/connector-astar:${tag}" - docker push "analoglabs/connector-astar:${tag}" -fi From a1f76cb0398e6c46d605d11d9e85ab169d91ad3f Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Thu, 18 Jan 2024 18:45:09 -0300 Subject: [PATCH 02/26] Remove comment --- rosetta-client/src/client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index 17bb9257..e0b9ada4 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -21,9 +21,8 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::{pin::Pin, str::FromStr}; -// TODO: Use -#[allow(clippy::large_enum_variant)] /// Generic Client +#[allow(clippy::large_enum_variant)] pub enum GenericClient { Ethereum(EthereumClient), Astar(AstarClient), From f1c0c5c0d71c21a77d5d6ba532883bb8281062f9 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Fri, 19 Jan 2024 02:12:27 -0300 Subject: [PATCH 03/26] Create ethereum types --- Cargo.lock | 2 + chains/ethereum/config/Cargo.toml | 3 + chains/ethereum/config/src/lib.rs | 35 ++ chains/ethereum/config/src/serde_utils.rs | 37 +- chains/ethereum/config/src/types.rs | 4 + chains/ethereum/config/src/types/block.rs | 60 +++ chains/ethereum/config/src/types/constants.rs | 14 + chains/ethereum/config/src/types/header.rs | 158 ++++++ .../ethereum/config/src/types/transaction.rs | 17 + .../src/types/transaction/access_list.rs | 130 +++++ .../config/src/types/transaction/eip1559.rs | 157 ++++++ .../config/src/types/transaction/eip2930.rs | 121 +++++ .../config/src/types/transaction/legacy.rs | 122 +++++ .../src/types/transaction/rpc_transaction.rs | 450 ++++++++++++++++++ .../config/src/types/transaction/signature.rs | 133 ++++++ .../types/transaction/signed_transaction.rs | 209 ++++++++ .../types/transaction/typed_transaction.rs | 194 ++++++++ rosetta-core/src/lib.rs | 1 + rosetta-core/src/traits.rs | 74 ++- scripts/check.sh | 23 + 20 files changed, 1940 insertions(+), 4 deletions(-) create mode 100644 chains/ethereum/config/src/types/block.rs create mode 100644 chains/ethereum/config/src/types/constants.rs create mode 100644 chains/ethereum/config/src/types/header.rs create mode 100644 chains/ethereum/config/src/types/transaction.rs create mode 100644 chains/ethereum/config/src/types/transaction/access_list.rs create mode 100644 chains/ethereum/config/src/types/transaction/eip1559.rs create mode 100644 chains/ethereum/config/src/types/transaction/eip2930.rs create mode 100644 chains/ethereum/config/src/types/transaction/legacy.rs create mode 100644 chains/ethereum/config/src/types/transaction/rpc_transaction.rs create mode 100644 chains/ethereum/config/src/types/transaction/signature.rs create mode 100644 chains/ethereum/config/src/types/transaction/signed_transaction.rs create mode 100644 chains/ethereum/config/src/types/transaction/typed_transaction.rs diff --git a/Cargo.lock b/Cargo.lock index f7e80d78..1fed073d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5135,6 +5135,8 @@ name = "rosetta-config-ethereum" version = "0.5.0" dependencies = [ "anyhow", + "derivative", + "ethbloom", "ethereum-types", "hex-literal", "impl-serde", diff --git a/chains/ethereum/config/Cargo.toml b/chains/ethereum/config/Cargo.toml index 37c4803e..b73b39ec 100644 --- a/chains/ethereum/config/Cargo.toml +++ b/chains/ethereum/config/Cargo.toml @@ -8,7 +8,10 @@ description = "Ethereum configuration." [dependencies] anyhow = "1.0" +derivative = { version = "2.2", default-features = false, features = ["use_core"] } +ethbloom = { version = "0.13", default-features = false } ethereum-types = { version = "0.14", default-features = false, features = ["rlp", "ethbloom", "num-traits"] } +hex-literal = { version = "0.4" } rosetta-config-astar = { workspace = true } rosetta-core.workspace = true diff --git a/chains/ethereum/config/src/lib.rs b/chains/ethereum/config/src/lib.rs index a4debad2..be9614f6 100644 --- a/chains/ethereum/config/src/lib.rs +++ b/chains/ethereum/config/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), no_std)] + #[cfg(feature = "serde")] mod serde_utils; mod types; @@ -13,6 +15,39 @@ use rosetta_core::{ use std::sync::Arc; pub use types::*; +#[cfg(not(feature = "std"))] +#[cfg_attr(test, macro_use)] +extern crate alloc; + +#[cfg(feature = "std")] +pub(crate) mod rstd { + #[cfg(feature = "serde")] + pub use std::{option, result}; + + pub use std::{ + // borrow, boxed, cmp, convert, default, fmt, hash, iter, marker, mem, ops, rc, result, + // time, + vec, + }; + // pub mod error { + // pub use std::error::Error; + // } +} + +#[cfg(not(feature = "std"))] +pub(crate) mod rstd { + #[cfg(feature = "serde")] + pub use core::{option, result}; + + pub use alloc::vec; + // pub use alloc::{borrow, boxed, rc, vec}; + // pub use core::{cmp, convert, default, fmt, hash, iter, marker, mem, ops, result, time}; + // pub mod error { + // pub trait Error {} + // impl Error for T {} + // } +} + /// Retrieve the [`BlockchainConfig`] from the provided polygon `network` /// /// # Errors diff --git a/chains/ethereum/config/src/serde_utils.rs b/chains/ethereum/config/src/serde_utils.rs index e3a2742a..204158e6 100644 --- a/chains/ethereum/config/src/serde_utils.rs +++ b/chains/ethereum/config/src/serde_utils.rs @@ -1,3 +1,4 @@ +use crate::rstd::{option::Option, result::Result, vec::Vec}; use impl_serde::serialize::{deserialize_check_len, serialize_uint, ExpectedLen}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -9,7 +10,7 @@ pub mod uint_to_hex { #[allow(clippy::trivially_copy_pass_by_ref)] pub fn serialize(value: &T, serializer: S) -> Result where - T: SerializableNumber, + T: SerializableNumber + core::fmt::Debug, S: Serializer, { T::serialize_eth_uint(value, serializer) @@ -46,6 +47,20 @@ pub mod bytes_to_hex { } } +/// Deserialize that always returns `Some(T)` or `Some(T::default())` must be used with +/// `#[serde(deserialize_with = "deserialize_null_default")]` attribute +/// +/// # Errors +/// returns an error if fails to deserialize T +pub fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result +where + T: Default + Deserialize<'de>, + D: Deserializer<'de>, +{ + let opt = as Deserialize<'de>>::deserialize(deserializer)?; + Ok(opt.unwrap_or_default()) +} + /// Serialize a primitive uint as hexadecimal string, must be used with `#[serde(serialize_with = /// "serialize_uint")]` attribute pub trait SerializableNumber { @@ -94,7 +109,7 @@ pub trait DeserializableNumber<'de>: Sized { impl<'de, T> DeserializableNumber<'de> for Option where - T: DeserializableNumber<'de>, + T: DeserializableNumber<'de> + core::fmt::Debug, { /// Deserialize a primitive uint from hexadecimal string /// # Errors @@ -112,6 +127,24 @@ where /// Helper for deserializing optional uints from hexadecimal string struct DeserializeWrapper(T); +impl core::fmt::Debug for DeserializeWrapper +where + T: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("DeserializeWrapper").field(&self.0).finish() + } +} + +impl core::fmt::Display for DeserializeWrapper +where + T: core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + ::fmt(&self.0, f) + } +} + impl DeserializeWrapper { fn into_inner(self) -> T { self.0 diff --git a/chains/ethereum/config/src/types.rs b/chains/ethereum/config/src/types.rs index 40f16718..a1a2f4fa 100644 --- a/chains/ethereum/config/src/types.rs +++ b/chains/ethereum/config/src/types.rs @@ -1,3 +1,7 @@ +pub mod block; +pub mod constants; +pub mod header; +pub mod transaction; pub use ethereum_types; use ethereum_types::{Address, Bloom, H256, U256}; diff --git a/chains/ethereum/config/src/types/block.rs b/chains/ethereum/config/src/types/block.rs new file mode 100644 index 00000000..bf5b745b --- /dev/null +++ b/chains/ethereum/config/src/types/block.rs @@ -0,0 +1,60 @@ +use crate::{header::Header, rstd::vec::Vec}; +use ethereum_types::{H256, U256}; + +#[cfg(feature = "serde")] +use crate::serde_utils::{bytes_to_hex, uint_to_hex}; + +/// 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 = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct Block { + /// Hash of the block + pub hash: H256, + + /// Block header. + #[cfg_attr(feature = "serde", serde(flatten))] + pub header: Header, + + /// Total difficulty + #[cfg_attr(feature = "serde", serde(default))] + pub total_difficulty: Option, + + /// Seal fields + #[cfg_attr( + feature = "serde", + serde( + default, + rename = "sealFields", + with = "bytes_to_hex", + skip_serializing_if = "Vec::is_empty", + ) + )] + pub seal_fields: Vec, + + /// Transactions + #[cfg_attr( + feature = "serde", + serde(bound = "TX: serde::Serialize + serde::de::DeserializeOwned") + )] + pub transactions: Vec, + + /// Uncles' hashes + #[cfg_attr( + feature = "serde", + serde(bound = "OMMERS: serde::Serialize + serde::de::DeserializeOwned") + )] + pub uncles: Vec, + + /// Size in bytes + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub size: Option, +} diff --git a/chains/ethereum/config/src/types/constants.rs b/chains/ethereum/config/src/types/constants.rs new file mode 100644 index 00000000..d0743183 --- /dev/null +++ b/chains/ethereum/config/src/types/constants.rs @@ -0,0 +1,14 @@ +use ethereum_types::H256; +use hex_literal::hex; + +/// Keccak256 over empty array. +pub const KECCAK_EMPTY: H256 = + H256(hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + +/// Ommer root of empty list. +pub const EMPTY_OMMER_ROOT_HASH: H256 = + H256(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")); + +/// Root hash of an empty trie. +pub const EMPTY_ROOT_HASH: H256 = + H256(hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")); diff --git a/chains/ethereum/config/src/types/header.rs b/chains/ethereum/config/src/types/header.rs new file mode 100644 index 00000000..fc5c42bc --- /dev/null +++ b/chains/ethereum/config/src/types/header.rs @@ -0,0 +1,158 @@ +use crate::{ + constants::{EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH}, + rstd::vec::Vec, +}; +use ethbloom::Bloom; +use ethereum_types::{H160, H256, U256}; + +#[cfg(feature = "serde")] +use crate::serde_utils::{bytes_to_hex, uint_to_hex}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct Header { + /// The Keccak 256-bit hash of the parent + /// block’s header, in its entirety; formally Hp. + pub parent_hash: H256, + + /// The Keccak 256-bit hash of the ommers list portion of this block; formally Ho. + #[cfg_attr(feature = "serde", serde(rename = "sha3Uncles"))] + pub ommers_hash: H256, + + /// The 160-bit address to which all fees collected from the successful mining of this block + /// be transferred; formally Hc. + #[cfg_attr(feature = "serde", serde(rename = "miner", alias = "beneficiary"))] + pub beneficiary: H160, + + /// The Keccak 256-bit hash of the root node of the state trie, after all transactions are + /// executed and finalisations applied; formally Hr. + pub state_root: H256, + + /// The Keccak 256-bit hash of the root node of the trie structure populated with each + /// transaction in the transactions list portion of the block; formally Ht. + pub transactions_root: H256, + + /// The Keccak 256-bit hash of the root node of the trie structure populated with the receipts + /// of each transaction in the transactions list portion of the block; formally He. + pub receipts_root: H256, + + /// The Bloom filter composed from indexable information (logger address and log topics) + /// contained in each log entry from the receipt of each transaction in the transactions list; + /// formally Hb. + pub logs_bloom: Bloom, + + /// A scalar value corresponding to the difficulty level of this block. This can be calculated + /// from the previous block’s difficulty level and the timestamp; formally Hd. + pub difficulty: U256, + + /// A scalar value equal to the number of ancestor blocks. The genesis block has a number of + /// zero; formally Hi. + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub number: u64, + + /// A scalar value equal to the current limit of gas expenditure per block; formally Hl. + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub gas_limit: u64, + + /// A scalar value equal to the total gas used in transactions in this block; formally Hg. + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub gas_used: u64, + + /// A scalar value equal to the reasonable output of Unix’s time() at this block’s inception; + /// formally Hs. + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub timestamp: u64, + + /// An arbitrary byte array containing data relevant to this block. This must be 32 bytes or + /// fewer; formally Hx. + #[cfg_attr(feature = "serde", serde(default, with = "bytes_to_hex"))] + pub extra_data: Vec, + + /// A 256-bit hash which, combined with the + /// nonce, proves that a sufficient amount of computation has been carried out on this block; + /// formally Hm. + #[cfg_attr(feature = "serde", serde(default))] + pub mix_hash: H256, + + /// A 64-bit value which, combined with the mixhash, proves that a sufficient amount of + /// computation has been carried out on this block; formally Hn. + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub nonce: u64, + + /// A scalar representing EIP1559 base fee which can move up or down each block according + /// to a formula which is a function of gas used in parent block and gas target + /// (block gas limit divided by elasticity multiplier) of parent block. + /// The algorithm results in the base fee per gas increasing when blocks are + /// above the gas target, and decreasing when blocks are below the gas target. The base fee per + /// gas is burned. + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none", with = "uint_to_hex") + )] + pub base_fee_per_gas: Option, + + /// The Keccak 256-bit hash of the withdrawals list portion of this block. + /// + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))] + pub withdrawals_root: Option, + + /// The total amount of blob gas consumed by the transactions within the block, added in + /// EIP-4844. + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none", with = "uint_to_hex") + )] + pub blob_gas_used: Option, + + /// A running total of blob gas consumed in excess of the target, prior to the block. Blocks + /// with above-target blob gas consumption increase this value, blocks with below-target blob + /// gas consumption decrease it (bounded at 0). This was added in EIP-4844. + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none", with = "uint_to_hex") + )] + pub excess_blob_gas: Option, + + /// The hash of the parent beacon block's root is included in execution blocks, as proposed by + /// EIP-4788. + /// + /// This enables trust-minimized access to consensus state, supporting staking pools, bridges, + /// and more. + /// + /// The beacon roots contract handles root storage, enhancing Ethereum's functionalities. + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))] + pub parent_beacon_block_root: Option, +} + +impl Default for Header { + fn default() -> Self { + Self { + parent_hash: H256::zero(), + ommers_hash: EMPTY_OMMER_ROOT_HASH, + beneficiary: H160::zero(), + state_root: EMPTY_ROOT_HASH, + transactions_root: EMPTY_ROOT_HASH, + receipts_root: EMPTY_ROOT_HASH, + logs_bloom: Bloom::zero(), + difficulty: U256::zero(), + number: 0, + gas_limit: 0, + gas_used: 0, + timestamp: 0, + extra_data: Vec::new(), + mix_hash: H256::zero(), + nonce: 0, + base_fee_per_gas: None, + withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, + } + } +} diff --git a/chains/ethereum/config/src/types/transaction.rs b/chains/ethereum/config/src/types/transaction.rs new file mode 100644 index 00000000..328121f9 --- /dev/null +++ b/chains/ethereum/config/src/types/transaction.rs @@ -0,0 +1,17 @@ +mod access_list; +mod eip1559; +mod eip2930; +mod legacy; +mod rpc_transaction; +mod signature; +mod signed_transaction; +mod typed_transaction; + +pub use access_list::{AccessList, AccessListItem, AccessListWithGasUsed}; +pub use eip1559::Eip1559Transaction; +pub use eip2930::Eip2930Transaction; +pub use legacy::LegacyTransaction; +pub use rpc_transaction::RpcTransaction; +pub use signature::Signature; +pub use signed_transaction::SignedTransaction; +pub use typed_transaction::TypedTransaction; diff --git a/chains/ethereum/config/src/types/transaction/access_list.rs b/chains/ethereum/config/src/types/transaction/access_list.rs new file mode 100644 index 00000000..fe9fa089 --- /dev/null +++ b/chains/ethereum/config/src/types/transaction/access_list.rs @@ -0,0 +1,130 @@ +use crate::rstd::{vec, vec::Vec}; +use ethereum_types::{H160, H256, U256}; + +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct AccessList(pub Vec); + +impl AccessList { + #[must_use] + pub const fn new() -> Self { + Self(Vec::new()) + } + + #[must_use] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } + + #[must_use] + pub fn into_raw(self) -> Vec<(H160, Vec)> { + self.0 + .into_iter() + .map(|item| (item.address, item.storage_keys)) + .collect::>() + } +} + +impl From)>> for AccessList { + fn from(src: Vec<(H160, Vec)>) -> Self { + Self( + src.into_iter() + .map(|(address, storage_keys)| AccessListItem { address, storage_keys }) + .collect(), + ) + } +} + +impl IntoIterator for AccessList { + type Item = AccessListItem; + type IntoIter = vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct AccessListWithGasUsed { + pub access_list: AccessList, + pub gas_used: U256, +} + +impl From> for AccessList { + fn from(src: Vec) -> Self { + Self(src) + } +} + +/// Access list item +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct AccessListItem { + /// Accessed address + pub address: H160, + /// Accessed storage keys + pub storage_keys: Vec, +} + +#[cfg(all(test, feature = "serde"))] +mod tests { + use super::{AccessList, AccessListItem, H160, H256}; + + #[test] + fn serde_encode_works() { + let access_list = AccessList(vec![AccessListItem { + address: H160::from(hex_literal::hex!("8e5660b4ab70168b5a6feea0e0315cb49c8cd539")), + storage_keys: vec![ + H256::zero(), + H256::from(hex_literal::hex!( + "a19fd53308a1c44a3ed22d3f20ed4229aa8909e0d0a90510ca482367ad42caa6" + )), + H256::from(hex_literal::hex!( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )), + ], + }]); + + // can encode as json + let actual = serde_json::to_value(access_list.clone()).unwrap(); + let expected = serde_json::json!([ + { + "address": "0x8e5660b4ab70168b5a6feea0e0315cb49c8cd539", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xa19fd53308a1c44a3ed22d3f20ed4229aa8909e0d0a90510ca482367ad42caa6", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ], + }, + ]); + assert_eq!(expected, actual); + + // can decode json + let json_str = serde_json::to_string(&access_list).unwrap(); + let decoded = serde_json::from_str::(&json_str).unwrap(); + assert_eq!(access_list, decoded); + } +} diff --git a/chains/ethereum/config/src/types/transaction/eip1559.rs b/chains/ethereum/config/src/types/transaction/eip1559.rs new file mode 100644 index 00000000..19f5a77f --- /dev/null +++ b/chains/ethereum/config/src/types/transaction/eip1559.rs @@ -0,0 +1,157 @@ +use super::access_list::AccessList; +use crate::rstd::vec::Vec; +use ethereum_types::{H160, U256}; + +#[cfg(feature = "serde")] +use crate::serde_utils::{bytes_to_hex, uint_to_hex}; + +/// Transactions with type 0x2 are transactions introduced in EIP-1559, included in Ethereum's +/// London fork. EIP-1559 addresses the network congestion and overpricing of transaction fees +/// caused by the historical fee market, in which users send transactions specifying a gas price bid +/// using the gasPrice parameter, and miners choose transactions with the highest bids. +/// +/// EIP-1559 transactions don’t specify gasPrice, and instead use an in-protocol, dynamically +/// changing base fee per gas. At each block, the base fee per gas is adjusted to address network +/// congestion as measured by a gas target. +/// +/// An EIP-1559 transaction always pays the base fee of the block it’s included in, and it pays a +/// priority fee as priced by `max_priority_fee_per_gas` or, if the base fee per gas + +/// `max_priority_fee_per_gas` exceeds `max_fee_per_gas`, it pays a priority fee as priced by +/// `max_fee_per_gas` minus the base fee per gas. The base fee is burned, and the priority fee is +/// paid to the miner that included the transaction. A transaction’s priority fee per gas +/// incentivizes miners to include the transaction over other transactions with lower priority fees +/// per gas. +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct Eip1559Transaction { + /// The chain ID of the transaction. It is mandatory for EIP-1559 transactions. + /// + /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 + /// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub chain_id: u64, + + /// The nonce of the transaction. + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub nonce: u64, + + /// Represents the maximum tx fee that will go to the miner as part of the user's + /// fee payment. It serves 3 purposes: + /// 1. Compensates miners for the uncle/ommer risk + fixed costs of including transaction in a + /// block; + /// 2. Allows users with high opportunity costs to pay a premium to miners; + /// 3. In times where demand exceeds the available block space (i.e. 100% full, 30mm gas), + /// this component allows first price auctions (i.e. the pre-1559 fee model) to happen on the + /// priority fee. + /// + /// Incorporated as part of the London upgrade via [EIP-1559]. + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + pub max_priority_fee_per_gas: U256, + + /// Represents the maximum amount that a user is willing to pay for their tx (inclusive of + /// baseFeePerGas and maxPriorityFeePerGas). The difference between maxFeePerGas and + /// baseFeePerGas + maxPriorityFeePerGas is “refunded” to the user. + /// + /// Incorporated as part of the London upgrade via [EIP-1559]. + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + pub max_fee_per_gas: U256, + + /// Supplied gas + #[cfg_attr(feature = "serde", serde(rename = "gas", with = "uint_to_hex",))] + pub gas_limit: u64, + + /// Recipient address (None for contract creation) + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub to: Option, + + /// Transferred value + pub value: U256, + + /// The data of the transaction. + #[cfg_attr( + feature = "serde", + serde(with = "bytes_to_hex", skip_serializing_if = "Vec::is_empty") + )] + pub data: Vec, + + /// Optional access list introduced in EIP-2930. + /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "AccessList::is_empty"))] + pub access_list: AccessList, +} + +#[cfg(all(test, feature = "serde"))] +pub mod tests { + use super::Eip1559Transaction; + use crate::transaction::{AccessList, AccessListItem}; + use ethereum_types::{H160, H256}; + use hex_literal::hex; + + pub fn build_eip1559() -> (Eip1559Transaction, serde_json::Value) { + let tx = Eip1559Transaction { + chain_id: 1, + nonce: 117, + max_priority_fee_per_gas: 100_000_000.into(), + max_fee_per_gas: 28_379_509_371u128.into(), + gas_limit: 187_293, + to: Some(hex!("3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad").into()), + value: 3_650_000_000_000_000_000u128.into(), + data: hex!("3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006547d41700000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000032a767a9562d00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000032a767a9562d000000000000000000000000000000000000000000000021b60af11987fa0670342f00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb8b55ee890426341fe45ee6dc788d2d93d25b59063000000000000000000000000000000000000000000").to_vec(), + access_list: AccessList(vec![AccessListItem { + address: H160::from(hex!("3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad")), + storage_keys: vec![ + H256::zero(), + H256::from(hex!( + "a19fd53308a1c44a3ed22d3f20ed4229aa8909e0d0a90510ca482367ad42caa6" + )), + H256::from(hex!( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )), + ], + }]), + }; + let json = serde_json::json!({ + "chainId": "0x1", + "nonce": "0x75", + "maxPriorityFeePerGas": "0x5f5e100", + "maxFeePerGas": "0x69b8cf27b", + "gas": "0x2db9d", + "to": "0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad", + "value": "0x32a767a9562d0000", + "data": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006547d41700000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000032a767a9562d00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000032a767a9562d000000000000000000000000000000000000000000000021b60af11987fa0670342f00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb8b55ee890426341fe45ee6dc788d2d93d25b59063000000000000000000000000000000000000000000", + "accessList": [ + { + "address": "0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xa19fd53308a1c44a3ed22d3f20ed4229aa8909e0d0a90510ca482367ad42caa6", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ] + } + ], + // "v": "0x1", + // "r": "0xbde8e920a9acce0c9950f112d02d457d517835297b2610b4d0bcd56df114010f", + // "s": "0x66ee7972cde2c5bd85fdb06aa358da04944b3ad5e56fe3e06d8fcb1137a52939" + }); + (tx, json) + } + + #[test] + fn serde_encode_works() { + let (tx, expected) = build_eip1559(); + let actual = serde_json::to_value(&tx).unwrap(); + assert_eq!(expected, actual); + + // can decode json + let json_str = serde_json::to_string(&tx).unwrap(); + let decoded = serde_json::from_str::(&json_str).unwrap(); + assert_eq!(tx, decoded); + } +} diff --git a/chains/ethereum/config/src/types/transaction/eip2930.rs b/chains/ethereum/config/src/types/transaction/eip2930.rs new file mode 100644 index 00000000..5bbb9511 --- /dev/null +++ b/chains/ethereum/config/src/types/transaction/eip2930.rs @@ -0,0 +1,121 @@ +use super::access_list::AccessList; +use crate::rstd::vec::Vec; +use ethereum_types::{H160, U256}; + +#[cfg(feature = "serde")] +use crate::serde_utils::{bytes_to_hex, uint_to_hex}; + +/// Transactions with type 0x1 are transactions introduced in EIP-2930. They contain, along with the +/// legacy parameters, an access list which specifies an array of addresses and storage keys that +/// the transaction plans to access (an access list) +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct Eip2930Transaction { + /// The chain ID of the transaction. It is mandatory for EIP-2930 transactions. + /// + /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 + /// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 + /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub chain_id: u64, + + /// The nonce of the transaction. + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub nonce: u64, + + /// Gas price + pub gas_price: U256, + + /// Supplied gas + #[cfg_attr(feature = "serde", serde(rename = "gas", with = "uint_to_hex"))] + pub gas_limit: u64, + + /// Recipient address (None for contract creation) + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub to: Option, + + /// Transferred value + pub value: U256, + + /// The data of the transaction. + #[cfg_attr( + feature = "serde", + serde(with = "bytes_to_hex", skip_serializing_if = "Vec::is_empty") + )] + pub data: Vec, + + /// Optional access list introduced in EIP-2930. + /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "AccessList::is_empty"))] + pub access_list: AccessList, +} + +#[cfg(all(test, feature = "serde"))] +pub mod tests { + use super::Eip2930Transaction; + use crate::transaction::{AccessList, AccessListItem}; + use ethereum_types::{H160, H256}; + use hex_literal::hex; + + pub fn build_eip2930() -> (Eip2930Transaction, serde_json::Value) { + let tx = Eip2930Transaction { + chain_id: 1, + nonce: 117, + gas_price: 28_379_509_371u128.into(), + gas_limit: 187_293, + to: Some(hex!("3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad").into()), + value: 3_650_000_000_000_000_000u128.into(), + data: hex!("3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006547d41700000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000032a767a9562d00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000032a767a9562d000000000000000000000000000000000000000000000021b60af11987fa0670342f00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb8b55ee890426341fe45ee6dc788d2d93d25b59063000000000000000000000000000000000000000000").to_vec(), + access_list: AccessList(vec![AccessListItem { + address: H160::from(hex!("3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad")), + storage_keys: vec![ + H256::zero(), + H256::from(hex!( + "a19fd53308a1c44a3ed22d3f20ed4229aa8909e0d0a90510ca482367ad42caa6" + )), + H256::from(hex!( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )), + ], + }]), + }; + let json = serde_json::json!({ + "chainId": "0x1", + "nonce": "0x75", + "gasPrice": "0x69b8cf27b", + "gas": "0x2db9d", + "to": "0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad", + "value": "0x32a767a9562d0000", + "data": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006547d41700000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000032a767a9562d00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000032a767a9562d000000000000000000000000000000000000000000000021b60af11987fa0670342f00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb8b55ee890426341fe45ee6dc788d2d93d25b59063000000000000000000000000000000000000000000", + "accessList": [ + { + "address": "0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xa19fd53308a1c44a3ed22d3f20ed4229aa8909e0d0a90510ca482367ad42caa6", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ] + } + ], + }); + (tx, json) + } + + #[test] + fn serde_encode_works() { + let (tx, expected) = build_eip2930(); + let actual = serde_json::to_value(&tx).unwrap(); + assert_eq!(expected, actual); + + // can decode json + let json_str = serde_json::to_string(&tx).unwrap(); + let decoded = serde_json::from_str::(&json_str).unwrap(); + assert_eq!(tx, decoded); + } +} diff --git a/chains/ethereum/config/src/types/transaction/legacy.rs b/chains/ethereum/config/src/types/transaction/legacy.rs new file mode 100644 index 00000000..8e931aff --- /dev/null +++ b/chains/ethereum/config/src/types/transaction/legacy.rs @@ -0,0 +1,122 @@ +use crate::rstd::vec::Vec; +use ethereum_types::{H160, U256}; + +#[cfg(feature = "serde")] +use crate::serde_utils::{bytes_to_hex, uint_to_hex}; + +/// Legacy transaction that use the transaction format existing before typed transactions were +/// introduced in EIP-2718. Legacy transactions don’t use access lists or incorporate EIP-1559 fee +/// market changes. +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct LegacyTransaction { + /// The nonce of the transaction. If set to `None`, no checks are performed. + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub nonce: u64, + + /// Gas price + pub gas_price: U256, + + /// Supplied gas + #[cfg_attr(feature = "serde", serde(rename = "gas", with = "uint_to_hex"))] + pub gas_limit: u64, + + /// Recipient address (None for contract creation) + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub to: Option, + + /// Transferred value + pub value: U256, + + /// The data of the transaction. + #[cfg_attr( + feature = "serde", + serde(with = "bytes_to_hex", skip_serializing_if = "Vec::is_empty") + )] + pub data: Vec, + + /// The chain ID of the transaction. If set to `None`, no checks are performed. + /// + /// Incorporated as part of the Spurious Dragon upgrade via [EIP-155]. + /// + /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none", with = "uint_to_hex") + )] + pub chain_id: Option, +} + +#[cfg(all(test, feature = "serde"))] +pub mod tests { + use super::LegacyTransaction; + use hex_literal::hex; + + pub fn build_legacy(eip155: bool) -> (LegacyTransaction, serde_json::Value) { + if eip155 { + let tx = LegacyTransaction { + chain_id: Some(1), + nonce: 137, + gas_price: 20_400_000_000u64.into(), + gas_limit: 1_000_000, + to: Some(hex!("dc6c91b569c98f9f6f74d90f9beff99fdaf4248b").into()), + value: 278_427_500_000_000_000u64.into(), + data: hex!("288b8133920339b815ee42a02099dcca27c01d192418334751613a1eea786a0c3a673cec000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000032464a3bc15000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000003dd2c560933380000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000b14232b0204b2f7bb6ba5aff59ef36030f7fe38b00000000000000000000000041f8d14c9475444f30a80431c68cf24dc9a8369a000000000000000000000000b9e29984fe50602e7a619662ebed4f90d93824c7000000000000000000000000dc6c91b569c98f9f6f74d90f9beff99fdaf4248b0000000000000000000000000000000000000000000000000000000002faf08000000000000000000000000000000000000000000000000003dd2c560933380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d7625241afaa81faf3c2bd525f64f6e0ec3af39c1053d672b65c2f64992521e6f454e67000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000581b96d32320d6cb174e807b585a41f4faa8ba7da95e117f2abbcadbb257d37a5fcc16c2ba6db86200888ed85dd5eba547bb07fa0f9910950d3133026abafdd5c09e1f3896a7abb3c99e1f38f77be69448ee7770d18c001e0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561c7dd7d43db98e3b7a6e18b4e97f0e254a5a6bb9b373d49d7e6676ccb1b02d50f131bca36928cb48cd0daec0499e9e93a390253c733607437bbebf0aa13b7080911f3896a7abb3c99e1f38f77be69448ee7770d18c0400000000000000000000").to_vec(), + }; + let json = serde_json::json!({ + "nonce": "0x89", + "gas": "0xf4240", + "gasPrice": "0x4bfef4c00", + "to": "0xdc6c91b569c98f9f6f74d90f9beff99fdaf4248b", + "value": "0x3dd2c5609333800", + "data": "0x288b8133920339b815ee42a02099dcca27c01d192418334751613a1eea786a0c3a673cec000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000032464a3bc15000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000003dd2c560933380000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000b14232b0204b2f7bb6ba5aff59ef36030f7fe38b00000000000000000000000041f8d14c9475444f30a80431c68cf24dc9a8369a000000000000000000000000b9e29984fe50602e7a619662ebed4f90d93824c7000000000000000000000000dc6c91b569c98f9f6f74d90f9beff99fdaf4248b0000000000000000000000000000000000000000000000000000000002faf08000000000000000000000000000000000000000000000000003dd2c560933380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d7625241afaa81faf3c2bd525f64f6e0ec3af39c1053d672b65c2f64992521e6f454e67000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000581b96d32320d6cb174e807b585a41f4faa8ba7da95e117f2abbcadbb257d37a5fcc16c2ba6db86200888ed85dd5eba547bb07fa0f9910950d3133026abafdd5c09e1f3896a7abb3c99e1f38f77be69448ee7770d18c001e0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561c7dd7d43db98e3b7a6e18b4e97f0e254a5a6bb9b373d49d7e6676ccb1b02d50f131bca36928cb48cd0daec0499e9e93a390253c733607437bbebf0aa13b7080911f3896a7abb3c99e1f38f77be69448ee7770d18c0400000000000000000000", + "chainId": "0x1", + }); + (tx, json) + } else { + let tx = LegacyTransaction { + chain_id: None, + nonce: 3166, + gas_price: 60_000_000_000u64.into(), + gas_limit: 300_000, + to: Some(hex!("6b92c944c82c694725acbd1c000c277ea1a44f00").into()), + value: 0.into(), + data: hex!("41c0e1b5").into(), + }; + let json = serde_json::json!({ + "gas": "0x493e0", + "gasPrice": "0xdf8475800", + "data": "0x41c0e1b5", + "nonce": "0xc5e", + "to": "0x6b92c944c82c694725acbd1c000c277ea1a44f00", + "value": "0x0", + }); + (tx, json) + } + } + + #[test] + fn serde_encode_works() { + // With EIP155 + let (tx, expected) = build_legacy(true); + let actual = serde_json::to_value(&tx).unwrap(); + assert_eq!(expected, actual); + let json_str = serde_json::to_string(&tx).unwrap(); + let decoded = serde_json::from_str::(&json_str).unwrap(); + assert_eq!(tx, decoded); + + // Without EIP155 + let (tx, expected) = build_legacy(false); + let actual = serde_json::to_value(&tx).unwrap(); + assert_eq!(expected, actual); + let json_str = serde_json::to_string(&tx).unwrap(); + let decoded = serde_json::from_str::(&json_str).unwrap(); + assert_eq!(tx, decoded); + } +} diff --git a/chains/ethereum/config/src/types/transaction/rpc_transaction.rs b/chains/ethereum/config/src/types/transaction/rpc_transaction.rs new file mode 100644 index 00000000..055d8739 --- /dev/null +++ b/chains/ethereum/config/src/types/transaction/rpc_transaction.rs @@ -0,0 +1,450 @@ +use crate::{ + rstd::vec::Vec, + transaction::{ + access_list::AccessList, eip1559::Eip1559Transaction, eip2930::Eip2930Transaction, + legacy::LegacyTransaction, signature::Signature, signed_transaction::SignedTransaction, + typed_transaction::TypedTransaction, + }, +}; +use ethereum_types::{H160, H256, H512, U256}; + +#[cfg(feature = "serde")] +use crate::serde_utils::{bytes_to_hex, deserialize_null_default, uint_to_hex}; + +/// Transaction +#[derive(Clone, Default, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct RpcTransaction { + /// Hash + pub hash: H256, + + /// Nonce + #[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] + pub nonce: u64, + + /// Block hash + #[cfg_attr(feature = "serde", serde(default))] + pub block_hash: Option, + + /// Block number + #[cfg_attr(feature = "serde", serde(default, with = "uint_to_hex"))] + pub block_number: Option, + + /// Transaction Index + #[cfg_attr(feature = "serde", serde(default, with = "uint_to_hex"))] + pub transaction_index: Option, + + /// Sender + pub from: H160, + + /// Recipient + #[cfg_attr(feature = "serde", serde(default))] + pub to: Option, + + /// Transfered value + pub value: U256, + + /// Gas Price + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))] + pub gas_price: Option, + + /// Max BaseFeePerGas the user is willing to pay. + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))] + pub max_fee_per_gas: Option, + + /// The miner's tip. + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))] + pub max_priority_fee_per_gas: Option, + + /// Gas limit + #[cfg_attr(feature = "serde", serde(default, rename = "gas"))] + pub gas_limit: U256, + + /// Data + #[cfg_attr( + feature = "serde", + serde(default, with = "bytes_to_hex", skip_serializing_if = "Vec::is_empty") + )] + pub input: Vec, + + /// Creates contract + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))] + pub creates: Option, + + /// Raw transaction data + #[cfg_attr( + feature = "serde", + serde(default, with = "bytes_to_hex", skip_serializing_if = "Vec::is_empty") + )] + pub raw: Vec, + + /// Public key of the signer. + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))] + pub public_key: Option, + + /// The network id of the transaction, if any. + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none", with = "uint_to_hex",) + )] + pub chain_id: Option, + + /// The V field of the signature. + #[cfg_attr(feature = "serde", serde(default, flatten))] + pub signature: Signature, + + /// Pre-pay to warm storage access. + #[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_null_default"))] + pub access_list: AccessList, + + /// EIP-2718 type + #[cfg_attr( + feature = "serde", + serde( + default, + rename = "type", + skip_serializing_if = "Option::is_none", + with = "uint_to_hex" + ) + )] + pub transaction_type: Option, +} + +impl TryFrom for LegacyTransaction { + type Error = &'static str; + + fn try_from(tx: RpcTransaction) -> Result { + if let Some(transaction_type) = tx.transaction_type { + if transaction_type != 0 { + return Err("transaction type is not 0"); + } + } + + if !tx.access_list.is_empty() { + return Err("legacy tx doesn't support access list"); + } + if tx.max_fee_per_gas.is_some() { + return Err("legacy tx doesn't support max_fee_per_gas"); + } + if tx.max_priority_fee_per_gas.is_some() { + return Err("legacy tx doesn't support max_priority_fee_per_gas"); + } + let Some(gas_price) = tx.gas_price else { + return Err("legacy tx gas_price is mandatory"); + }; + + let chain_id = if tx.signature.r.is_zero() && tx.signature.s.is_zero() { + tx.chain_id.or_else(|| tx.signature.v.chain_id()) + } else { + tx.signature.v.chain_id() + }; + + Ok(Self { + nonce: tx.nonce, + gas_price, + gas_limit: u64::try_from(tx.gas_limit).unwrap_or(u64::MAX), + to: tx.to, + value: tx.value, + data: tx.input, + chain_id, + }) + } +} + +impl TryFrom for Eip2930Transaction { + type Error = &'static str; + + fn try_from(tx: RpcTransaction) -> Result { + if let Some(transaction_type) = tx.transaction_type { + if transaction_type != 1 { + return Err("transaction type is not 0"); + } + } + + if tx.max_fee_per_gas.is_some() { + return Err("EIP2930 Tx doesn't support max_fee_per_gas"); + } + if tx.max_priority_fee_per_gas.is_some() { + return Err("EIP2930 Tx doesn't support max_priority_fee_per_gas"); + } + let Some(chain_id) = tx.chain_id else { + return Err("chain_id is mandatory for EIP2930 transactions"); + }; + let Some(gas_price) = tx.gas_price else { + return Err("gas_price is mandatory for EIP2930 transactions"); + }; + + Ok(Self { + nonce: tx.nonce, + gas_price, + gas_limit: u64::try_from(tx.gas_limit).unwrap_or(u64::MAX), + to: tx.to, + value: tx.value, + data: tx.input, + chain_id, + access_list: tx.access_list, + }) + } +} + +impl TryFrom for Eip1559Transaction { + type Error = &'static str; + + fn try_from(tx: RpcTransaction) -> Result { + if let Some(transaction_type) = tx.transaction_type { + if transaction_type != 2 { + return Err("transaction type is not 0"); + } + } + + let Some(chain_id) = tx.chain_id else { + return Err("chain_id is mandatory for EIP1559 transactions"); + }; + let Some(max_fee_per_gas) = tx.max_fee_per_gas else { + return Err("max_fee_per_gas is mandatory for EIP1559 transactions"); + }; + let Some(max_priority_fee_per_gas) = tx.max_priority_fee_per_gas else { + return Err("max_priority_fee_per_gas is mandatory for EIP1559 transactions"); + }; + + Ok(Self { + nonce: tx.nonce, + max_fee_per_gas, + max_priority_fee_per_gas, + gas_limit: u64::try_from(tx.gas_limit).unwrap_or(u64::MAX), + to: tx.to, + value: tx.value, + data: tx.input, + chain_id, + access_list: tx.access_list, + }) + } +} + +impl TryFrom for TypedTransaction { + type Error = &'static str; + + fn try_from(tx: RpcTransaction) -> Result { + let typed_tx = match tx.transaction_type { + Some(0) => Self::Legacy(tx.try_into()?), + Some(1) => Self::Eip2930(tx.try_into()?), + Some(2) => Self::Eip1559(tx.try_into()?), + Some(_) => return Err("unknown transaction type"), + None => { + if tx.max_fee_per_gas.is_some() || tx.max_priority_fee_per_gas.is_some() { + Self::Eip1559(tx.try_into()?) + } else { + Self::Legacy(tx.try_into()?) + } + }, + }; + Ok(typed_tx) + } +} + +impl TryFrom for SignedTransaction { + type Error = &'static str; + + fn try_from(tx: RpcTransaction) -> Result { + let tx_hash = tx.hash; + let signature = tx.signature; + let payload = match tx.transaction_type { + Some(0) => TypedTransaction::Legacy(tx.try_into()?), + Some(1) => TypedTransaction::Eip2930(tx.try_into()?), + Some(2) => TypedTransaction::Eip1559(tx.try_into()?), + Some(_) => return Err("unknown transaction type"), + None => { + if tx.max_fee_per_gas.is_some() || tx.max_priority_fee_per_gas.is_some() { + TypedTransaction::Eip1559(tx.try_into()?) + } else if tx.access_list.is_empty() { + TypedTransaction::Legacy(tx.try_into()?) + } else { + TypedTransaction::Eip2930(tx.try_into()?) + } + }, + }; + Ok(Self { tx_hash, payload, signature }) + } +} + +#[cfg(all(test, feature = "serde"))] +mod tests { + use super::RpcTransaction; + use crate::transaction::{access_list::AccessList, signature::Signature}; + use ethereum_types::{H160, H256, U256}; + use hex_literal::hex; + + #[test] + fn decode_legacy_json_works() { + let json = r#" + { + "hash": "0x831a62a594cb62b250a606a63d3a762300815c8d3765c6192d46d6bca440faa6", + "nonce": "0x32a", + "blockHash": "0xdbdb6ab6ef116b498ceab7141a8ab1646960e2550bafbe3e8e22f1daffacc7cf", + "blockNumber": "0x15780", + "transactionIndex": "0x0", + "from": "0x32be343b94f860124dc4fee278fdcbd38c102d88", + "to": "0x78293691c74717191d1d417b531f398350d54e89", + "value": "0x5fc1b97136320000", + "gasPrice": "0xde197ae65", + "gas": "0x5208", + "input": "0x", + "v": "0x1c", + "r": "0xc8fc04e29b0859a7f265b67af7d4c5c6bc9e3d5a8de4950f89fa71a12a3cf8ae", + "s": "0x7dd15a10f9f2c8d1519a6044d880d04756798fc23923ff94f4823df8dc5b987a", + "type": "0x0" + }"#; + let expected = RpcTransaction { + hash: H256(hex!("831a62a594cb62b250a606a63d3a762300815c8d3765c6192d46d6bca440faa6")), + nonce: 810, + block_hash: Some(H256(hex!( + "dbdb6ab6ef116b498ceab7141a8ab1646960e2550bafbe3e8e22f1daffacc7cf" + ))), + block_number: Some(87936), + transaction_index: Some(0), + gas_price: Some(59_619_389_029u128.into()), + gas_limit: 21000.into(), + from: H160(hex!("32be343b94f860124dc4fee278fdcbd38c102d88")), + to: Some(H160(hex!("78293691c74717191d1d417b531f398350d54e89"))), + value: 6_900_000_000_000_000_000u128.into(), + input: Vec::new(), + chain_id: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + creates: None, + raw: Vec::new(), + public_key: None, + signature: Signature { + v: 0x1c.into(), + r: U256::from_big_endian(&hex!( + "c8fc04e29b0859a7f265b67af7d4c5c6bc9e3d5a8de4950f89fa71a12a3cf8ae" + )), + s: U256::from_big_endian(&hex!( + "7dd15a10f9f2c8d1519a6044d880d04756798fc23923ff94f4823df8dc5b987a" + )), + }, + access_list: AccessList::default(), + transaction_type: Some(0), + }; + let actual = serde_json::from_str::(json).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn decode_eip1559_json_works() { + let json = r#" + { + "blockHash": "0xfdee00b60ddb4fd465426871a247ca905ff2acd5425b2222ab495157038772f3", + "blockNumber": "0x11abc28", + "from": "0x1e8c05fa1e52adcb0b66808fa7b843d106f506d5", + "gas": "0x2335e", + "gasPrice": "0xb9c7097c0", + "maxPriorityFeePerGas": "0x5f5e100", + "maxFeePerGas": "0xbdee918d2", + "hash": "0x24cce1f28e0462c26ece316d6ae808a972d41161a237f14d31ab22c11edfb122", + "input": "0x161ac21f0000000000000000000000001fe1ffffef6b4dca417d321ccd37e081f604d1c70000000000000000000000000000a26b00c1f0df003000390027140000faa71900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002360c6ebe", + "nonce": "0x1cca", + "to": "0x00005ea00ac477b1030ce78506496e8c2de24bf5", + "transactionIndex": "0x5f", + "value": "0x38d7ea4c680000", + "type": "0x2", + "accessList": [], + "chainId": "0x1", + "v": "0x0", + "r": "0x8623bae9c86fb05f96cebd0f07247afc363f0ed3e1cf381ef99277ebf2b6c84a", + "s": "0x766ba586a5aac2769cf5ce9e3c6fccf01ad6c57eeefc3770e4a2f49516837ae2" + } + "#; + let expected = RpcTransaction { + hash: hex!("24cce1f28e0462c26ece316d6ae808a972d41161a237f14d31ab22c11edfb122").into(), + nonce: 7370, + block_hash: Some(hex!("fdee00b60ddb4fd465426871a247ca905ff2acd5425b2222ab495157038772f3").into()), + block_number: Some(18_529_320), + transaction_index: Some(95), + gas_price: Some(49_869_264_832_u64.into()), + gas_limit: 0x2335e.into(), + from: H160(hex!("1e8c05fa1e52adcb0b66808fa7b843d106f506d5")), + to: Some(H160(hex!("00005ea00ac477b1030ce78506496e8c2de24bf5"))), + value: 16_000_000_000_000_000u128.into(), + input: hex!("161ac21f0000000000000000000000001fe1ffffef6b4dca417d321ccd37e081f604d1c70000000000000000000000000000a26b00c1f0df003000390027140000faa71900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002360c6ebe").into(), + chain_id: Some(1), + max_priority_fee_per_gas: Some(100_000_000.into()), + max_fee_per_gas: Some(50_984_458_450_u64.into()), + creates: None, + raw: Vec::new(), + public_key: None, + signature: Signature { + v: 0x0.into(), + r: hex!("8623bae9c86fb05f96cebd0f07247afc363f0ed3e1cf381ef99277ebf2b6c84a").into(), + s: hex!("766ba586a5aac2769cf5ce9e3c6fccf01ad6c57eeefc3770e4a2f49516837ae2").into(), + }, + access_list: AccessList::default(), + transaction_type: Some(2), + }; + let actual = serde_json::from_str::(json).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn decode_astar_json_works() { + let json = r#" + { + "hash": "0x543865875066b0c3b7039866deb8666c7740f83cc8a920b6b261cf30db1e6bdb", + "nonce": "0x71f1", + "blockHash": "0x73f9f64e13cf96569683db7eb494d52dcb52a98feae0b0519663d0c92702f3d2", + "blockNumber": "0x4a3b18", + "transactionIndex": "0x0", + "from": "0x530de54355b619bd9b3b46ab5054933b72ca8cc0", + "to": "0xa55d9ef16af921b70fed1421c1d298ca5a3a18f1", + "value": "0x0", + "gasPrice": "0x3b9aca000", + "gas": "0x61a80", + "input": "0x3798c7f200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000006551475800000000000000000000000000000000000000000000000000000000014a139f0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004415641580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054d415449430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000045ff8a5800000000000000000000000000000000000000000000000000000000036e5f480", + "creates": null, + "raw": "0xf9022f8271f18503b9aca00083061a8094a55d9ef16af921b70fed1421c1d298ca5a3a18f180b901c43798c7f200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000006551475800000000000000000000000000000000000000000000000000000000014a139f0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004415641580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054d415449430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000045ff8a5800000000000000000000000000000000000000000000000000000000036e5f4808204c4a04c58b0730a3487da33a44b7b501387fa48d6a6339d32ff520bcefc1da16945c1a062fb6b5c6c631b8d5205d59c0716c973995b47eb1eb329100e790a0957bff72c", + "publicKey": "0x75159f240a12daf62cd20487a6dca0093a6e8a139dacf8f8888fe582a1d08ae423f742a04b82579083e86c1b78104c7137e211be1d396a1c3c14fa840d9e094a", + "chainId": "0x250", + "standardV": "0x1", + "v": "0x4c4", + "r": "0x4c58b0730a3487da33a44b7b501387fa48d6a6339d32ff520bcefc1da16945c1", + "s": "0x62fb6b5c6c631b8d5205d59c0716c973995b47eb1eb329100e790a0957bff72c", + "accessList": null, + "type": "0x0" + } + "#; + let expected = RpcTransaction { + hash: hex!("543865875066b0c3b7039866deb8666c7740f83cc8a920b6b261cf30db1e6bdb").into(), + nonce: 0x71f1, + block_hash: Some(hex!("73f9f64e13cf96569683db7eb494d52dcb52a98feae0b0519663d0c92702f3d2").into()), + block_number: Some(0x004a_3b18), + transaction_index: Some(0x0), + gas_price: Some(0x0003_b9ac_a000_u64.into()), + gas_limit: 0x61a80.into(), + from: H160::from(hex!("530de54355b619bd9b3b46ab5054933b72ca8cc0")), + to: Some(H160::from(hex!("a55d9ef16af921b70fed1421c1d298ca5a3a18f1"))), + value: 0.into(), + input: hex!("3798c7f200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000006551475800000000000000000000000000000000000000000000000000000000014a139f0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004415641580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054d415449430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000045ff8a5800000000000000000000000000000000000000000000000000000000036e5f480").into(), + chain_id: Some(0x250), + max_priority_fee_per_gas: None, + max_fee_per_gas: None, + creates: None, + raw: hex!("f9022f8271f18503b9aca00083061a8094a55d9ef16af921b70fed1421c1d298ca5a3a18f180b901c43798c7f200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000006551475800000000000000000000000000000000000000000000000000000000014a139f0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004415641580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054d415449430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000045ff8a5800000000000000000000000000000000000000000000000000000000036e5f4808204c4a04c58b0730a3487da33a44b7b501387fa48d6a6339d32ff520bcefc1da16945c1a062fb6b5c6c631b8d5205d59c0716c973995b47eb1eb329100e790a0957bff72c").to_vec(), + public_key: Some(hex!("75159f240a12daf62cd20487a6dca0093a6e8a139dacf8f8888fe582a1d08ae423f742a04b82579083e86c1b78104c7137e211be1d396a1c3c14fa840d9e094a").into()), + signature: Signature { + v: 0x4c4.into(), + r: hex!("4c58b0730a3487da33a44b7b501387fa48d6a6339d32ff520bcefc1da16945c1").into(), + s: hex!("62fb6b5c6c631b8d5205d59c0716c973995b47eb1eb329100e790a0957bff72c").into(), + }, + access_list: AccessList::default(), + transaction_type: Some(0), + }; + let actual = serde_json::from_str::(json).unwrap(); + assert_eq!(expected, actual); + } +} diff --git a/chains/ethereum/config/src/types/transaction/signature.rs b/chains/ethereum/config/src/types/transaction/signature.rs new file mode 100644 index 00000000..d0f1f46d --- /dev/null +++ b/chains/ethereum/config/src/types/transaction/signature.rs @@ -0,0 +1,133 @@ +use ethereum_types::{H520, U256}; + +#[cfg(feature = "serde")] +use crate::serde_utils::uint_to_hex; + +/// An ECDSA signature +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct Signature { + /// The ECDSA recovery id, this value encodes the parity of the y-coordinate of the secp256k1 + /// signature. May also encode the chain_id for legacy EIP-155 transactions. + pub v: RecoveryId, + /// The ECDSA signature r + pub r: U256, + /// The ECDSA signature s + pub s: U256, +} + +impl Signature { + #[allow(clippy::cast_possible_truncation)] + pub fn to_raw_signature(&self, output: &mut [u8; 65]) { + self.r.to_big_endian(&mut output[0..32]); + self.s.to_big_endian(&mut output[32..64]); + // output[0..32].copy_from_slice(self.r.as_fixed_bytes()); + // output[32..64].copy_from_slice(self.s.as_fixed_bytes()); + output[64] = self.v.y_parity() as u8; + } +} + +impl From for H520 { + fn from(value: Signature) -> Self { + let mut output = [0u8; 65]; + value.to_raw_signature(&mut output); + Self(output) + } +} + +/// The ECDSA recovery id, encodes the parity of the y-coordinate and for EIP-155 compatible +/// transactions also encodes the chain id +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct RecoveryId(#[cfg_attr(feature = "serde", serde(with = "uint_to_hex"))] u64); + +impl RecoveryId { + #[must_use] + pub fn new(v: u64) -> Self { + debug_assert!(v >= 35 || matches!(v, 0 | 1 | 27 | 28)); + Self(v) + } + + #[must_use] + pub const fn as_u64(self) -> u64 { + self.0 + } + + /// Returns the parity (0 for even, 1 for odd) of the y-value of a secp256k1 signature. + #[must_use] + pub const fn y_parity(self) -> u64 { + let v = self.as_u64(); + + // if v is greather or equal to 35, it is an EIP-155 signature + // [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 + if v >= 35 { + return (v - 35) & 1; + } + + // 27 or 28, it is a legacy signature + if v == 27 || v == 28 { + return v - 27; + } + + // otherwise, simply return the parity of the least significant bit + v & 1 + } + + #[must_use] + pub const fn chain_id(self) -> Option { + let v = self.as_u64(); + if v >= 35 { + Some((v - 35) >> 1) + } else { + None + } + } + + #[must_use] + pub const fn is_eip155(self) -> bool { + self.chain_id().is_some() + } + + /// Applies [EIP155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) + #[must_use] + pub fn as_eip155>(self, chain_id: I) -> u64 { + let chain_id = chain_id.into(); + self.y_parity() + 35 + (chain_id * 2) + } + + /// the recovery id is encoded as 0 or 1 for EIP-2930. + #[must_use] + pub const fn is_eip2930(self) -> bool { + self.as_u64() < 2 + } + + /// Returns a legacy signature, with + #[must_use] + pub const fn as_legacy(self) -> u64 { + self.y_parity() + 27 + } +} + +impl From for u64 { + fn from(v: RecoveryId) -> Self { + v.as_u64() + } +} + +impl From for RecoveryId { + fn from(v: u64) -> Self { + Self::new(v) + } +} diff --git a/chains/ethereum/config/src/types/transaction/signed_transaction.rs b/chains/ethereum/config/src/types/transaction/signed_transaction.rs new file mode 100644 index 00000000..80ccd615 --- /dev/null +++ b/chains/ethereum/config/src/types/transaction/signed_transaction.rs @@ -0,0 +1,209 @@ +use derivative::Derivative; +use ethereum_types::H256; + +use super::signature::Signature; + +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(Derivative)] +#[derivative(Clone, PartialEq, Eq, Debug)] +pub struct SignedTransaction { + #[cfg_attr(feature = "serde", serde(rename = "hash"))] + pub tx_hash: H256, + #[cfg_attr( + feature = "serde", + serde( + bound( + serialize = "T: serde::Serialize", + deserialize = "T: serde::de::DeserializeOwned", + ), + flatten + ) + )] + pub payload: T, + #[cfg_attr(feature = "serde", serde(flatten))] + pub signature: Signature, +} + +impl SignedTransaction { + pub const fn new(tx_hash: H256, payload: T, signature: Signature) -> Self { + Self { tx_hash, payload, signature } + } +} + +#[cfg(all(test, feature = "serde"))] +mod tests { + use super::SignedTransaction; + use crate::transaction::{ + access_list::{AccessList, AccessListItem}, + eip2930::Eip2930Transaction, + legacy::LegacyTransaction, + rpc_transaction::RpcTransaction, + signature::{RecoveryId, Signature}, + typed_transaction::TypedTransaction, + }; + use ethereum_types::{H160, H256, U256}; + use hex_literal::hex; + + fn build_eip2930() -> (H256, Eip2930Transaction, Signature) { + let tx_hash = + H256(hex!("a777326ad77731344d00263b06843be6ef05cbe9ab699e2ed0d1448f8b2b50a3")); + let tx = Eip2930Transaction { + chain_id: 1, + nonce: 117, + gas_price: 28_379_509_371u128.into(), + gas_limit: 187_293, + to: Some(H160(hex!("3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad"))), + value: 3_650_000_000_000_000_000u128.into(), + data: hex!("3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006547d41700000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000032a767a9562d00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000032a767a9562d000000000000000000000000000000000000000000000021b60af11987fa0670342f00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb8b55ee890426341fe45ee6dc788d2d93d25b59063000000000000000000000000000000000000000000").to_vec(), + access_list: AccessList(vec![AccessListItem { + address: H160(hex!("3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad")), + storage_keys: vec![ + H256::zero(), + H256(hex!( + "a19fd53308a1c44a3ed22d3f20ed4229aa8909e0d0a90510ca482367ad42caa6" + )), + H256(hex!( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )), + ], + }]), + }; + let signature = Signature { + v: RecoveryId::new(0x01), + r: hex!("5fe8eb06ac27f44de3e8d1c7214f750b9fc8291ab63d71ea6a4456cfd328deb9").into(), + s: hex!("41425cc35a5ed1c922c898cb7fda5cf3b165b4792ada812700bf55cbc21a75a1").into(), + }; + (tx_hash, tx, signature) + } + + #[test] + fn serde_encode_works() { + let (tx_hash, tx, sig) = build_eip2930(); + let signed_tx = SignedTransaction::new(tx_hash, tx, sig); + let actual = serde_json::to_value(&signed_tx).unwrap(); + let expected = serde_json::json!({ + "hash": "0xa777326ad77731344d00263b06843be6ef05cbe9ab699e2ed0d1448f8b2b50a3", + "chainId": "0x1", + "nonce": "0x75", + "gasPrice": "0x69b8cf27b", + "gas": "0x2db9d", + "to": "0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad", + "value": "0x32a767a9562d0000", + "data": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006547d41700000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000032a767a9562d00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000032a767a9562d000000000000000000000000000000000000000000000021b60af11987fa0670342f00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb8b55ee890426341fe45ee6dc788d2d93d25b59063000000000000000000000000000000000000000000", + "accessList": [ + { + "address": "0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xa19fd53308a1c44a3ed22d3f20ed4229aa8909e0d0a90510ca482367ad42caa6", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ] + } + ], + "v": "0x1", + "r": "0x5fe8eb06ac27f44de3e8d1c7214f750b9fc8291ab63d71ea6a4456cfd328deb9", + "s": "0x41425cc35a5ed1c922c898cb7fda5cf3b165b4792ada812700bf55cbc21a75a1" + }); + assert_eq!(expected, actual); + + // can decode json + let json_str = serde_json::to_string(&signed_tx).unwrap(); + let decoded = + serde_json::from_str::>(&json_str).unwrap(); + assert_eq!(signed_tx, decoded); + } + + #[test] + fn serde_decode_legacy_tx() { + let json_tx = r#" + { + "hash": "0xb3fbbda7862791ec65c07b1162bd6c6aa10efc89196a8727790a9b03b3ca7bab", + "nonce": "0x115", + "blockHash": "0x533ae98e36b11720a6095de0cbae802e80719cede1e3a65e02379436993a2007", + "blockNumber": "0x37cd6", + "transactionIndex": "0x0", + "from": "0xcf684dfb8304729355b58315e8019b1aa2ad1bac", + "to": null, + "value": "0x0", + "gasPrice": "0xba43b7400", + "gas": "0x2f4d60", + "input": "0x60606040526009600060146101000a81548160ff021916908302179055505b6000600033600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550600091505b600060149054906101000a900460ff1660ff168260ff16101561010457600090505b600060149054906101000a900460ff1660ff168160ff1610156100f6578082600060149054906101000a900460ff1602016001600050826009811015610002579090601202016000508360098110156100025790906002020160005060010160146101000a81548160ff021916908302179055505b8080600101915050610074565b5b8180600101925050610052565b5b5050610160806101166000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900480634166c1fd1461004457806341c0e1b51461007457610042565b005b61005b600480359060200180359060200150610081565b604051808260ff16815260200191505060405180910390f35b61007f6004506100cc565b005b60006001600050836009811015610002579090601202016000508260098110156100025790906002020160005060010160149054906101000a900460ff1690506100c6565b92915050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015d57600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b56", + "chainId": "0x1", + "v": "0x1b", + "r": "0x834b0e7866457890809cb81a33a59380e890e1cc0d6e17a81382e99132b16bc8", + "s": "0x65dcc7686efc8f7937b3ae0d09d682cd3a7ead281a920ec39d4e2b0c34e972be", + "type": "0x0" + }"#; + let actual = serde_json::from_str::(json_tx).unwrap(); + let actual = SignedTransaction::::try_from(actual).unwrap(); + let expected = SignedTransaction { + tx_hash: H256(hex!("b3fbbda7862791ec65c07b1162bd6c6aa10efc89196a8727790a9b03b3ca7bab")), + payload: TypedTransaction::Legacy(LegacyTransaction { + chain_id: None, + nonce: 0x0115, + gas_price: 0x000b_a43b_7400_u64.into(), + gas_limit: 0x002f_4d60, + to: None, + value: U256::zero(), + data: hex!("60606040526009600060146101000a81548160ff021916908302179055505b6000600033600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550600091505b600060149054906101000a900460ff1660ff168260ff16101561010457600090505b600060149054906101000a900460ff1660ff168160ff1610156100f6578082600060149054906101000a900460ff1602016001600050826009811015610002579090601202016000508360098110156100025790906002020160005060010160146101000a81548160ff021916908302179055505b8080600101915050610074565b5b8180600101925050610052565b5b5050610160806101166000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900480634166c1fd1461004457806341c0e1b51461007457610042565b005b61005b600480359060200180359060200150610081565b604051808260ff16815260200191505060405180910390f35b61007f6004506100cc565b005b60006001600050836009811015610002579090601202016000508260098110156100025790906002020160005060010160149054906101000a900460ff1690506100c6565b92915050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015d57600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b56").to_vec(), + }), + signature: Signature { + v: RecoveryId::new(0x1b), + r: hex!("834b0e7866457890809cb81a33a59380e890e1cc0d6e17a81382e99132b16bc8").into(), + s: hex!("65dcc7686efc8f7937b3ae0d09d682cd3a7ead281a920ec39d4e2b0c34e972be").into(), + }, + }; + + assert_eq!(expected, actual); + } + + #[test] + fn serde_decode_legacy_eip155() { + let json_tx = r#" + { + "blockHash": "0x1b05659b54037e74a4f8f5b9c46ee9d53b8eb5a573fb53c4ffb65bc381ff0076", + "blockNumber": "0x81ed64", + "from": "0x1f3896a7abb3c99e1f38f77be69448ee7770d18c", + "gas": "0xf4240", + "gasPrice": "0x4bfef4c00", + "hash": "0xdf99f8176f765d84ed1c00a12bba00206c6da97986c802a532884aca5aaa3809", + "input": "0x288b8133920339b815ee42a02099dcca27c01d192418334751613a1eea786a0c3a673cec000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000032464a3bc15000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000003dd2c560933380000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000b14232b0204b2f7bb6ba5aff59ef36030f7fe38b00000000000000000000000041f8d14c9475444f30a80431c68cf24dc9a8369a000000000000000000000000b9e29984fe50602e7a619662ebed4f90d93824c7000000000000000000000000dc6c91b569c98f9f6f74d90f9beff99fdaf4248b0000000000000000000000000000000000000000000000000000000002faf08000000000000000000000000000000000000000000000000003dd2c560933380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d7625241afaa81faf3c2bd525f64f6e0ec3af39c1053d672b65c2f64992521e6f454e67000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000581b96d32320d6cb174e807b585a41f4faa8ba7da95e117f2abbcadbb257d37a5fcc16c2ba6db86200888ed85dd5eba547bb07fa0f9910950d3133026abafdd5c09e1f3896a7abb3c99e1f38f77be69448ee7770d18c001e0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561c7dd7d43db98e3b7a6e18b4e97f0e254a5a6bb9b373d49d7e6676ccb1b02d50f131bca36928cb48cd0daec0499e9e93a390253c733607437bbebf0aa13b7080911f3896a7abb3c99e1f38f77be69448ee7770d18c0400000000000000000000", + "nonce": "0x89", + "to": "0xdc6c91b569c98f9f6f74d90f9beff99fdaf4248b", + "transactionIndex": "0x59", + "value": "0x3dd2c5609333800", + "type": "0x0", + "chainId": "0x1", + "v": "0x25", + "r": "0x20d7064f0b3c956e603c994fd83247499ede5a1209d6c997d2b2ea29b5627a7", + "s": "0x6f6c3ceb0a57952386cbb9ceb3e4d05f1d4bc8d30b67d56281d89775f972a34d" + }"#; + let actual = serde_json::from_str::(json_tx).unwrap(); + let actual = SignedTransaction::::try_from(actual).unwrap(); + let expected = SignedTransaction { + tx_hash: H256(hex!("df99f8176f765d84ed1c00a12bba00206c6da97986c802a532884aca5aaa3809")), + payload: TypedTransaction::Legacy(LegacyTransaction { + chain_id: Some(0x1), + nonce: 0x89, + gas_price: 0x0004_bfef_4c00_u64.into(), + gas_limit: 0xf4240, + to: Some(H160(hex!("dc6c91b569c98f9f6f74d90f9beff99fdaf4248b"))), + value: U256::from(0x03dd_2c56_0933_3800_u128), + data: hex!("288b8133920339b815ee42a02099dcca27c01d192418334751613a1eea786a0c3a673cec000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000032464a3bc15000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000003dd2c560933380000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000b14232b0204b2f7bb6ba5aff59ef36030f7fe38b00000000000000000000000041f8d14c9475444f30a80431c68cf24dc9a8369a000000000000000000000000b9e29984fe50602e7a619662ebed4f90d93824c7000000000000000000000000dc6c91b569c98f9f6f74d90f9beff99fdaf4248b0000000000000000000000000000000000000000000000000000000002faf08000000000000000000000000000000000000000000000000003dd2c560933380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d7625241afaa81faf3c2bd525f64f6e0ec3af39c1053d672b65c2f64992521e6f454e67000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000581b96d32320d6cb174e807b585a41f4faa8ba7da95e117f2abbcadbb257d37a5fcc16c2ba6db86200888ed85dd5eba547bb07fa0f9910950d3133026abafdd5c09e1f3896a7abb3c99e1f38f77be69448ee7770d18c001e0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561c7dd7d43db98e3b7a6e18b4e97f0e254a5a6bb9b373d49d7e6676ccb1b02d50f131bca36928cb48cd0daec0499e9e93a390253c733607437bbebf0aa13b7080911f3896a7abb3c99e1f38f77be69448ee7770d18c0400000000000000000000").to_vec(), + }), + signature: Signature { + v: RecoveryId::new(0x25), + r: hex!("020d7064f0b3c956e603c994fd83247499ede5a1209d6c997d2b2ea29b5627a7").into(), + s: hex!("6f6c3ceb0a57952386cbb9ceb3e4d05f1d4bc8d30b67d56281d89775f972a34d").into(), + }, + }; + + assert_eq!(expected, actual); + } +} diff --git a/chains/ethereum/config/src/types/transaction/typed_transaction.rs b/chains/ethereum/config/src/types/transaction/typed_transaction.rs new file mode 100644 index 00000000..cebab98c --- /dev/null +++ b/chains/ethereum/config/src/types/transaction/typed_transaction.rs @@ -0,0 +1,194 @@ +use ethereum_types::{H160, U256}; + +use super::{ + eip1559::Eip1559Transaction, eip2930::Eip2930Transaction, legacy::LegacyTransaction, AccessList, +}; + +/// The [`TypedTransaction`] enum represents all Ethereum transaction types. +/// +/// Its variants correspond to specific allowed transactions: +/// 1. Legacy (pre-EIP2718) [`LegacyTransaction`] +/// 2. EIP2930 (state access lists) [`Eip2930Transaction`] +/// 3. EIP1559 [`Eip1559Transaction`] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(tag = "type"))] +pub enum TypedTransaction { + #[cfg_attr(feature = "serde", serde(rename = "0x0"))] + Legacy(LegacyTransaction), + #[cfg_attr(feature = "serde", serde(rename = "0x1"))] + Eip2930(Eip2930Transaction), + #[cfg_attr(feature = "serde", serde(rename = "0x2"))] + Eip1559(Eip1559Transaction), +} + +impl TypedTransaction { + #[must_use] + pub fn data(&self) -> &[u8] { + match self { + Self::Legacy(tx) => tx.data.as_ref(), + Self::Eip2930(tx) => tx.data.as_ref(), + Self::Eip1559(tx) => tx.data.as_ref(), + } + } + + #[must_use] + pub const fn to(&self) -> Option { + match self { + Self::Legacy(tx) => tx.to, + Self::Eip2930(tx) => tx.to, + Self::Eip1559(tx) => tx.to, + } + } + + #[must_use] + pub const fn nonce(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.nonce, + Self::Eip2930(tx) => tx.nonce, + Self::Eip1559(tx) => tx.nonce, + } + } + + #[must_use] + pub const fn gas_limit(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.gas_limit, + Self::Eip2930(tx) => tx.gas_limit, + Self::Eip1559(tx) => tx.gas_limit, + } + } + + #[must_use] + pub const fn value(&self) -> U256 { + match self { + Self::Legacy(tx) => tx.value, + Self::Eip2930(tx) => tx.value, + Self::Eip1559(tx) => tx.value, + } + } + + #[must_use] + pub const fn chain_id(&self) -> Option { + match self { + Self::Legacy(tx) => tx.chain_id, + Self::Eip2930(tx) => Some(tx.chain_id), + Self::Eip1559(tx) => Some(tx.chain_id), + } + } + + #[must_use] + pub const fn access_list(&self) -> Option<&AccessList> { + match self { + Self::Legacy(_) => None, + Self::Eip2930(tx) => Some(&tx.access_list), + Self::Eip1559(tx) => Some(&tx.access_list), + } + } + + #[must_use] + pub const fn tx_type(&self) -> u8 { + match self { + Self::Legacy(_) => 0x0, + Self::Eip2930(_) => 0x1, + Self::Eip1559(_) => 0x2, + } + } +} + +impl From for TypedTransaction { + fn from(tx: LegacyTransaction) -> Self { + Self::Legacy(tx) + } +} + +impl From for TypedTransaction { + fn from(tx: Eip2930Transaction) -> Self { + Self::Eip2930(tx) + } +} + +impl From for TypedTransaction { + fn from(tx: Eip1559Transaction) -> Self { + Self::Eip1559(tx) + } +} + +#[cfg(all(test, feature = "serde"))] +mod tests { + use super::TypedTransaction; + use crate::transaction::{ + eip1559::tests::build_eip1559, eip2930::tests::build_eip2930, legacy::tests::build_legacy, + }; + + #[allow(clippy::unwrap_used)] + fn build_typed_transaction>( + builder: fn() -> (T, serde_json::Value), + ) -> (TypedTransaction, serde_json::Value) { + let (tx, mut expected) = builder(); + let tx: TypedTransaction = tx.into(); + let tx_type = match &tx { + TypedTransaction::Legacy(_) => "0x0", + TypedTransaction::Eip2930(_) => "0x1", + TypedTransaction::Eip1559(_) => "0x2", + }; + // Add the type field to the json + let old_value = expected + .as_object_mut() + .unwrap() + .insert("type".to_string(), serde_json::json!(tx_type)); + + // Guarantee that the type field was not already present + assert_eq!(old_value, None); + (tx, expected) + } + + #[test] + fn can_encode_eip1559() { + let (tx, expected) = build_typed_transaction(build_eip1559); + let actual = serde_json::to_value(&tx).unwrap(); + assert_eq!(expected, actual); + + // can decode json + let json = serde_json::to_value(&tx).unwrap(); + let decoded = serde_json::from_value::(json).unwrap(); + assert_eq!(tx, decoded); + } + + #[test] + fn can_encode_eip2930() { + let (tx, expected) = build_typed_transaction(build_eip2930); + let actual = serde_json::to_value(&tx).unwrap(); + assert_eq!(expected, actual); + + // can decode json + let json_str = serde_json::to_string(&tx).unwrap(); + let decoded = serde_json::from_str::(&json_str).unwrap(); + assert_eq!(tx, decoded); + } + + #[test] + fn can_encode_legacy() { + let (tx, expected) = build_typed_transaction(|| build_legacy(false)); + let actual = serde_json::to_value(&tx).unwrap(); + assert_eq!(expected, actual); + + // can decode json + let json_str = serde_json::to_string(&tx).unwrap(); + let decoded = serde_json::from_str::(&json_str).unwrap(); + assert_eq!(tx, decoded); + } + + #[test] + fn can_encode_legacy_eip155() { + let (tx, expected) = build_typed_transaction(|| build_legacy(true)); + let actual = serde_json::to_value(&tx).unwrap(); + assert_eq!(expected, actual); + + // can decode json + let json_str = serde_json::to_string(&tx).unwrap(); + let decoded = serde_json::from_str::(&json_str).unwrap(); + assert_eq!(tx, decoded); + } +} diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index e9e69b00..e984c5ad 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -1,4 +1,5 @@ mod node_uri; +mod traits; use crate::{ crypto::{ diff --git a/rosetta-core/src/traits.rs b/rosetta-core/src/traits.rs index f018e3fd..abdc2a8b 100644 --- a/rosetta-core/src/traits.rs +++ b/rosetta-core/src/traits.rs @@ -3,6 +3,7 @@ use core::{ fmt::{Debug, Display}, str::FromStr, }; +use std::vec::Vec; /// Macro for creating `Maybe*` marker traits. /// @@ -70,24 +71,93 @@ pub trait Header: Clone + Send + Sync + Eq + Debug + 'static { /// Header hash type type Hash: HashOutput; + + /// Returns a reference to the header number. + fn number(&self) -> &Self::Number; + + /// Returns the hash of the header. + fn hash(&self) -> Self::Hash; +} + +/// Something that acts like a [`SignaturePayload`](Extrinsic::SignaturePayload) of an +/// [`Transaction`]. +pub trait SignaturePayload { + /// The type of the address that signed the extrinsic. + /// + /// Particular to a signed extrinsic. + type SignatureAddress; + + /// The signature type of the extrinsic. + /// + /// Particular to a signed extrinsic. + type Signature; + + /// The additional data that is specific to the signed extrinsic. + /// + /// Particular to a signed extrinsic. + type SignatureExtra; +} + +impl SignaturePayload for () { + type SignatureAddress = (); + type Signature = (); + type SignatureExtra = (); } /// Something that acts like an `Extrinsic`. pub trait Transaction: Sized { /// The function call. type Call; + + /// The payload we carry for signed transactions. + /// + /// Usually it will contain a `Signature` and + /// may include some additional data that are specific to signed + /// transaction. + type SignaturePayload: SignaturePayload; + + /// Is this `Extrinsic` signed? + /// If no information are available about signed/unsigned, `None` should be returned. + fn is_signed(&self) -> Option { + None + } + + /// Create new instance of the extrinsic. + /// + /// Extrinsics can be split into: + /// 1. Inherents (no signature; created by validators during block production) + /// 2. Unsigned Transactions (no signature; represent "system calls" or other special kinds of + /// calls) 3. Signed Transactions (with signature; a regular transactions with known origin) + fn new(_call: Self::Call, _signed_data: Option) -> Option { + None + } } -pub trait Block { +pub trait Block: Clone + Send + Sync + Eq + Debug + 'static { /// Type for extrinsics. type Transaction: Member + Transaction; /// Header type. type Header: Header; /// Block hash type. type Hash: HashOutput; + + /// Returns a reference to the header. + fn header(&self) -> &Self::Header; + + /// Returns a reference to the list of transactions. + fn transactions(&self) -> &[Self::Transaction]; + + /// Split the block into header and list of transactions. + fn deconstruct(self) -> (Self::Header, Vec); + + /// Creates new block from header and transactions. + fn new(header: Self::Header, extrinsics: Vec) -> Self; + + /// Returns the hash of the block. + fn hash(&self) -> Self::Hash; } -pub trait BlockchainPrimitives { +pub trait BlockchainConfig { type Block: Clone + Send + Sync + 'static; type Transaction: Clone + Send + Sync + 'static; } diff --git a/scripts/check.sh b/scripts/check.sh index 78dc6985..af924cec 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -72,6 +72,29 @@ exec_cmd 'cargo fmt' 'cargo +nightly fmt --all -- --check' exec_cmd 'dprint check' 'dprint check' exec_cmd 'cargo deny' 'cargo deny check' +# Run clippy on all packages with different feature flags +LINT_FLAGS='-- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' + + +exec_cmd 'ethereum build all-features' 'cargo build -p rosetta-config-ethereum --all-features' +exec_cmd 'ethereum test all-features' 'cargo test -p rosetta-config-ethereum --all-features' +exec_cmd 'ethereum clippy all-features' "cargo clippy -p rosetta-config-ethereum --all-features ${LINT_FLAGS}" +ethereumFeatures=('std' 'std,serde' 'std,scale-info' 'std,scale-codec') +for features in "${ethereumFeatures[@]}"; +do + exec_cmd "ethereum build ${features}" "cargo build -p rosetta-config-ethereum --no-default-features --features=${features}" + exec_cmd "ethereum test ${features}" "cargo test -p rosetta-config-ethereum --no-default-features --features=${features}" + exec_cmd "ethereum clippy ${features}" "cargo clippy -p rosetta-config-ethereum --no-default-features --features=${features} ${LINT_FLAGS}" +done +# exec_cmd 'ethereum build std' 'cargo build -p rosetta-config-ethereum --no-default-features --features=std' +# exec_cmd 'ethereum build std + serde' 'cargo build -p rosetta-config-ethereum --no-default-features --features=std,serde' +# exec_cmd 'ethereum build std + scale-info' 'cargo build -p rosetta-config-ethereum --no-default-features --features=std,scale-info' +# exec_cmd 'ethereum test all-features' 'cargo test -p rosetta-config-ethereum --all-features' +# exec_cmd 'ethereum test std + serde' 'cargo test -p rosetta-config-ethereum --no-default-features --features=std,serde' +# exec_cmd 'ethereum clippy std' "cargo clippy -p rosetta-config-ethereum --no-default-features --features=std ${LINT_FLAGS}" +# exec_cmd 'ethereum clippy std + serde' "cargo clippy -p rosetta-config-ethereum --no-default-features --features=std,serde ${LINT_FLAGS}" +# exec_cmd 'ethereum' 'cargo build -p rosetta-config-ethereum --no-default-features --target=wasm32-unknown-unknown' + # exec_cmd 'clippy rosetta-server-astar' 'cargo clippy --locked -p rosetta-server-astar --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' # exec_cmd 'clippy rosetta-server-ethereum' 'cargo clippy --locked -p rosetta-server-ethereum --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' # exec_cmd 'clippy rosetta-server-polkadot' 'cargo clippy --locked -p rosetta-server-polkadot --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' From ae42ff501291b940d7dbac9bd0b8df437fd2c699 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Fri, 19 Jan 2024 03:18:24 -0300 Subject: [PATCH 04/26] Refactor traits --- chains/ethereum/config/src/lib.rs | 116 ++++++++++++++++++++++++---- chains/ethereum/config/src/types.rs | 6 +- rosetta-core/src/lib.rs | 2 +- rosetta-core/src/traits.rs | 17 ++-- 4 files changed, 116 insertions(+), 25 deletions(-) diff --git a/chains/ethereum/config/src/lib.rs b/chains/ethereum/config/src/lib.rs index be9614f6..a5a72458 100644 --- a/chains/ethereum/config/src/lib.rs +++ b/chains/ethereum/config/src/lib.rs @@ -4,15 +4,14 @@ mod serde_utils; mod types; -use anyhow::Result; pub use ethereum_types; +use ethereum_types::H256; use rosetta_config_astar::config as astar_config; use rosetta_core::{ crypto::{address::AddressFormat, Algorithm}, BlockchainConfig, NodeUri, }; -use std::sync::Arc; pub use types::*; #[cfg(not(feature = "std"))] @@ -22,13 +21,11 @@ extern crate alloc; #[cfg(feature = "std")] pub(crate) mod rstd { #[cfg(feature = "serde")] - pub use std::{option, result}; + pub use std::option; - pub use std::{ - // borrow, boxed, cmp, convert, default, fmt, hash, iter, marker, mem, ops, rc, result, - // time, - vec, - }; + // borrow, boxed, cmp, default, hash, iter, marker, mem, ops, rc, result, + // time, + pub use std::{convert, fmt, result, str, sync, vec}; // pub mod error { // pub use std::error::Error; // } @@ -37,9 +34,10 @@ pub(crate) mod rstd { #[cfg(not(feature = "std"))] pub(crate) mod rstd { #[cfg(feature = "serde")] - pub use core::{option, result}; + pub use core::option; - pub use alloc::vec; + pub use alloc::{sync, vec}; + pub use core::{convert, fmt, result, str}; // pub use alloc::{borrow, boxed, rc, vec}; // pub use core::{cmp, convert, default, fmt, hash, iter, marker, mem, ops, result, time}; // pub mod error { @@ -48,11 +46,101 @@ pub(crate) mod rstd { // } } +impl rosetta_core::traits::Transaction for types::SignedTransaction { + type Call = (); + + type SignaturePayload = (); +} + +#[derive(Clone, Copy, Default, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] +#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct BlockHash(pub ethereum_types::H256); + +impl From for BlockHash { + fn from(hash: H256) -> Self { + Self(hash) + } +} + +impl From for H256 { + fn from(block_hash: BlockHash) -> Self { + block_hash.0 + } +} + +impl rstd::convert::AsMut<[u8]> for BlockHash { + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_bytes_mut() + } +} + +impl rstd::convert::AsRef<[u8]> for BlockHash { + fn as_ref(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl rstd::str::FromStr for BlockHash { + type Err = ::Err; + + fn from_str(s: &str) -> rstd::result::Result { + let hash = ::from_str(s)?; + Ok(Self(hash)) + } +} + +impl rstd::fmt::Display for BlockHash { + fn fmt(&self, f: &mut rstd::fmt::Formatter<'_>) -> rstd::fmt::Result { + rstd::fmt::Display::fmt(&self.0, f) + } +} + +impl rosetta_core::traits::HashOutput for BlockHash {} + +impl rosetta_core::traits::Header for types::header::Header { + type Number = u64; + + type Hash = BlockHash; + + fn number(&self) -> Self::Number { + self.number + } + + fn hash(&self) -> Self::Hash { + // TODO: compute header hash + BlockHash(H256::zero()) + } +} + +impl rosetta_core::traits::Block for types::FullBlock { + type Transaction = types::SignedTransaction; + type Header = types::header::Header; + type Hash = BlockHash; + + fn header(&self) -> &Self::Header { + &self.header + } + + fn transactions(&self) -> &[Self::Transaction] { + self.transactions.as_slice() + } + + fn hash(&self) -> Self::Hash { + BlockHash(self.hash) + } +} + /// Retrieve the [`BlockchainConfig`] from the provided polygon `network` /// /// # Errors /// Returns `Err` if the network is not supported -pub fn polygon_config(network: &str) -> Result { +pub fn polygon_config(network: &str) -> anyhow::Result { let (network, bip44_id, is_dev) = match network { "dev" => ("dev", 1, true), "mumbai" => ("mumbai", 1, true), @@ -66,7 +154,7 @@ pub fn polygon_config(network: &str) -> Result { /// /// # Errors /// Returns `Err` if the network is not supported -pub fn arbitrum_config(network: &str) -> Result { +pub fn arbitrum_config(network: &str) -> anyhow::Result { let (network, bip44_id, is_dev) = match network { "dev" => ("dev", 1, true), "goerli" => ("goerli", 1, true), @@ -81,7 +169,7 @@ pub fn arbitrum_config(network: &str) -> Result { /// /// # Errors /// Returns `Err` if the network is not supported -pub fn config(network: &str) -> Result { +pub fn config(network: &str) -> anyhow::Result { let (network, symbol, bip44_id, is_dev) = match network { "dev" => ("dev", "ETH", 1, true), "mainnet" => ("mainnet", "ETH", 60, false), @@ -130,7 +218,7 @@ fn evm_config( NodeUri::parse("ws://127.0.0.1:8545/ws").expect("uri is valid; qed") }, node_image: "ethereum/client-go:v1.12.2", - node_command: Arc::new(|network, port| { + node_command: rstd::sync::Arc::new(|network, port| { let mut params = if network == "dev" { vec!["--dev".into(), "--dev.period=1".into(), "--ipcdisable".into()] } else { diff --git a/chains/ethereum/config/src/types.rs b/chains/ethereum/config/src/types.rs index a1a2f4fa..49dbb314 100644 --- a/chains/ethereum/config/src/types.rs +++ b/chains/ethereum/config/src/types.rs @@ -8,6 +8,10 @@ use ethereum_types::{Address, Bloom, H256, U256}; #[cfg(feature = "serde")] use crate::serde_utils::{bytes_to_hex, uint_to_hex}; +pub type SignedTransaction = transaction::SignedTransaction; +pub type FullBlock = block::Block; +pub type BlockRef = block::Block; + #[derive(Clone, Debug)] #[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] #[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode, parity_scale_codec::Decode))] @@ -421,9 +425,9 @@ pub struct EIP1186ProofResponse { #[cfg(all(test, feature = "serde"))] mod tests { + use crate::rstd::str::FromStr; use hex_literal::hex; use serde_json::json; - use std::str::FromStr; use super::{AtBlock, CallResult, EIP1186ProofResponse, StorageProof}; use ethereum_types::{Address, H256, U256}; diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index e984c5ad..460544a8 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -1,5 +1,5 @@ mod node_uri; -mod traits; +pub mod traits; use crate::{ crypto::{ diff --git a/rosetta-core/src/traits.rs b/rosetta-core/src/traits.rs index abdc2a8b..97f57470 100644 --- a/rosetta-core/src/traits.rs +++ b/rosetta-core/src/traits.rs @@ -3,7 +3,6 @@ use core::{ fmt::{Debug, Display}, str::FromStr, }; -use std::vec::Vec; /// Macro for creating `Maybe*` marker traits. /// @@ -72,8 +71,8 @@ pub trait Header: Clone + Send + Sync + Eq + Debug + 'static { /// Header hash type type Hash: HashOutput; - /// Returns a reference to the header number. - fn number(&self) -> &Self::Number; + /// Returns the header block number. + fn number(&self) -> Self::Number; /// Returns the hash of the header. fn hash(&self) -> Self::Hash; @@ -147,17 +146,17 @@ pub trait Block: Clone + Send + Sync + Eq + Debug + 'static { /// Returns a reference to the list of transactions. fn transactions(&self) -> &[Self::Transaction]; - /// Split the block into header and list of transactions. - fn deconstruct(self) -> (Self::Header, Vec); - - /// Creates new block from header and transactions. - fn new(header: Self::Header, extrinsics: Vec) -> Self; - /// Returns the hash of the block. fn hash(&self) -> Self::Hash; } pub trait BlockchainConfig { + const NAME: &'static str; + const SYMBOL: &'static str; + const BIP44: u32; + const DEV: bool; + type Block: Clone + Send + Sync + 'static; type Transaction: Clone + Send + Sync + 'static; + type UnsignedTransaction: Clone + Send + Sync + 'static; } From 1a74997c7cff008ec10d88d3c02699683abc5dd2 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Fri, 19 Jan 2024 10:54:02 -0300 Subject: [PATCH 05/26] Add blockidentifier --- chains/astar/server/src/lib.rs | 2 + chains/ethereum/config/src/lib.rs | 4 +- chains/ethereum/server/src/lib.rs | 2 + chains/polkadot/server/src/lib.rs | 2 + rosetta-client/src/client.rs | 8 ++++ rosetta-core/src/lib.rs | 4 ++ rosetta-core/src/traits.rs | 69 ++++++++++++++++++++++++------- scripts/check.sh | 29 +++++-------- 8 files changed, 82 insertions(+), 38 deletions(-) diff --git a/chains/astar/server/src/lib.rs b/chains/astar/server/src/lib.rs index d5214c17..e97ddf27 100644 --- a/chains/astar/server/src/lib.rs +++ b/chains/astar/server/src/lib.rs @@ -127,6 +127,8 @@ impl BlockchainClient for AstarClient { type Call = EthQuery; type CallResult = EthQueryResult; + type BlockIdentifier = BlockIdentifier; + fn config(&self) -> &BlockchainConfig { self.client.config() } diff --git a/chains/ethereum/config/src/lib.rs b/chains/ethereum/config/src/lib.rs index a5a72458..66998298 100644 --- a/chains/ethereum/config/src/lib.rs +++ b/chains/ethereum/config/src/lib.rs @@ -104,11 +104,9 @@ impl rstd::fmt::Display for BlockHash { impl rosetta_core::traits::HashOutput for BlockHash {} impl rosetta_core::traits::Header for types::header::Header { - type Number = u64; - type Hash = BlockHash; - fn number(&self) -> Self::Number { + fn number(&self) -> rosetta_core::traits::BlockNumber { self.number } diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index 29a2785a..7d785d6a 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -93,6 +93,8 @@ impl BlockchainClient for MaybeWsEthereumClient { type Call = EthQuery; type CallResult = EthQueryResult; + type BlockIdentifier = BlockIdentifier; + fn config(&self) -> &BlockchainConfig { match self { Self::Http(http_client) => http_client.config(), diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index e22eb6a0..e75a7310 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -133,6 +133,8 @@ impl BlockchainClient for PolkadotClient { type Call = CallRequest; type CallResult = Value; + type BlockIdentifier = BlockIdentifier; + fn config(&self) -> &BlockchainConfig { &self.config } diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index e0b9ada4..9d7b95e3 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -108,6 +108,12 @@ pub enum GenericCallResult { Polkadot(Value), } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum GenericBlockIdentifier { + Ethereum(::BlockIdentifier), + Polkadot(::BlockIdentifier), +} + macro_rules! dispatch { ($self:tt$($method:tt)+) => { match $self { @@ -126,6 +132,8 @@ impl BlockchainClient for GenericClient { type Call = GenericCall; type CallResult = GenericCallResult; + type BlockIdentifier = GenericBlockIdentifier; + fn config(&self) -> &BlockchainConfig { dispatch!(self.config()) } diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index 460544a8..446c20db 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -117,6 +117,8 @@ pub trait BlockchainClient: Sized + Send + Sync + 'static { type Call: Send + Sync + Sized + 'static; type CallResult: Send + Sync + Sized + 'static; + type BlockIdentifier: Clone + Send + Sync + Sized + Eq + 'static; + fn config(&self) -> &BlockchainConfig; fn genesis_block(&self) -> &BlockIdentifier; async fn node_version(&self) -> Result; @@ -156,6 +158,8 @@ where type Call = ::Call; type CallResult = ::CallResult; + type BlockIdentifier = ::BlockIdentifier; + fn config(&self) -> &BlockchainConfig { BlockchainClient::config(Self::as_ref(self)) } diff --git a/rosetta-core/src/traits.rs b/rosetta-core/src/traits.rs index 97f57470..af1c3a01 100644 --- a/rosetta-core/src/traits.rs +++ b/rosetta-core/src/traits.rs @@ -9,17 +9,6 @@ use core::{ /// Such a maybe-marker trait requires the given bound when `feature = std` and doesn't require /// the bound on `no_std`. This is useful for situations where you require that a type implements /// a certain trait with `feature = std`, but not on `no_std`. -/// -/// # Example -/// -/// ``` -/// sp_core::impl_maybe_marker! { -/// /// A marker for a type that implements `Debug` when `feature = std`. -/// trait MaybeDebug: std::fmt::Debug; -/// /// A marker for a type that implements `Debug + Display` when `feature = std`. -/// trait MaybeDebugDisplay: std::fmt::Debug, std::fmt::Display; -/// } -/// ``` macro_rules! impl_maybe_marker { ( $( @@ -64,15 +53,14 @@ pub trait HashOutput: { } -pub trait Header: Clone + Send + Sync + Eq + Debug + 'static { - /// Header number. - type Number; +pub type BlockNumber = u64; +pub trait Header: Clone + Send + Sync + Eq + Debug + 'static { /// Header hash type type Hash: HashOutput; /// Returns the header block number. - fn number(&self) -> Self::Number; + fn number(&self) -> BlockNumber; /// Returns the hash of the header. fn hash(&self) -> Self::Hash; @@ -156,7 +144,56 @@ pub trait BlockchainConfig { const BIP44: u32; const DEV: bool; - type Block: Clone + Send + Sync + 'static; + const CHECKPOINT: Option<(BlockNumber, <::Header as Header>::Hash)>; + + type Block: Block; type Transaction: Clone + Send + Sync + 'static; type UnsignedTransaction: Clone + Send + Sync + 'static; } + +pub trait Query { + type Params: Send + Sync + 'static; + type Result: Send + Sync + 'static; +} + +// pub enum QueryEnum { +// BlockHeaderByHash(::Hash), +// BlockByHash(T::Block), +// Query(CUSTOM), +// } + +// pub enum SubscribeEnum { +// NewHeads, +// FinalizedHeads, +// Subscribe(CUSTOM), +// } + +// pub trait BlockchainClient { +// type Config: BlockchainConfig; +// type Error: std::error::Error + Send + Sync + 'static; +// type TxParams: Send + Sync + 'static; + +// type QueryFuture<'a, Q>: core::future::Future> +// + Send +// + 'a +// where +// Q: Query, +// Self: 'a; + +// fn query(&self, params: Q::Params) -> Self::QueryFuture<'_, Q>; + +// fn build_transaction( +// &self, +// params: Self::TxParams, +// ) -> ::Transaction; + +// fn submit_transaction( +// &self, +// tx: ::Transaction, +// ) -> Result<(), Self::Error>; + +// fn poll_next_event( +// &self, +// tx: ::Transaction, +// ) -> Result<(), Self::Error>; +// } diff --git a/scripts/check.sh b/scripts/check.sh index af924cec..392bfaf0 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -73,27 +73,18 @@ exec_cmd 'dprint check' 'dprint check' exec_cmd 'cargo deny' 'cargo deny check' # Run clippy on all packages with different feature flags -LINT_FLAGS='-- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' - +# LINT_FLAGS='-- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' -exec_cmd 'ethereum build all-features' 'cargo build -p rosetta-config-ethereum --all-features' -exec_cmd 'ethereum test all-features' 'cargo test -p rosetta-config-ethereum --all-features' -exec_cmd 'ethereum clippy all-features' "cargo clippy -p rosetta-config-ethereum --all-features ${LINT_FLAGS}" -ethereumFeatures=('std' 'std,serde' 'std,scale-info' 'std,scale-codec') -for features in "${ethereumFeatures[@]}"; -do - exec_cmd "ethereum build ${features}" "cargo build -p rosetta-config-ethereum --no-default-features --features=${features}" - exec_cmd "ethereum test ${features}" "cargo test -p rosetta-config-ethereum --no-default-features --features=${features}" - exec_cmd "ethereum clippy ${features}" "cargo clippy -p rosetta-config-ethereum --no-default-features --features=${features} ${LINT_FLAGS}" -done -# exec_cmd 'ethereum build std' 'cargo build -p rosetta-config-ethereum --no-default-features --features=std' -# exec_cmd 'ethereum build std + serde' 'cargo build -p rosetta-config-ethereum --no-default-features --features=std,serde' -# exec_cmd 'ethereum build std + scale-info' 'cargo build -p rosetta-config-ethereum --no-default-features --features=std,scale-info' +# exec_cmd 'ethereum build all-features' 'cargo build -p rosetta-config-ethereum --all-features' # exec_cmd 'ethereum test all-features' 'cargo test -p rosetta-config-ethereum --all-features' -# exec_cmd 'ethereum test std + serde' 'cargo test -p rosetta-config-ethereum --no-default-features --features=std,serde' -# exec_cmd 'ethereum clippy std' "cargo clippy -p rosetta-config-ethereum --no-default-features --features=std ${LINT_FLAGS}" -# exec_cmd 'ethereum clippy std + serde' "cargo clippy -p rosetta-config-ethereum --no-default-features --features=std,serde ${LINT_FLAGS}" -# exec_cmd 'ethereum' 'cargo build -p rosetta-config-ethereum --no-default-features --target=wasm32-unknown-unknown' +# exec_cmd 'ethereum clippy all-features' "cargo clippy -p rosetta-config-ethereum --all-features ${LINT_FLAGS}" +# ethereumFeatures=('std' 'std,serde' 'std,scale-info' 'std,scale-codec') +# for features in "${ethereumFeatures[@]}"; +# do +# exec_cmd "ethereum build ${features}" "cargo build -p rosetta-config-ethereum --no-default-features --features=${features}" +# exec_cmd "ethereum test ${features}" "cargo test -p rosetta-config-ethereum --no-default-features --features=${features}" +# exec_cmd "ethereum clippy ${features}" "cargo clippy -p rosetta-config-ethereum --no-default-features --features=${features} ${LINT_FLAGS}" +# done # exec_cmd 'clippy rosetta-server-astar' 'cargo clippy --locked -p rosetta-server-astar --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' # exec_cmd 'clippy rosetta-server-ethereum' 'cargo clippy --locked -p rosetta-server-ethereum --examples --tests -- -Dwarnings -Dclippy::unwrap_used -Dclippy::expect_used -Dclippy::nursery -Dclippy::pedantic -Aclippy::module_name_repetitions' From 5e0d2bc58dcae4ba662592fedbba6fad06e6134d Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Fri, 19 Jan 2024 16:32:03 -0300 Subject: [PATCH 06/26] Fix unit tests --- chains/astar/server/src/lib.rs | 108 +++++++++++++--- chains/ethereum/server/src/client.rs | 67 +++++----- chains/ethereum/server/src/lib.rs | 26 ++-- chains/ethereum/server/src/utils.rs | 2 +- chains/polkadot/server/src/lib.rs | 94 +++++++++----- rosetta-client/src/client.rs | 115 +++++++++++++++--- rosetta-client/src/wallet.rs | 74 ++++++++--- rosetta-core/src/lib.rs | 34 +++--- rosetta-docker/src/lib.rs | 23 ++-- rosetta-types/src/block_identifier.rs | 6 +- rosetta-types/src/partial_block_identifier.rs | 12 +- scripts/reset_docker.sh | 2 +- 12 files changed, 392 insertions(+), 171 deletions(-) diff --git a/chains/astar/server/src/lib.rs b/chains/astar/server/src/lib.rs index e97ddf27..5d6faaaf 100644 --- a/chains/astar/server/src/lib.rs +++ b/chains/astar/server/src/lib.rs @@ -13,9 +13,7 @@ use rosetta_core::{ address::{Address, AddressFormat}, PublicKey, }, - types::{ - Block, BlockIdentifier, Coin, PartialBlockIdentifier, Transaction, TransactionIdentifier, - }, + types::{Block, BlockIdentifier, PartialBlockIdentifier, Transaction, TransactionIdentifier}, BlockchainClient, BlockchainConfig, }; use rosetta_server::ws::default_client; @@ -26,7 +24,9 @@ use subxt::{ backend::{ legacy::{rpc_methods::BlockNumber, LegacyBackend, LegacyRpcMethods}, rpc::RpcClient, + BlockRef, }, + config::substrate::U256, dynamic::Value as SubtxValue, ext::sp_core::{self, crypto::Ss58AddressFormat}, tx::PairSigner, @@ -74,7 +74,7 @@ impl AstarClient { async fn account_info( &self, address: &Address, - maybe_block: Option<&BlockIdentifier>, + maybe_block: Option<&PartialBlockIdentifier>, ) -> Result>> { let account: AccountId32 = address .address() @@ -86,16 +86,87 @@ impl AstarClient { let storage_query = subxt::dynamic::storage("System", "Account", vec![SubtxValue::from_bytes(account)]); - let block_hash = { - let block_number = maybe_block.map(|block| BlockNumber::from(block.index)); - self.rpc_methods - .chain_get_block_hash(block_number) + // TODO: Change the `PartialBlockIdentifier` for distinguish between ethereum blocks and + // substrate blocks. + let block_hash = match maybe_block { + Some(PartialBlockIdentifier { hash: Some(block_hash), .. }) => { + // If a hash if provided, we don't know if it's a ethereum block hash or substrate + // block hash. We try to fetch the block using ethereum first, and + // if it fails, we try to fetch it using substrate. + let ethereum_block = self + .client + .block(&PartialBlockIdentifier { index: None, hash: Some(*block_hash) }) + .await; + + if let Ok(ethereum_block) = ethereum_block { + // Convert ethereum block to substrate block by fetching the block by number. + let substrate_block_number = + BlockNumber::Number(ethereum_block.block_identifier.index); + let substrate_block_hash = self + .rpc_methods + .chain_get_block_hash(Some(substrate_block_number)) + .await? + .map(BlockRef::from_hash) + .ok_or_else(|| anyhow::anyhow!("no block hash found"))?; + + // Verify if the ethereum block belongs to this substrate block. + let query_current_eth_block = + astar_metadata::storage().ethereum().current_block(); + + // Fetch ethereum block from `ethereum.current_block` state. + let Some(actual_eth_block) = self + .ws_client + .storage() + .at(substrate_block_hash.clone()) + .fetch(&query_current_eth_block) + .await? + else { + // This error should not happen, once all astar blocks must have one + // ethereum block + anyhow::bail!("[report this bug!] no ethereum block found for astar at block {substrate_block_hash:?}"); + }; + + // Verify if the ethereum block hash matches the provided ethereum block hash. + // TODO: compute the block hash + if U256(actual_eth_block.header.number.0) != + U256::from(ethereum_block.block_identifier.index) + { + anyhow::bail!("ethereum block hash mismatch"); + } + if actual_eth_block.header.parent_hash.as_fixed_bytes() != + ðereum_block.parent_block_identifier.hash + { + anyhow::bail!("ethereum block hash mismatch"); + } + substrate_block_hash + } else { + self.rpc_methods + .chain_get_block_hash(Some(BlockNumber::Hex(U256::from_big_endian( + block_hash, + )))) + .await? + .map(BlockRef::from_hash) + .ok_or_else(|| anyhow::anyhow!("no block hash found"))? + } + }, + Some(PartialBlockIdentifier { index: Some(block_number), .. }) => { + // If a block number is provided, the value is the same for ethereum blocks and + // substrate blocks. + self.rpc_methods + .chain_get_block_hash(Some(BlockNumber::Number(*block_number))) + .await? + .map(BlockRef::from_hash) + .ok_or_else(|| anyhow::anyhow!("no block hash found"))? + }, + Some(PartialBlockIdentifier { .. }) | None => self + .rpc_methods + .chain_get_block_hash(None) .await? - .ok_or_else(|| anyhow::anyhow!("no block hash found"))? + .map(BlockRef::from_hash) + .ok_or_else(|| anyhow::anyhow!("no block hash found"))?, }; let account_info = self.ws_client.storage().at(block_hash).fetch(&storage_query).await?; - account_info.map_or_else( || { Ok(AccountInfo::> { @@ -127,13 +198,14 @@ impl BlockchainClient for AstarClient { type Call = EthQuery; type CallResult = EthQueryResult; + type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; fn config(&self) -> &BlockchainConfig { self.client.config() } - fn genesis_block(&self) -> &BlockIdentifier { + fn genesis_block(&self) -> Self::BlockIdentifier { self.client.genesis_block() } @@ -141,15 +213,15 @@ impl BlockchainClient for AstarClient { self.client.node_version().await } - async fn current_block(&self) -> Result { + async fn current_block(&self) -> Result { self.client.current_block().await } - async fn finalized_block(&self) -> Result { + async fn finalized_block(&self) -> Result { self.client.finalized_block().await } - async fn balance(&self, address: &Address, block: &BlockIdentifier) -> Result { + async fn balance(&self, address: &Address, block: &Self::AtBlock) -> Result { let balance = match address.format() { AddressFormat::Ss58(_) => { let account_info = self.account_info(address, Some(block)).await?; @@ -170,10 +242,6 @@ impl BlockchainClient for AstarClient { Ok(balance) } - async fn coins(&self, address: &Address, block: &BlockIdentifier) -> Result> { - self.client.coins(address, block).await - } - async fn faucet(&self, address: &Address, value: u128) -> Result> { // convert address let dest = { @@ -213,13 +281,13 @@ impl BlockchainClient for AstarClient { self.client.submit(transaction).await } - async fn block(&self, block_identifier: &PartialBlockIdentifier) -> Result { + async fn block(&self, block_identifier: &Self::AtBlock) -> Result { self.client.block(block_identifier).await } async fn block_transaction( &self, - block_identifier: &BlockIdentifier, + block_identifier: &Self::BlockIdentifier, tx: &TransactionIdentifier, ) -> Result { self.client.block_transaction(block_identifier, tx).await diff --git a/chains/ethereum/server/src/client.rs b/chains/ethereum/server/src/client.rs index d94846cf..7b58fb5b 100644 --- a/chains/ethereum/server/src/client.rs +++ b/chains/ethereum/server/src/client.rs @@ -7,7 +7,7 @@ use anyhow::{Context, Result}; use ethers::{ prelude::*, providers::{JsonRpcClient, Middleware, Provider}, - types::Bytes, + types::{Bytes, U64}, utils::{keccak256, rlp::Encodable}, }; use rosetta_config_ethereum::{ @@ -84,8 +84,8 @@ where &self.config } - pub const fn genesis_block(&self) -> &BlockIdentifier { - &self.genesis_block.identifier + pub fn genesis_block(&self) -> BlockIdentifier { + self.genesis_block.identifier.clone() } pub async fn node_version(&self) -> Result { @@ -94,11 +94,16 @@ where pub async fn current_block(&self) -> Result { let index = self.client.get_block_number().await?.as_u64(); - let Some(block_hash) = self.client.get_block(index).await?.context("missing block")?.hash + let Some(block_hash) = self + .client + .get_block(BlockId::Number(BlockNumber::Number(U64::from(index)))) + .await? + .context("missing block")? + .hash else { anyhow::bail!("FATAL: block hash is missing"); }; - Ok(BlockIdentifier { index, hash: hex::encode(block_hash) }) + Ok(BlockIdentifier { index, hash: block_hash.0 }) } pub async fn finalized_block(&self, latest_block: Option) -> Result { @@ -130,16 +135,24 @@ where Ok(finalized_block) } - pub async fn balance(&self, address: &Address, block: &BlockIdentifier) -> Result { - let block = hex::decode(&block.hash)? - .try_into() - .map_err(|_| anyhow::anyhow!("invalid block hash"))?; + pub async fn balance( + &self, + address: &Address, + block_identifier: &PartialBlockIdentifier, + ) -> Result { + println!("balance address: {address:?} block: {block_identifier:?}"); + let block_id = block_identifier.hash.as_ref().map_or_else( + || { + let index = block_identifier + .index + .map_or(BlockNumber::Latest, |index| BlockNumber::Number(U64::from(index))); + BlockId::Number(index) + }, + |hash| BlockId::Hash(H256(*hash)), + ); let address: H160 = address.address().parse()?; - Ok(self - .client - .get_balance(address, Some(BlockId::Hash(H256(block)))) - .await? - .as_u128()) + println!("balance at block: {block_id:?}"); + Ok(self.client.get_balance(address, Some(block_id)).await?.as_u128()) } #[allow(clippy::unused_async)] @@ -211,14 +224,15 @@ where } pub async fn block(&self, block_identifier: &PartialBlockIdentifier) -> Result { - let block_id = if let Some(hash) = block_identifier.hash.as_ref() { - BlockId::Hash(H256::from_str(hash)?) - } else { - let index = block_identifier - .index - .map_or(BlockNumber::Latest, |index| BlockNumber::Number(U64::from(index))); - BlockId::Number(index) - }; + let block_id = block_identifier.hash.as_ref().map_or_else( + || { + let index = block_identifier + .index + .map_or(BlockNumber::Latest, |index| BlockNumber::Number(U64::from(index))); + BlockId::Number(index) + }, + |hash| BlockId::Hash(H256(*hash)), + ); let block = self .client .get_block_with_txs(block_id) @@ -244,13 +258,10 @@ where transactions.push(transaction); } Ok(Block { - block_identifier: BlockIdentifier { - index: block_number.as_u64(), - hash: hex::encode(block_hash), - }, + block_identifier: BlockIdentifier { index: block_number.as_u64(), hash: block_hash.0 }, parent_block_identifier: BlockIdentifier { index: block_number.as_u64().saturating_sub(1), - hash: hex::encode(block.parent_hash), + hash: block.parent_hash.0, }, timestamp: i64::try_from(block.timestamp.as_u64()).context("timestamp overflow")?, transactions, @@ -266,7 +277,7 @@ where let tx_id = H256::from_str(&tx.hash)?; let block = self .client - .get_block(BlockId::Hash(H256::from_str(&block.hash)?)) + .get_block(BlockId::Hash(H256(block.hash))) .await? .context("block not found")?; let transaction = diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index 7d785d6a..169be6c6 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -6,9 +6,7 @@ pub use rosetta_config_ethereum::{ }; use rosetta_core::{ crypto::{address::Address, PublicKey}, - types::{ - Block, BlockIdentifier, Coin, PartialBlockIdentifier, Transaction, TransactionIdentifier, - }, + types::{Block, BlockIdentifier, PartialBlockIdentifier, Transaction, TransactionIdentifier}, BlockchainClient, BlockchainConfig, }; use rosetta_server::ws::{default_client, DefaultClient}; @@ -93,6 +91,7 @@ impl BlockchainClient for MaybeWsEthereumClient { type Call = EthQuery; type CallResult = EthQueryResult; + type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; fn config(&self) -> &BlockchainConfig { @@ -102,7 +101,7 @@ impl BlockchainClient for MaybeWsEthereumClient { } } - fn genesis_block(&self) -> &BlockIdentifier { + fn genesis_block(&self) -> Self::BlockIdentifier { match self { Self::Http(http_client) => http_client.genesis_block(), Self::Ws(ws_client) => ws_client.genesis_block(), @@ -116,35 +115,28 @@ impl BlockchainClient for MaybeWsEthereumClient { } } - async fn current_block(&self) -> Result { + async fn current_block(&self) -> Result { match self { Self::Http(http_client) => http_client.current_block().await, Self::Ws(ws_client) => ws_client.current_block().await, } } - async fn finalized_block(&self) -> Result { + async fn finalized_block(&self) -> Result { let block = match self { Self::Http(http_client) => http_client.finalized_block(None).await?, Self::Ws(ws_client) => ws_client.finalized_block(None).await?, }; - Ok(BlockIdentifier { index: block.number, hash: hex::encode(block.hash) }) + Ok(BlockIdentifier { index: block.number, hash: block.hash.0 }) } - async fn balance(&self, address: &Address, block: &BlockIdentifier) -> Result { + async fn balance(&self, address: &Address, block: &Self::AtBlock) -> Result { match self { Self::Http(http_client) => http_client.balance(address, block).await, Self::Ws(ws_client) => ws_client.balance(address, block).await, } } - async fn coins(&self, address: &Address, block: &BlockIdentifier) -> Result> { - match self { - Self::Http(http_client) => http_client.coins(address, block).await, - Self::Ws(ws_client) => ws_client.coins(address, block).await, - } - } - async fn faucet(&self, address: &Address, param: u128) -> Result> { match self { Self::Http(http_client) => http_client.faucet(address, param).await, @@ -170,7 +162,7 @@ impl BlockchainClient for MaybeWsEthereumClient { } } - async fn block(&self, block_identifier: &PartialBlockIdentifier) -> Result { + async fn block(&self, block_identifier: &Self::AtBlock) -> Result { match self { Self::Http(http_client) => http_client.block(block_identifier).await, Self::Ws(ws_client) => ws_client.block(block_identifier).await, @@ -179,7 +171,7 @@ impl BlockchainClient for MaybeWsEthereumClient { async fn block_transaction( &self, - block: &BlockIdentifier, + block: &Self::BlockIdentifier, tx: &TransactionIdentifier, ) -> Result { match self { diff --git a/chains/ethereum/server/src/utils.rs b/chains/ethereum/server/src/utils.rs index 00333eca..2d68cb6c 100644 --- a/chains/ethereum/server/src/utils.rs +++ b/chains/ethereum/server/src/utils.rs @@ -44,7 +44,7 @@ impl TryFrom> for NonPendingBlock { Ok(Self { hash, number: number.as_u64(), - identifier: BlockIdentifier::new(number.as_u64(), hex::encode(hash)), + identifier: BlockIdentifier::new(number.as_u64(), hash.0), block, }) } diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index e75a7310..97ac4164 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -5,7 +5,7 @@ pub use rosetta_config_polkadot::{PolkadotMetadata, PolkadotMetadataParams}; use rosetta_core::{ crypto::{address::Address, PublicKey}, types::{ - Block, BlockIdentifier, CallRequest, Coin, PartialBlockIdentifier, Transaction, + Block, BlockIdentifier, CallRequest, PartialBlockIdentifier, Transaction, TransactionIdentifier, }, BlockchainClient, BlockchainConfig, EmptyEventStream, @@ -16,11 +16,17 @@ use sp_keyring::AccountKeyring; use std::time::Duration; use subxt::{ backend::{ - legacy::{rpc_methods::BlockNumber, LegacyRpcMethods}, + legacy::{ + rpc_methods::{BlockNumber, NumberOrHex}, + LegacyRpcMethods, + }, rpc::RpcClient, }, blocks::BlockRef, - config::{Hasher, Header}, + config::{ + substrate::{H256, U256}, + Hasher, Header, + }, // dynamic::Value as SubtxValue, tx::{PairSigner, SubmittableExtrinsic}, utils::{AccountId32, MultiAddress}, @@ -64,14 +70,14 @@ impl PolkadotClient { (client, rpc_methods) }; let genesis = client.genesis_hash(); - let genesis_block = BlockIdentifier { index: 0, hash: hex::encode(genesis.as_ref()) }; + let genesis_block = BlockIdentifier { index: 0, hash: genesis.0 }; Ok(Self { config, client, rpc_methods, genesis_block }) } async fn account_info( &self, address: &Address, - maybe_block: Option<&BlockIdentifier>, + maybe_block: Option, ) -> Result> { let account: AccountId32 = address .address() @@ -82,23 +88,35 @@ impl PolkadotClient { // Build a dynamic storage query to iterate account information. // let storage_query = // subxt::dynamic::storage("System", "Account", vec![SubtxValue::from_bytes(account)]); - let storage_query = westend_dev_metadata::storage().system().account(account); - let block_hash = { - let block_number = maybe_block.map(|block| BlockNumber::from(block.index)); - self.rpc_methods - .chain_get_block_hash(block_number) - .await? - .map(BlockRef::from_hash) - .ok_or_else(|| anyhow::anyhow!("no block hash found"))? + // Convert `BlockNumber` to `BlockRef` + let block_hash = match maybe_block { + Some(NumberOrHex::Hex(block_hash)) => { + // Convert U256 to BlockRef + let mut bytes = [0u8; 32]; + block_hash.to_big_endian(&mut bytes); + let block_hash = H256(bytes); + BlockRef::from_hash(block_hash) + }, + Some(NumberOrHex::Number(block_number)) => { + // Get block hash by number + self.rpc_methods + .chain_get_block_hash(Some(BlockNumber::Number(block_number))) + .await? + .map(BlockRef::from_hash) + .ok_or_else(|| anyhow::anyhow!("no block hash found"))? + }, + None => { + // Get latest block hash + self.rpc_methods + .chain_get_block_hash(None) + .await? + .map(BlockRef::from_hash) + .ok_or_else(|| anyhow::anyhow!("[report this bug] latest block not found"))? + }, }; - - let account_info = self.client.storage().at(block_hash).fetch(&storage_query).await?; - - // let account_info = self.client.storage().at(block_hash).fetch(&storage_query).await?; - - account_info.map_or_else( + self.client.storage().at(block_hash).fetch(&storage_query).await?.map_or_else( || { Ok(AccountInfo { nonce: 0, @@ -133,14 +151,15 @@ impl BlockchainClient for PolkadotClient { type Call = CallRequest; type CallResult = Value; + type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; fn config(&self) -> &BlockchainConfig { &self.config } - fn genesis_block(&self) -> &BlockIdentifier { - &self.genesis_block + fn genesis_block(&self) -> BlockIdentifier { + self.genesis_block.clone() } async fn node_version(&self) -> Result { @@ -151,7 +170,7 @@ impl BlockchainClient for PolkadotClient { let block = self.rpc_methods.chain_get_block(None).await?.context("no current block")?; let index = u64::from(block.block.header.number); let hash = block.block.header.hash(); - Ok(BlockIdentifier { index, hash: hex::encode(hash.as_ref()) }) + Ok(BlockIdentifier { index, hash: hash.0 }) } async fn finalized_block(&self) -> Result { @@ -163,18 +182,27 @@ impl BlockchainClient for PolkadotClient { .context("no finalized block")?; let index = u64::from(block.block.header.number); let hash = block.block.header.hash(); - Ok(BlockIdentifier { index, hash: hex::encode(hash.as_ref()) }) + Ok(BlockIdentifier { index, hash: hash.0 }) } - async fn balance(&self, address: &Address, block: &BlockIdentifier) -> Result { - let account_info = self.account_info(address, Some(block)).await?; + async fn balance( + &self, + address: &Address, + block_identifier: &PartialBlockIdentifier, + ) -> Result { + let block_number = match block_identifier { + PartialBlockIdentifier { hash: Some(block_bash), .. } => { + Some(BlockNumber::Hex(U256::from_big_endian(block_bash))) + }, + PartialBlockIdentifier { index: Some(block_number), .. } => { + Some(BlockNumber::Number(*block_number)) + }, + PartialBlockIdentifier { hash: None, index: None } => None, + }; + let account_info = self.account_info(address, block_number).await?; Ok(account_info.data.free) } - async fn coins(&self, _address: &Address, _block: &BlockIdentifier) -> Result> { - anyhow::bail!("not a utxo chain") - } - async fn faucet(&self, address: &Address, value: u128) -> Result> { let address: AccountId32 = address .address() @@ -239,7 +267,7 @@ impl BlockchainClient for PolkadotClient { async fn block(&self, block_identifier: &PartialBlockIdentifier) -> Result { let block_hash = if let Some(hash) = block_identifier.hash.as_ref() { - hash.parse()? + H256(*hash) } else { self.rpc_methods .chain_get_block_hash(block_identifier.index.map(BlockNumber::from)) @@ -265,11 +293,11 @@ impl BlockchainClient for PolkadotClient { Ok(Block { block_identifier: BlockIdentifier { index: u64::from(block.number()), - hash: hex::encode(block.hash()), + hash: block.hash().to_fixed_bytes(), }, parent_block_identifier: BlockIdentifier { index: u64::from(block.number().saturating_sub(1)), - hash: hex::encode(block.header().parent_hash), + hash: block.header().parent_hash.to_fixed_bytes(), }, timestamp: i64::try_from(Duration::from_millis(timestamp).as_nanos()) .context("timestamp overflow")?, @@ -283,7 +311,7 @@ impl BlockchainClient for PolkadotClient { block_identifier: &BlockIdentifier, transaction_identifier: &TransactionIdentifier, ) -> Result { - let block_hash = block_identifier.hash.parse::<::Hash>()?; + let block_hash = ::Hash::from(block_identifier.hash); let transaction_hash = transaction_identifier.hash.parse()?; let block = self.client.blocks().at(block_hash).await?; let extrinsic = block diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index 9d7b95e3..6e277ed1 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -1,10 +1,7 @@ #![allow(missing_docs)] use crate::{ crypto::{address::Address, PublicKey}, - types::{ - Block, BlockIdentifier, CallRequest, Coin, PartialBlockIdentifier, Transaction, - TransactionIdentifier, - }, + types::{Block, CallRequest, Transaction, TransactionIdentifier}, Blockchain, BlockchainConfig, }; use anyhow::Result; @@ -108,6 +105,21 @@ pub enum GenericCallResult { Polkadot(Value), } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum GenericAtBlock { + Ethereum(::AtBlock), + Polkadot(::AtBlock), +} + +impl From for GenericAtBlock { + fn from(block: GenericBlockIdentifier) -> Self { + match block { + GenericBlockIdentifier::Ethereum(block) => Self::Ethereum(block.into()), + GenericBlockIdentifier::Polkadot(block) => Self::Polkadot(block.into()), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum GenericBlockIdentifier { Ethereum(::BlockIdentifier), @@ -132,34 +144,71 @@ impl BlockchainClient for GenericClient { type Call = GenericCall; type CallResult = GenericCallResult; + type AtBlock = GenericAtBlock; type BlockIdentifier = GenericBlockIdentifier; fn config(&self) -> &BlockchainConfig { dispatch!(self.config()) } - fn genesis_block(&self) -> &BlockIdentifier { - dispatch!(self.genesis_block()) + fn genesis_block(&self) -> Self::BlockIdentifier { + // dispatch!(self.genesis_block()) + match self { + Self::Ethereum(client) => GenericBlockIdentifier::Ethereum(client.genesis_block()), + Self::Astar(client) => GenericBlockIdentifier::Ethereum(client.genesis_block()), + Self::Polkadot(client) => GenericBlockIdentifier::Polkadot(client.genesis_block()), + } } async fn node_version(&self) -> Result { dispatch!(self.node_version().await) } - async fn current_block(&self) -> Result { - dispatch!(self.current_block().await) - } - - async fn finalized_block(&self) -> Result { - dispatch!(self.finalized_block().await) + async fn current_block(&self) -> Result { + // dispatch!(self.current_block().await) + match self { + Self::Ethereum(client) => { + client.current_block().await.map(GenericBlockIdentifier::Ethereum) + }, + Self::Astar(client) => { + client.current_block().await.map(GenericBlockIdentifier::Ethereum) + }, + Self::Polkadot(client) => { + client.current_block().await.map(GenericBlockIdentifier::Polkadot) + }, + } } - async fn balance(&self, address: &Address, block: &BlockIdentifier) -> Result { - dispatch!(self.balance(address, block).await) + async fn finalized_block(&self) -> Result { + // dispatch!(self.finalized_block().await) + match self { + Self::Ethereum(client) => { + client.finalized_block().await.map(GenericBlockIdentifier::Ethereum) + }, + Self::Astar(client) => { + client.finalized_block().await.map(GenericBlockIdentifier::Ethereum) + }, + Self::Polkadot(client) => { + client.finalized_block().await.map(GenericBlockIdentifier::Polkadot) + }, + } } - async fn coins(&self, address: &Address, block: &BlockIdentifier) -> Result> { - dispatch!(self.coins(address, block).await) + async fn balance(&self, address: &Address, block: &Self::AtBlock) -> Result { + match self { + Self::Ethereum(client) => match block { + GenericAtBlock::Ethereum(at_block) => client.balance(address, at_block).await, + GenericAtBlock::Polkadot(_) => anyhow::bail!("invalid block identifier"), + }, + Self::Astar(client) => match block { + GenericAtBlock::Ethereum(at_block) => client.balance(address, at_block).await, + GenericAtBlock::Polkadot(_) => anyhow::bail!("invalid block identifier"), + }, + Self::Polkadot(client) => match block { + GenericAtBlock::Polkadot(at_block) => client.balance(address, at_block).await, + GenericAtBlock::Ethereum(_) => anyhow::bail!("invalid block identifier"), + }, + } } async fn faucet(&self, address: &Address, param: u128) -> Result> { @@ -189,16 +238,42 @@ impl BlockchainClient for GenericClient { dispatch!(self.submit(transaction).await) } - async fn block(&self, block: &PartialBlockIdentifier) -> Result { - dispatch!(self.block(block).await) + async fn block(&self, block: &GenericAtBlock) -> Result { + match self { + Self::Ethereum(client) => match block { + GenericAtBlock::Ethereum(at_block) => client.block(at_block).await, + GenericAtBlock::Polkadot(_) => anyhow::bail!("invalid block identifier"), + }, + Self::Astar(client) => match block { + GenericAtBlock::Ethereum(at_block) => client.block(at_block).await, + GenericAtBlock::Polkadot(_) => anyhow::bail!("invalid block identifier"), + }, + Self::Polkadot(client) => match block { + GenericAtBlock::Polkadot(at_block) => client.block(at_block).await, + GenericAtBlock::Ethereum(_) => anyhow::bail!("invalid block identifier"), + }, + } } async fn block_transaction( &self, - block: &BlockIdentifier, + block: &Self::BlockIdentifier, tx: &TransactionIdentifier, ) -> Result { - dispatch!(self.block_transaction(block, tx).await) + match self { + Self::Ethereum(client) => match block { + Self::BlockIdentifier::Ethereum(block) => client.block_transaction(block, tx).await, + Self::BlockIdentifier::Polkadot(_) => anyhow::bail!("invalid block identifier"), + }, + Self::Astar(client) => match block { + Self::BlockIdentifier::Ethereum(block) => client.block_transaction(block, tx).await, + Self::BlockIdentifier::Polkadot(_) => anyhow::bail!("invalid block identifier"), + }, + Self::Polkadot(client) => match block { + Self::BlockIdentifier::Polkadot(block) => client.block_transaction(block, tx).await, + Self::BlockIdentifier::Ethereum(_) => anyhow::bail!("invalid block identifier"), + }, + } } async fn call(&self, req: &GenericCall) -> Result { diff --git a/rosetta-client/src/wallet.rs b/rosetta-client/src/wallet.rs index 84efa8c3..3385191a 100644 --- a/rosetta-client/src/wallet.rs +++ b/rosetta-client/src/wallet.rs @@ -1,10 +1,10 @@ use crate::{ - client::{GenericClient, GenericMetadata, GenericMetadataParams}, + client::{GenericBlockIdentifier, GenericClient, GenericMetadata, GenericMetadataParams}, crypto::{address::Address, bip32::DerivedSecretKey, bip44::ChildNumber}, mnemonic::MnemonicStore, signer::{RosettaAccount, RosettaPublicKey, Signer}, tx_builder::GenericTransactionBuilder, - types::{AccountIdentifier, Amount, BlockIdentifier, Coin, PublicKey, TransactionIdentifier}, + types::{AccountIdentifier, Amount, BlockIdentifier, PublicKey, TransactionIdentifier}, Blockchain, BlockchainConfig, }; use anyhow::Result; @@ -99,16 +99,52 @@ impl Wallet { /// Returns the latest finalized block identifier. #[allow(clippy::missing_errors_doc)] pub async fn status(&self) -> Result { - self.client.finalized_block().await + // self.client.finalized_block().await + match &self.client { + GenericClient::Astar(client) => client.finalized_block().await, + GenericClient::Ethereum(client) => client.finalized_block().await, + GenericClient::Polkadot(client) => client.finalized_block().await, + } } /// Returns the balance of the wallet. #[allow(clippy::missing_errors_doc)] pub async fn balance(&self) -> Result { + println!("will current_block"); let block = self.client.current_block().await?; + println!("got block: {block:?}"); let address = Address::new(self.client.config().address_format, self.account.address.clone()); - let balance = self.client.balance(&address, &block).await?; + let balance = match &self.client { + GenericClient::Astar(client) => match block { + GenericBlockIdentifier::Ethereum(block) => { + let block_identifier = PartialBlockIdentifier::from(block); + println!("will get balance: {block_identifier:?}"); + client.balance(&address, &block_identifier).await? + }, + GenericBlockIdentifier::Polkadot(_) => { + anyhow::bail!("[this is bug] client returned an invalid block identifier") + }, + }, + GenericClient::Ethereum(client) => match block { + GenericBlockIdentifier::Ethereum(block) => { + let block_identifier = PartialBlockIdentifier::from(block); + client.balance(&address, &block_identifier).await? + }, + GenericBlockIdentifier::Polkadot(_) => { + anyhow::bail!("[this is bug] client returned an invalid block identifier") + }, + }, + GenericClient::Polkadot(client) => match block { + GenericBlockIdentifier::Polkadot(block) => { + let block_identifier = PartialBlockIdentifier::from(block); + client.balance(&address, &block_identifier).await? + }, + GenericBlockIdentifier::Ethereum(_) => { + anyhow::bail!("[this is bug] client returned an invalid block identifier") + }, + }, + }; Ok(Amount { value: format!("{balance}"), currency: self.client.config().currency(), @@ -127,8 +163,12 @@ impl Wallet { /// Returns block data /// Takes `PartialBlockIdentifier` #[allow(clippy::missing_errors_doc)] - pub async fn block(&self, data: PartialBlockIdentifier) -> Result { - self.client.block(&data).await + pub async fn block(&self, at_block: PartialBlockIdentifier) -> Result { + match &self.client { + GenericClient::Astar(client) => client.block(&at_block).await, + GenericClient::Ethereum(client) => client.block(&at_block).await, + GenericClient::Polkadot(client) => client.block(&at_block).await, + } } /// Returns transactions included in a block @@ -141,16 +181,18 @@ impl Wallet { block_identifer: BlockIdentifier, tx_identifier: TransactionIdentifier, ) -> Result { - self.client.block_transaction(&block_identifer, &tx_identifier).await - } - - /// Returns the coins of the wallet. - #[allow(clippy::missing_errors_doc)] - pub async fn coins(&self) -> Result> { - let block = self.client.current_block().await?; - let address = - Address::new(self.client.config().address_format, self.account.address.clone()); - self.client.coins(&address, &block).await + match &self.client { + GenericClient::Astar(client) => { + client.block_transaction(&block_identifer, &tx_identifier).await + }, + GenericClient::Ethereum(client) => { + client.block_transaction(&block_identifer, &tx_identifier).await + }, + GenericClient::Polkadot(client) => { + client.block_transaction(&block_identifer, &tx_identifier).await + }, + } + // self.client.block_transaction(&block_identifer, &tx_identifier).await } /// Returns the on chain metadata. diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index 446c20db..83457fbf 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -7,8 +7,8 @@ use crate::{ Algorithm, PublicKey, SecretKey, }, types::{ - Block, BlockIdentifier, Coin, Currency, CurveType, NetworkIdentifier, - PartialBlockIdentifier, SignatureType, Transaction, TransactionIdentifier, + Block, BlockIdentifier, Currency, CurveType, NetworkIdentifier, SignatureType, Transaction, + TransactionIdentifier, }, }; use anyhow::Result; @@ -117,15 +117,15 @@ pub trait BlockchainClient: Sized + Send + Sync + 'static { type Call: Send + Sync + Sized + 'static; type CallResult: Send + Sync + Sized + 'static; + type AtBlock: Clone + Send + Sync + Sized + Eq + From + 'static; type BlockIdentifier: Clone + Send + Sync + Sized + Eq + 'static; fn config(&self) -> &BlockchainConfig; - fn genesis_block(&self) -> &BlockIdentifier; + fn genesis_block(&self) -> Self::BlockIdentifier; async fn node_version(&self) -> Result; - async fn current_block(&self) -> Result; - async fn finalized_block(&self) -> Result; - async fn balance(&self, address: &Address, block: &BlockIdentifier) -> Result; - async fn coins(&self, address: &Address, block: &BlockIdentifier) -> Result>; + async fn current_block(&self) -> Result; + async fn finalized_block(&self) -> Result; + async fn balance(&self, address: &Address, block: &Self::AtBlock) -> Result; async fn faucet(&self, address: &Address, param: u128) -> Result>; async fn metadata( &self, @@ -133,10 +133,10 @@ pub trait BlockchainClient: Sized + Send + Sync + 'static { params: &Self::MetadataParams, ) -> Result; async fn submit(&self, transaction: &[u8]) -> Result>; - async fn block(&self, block: &PartialBlockIdentifier) -> Result; + async fn block(&self, block: &Self::AtBlock) -> Result; async fn block_transaction( &self, - block: &BlockIdentifier, + block: &Self::BlockIdentifier, tx: &TransactionIdentifier, ) -> Result; async fn call(&self, req: &Self::Call) -> Result; @@ -158,29 +158,27 @@ where type Call = ::Call; type CallResult = ::CallResult; + type AtBlock = ::AtBlock; type BlockIdentifier = ::BlockIdentifier; fn config(&self) -> &BlockchainConfig { BlockchainClient::config(Self::as_ref(self)) } - fn genesis_block(&self) -> &BlockIdentifier { + fn genesis_block(&self) -> Self::BlockIdentifier { BlockchainClient::genesis_block(Self::as_ref(self)) } async fn node_version(&self) -> Result { BlockchainClient::node_version(Self::as_ref(self)).await } - async fn current_block(&self) -> Result { + async fn current_block(&self) -> Result { BlockchainClient::current_block(Self::as_ref(self)).await } - async fn finalized_block(&self) -> Result { + async fn finalized_block(&self) -> Result { BlockchainClient::finalized_block(Self::as_ref(self)).await } - async fn balance(&self, address: &Address, block: &BlockIdentifier) -> Result { + async fn balance(&self, address: &Address, block: &Self::AtBlock) -> Result { BlockchainClient::balance(Self::as_ref(self), address, block).await } - async fn coins(&self, address: &Address, block: &BlockIdentifier) -> Result> { - BlockchainClient::coins(Self::as_ref(self), address, block).await - } async fn faucet(&self, address: &Address, param: u128) -> Result> { BlockchainClient::faucet(Self::as_ref(self), address, param).await } @@ -194,12 +192,12 @@ where async fn submit(&self, transaction: &[u8]) -> Result> { BlockchainClient::submit(Self::as_ref(self), transaction).await } - async fn block(&self, block: &PartialBlockIdentifier) -> Result { + async fn block(&self, block: &Self::AtBlock) -> Result { BlockchainClient::block(Self::as_ref(self), block).await } async fn block_transaction( &self, - block: &BlockIdentifier, + block: &Self::BlockIdentifier, tx: &TransactionIdentifier, ) -> Result { BlockchainClient::block_transaction(Self::as_ref(self), block, tx).await diff --git a/rosetta-docker/src/lib.rs b/rosetta-docker/src/lib.rs index 95d9c7eb..630d3d8f 100644 --- a/rosetta-docker/src/lib.rs +++ b/rosetta-docker/src/lib.rs @@ -315,7 +315,10 @@ pub mod tests { use super::Env; use anyhow::Result; use nanoid::nanoid; - use rosetta_core::{types::PartialBlockIdentifier, BlockchainClient, BlockchainConfig}; + use rosetta_core::{ + types::{BlockIdentifier, PartialBlockIdentifier}, + BlockchainClient, BlockchainConfig, + }; use std::future::Future; fn env_id() -> String { @@ -331,7 +334,7 @@ pub mod tests { config: BlockchainConfig, ) -> Result<()> where - T: BlockchainClient, + T: BlockchainClient, Fut: Future> + Send, F: FnMut(BlockchainConfig) -> Fut + Send, { @@ -352,10 +355,7 @@ pub mod tests { // Check if the current block is consistent let expected_current = client.current_block().await?; let actual_current = client - .block(&PartialBlockIdentifier { - index: None, - hash: Some(expected_current.hash.clone()), - }) + .block(&PartialBlockIdentifier { index: None, hash: Some(expected_current.hash) }) .await? .block_identifier; assert_eq!(expected_current, actual_current); @@ -363,10 +363,7 @@ pub mod tests { // Check if the finalized block is consistent let expected_finalized = client.finalized_block().await?; let actual_finalized = client - .block(&PartialBlockIdentifier { - index: None, - hash: Some(expected_finalized.hash.clone()), - }) + .block(&PartialBlockIdentifier { index: None, hash: Some(expected_finalized.hash) }) .await? .block_identifier; assert_eq!(expected_finalized, actual_finalized); @@ -378,7 +375,7 @@ pub mod tests { #[allow(clippy::missing_panics_doc, clippy::missing_errors_doc)] pub async fn account(start_connector: F, config: BlockchainConfig) -> Result<()> where - T: BlockchainClient, + T: BlockchainClient, Fut: Future> + Send, F: FnMut(BlockchainConfig) -> Fut + Send, { @@ -400,7 +397,7 @@ pub mod tests { #[allow(clippy::missing_panics_doc, clippy::missing_errors_doc)] pub async fn construction(start_connector: F, config: BlockchainConfig) -> Result<()> where - T: BlockchainClient, + T: BlockchainClient, Fut: Future> + Send, F: FnMut(BlockchainConfig) -> Fut + Send, { @@ -414,8 +411,10 @@ pub mod tests { let bob = env.ephemeral_wallet().await?; assert_ne!(alice.public_key(), bob.public_key()); + println!("will verify balance"); // Alice and bob have no balance let balance = alice.balance().await?; + println!("got balance"); assert_eq!(balance.value, "0"); let balance = bob.balance().await?; assert_eq!(balance.value, "0"); diff --git a/rosetta-types/src/block_identifier.rs b/rosetta-types/src/block_identifier.rs index 56600282..42937756 100644 --- a/rosetta-types/src/block_identifier.rs +++ b/rosetta-types/src/block_identifier.rs @@ -16,14 +16,14 @@ pub struct BlockIdentifier { pub index: u64, /// This should be normalized according to the case specified in the block_hash_case network /// options. - #[serde(rename = "hash")] - pub hash: String, + #[serde(skip_serializing)] + pub hash: [u8; 32], } impl BlockIdentifier { /// The `block_identifier` uniquely identifies a block in a particular network. #[must_use] - pub const fn new(index: u64, hash: String) -> Self { + pub const fn new(index: u64, hash: [u8; 32]) -> Self { Self { index, hash } } } diff --git a/rosetta-types/src/partial_block_identifier.rs b/rosetta-types/src/partial_block_identifier.rs index fa397012..0b50bb2d 100644 --- a/rosetta-types/src/partial_block_identifier.rs +++ b/rosetta-types/src/partial_block_identifier.rs @@ -8,6 +8,8 @@ * Generated by: https://openapi-generator.tech */ +use crate::BlockIdentifier; + /// `PartialBlockIdentifier` : When fetching data by `BlockIdentifier`, it may be possible to only /// specify the index or hash. If neither property is specified, it is assumed that the client is /// making a request at the current block. @@ -15,8 +17,14 @@ pub struct PartialBlockIdentifier { #[serde(rename = "index", skip_serializing_if = "Option::is_none")] pub index: Option, - #[serde(rename = "hash", skip_serializing_if = "Option::is_none")] - pub hash: Option, + #[serde(skip_serializing)] + pub hash: Option<[u8; 32]>, +} + +impl From for PartialBlockIdentifier { + fn from(block_identifier: BlockIdentifier) -> Self { + Self { index: Some(block_identifier.index), hash: Some(block_identifier.hash) } + } } impl PartialBlockIdentifier { diff --git a/scripts/reset_docker.sh b/scripts/reset_docker.sh index 0fffe041..af9c542a 100755 --- a/scripts/reset_docker.sh +++ b/scripts/reset_docker.sh @@ -24,7 +24,7 @@ fi # Remove all containers dockerContainers=() -while IFS='' read -r line; do dockerContainers+=("${line}"); done < <(docker ps -a -q) +while IFS='' read -r line; do dockerContainers+=("${line}"); done < <(docker ps -a -q --filter status=paused) if [[ "${#dockerContainers[@]}" -gt 0 ]]; then docker rm "${dockerContainers[@]}" fi From 5ed58ae7275f18a4a6b4ce2da82941af12928b10 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 20 Jan 2024 02:12:20 -0300 Subject: [PATCH 07/26] Fix arbitrum tests --- .github/workflows/ci.yaml | 24 +- Cargo.lock | 20 +- .../rosetta-testing-arbitrum/Cargo.toml | 15 +- .../rosetta-testing-arbitrum/src/lib.rs | 614 ++++++++++-------- rosetta-client/src/wallet.rs | 12 +- scripts/reset_docker.sh | 2 +- 6 files changed, 358 insertions(+), 329 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 416c0fca..a317d610 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -59,7 +59,7 @@ jobs: needs: [rustfmt] strategy: matrix: - crate: [rosetta-server-astar, rosetta-server-ethereum, rosetta-server-polkadot, rosetta-client] + crate: [rosetta-server-astar, rosetta-server-ethereum, rosetta-server-polkadot, rosetta-client, rosetta-testing-arbitrum] name: ${{ matrix.crate }} runs-on: self-hosted steps: @@ -94,6 +94,16 @@ jobs: - name: Pull nodes run: ./scripts/pull_nodes.sh + + - name: Checkout nitro-testnode + if: ${{ matrix.crate == 'rosetta-testing-arbitrum' }} + run: git clone -b release --depth=1 --no-tags --recurse-submodules https://github.com/ManojJiSharma/nitro-testnode.git + + - name: Start arbitrum nitro-testnode + if: ${{ matrix.crate == 'rosetta-testing-arbitrum' }} + run: | + cd nitro-testnode + ./test-node.bash --detach - name: test (${{ matrix.crate }}) run: cargo test --locked -p ${{ matrix.crate }} @@ -119,18 +129,11 @@ jobs: components: clippy target: x86_64-unknown-linux-musl override: true - - - name: Checkout code - run: git clone -b release --recurse-submodules https://github.com/ManojJiSharma/nitro-testnode.git - - - name: Run the arbitrum nitro-testnode - run: | - cd nitro-testnode - ./test-node.bash --detach - name: clippy run: | cargo clippy --locked --workspace --examples --tests --all-features \ + --exclude rosetta-testing-arbitrum \ --exclude rosetta-server-astar \ --exclude rosetta-server-ethereum \ --exclude rosetta-server-polkadot \ @@ -149,10 +152,11 @@ jobs: - name: cargo test run: | cargo test --locked --workspace --all-features \ + --exclude rosetta-testing-arbitrum \ --exclude rosetta-server-astar \ --exclude rosetta-server-ethereum \ --exclude rosetta-server-polkadot \ --exclude rosetta-client - name: Cleanup Docker - run: ./scripts/reset_docker.sh \ No newline at end of file + run: ./scripts/reset_docker.sh diff --git a/Cargo.lock b/Cargo.lock index a452b7ed..342b9d60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5346,17 +5346,16 @@ dependencies = [ name = "rosetta-testing-arbitrum" version = "0.1.0" dependencies = [ - "alloy-sol-types 0.5.4", + "alloy-sol-types 0.6.0", "anyhow", "ethers", "ethers-solc", + "hex-literal", "rosetta-client", "rosetta-config-ethereum", "rosetta-core", "rosetta-docker", "rosetta-server-ethereum", - "sequential-test", - "serde_json", "sha3", "tokio", "tracing", @@ -6008,21 +6007,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -[[package]] -name = "sequential-macro" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5facc5f409a55d25bf271c853402a00e1187097d326757043f5dd711944d07" - -[[package]] -name = "sequential-test" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d9c0d773bc7e7733264f460e5dfa00b2510421ddd6284db0749eef8dfb79e9" -dependencies = [ - "sequential-macro", -] - [[package]] name = "serde" version = "1.0.195" diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/Cargo.toml b/chains/arbitrum/testing/rosetta-testing-arbitrum/Cargo.toml index f2139ce0..f94cf5c3 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/Cargo.toml +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/Cargo.toml @@ -6,21 +6,18 @@ license = "MIT" repository = "https://github.com/analog-labs/chain-connectors" description = "Arbitrum unit test." -[dependencies] +[dev-dependencies] +alloy-sol-types = { version = "0.6" } anyhow = "1.0" ethers = { version = "2.0", default-features = true, features = ["abigen", "rustls"] } +ethers-solc = "2.0" +hex-literal = "0.4" +rosetta-client.workspace = true rosetta-config-ethereum.workspace = true rosetta-core.workspace = true rosetta-docker = { workspace = true, features = ["tests"] } rosetta-server-ethereum.workspace = true -sequential-test = "0.2.4" -serde_json.workspace = true +sha3 = "0.10" tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } tracing = "0.1.40" - -[dev-dependencies] -alloy-sol-types = { version = "0.5" } -ethers-solc = "2.0" -rosetta-client.workspace = true -sha3 = "0.10" url = "2.4" diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs index 360961d9..8cd32a47 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs @@ -20,7 +20,6 @@ //! - `rosetta_client`: Client library for Rosetta API interactions. //! - `rosetta_config_ethereum`: Configuration for Ethereum Rosetta server. //! - `rosetta_server_arbitrum`: Custom client implementation for interacting with Arbitrum. -//! - `sequential_test`: Macro for ensuring sequential execution of asynchronous tests. //! - `sha3`: SHA-3 (Keccak) implementation for hashing. //! - `tokio`: Asynchronous runtime for running async functions. //! @@ -43,18 +42,25 @@ mod tests { use ethers::{ providers::Middleware, signers::{LocalWallet, Signer}, - types::{transaction::eip2718::TypedTransaction, TransactionRequest, H160, H256, U256}, - utils::hex, + types::{ + transaction::eip2718::TypedTransaction, BlockId, BlockNumber, TransactionRequest, H160, + H256, U256, U64, + }, }; use ethers_solc::{artifacts::Source, CompilerInput, EvmVersion, Solc}; + use hex_literal::hex; use rosetta_client::Wallet; use rosetta_config_ethereum::{AtBlock, CallResult}; use rosetta_core::{types::PartialBlockIdentifier, BlockchainClient}; use rosetta_server_ethereum::MaybeWsEthereumClient; - use sequential_test::sequential; use sha3::Digest; - use std::{collections::BTreeMap, future::Future, path::Path, str::FromStr}; - use tokio::sync::oneshot::{error::TryRecvError, Receiver}; + use std::{ + collections::BTreeMap, + future::Future, + path::Path, + sync::atomic::{AtomicU64, Ordering}, + time::Duration, + }; sol! { interface TestContract { @@ -65,38 +71,135 @@ mod tests { } } - async fn run_test(_future: Fut, mut stop_rx: Receiver<()>) { + macro_rules! create_account { + ($name: literal, $value: expr) => {{ + use ethers::core::k256::ecdsa::SigningKey; + let private_key: [u8; 32] = + sha3::Keccak256::digest(concat!(module_path!(), "::", $name)).into(); + let address = ::ethers::utils::secret_key_to_address( + &SigningKey::from_bytes(private_key.as_ref().into()).unwrap(), + ); + sync_send_funds(address, { $value }).await.unwrap(); + private_key + }}; + } + + /// Arbitrum faucet account private key. + const FAUCET_ACCOUNT_PRIVATE_KEY: [u8; 32] = + hex!("8aab161e2a1e57367b60bd870861e3042c2513f8a856f9fee014e7b96e0a2a36"); + + /// Account used exclusively to continuously sending tx to mine new blocks. + const BLOCK_MANEGER_PRIVATE_KEY: [u8; 32] = + hex!("b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659"); + // const BLOCK_MANEGER_ADDRESS: H160 = H160(hex!("3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E")); + + /// Arbitrum rpc url + const ARBITRUM_RPC_URL: &str = "http://localhost:8547"; + + /// Send funds to the provided account + async fn sync_send_funds + Send>(dest: H160, amount: I) -> Result<()> { + // Guarantee the faucet nonce is incremented is sequentially + static NONCE: std::sync::OnceLock = + std::sync::OnceLock::new(); + + let amount = amount.into(); + // Connect to the provider + let wallet = LocalWallet::from_bytes(&FAUCET_ACCOUNT_PRIVATE_KEY)?; + let provider = + ethers::providers::Provider::::try_from(ARBITRUM_RPC_URL) + .context("Failed to create HTTP provider")? + .interval(Duration::from_secs(1)); + + // retrieve the current nonce + let nonce = provider + .get_transaction_count(wallet.address(), Some(BlockId::Number(BlockNumber::Latest))) + .await? + .as_u64(); + let nonce = NONCE.get_or_init(|| AtomicU64::new(nonce)); + let nonce = nonce.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + + // Retrieve chain id + let chain_id = provider.get_chainid().await?.as_u64(); + + // Create a transaction request + let transaction_request = TransactionRequest { + from: None, + to: Some(ethers::types::NameOrAddress::Address(dest)), + value: Some(amount), + gas: Some(U256::from(210_000)), + gas_price: Some(U256::from(500_000_000)), + nonce: Some(U256::from(nonce)), + data: None, + chain_id: Some(chain_id.into()), + }; + + // Sign and send the transaction + let tx: TypedTransaction = transaction_request.into(); + let signature = wallet.sign_transaction(&tx).await?; + let tx = tx.rlp_signed(&signature); + let receipt = provider + .send_raw_transaction(tx) + .await? + .confirmations(1) + .await? + .context("failed to retrieve tx receipt")?; + + // Verify if the tx reverted + if !matches!(receipt.status, Some(U64([1]))) { + anyhow::bail!("Transaction reverted: {:?}", receipt.transaction_hash); + } + Ok(()) + } + + /// Run the test in another thread and while sending txs to force arbitrum to mine new blocks + /// Panics if the test panics + async fn run_test + Send + 'static>(future: Fut) { + static TEST_ID_MANAGER: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(1); + static CURRENT_TEST: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + static NONCE: std::sync::OnceLock = + std::sync::OnceLock::new(); + + // Create a unique id for this test context + let test_id = TEST_ID_MANAGER.fetch_add(1, Ordering::SeqCst); + + let wallet = LocalWallet::from_bytes(&BLOCK_MANEGER_PRIVATE_KEY).unwrap(); + let provider = + ethers::providers::Provider::::try_from(ARBITRUM_RPC_URL) + .expect("Failed to create HTTP provider") + .interval(Duration::from_secs(1)); + let address = H160(hex!("8Db77D3B019a52788bD3804724f5653d7C9Cf0b6")); + + let nonce = provider + .get_transaction_count(wallet.address(), None) + .await + .expect("failed to retrieve account nonce") + .as_u64(); + + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + + let nonce = NONCE.get_or_init(|| AtomicU64::new(nonce)); + + // Run the test in another thread + let handler = tokio::spawn(future); + loop { - if matches!(stop_rx.try_recv(), Ok(()) | Err(TryRecvError::Closed)) { + let current_test = CURRENT_TEST.load(std::sync::atomic::Ordering::SeqCst); + if current_test == 0 { + let result = + CURRENT_TEST.compare_exchange(0, test_id, Ordering::Acquire, Ordering::Relaxed); + if result.is_ok() { + break; + } + } else if current_test == test_id { break; } - let hex_string = "0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659"; - let hex_string = &hex_string[2..]; - let mut private_key_result = [0; 32]; - let bytes = hex::decode(hex_string).expect("Failed to decode hex string"); - private_key_result.copy_from_slice(&bytes); - let result = MaybeWsEthereumClient::new( - "arbitrum", - "dev", - "ws://127.0.0.1:8548", - Some(private_key_result), - ) - .await; - assert!(result.is_ok(), "Error creating ArbitrumClient"); - let wallet = LocalWallet::from_bytes(&private_key_result).unwrap(); - let provider = ethers::providers::Provider::::try_from( - "http://localhost:8547", - ) - .expect("Failed to create HTTP provider"); - let address = H160::from_str("0x8Db77D3B019a52788bD3804724f5653d7C9Cf0b6").unwrap(); - let nonce = provider - .get_transaction_count( - H160::from_str("0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E").unwrap(), - None, - ) - .await - .unwrap(); - let chain_id = provider.get_chainid().await.unwrap().as_u64(); + tokio::time::sleep(std::time::Duration::from_millis(4000)).await; + } + + // Force arbitrum to mine a new block by sending a transaction until the test finishes + while !handler.is_finished() { + let next_nonce = nonce.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + // Create a transaction request let transaction_request = TransactionRequest { from: None, @@ -104,151 +207,125 @@ mod tests { value: Some(U256::from(1)), gas: Some(U256::from(210_000)), gas_price: Some(U256::from(500_000_000)), - nonce: Some(nonce), + nonce: Some(U256::from(next_nonce)), data: None, chain_id: Some(chain_id.into()), }; + + // Sign and send the transaction let tx: TypedTransaction = transaction_request.into(); - let signature = wallet.sign_transaction(&tx).await.unwrap(); - let tx = tx.rlp_signed(&signature); - let _ = provider - .send_raw_transaction(tx) - .await - .unwrap() - .confirmations(1) - .await - .unwrap() - .context("failed to retrieve tx receipt") - .unwrap() - .transaction_hash - .0 - .to_vec(); - tokio::time::sleep(std::time::Duration::from_secs(1)).await; + let signature = match wallet.sign_transaction(&tx).await { + Ok(signature) => signature, + Err(err) => { + CURRENT_TEST.store(0, std::sync::atomic::Ordering::SeqCst); + panic!("{err}"); + }, + }; + let tx: ethers::types::Bytes = tx.rlp_signed(&signature); + let pending_tx = match provider.send_raw_transaction(tx).await { + Ok(tx) => tx, + Err(err) => { + CURRENT_TEST.store(0, std::sync::atomic::Ordering::SeqCst); + panic!("{err}"); + }, + }; + + // Wait 500ms for the tx to be mined + match pending_tx.confirmations(1).await { + Ok(Some(_)) => { + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + }, + Ok(None) => { + CURRENT_TEST.store(0, std::sync::atomic::Ordering::SeqCst); + panic!("no tx receipt"); + }, + Err(err) => { + CURRENT_TEST.store(0, std::sync::atomic::Ordering::SeqCst); + panic!("{err}"); + }, + }; + } + if CURRENT_TEST.load(Ordering::SeqCst) == test_id { + CURRENT_TEST.store(0, Ordering::SeqCst); + } + + // Now is safe to panic + if let Err(err) = handler.await { + // Resume the panic on the main task + std::panic::resume_unwind(err.into_panic()); } } #[tokio::test] - #[sequential] + // #[sequential] async fn network_status() { - let hex_string = "0x8aab161e2a1e57367b60bd870861e3042c2513f8a856f9fee014e7b96e0a2a36"; - // Remove the "0x" prefix - let hex_string = &hex_string[2..]; - let mut result = [0; 32]; - // Parse the hexadecimal string into a Vec - let bytes = hex::decode(hex_string).expect("Failed to decode hex string"); - result.copy_from_slice(&bytes); - - match MaybeWsEthereumClient::new("arbitrum", "dev", "ws://127.0.0.1:8548", Some(result)) + run_test(async { + let private_key = create_account!("network_status", 20 * u128::pow(10, 18)); + let client = MaybeWsEthereumClient::new( + "arbitrum", + "dev", + "ws://127.0.0.1:8548", + Some(private_key), + ) .await - { - Ok(client) => { - // The client was successfully created, continue with the rest of the function - // ... - println!("Client created successfully"); - // Check if the genesis is consistent - let expected_genesis = client.genesis_block().clone(); - tracing::info!("expected_genesis=> {expected_genesis:?}"); - let actual_genesis = client - .block(&PartialBlockIdentifier { index: Some(0), hash: None }) - .await - .unwrap() - .block_identifier; - - tracing::info!("actual_genesis=> {actual_genesis:?}"); - assert_eq!(expected_genesis, actual_genesis); - // Check if the current block is consistent - let expected_current = client.current_block().await.unwrap(); - tracing::info!("expected_current=> {expected_current:?}"); - let actual_current = client - .block(&PartialBlockIdentifier { - index: None, - hash: Some(expected_current.hash.clone()), - }) - .await; - match actual_current { - Ok(block) => { - tracing::info!("actual_current=> {:?}", block.block_identifier); - assert_eq!(expected_current, block.block_identifier); - }, - Err(error) => { - tracing::error!("{error:?}"); - }, - } + .expect("Error creating client"); + // Check if the genesis is consistent + let expected_genesis = client.genesis_block().clone(); + let actual_genesis = client + .block(&PartialBlockIdentifier { index: Some(0), hash: None }) + .await + .unwrap() + .block_identifier; - // Check if the finalized block is consistent - let expected_finalized = client.finalized_block().await.unwrap(); - tracing::info!("expected_finalized=> {expected_finalized:?}"); - let actual_finalized = client - .block(&PartialBlockIdentifier { - index: None, - hash: Some(expected_finalized.hash.clone()), - }) - .await; - - match actual_finalized { - Ok(block) => { - tracing::info!("actual_finalized=> {:?}", block.block_identifier); - assert_eq!(expected_finalized, block.block_identifier); - }, - Err(error) => { - tracing::error!("ad{error:?}"); - }, - } + assert_eq!(expected_genesis, actual_genesis); + // Check if the current block is consistent + let expected_current = client.current_block().await.unwrap(); + let actual_current = client + .block(&PartialBlockIdentifier { index: None, hash: Some(expected_current.hash) }) + .await + .unwrap(); + assert_eq!(expected_current, actual_current.block_identifier); - tracing::info!("Arbitrum network is up and running"); - }, - Err(err) => { - // An error occurred while creating the client, handle the error here - eprintln!("Error creating client: {err:?}"); - }, - } + // Check if the finalized block is consistent + let expected_finalized = client.finalized_block().await.unwrap(); + let actual_finalized = client + .block(&PartialBlockIdentifier { index: None, hash: Some(expected_finalized.hash) }) + .await + .unwrap(); + assert_eq!(expected_finalized, actual_finalized.block_identifier); + }) + .await; } #[tokio::test] - #[sequential] + // #[sequential] async fn test_account() { - let (stop_tx, mut stop_rx) = tokio::sync::oneshot::channel::<()>(); - let handler = tokio::spawn(async move { - run_test(async {}, stop_rx).await; - }); - let hex_string = "0x8aab161e2a1e57367b60bd870861e3042c2513f8a856f9fee014e7b96e0a2a36"; - // Remove the "0x" prefix - let hex_string = &hex_string[2..]; - let mut private_key_result = [0; 32]; - // Parse the hexadecimal string into a Vec - let bytes = hex::decode(hex_string).expect("Failed to decode hex string"); - private_key_result.copy_from_slice(&bytes); - let result = MaybeWsEthereumClient::new( - "arbitrum", - "dev", - "ws://127.0.0.1:8548", - Some(private_key_result), - ) - .await; - assert!(result.is_ok(), "Error creating ArbitrumClient"); - let client = result.unwrap(); - let value = 100 * u128::pow(10, client.config().currency_decimals); - let wallet = Wallet::from_config( - client.config().clone(), - "ws://127.0.0.1:8548", - None, - Some(private_key_result), - ) + run_test(async { + let private_key = create_account!("test_account", 20 * u128::pow(10, 18)); + let client = MaybeWsEthereumClient::new( + "arbitrum", + "dev", + "ws://127.0.0.1:8548", + Some(private_key), + ) + .await + .expect("Error creating ArbitrumClient"); + let wallet = Wallet::from_config( + client.config().clone(), + "ws://127.0.0.1:8548", + None, + Some(private_key), + ) + .await + .unwrap(); + let value = 10 * u128::pow(10, client.config().currency_decimals); + let _ = wallet.faucet(value).await; + let amount = wallet.balance().await.unwrap(); + assert_eq!((amount.value), (value).to_string()); + assert_eq!(amount.currency, client.config().currency()); + assert!(amount.metadata.is_none()); + }) .await; - match wallet { - Ok(w) => { - let _ = w.faucet(value).await; - let amount = w.balance().await.unwrap(); - assert_eq!((amount.value), (value).to_string()); - assert_eq!(amount.currency, client.config().currency()); - assert!(amount.metadata.is_none()); - }, - Err(e) => { - println!("Error : {e:?}"); - }, - } - stop_tx.send(()).expect("Failed to send stop signal"); - handler.await.expect("Failed to join the background task"); } fn compile_snippet(source: &str) -> Result> { @@ -276,132 +353,105 @@ mod tests { Ok(bytecode) } - #[allow(clippy::needless_raw_string_hashes)] #[tokio::test] - #[sequential] + // #[sequential] async fn test_smart_contract() { - let (stop_tx, mut stop_rx) = tokio::sync::oneshot::channel::<()>(); - let handler = tokio::spawn(async move { - run_test(async {}, stop_rx).await; - }); - let hex_string = "0x8aab161e2a1e57367b60bd870861e3042c2513f8a856f9fee014e7b96e0a2a36"; - // Remove the "0x" prefix - let hex_string = &hex_string[2..]; - let mut private_key_result = [0; 32]; - // Parse the hexadecimal string into a Vec - let bytes = hex::decode(hex_string).expect("Failed to decode hex string"); - private_key_result.copy_from_slice(&bytes); - let result = MaybeWsEthereumClient::new( - "arbitrum", - "dev", - "ws://127.0.0.1:8548", - Some(private_key_result), - ) + run_test(async { + let private_key = create_account!("test_smart_contract", 20 * u128::pow(10, 18)); + let client = MaybeWsEthereumClient::new( + "arbitrum", + "dev", + "ws://127.0.0.1:8548", + Some(private_key), + ) + .await + .expect("Error creating ArbitrumClient"); + let faucet = 10 * u128::pow(10, client.config().currency_decimals); + let wallet = Wallet::from_config( + client.config().clone(), + "ws://127.0.0.1:8548", + None, + Some(private_key), + ) + .await + .unwrap(); + wallet.faucet(faucet).await.unwrap(); + + let bytes = compile_snippet( + r" + event AnEvent(); + function emitEvent() public { + emit AnEvent(); + } + ", + ) + .unwrap(); + let tx_hash = wallet.eth_deploy_contract(bytes).await.unwrap(); + let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap(); + let contract_address = receipt.contract_address.unwrap(); + let tx_hash = { + let call = TestContract::emitEventCall {}; + wallet.eth_send_call(contract_address.0, call.abi_encode(), 0).await.unwrap() + }; + let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap(); + assert_eq!(receipt.logs.len(), 1); + let topic = receipt.logs[0].topics[0]; + let expected = H256(sha3::Keccak256::digest("AnEvent()").into()); + assert_eq!(topic, expected); + }) .await; - assert!(result.is_ok(), "Error creating ArbitrumClient"); - - let client = result.unwrap(); - - let faucet = 100 * u128::pow(10, client.config().currency_decimals); - let wallet = Wallet::from_config( - client.config().clone(), - "ws://127.0.0.1:8548", - None, - Some(private_key_result), - ) - .await - .unwrap(); - wallet.faucet(faucet).await.unwrap(); - - let bytes = compile_snippet( - r" - event AnEvent(); - function emitEvent() public { - emit AnEvent(); - } - ", - ) - .unwrap(); - let tx_hash = wallet.eth_deploy_contract(bytes).await.unwrap(); - let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap(); - let contract_address = receipt.contract_address.unwrap(); - let tx_hash = { - let call = TestContract::emitEventCall {}; - wallet.eth_send_call(contract_address.0, call.abi_encode(), 0).await.unwrap() - }; - let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap(); - assert_eq!(receipt.logs.len(), 1); - let topic = receipt.logs[0].topics[0]; - let expected = H256(sha3::Keccak256::digest("AnEvent()").into()); - assert_eq!(topic, expected); - stop_tx.send(()).expect("Failed to send stop signal"); - handler.await.expect("Failed to join the background task"); } - #[allow(clippy::needless_raw_string_hashes)] #[tokio::test] - #[sequential] + // #[sequential] async fn test_smart_contract_view() { - let (stop_tx, mut stop_rx) = tokio::sync::oneshot::channel::<()>(); - let handler = tokio::spawn(async move { - run_test(async {}, stop_rx).await; - }); - let hex_string = "0x8aab161e2a1e57367b60bd870861e3042c2513f8a856f9fee014e7b96e0a2a36"; - // Remove the "0x" prefix - let hex_string = &hex_string[2..]; - let mut private_key_result = [0; 32]; - // Parse the hexadecimal string into a Vec - let bytes = hex::decode(hex_string).expect("Failed to decode hex string"); - private_key_result.copy_from_slice(&bytes); - let result = MaybeWsEthereumClient::new( - "arbitrum", - "dev", - "ws://127.0.0.1:8548", - Some(private_key_result), - ) - .await; - assert!(result.is_ok(), "Error creating ArbitrumClient"); - let client = result.unwrap(); - let faucet = 100 * u128::pow(10, client.config().currency_decimals); - let wallet = Wallet::from_config( - client.config().clone(), - "ws://127.0.0.1:8548", - None, - Some(private_key_result), - ) - .await - .unwrap(); - wallet.faucet(faucet).await.unwrap(); - let bytes = compile_snippet( - r" - function identity(bool a) public view returns (bool) { - return a; - } - ", - ) - .unwrap(); - let tx_hash = wallet.eth_deploy_contract(bytes).await.unwrap(); - let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap(); - let contract_address = receipt.contract_address.unwrap(); - - let response = { - let call = TestContract::identityCall { a: true }; - wallet - .eth_view_call(contract_address.0, call.abi_encode(), AtBlock::Latest) - .await - .unwrap() - }; - assert_eq!( - response, - CallResult::Success( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1 - ] - .to_vec() + run_test(async move { + let private_key = create_account!("test_smart_contract_view", 20 * u128::pow(10, 18)); + let client = MaybeWsEthereumClient::new( + "arbitrum", + "dev", + "ws://127.0.0.1:8548", + Some(private_key), + ) + .await + .expect("Error creating ArbitrumClient"); + let faucet = 10 * u128::pow(10, client.config().currency_decimals); + let wallet = Wallet::from_config( + client.config().clone(), + "ws://127.0.0.1:8548", + None, + Some(private_key), + ) + .await + .unwrap(); + wallet.faucet(faucet).await.unwrap(); + let bytes = compile_snippet( + r" + function identity(bool a) public view returns (bool) { + return a; + } + ", ) - ); - stop_tx.send(()).expect("Failed to send stop signal"); - handler.await.expect("Failed to join the background task"); + .unwrap(); + let tx_hash = wallet.eth_deploy_contract(bytes).await.unwrap(); + let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap(); + let contract_address = receipt.contract_address.unwrap(); + + let response = { + let call = TestContract::identityCall { a: true }; + wallet + .eth_view_call(contract_address.0, call.abi_encode(), AtBlock::Latest) + .await + .unwrap() + }; + assert_eq!( + response, + CallResult::Success( + hex!("0000000000000000000000000000000000000000000000000000000000000001") + .to_vec() + ) + ); + }) + .await; } } diff --git a/rosetta-client/src/wallet.rs b/rosetta-client/src/wallet.rs index b3262436..fdaa87c5 100644 --- a/rosetta-client/src/wallet.rs +++ b/rosetta-client/src/wallet.rs @@ -112,17 +112,13 @@ impl Wallet { /// Returns the balance of the wallet. #[allow(clippy::missing_errors_doc)] pub async fn balance(&self) -> Result { - println!("will current_block"); let block = self.client.current_block().await?; - println!("got block: {block:?}"); let address = Address::new(self.client.config().address_format, self.account.address.clone()); let balance = match &self.client { GenericClient::Astar(client) => match block { GenericBlockIdentifier::Ethereum(block) => { - let block_identifier = PartialBlockIdentifier::from(block); - println!("will get balance: {block_identifier:?}"); - client.balance(&address, &block_identifier).await? + client.balance(&address, &PartialBlockIdentifier::from(block)).await? }, GenericBlockIdentifier::Polkadot(_) => { anyhow::bail!("[this is bug] client returned an invalid block identifier") @@ -130,8 +126,7 @@ impl Wallet { }, GenericClient::Ethereum(client) => match block { GenericBlockIdentifier::Ethereum(block) => { - let block_identifier = PartialBlockIdentifier::from(block); - client.balance(&address, &block_identifier).await? + client.balance(&address, &PartialBlockIdentifier::from(block)).await? }, GenericBlockIdentifier::Polkadot(_) => { anyhow::bail!("[this is bug] client returned an invalid block identifier") @@ -139,8 +134,7 @@ impl Wallet { }, GenericClient::Polkadot(client) => match block { GenericBlockIdentifier::Polkadot(block) => { - let block_identifier = PartialBlockIdentifier::from(block); - client.balance(&address, &block_identifier).await? + client.balance(&address, &PartialBlockIdentifier::from(block)).await? }, GenericBlockIdentifier::Ethereum(_) => { anyhow::bail!("[this is bug] client returned an invalid block identifier") diff --git a/scripts/reset_docker.sh b/scripts/reset_docker.sh index af9c542a..0fffe041 100755 --- a/scripts/reset_docker.sh +++ b/scripts/reset_docker.sh @@ -24,7 +24,7 @@ fi # Remove all containers dockerContainers=() -while IFS='' read -r line; do dockerContainers+=("${line}"); done < <(docker ps -a -q --filter status=paused) +while IFS='' read -r line; do dockerContainers+=("${line}"); done < <(docker ps -a -q) if [[ "${#dockerContainers[@]}" -gt 0 ]]; then docker rm "${dockerContainers[@]}" fi From 450949b359782e5407562afe5dd14b849687b356 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 20 Jan 2024 02:14:03 -0300 Subject: [PATCH 08/26] Update Cargo.lock --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 342b9d60..5784d2b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4240,9 +4240,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" dependencies = [ "bitflags 2.4.2", "cfg-if", @@ -4272,9 +4272,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" dependencies = [ "cc", "libc", From 7c27858ea39daddcac912be1b31bb15539066454 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 20 Jan 2024 02:18:28 -0300 Subject: [PATCH 09/26] Remove unused license from deny.toml --- deny.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/deny.toml b/deny.toml index 2f83caca..151291ef 100644 --- a/deny.toml +++ b/deny.toml @@ -20,7 +20,6 @@ allow = [ "CC0-1.0", "ISC", "LicenseRef-ring", - "LicenseRef-webpki", "MIT", "MPL-2.0", "Unicode-DFS-2016", From b7caa3f9c1385fa319cbd17b32f0a7119f2ebbc7 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 20 Jan 2024 16:08:45 -0300 Subject: [PATCH 10/26] Refactor arbitrum tests to run concurrently --- Cargo.lock | 2 + .../rosetta-testing-arbitrum/Cargo.toml | 3 +- .../rosetta-testing-arbitrum/src/lib.rs | 290 ++++++++---------- 3 files changed, 140 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5784d2b5..c61a6b96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2349,6 +2349,7 @@ dependencies = [ "const-hex", "enr", "ethers-core", + "futures-channel", "futures-core", "futures-timer", "futures-util", @@ -5351,6 +5352,7 @@ dependencies = [ "ethers", "ethers-solc", "hex-literal", + "rand_core 0.6.4", "rosetta-client", "rosetta-config-ethereum", "rosetta-core", diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/Cargo.toml b/chains/arbitrum/testing/rosetta-testing-arbitrum/Cargo.toml index f94cf5c3..9ea2e651 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/Cargo.toml +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/Cargo.toml @@ -9,9 +9,10 @@ description = "Arbitrum unit test." [dev-dependencies] alloy-sol-types = { version = "0.6" } anyhow = "1.0" -ethers = { version = "2.0", default-features = true, features = ["abigen", "rustls"] } +ethers = { version = "2.0", default-features = true, features = ["abigen", "rustls", "ws"] } ethers-solc = "2.0" hex-literal = "0.4" +rand_core = { version = "0.6", features = ["getrandom"] } rosetta-client.workspace = true rosetta-config-ethereum.workspace = true rosetta-core.workspace = true diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs index 8cd32a47..c4b796a0 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs @@ -54,13 +54,21 @@ mod tests { use rosetta_core::{types::PartialBlockIdentifier, BlockchainClient}; use rosetta_server_ethereum::MaybeWsEthereumClient; use sha3::Digest; - use std::{ - collections::BTreeMap, - future::Future, - path::Path, - sync::atomic::{AtomicU64, Ordering}, - time::Duration, - }; + use std::{collections::BTreeMap, future::Future, path::Path, time::Duration}; + + /// Account used to fund other testing accounts. + const FUNDING_ACCOUNT_PRIVATE_KEY: [u8; 32] = + hex!("8aab161e2a1e57367b60bd870861e3042c2513f8a856f9fee014e7b96e0a2a36"); + + /// Account used exclusively to continuously sending tx to mine new blocks. + const BLOCK_INCREMENTER_PRIVATE_KEY: [u8; 32] = + hex!("b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659"); + + /// Arbitrum rpc url + const ARBITRUM_RPC_HTTP_URL: &str = "http://127.0.0.1:8547"; + const ARBITRUM_RPC_WS_URL: &str = "ws://127.0.0.1:8548"; + + type WsProvider = ethers::providers::Provider; sol! { interface TestContract { @@ -71,64 +79,44 @@ mod tests { } } - macro_rules! create_account { - ($name: literal, $value: expr) => {{ - use ethers::core::k256::ecdsa::SigningKey; - let private_key: [u8; 32] = - sha3::Keccak256::digest(concat!(module_path!(), "::", $name)).into(); - let address = ::ethers::utils::secret_key_to_address( - &SigningKey::from_bytes(private_key.as_ref().into()).unwrap(), - ); - sync_send_funds(address, { $value }).await.unwrap(); - private_key - }}; - } - - /// Arbitrum faucet account private key. - const FAUCET_ACCOUNT_PRIVATE_KEY: [u8; 32] = - hex!("8aab161e2a1e57367b60bd870861e3042c2513f8a856f9fee014e7b96e0a2a36"); + /// Send funds from funding account to the provided account. + /// This function is can be called concurrently. + async fn sync_send_funds(dest: H160, amount: u128) -> Result<()> { + // Guarantee the funding account nonce is incremented atomically + static NONCE: tokio::sync::Mutex = tokio::sync::Mutex::const_new(0); - /// Account used exclusively to continuously sending tx to mine new blocks. - const BLOCK_MANEGER_PRIVATE_KEY: [u8; 32] = - hex!("b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659"); - // const BLOCK_MANEGER_ADDRESS: H160 = H160(hex!("3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E")); - - /// Arbitrum rpc url - const ARBITRUM_RPC_URL: &str = "http://localhost:8547"; - - /// Send funds to the provided account - async fn sync_send_funds + Send>(dest: H160, amount: I) -> Result<()> { - // Guarantee the faucet nonce is incremented is sequentially - static NONCE: std::sync::OnceLock = - std::sync::OnceLock::new(); - - let amount = amount.into(); // Connect to the provider - let wallet = LocalWallet::from_bytes(&FAUCET_ACCOUNT_PRIVATE_KEY)?; + let wallet = LocalWallet::from_bytes(&FUNDING_ACCOUNT_PRIVATE_KEY)?; let provider = - ethers::providers::Provider::::try_from(ARBITRUM_RPC_URL) + ethers::providers::Provider::::try_from(ARBITRUM_RPC_HTTP_URL) .context("Failed to create HTTP provider")? .interval(Duration::from_secs(1)); - // retrieve the current nonce - let nonce = provider - .get_transaction_count(wallet.address(), Some(BlockId::Number(BlockNumber::Latest))) - .await? - .as_u64(); - let nonce = NONCE.get_or_init(|| AtomicU64::new(nonce)); - let nonce = nonce.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - // Retrieve chain id let chain_id = provider.get_chainid().await?.as_u64(); + // Acquire nonce lock + let mut nonce_lock = NONCE.lock().await; + + // Initialize nonce if necessary + if *nonce_lock == 0 { + // retrieve the current nonce, used to initialize the nonce if necessary, once + // `OnceLock` doesn't support async functions + let current_nonce = provider + .get_transaction_count(wallet.address(), Some(BlockId::Number(BlockNumber::Latest))) + .await? + .as_u64(); + *nonce_lock = current_nonce; + } + // Create a transaction request let transaction_request = TransactionRequest { from: None, to: Some(ethers::types::NameOrAddress::Address(dest)), - value: Some(amount), + value: Some(U256::from(amount)), gas: Some(U256::from(210_000)), gas_price: Some(U256::from(500_000_000)), - nonce: Some(U256::from(nonce)), + nonce: Some(U256::from(*nonce_lock)), data: None, chain_id: Some(chain_id.into()), }; @@ -137,134 +125,131 @@ mod tests { let tx: TypedTransaction = transaction_request.into(); let signature = wallet.sign_transaction(&tx).await?; let tx = tx.rlp_signed(&signature); - let receipt = provider - .send_raw_transaction(tx) - .await? - .confirmations(1) - .await? - .context("failed to retrieve tx receipt")?; + let pending_tx = provider.send_raw_transaction(tx).await?; + + // Increment and release nonce lock + // increment only after successfully send the tx to avoid nonce reuse + *nonce_lock += 1; + drop(nonce_lock); // Verify if the tx reverted + let receipt = + pending_tx.confirmations(1).await?.context("failed to retrieve tx receipt")?; if !matches!(receipt.status, Some(U64([1]))) { anyhow::bail!("Transaction reverted: {:?}", receipt.transaction_hash); } Ok(()) } + /// Creates a random account and send funds to it + async fn create_test_account(initial_balance: u128) -> Result<[u8; 32]> { + use ethers::core::k256::ecdsa::SigningKey; + use rand_core::OsRng; + let signing_key = SigningKey::random(&mut OsRng); + let address = ::ethers::utils::secret_key_to_address(&signing_key); + sync_send_funds(address, initial_balance).await?; + Ok(signing_key.to_bytes().into()) + } + /// Run the test in another thread and while sending txs to force arbitrum to mine new blocks - /// Panics if the test panics + /// # Panic + /// Panics if the future panics async fn run_test + Send + 'static>(future: Fut) { - static TEST_ID_MANAGER: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(1); - static CURRENT_TEST: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); - static NONCE: std::sync::OnceLock = - std::sync::OnceLock::new(); + // Guarantee that only one test is incrementing blocks at a time + static LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(()); - // Create a unique id for this test context - let test_id = TEST_ID_MANAGER.fetch_add(1, Ordering::SeqCst); + // Run the test in another thread + let test_handler = tokio::spawn(future); - let wallet = LocalWallet::from_bytes(&BLOCK_MANEGER_PRIVATE_KEY).unwrap(); - let provider = - ethers::providers::Provider::::try_from(ARBITRUM_RPC_URL) - .expect("Failed to create HTTP provider") - .interval(Duration::from_secs(1)); - let address = H160(hex!("8Db77D3B019a52788bD3804724f5653d7C9Cf0b6")); + // Acquire Lock + let guard = LOCK.lock().await; + + // Check if the test is finished after acquiring the lock + if test_handler.is_finished() { + // Release lock + drop(guard); + + // Now is safe to panic + if let Err(err) = test_handler.await { + std::panic::resume_unwind(err.into_panic()); + } + return; + } - let nonce = provider + // Connect to arbitrum node + let wallet = LocalWallet::from_bytes(&BLOCK_INCREMENTER_PRIVATE_KEY).unwrap(); + let provider = WsProvider::connect(ARBITRUM_RPC_WS_URL) + .await + .map(|provider| provider.interval(Duration::from_millis(500))) + .unwrap(); + + // Retrieve chain id + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + + // Retrieve current nonce + let mut nonce = provider .get_transaction_count(wallet.address(), None) .await .expect("failed to retrieve account nonce") .as_u64(); - let chain_id = provider.get_chainid().await.unwrap().as_u64(); + // Create a transaction request + let transaction_request = TransactionRequest { + from: None, + to: Some(wallet.address().into()), + value: None, + gas: Some(U256::from(210_000)), + gas_price: Some(U256::from(500_000_000)), + nonce: None, + data: None, + chain_id: Some(chain_id.into()), + }; + let mut tx: TypedTransaction = transaction_request.into(); - let nonce = NONCE.get_or_init(|| AtomicU64::new(nonce)); + // Mine a new block by sending a transaction until the test finishes + while !test_handler.is_finished() { + // Set tx nonce + tx.set_nonce(nonce); - // Run the test in another thread - let handler = tokio::spawn(future); - - loop { - let current_test = CURRENT_TEST.load(std::sync::atomic::Ordering::SeqCst); - if current_test == 0 { - let result = - CURRENT_TEST.compare_exchange(0, test_id, Ordering::Acquire, Ordering::Relaxed); - if result.is_ok() { - break; - } - } else if current_test == test_id { - break; - } - tokio::time::sleep(std::time::Duration::from_millis(4000)).await; - } - - // Force arbitrum to mine a new block by sending a transaction until the test finishes - while !handler.is_finished() { - let next_nonce = nonce.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - - // Create a transaction request - let transaction_request = TransactionRequest { - from: None, - to: Some(ethers::types::NameOrAddress::Address(address)), - value: Some(U256::from(1)), - gas: Some(U256::from(210_000)), - gas_price: Some(U256::from(500_000_000)), - nonce: Some(U256::from(next_nonce)), - data: None, - chain_id: Some(chain_id.into()), - }; + // Increment nonce + nonce += 1; // Sign and send the transaction - let tx: TypedTransaction = transaction_request.into(); - let signature = match wallet.sign_transaction(&tx).await { - Ok(signature) => signature, - Err(err) => { - CURRENT_TEST.store(0, std::sync::atomic::Ordering::SeqCst); - panic!("{err}"); - }, - }; + let signature = wallet.sign_transaction(&tx).await.expect("failed to sign tx"); let tx: ethers::types::Bytes = tx.rlp_signed(&signature); - let pending_tx = match provider.send_raw_transaction(tx).await { - Ok(tx) => tx, - Err(err) => { - CURRENT_TEST.store(0, std::sync::atomic::Ordering::SeqCst); - panic!("{err}"); - }, - }; + let receipt = provider + .send_raw_transaction(tx) + .await + .unwrap() + .confirmations(1) + .await + .unwrap() + .expect("tx receipt not found"); + + // Verify if the tx reverted + assert!(receipt.status.unwrap().as_u64() == 1, "Transaction reverted: {receipt:?}"); // Wait 500ms for the tx to be mined - match pending_tx.confirmations(1).await { - Ok(Some(_)) => { - tokio::time::sleep(std::time::Duration::from_millis(500)).await; - }, - Ok(None) => { - CURRENT_TEST.store(0, std::sync::atomic::Ordering::SeqCst); - panic!("no tx receipt"); - }, - Err(err) => { - CURRENT_TEST.store(0, std::sync::atomic::Ordering::SeqCst); - panic!("{err}"); - }, - }; - } - if CURRENT_TEST.load(Ordering::SeqCst) == test_id { - CURRENT_TEST.store(0, Ordering::SeqCst); + tokio::time::sleep(std::time::Duration::from_millis(500)).await; } + // Release lock + drop(guard); // Now is safe to panic - if let Err(err) = handler.await { + if let Err(err) = test_handler.await { // Resume the panic on the main task std::panic::resume_unwind(err.into_panic()); } } #[tokio::test] - // #[sequential] async fn network_status() { - run_test(async { - let private_key = create_account!("network_status", 20 * u128::pow(10, 18)); + let private_key = create_test_account(20 * u128::pow(10, 18)).await.unwrap(); + run_test(async move { let client = MaybeWsEthereumClient::new( "arbitrum", "dev", - "ws://127.0.0.1:8548", + ARBITRUM_RPC_WS_URL, Some(private_key), ) .await @@ -298,21 +283,20 @@ mod tests { } #[tokio::test] - // #[sequential] async fn test_account() { - run_test(async { - let private_key = create_account!("test_account", 20 * u128::pow(10, 18)); + let private_key = create_test_account(20 * u128::pow(10, 18)).await.unwrap(); + run_test(async move { let client = MaybeWsEthereumClient::new( "arbitrum", "dev", - "ws://127.0.0.1:8548", + ARBITRUM_RPC_WS_URL, Some(private_key), ) .await .expect("Error creating ArbitrumClient"); let wallet = Wallet::from_config( client.config().clone(), - "ws://127.0.0.1:8548", + ARBITRUM_RPC_WS_URL, None, Some(private_key), ) @@ -354,14 +338,13 @@ mod tests { } #[tokio::test] - // #[sequential] async fn test_smart_contract() { - run_test(async { - let private_key = create_account!("test_smart_contract", 20 * u128::pow(10, 18)); + let private_key = create_test_account(20 * u128::pow(10, 18)).await.unwrap(); + run_test(async move { let client = MaybeWsEthereumClient::new( "arbitrum", "dev", - "ws://127.0.0.1:8548", + ARBITRUM_RPC_WS_URL, Some(private_key), ) .await @@ -369,7 +352,7 @@ mod tests { let faucet = 10 * u128::pow(10, client.config().currency_decimals); let wallet = Wallet::from_config( client.config().clone(), - "ws://127.0.0.1:8548", + ARBITRUM_RPC_WS_URL, None, Some(private_key), ) @@ -403,14 +386,13 @@ mod tests { } #[tokio::test] - // #[sequential] async fn test_smart_contract_view() { + let private_key = create_test_account(20 * u128::pow(10, 18)).await.unwrap(); run_test(async move { - let private_key = create_account!("test_smart_contract_view", 20 * u128::pow(10, 18)); let client = MaybeWsEthereumClient::new( "arbitrum", "dev", - "ws://127.0.0.1:8548", + ARBITRUM_RPC_WS_URL, Some(private_key), ) .await @@ -418,7 +400,7 @@ mod tests { let faucet = 10 * u128::pow(10, client.config().currency_decimals); let wallet = Wallet::from_config( client.config().clone(), - "ws://127.0.0.1:8548", + ARBITRUM_RPC_WS_URL, None, Some(private_key), ) From 5f73a2eb8d7bd65e764e6125d09c87379a093ec5 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 20 Jan 2024 16:11:15 -0300 Subject: [PATCH 11/26] update docs --- chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs index c4b796a0..8d43ffe9 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs @@ -128,7 +128,7 @@ mod tests { let pending_tx = provider.send_raw_transaction(tx).await?; // Increment and release nonce lock - // increment only after successfully send the tx to avoid nonce reuse + // increment only after successfully send the tx to avoid nonce gaps *nonce_lock += 1; drop(nonce_lock); @@ -151,7 +151,7 @@ mod tests { Ok(signing_key.to_bytes().into()) } - /// Run the test in another thread and while sending txs to force arbitrum to mine new blocks + /// Run the test in another thread while sending txs to force arbitrum to mine new blocks /// # Panic /// Panics if the future panics async fn run_test + Send + 'static>(future: Fut) { From 47c2e2eea47965fde799e38503ab4f17844c0ddd Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 20 Jan 2024 17:18:31 -0300 Subject: [PATCH 12/26] Remove unused functions --- .../testing/rosetta-testing-arbitrum/src/lib.rs | 1 - rosetta-core/src/lib.rs | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs index 8d43ffe9..f29ba724 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs @@ -34,7 +34,6 @@ //! Note: The code assumes a local Arbitrum Nitro Testnet node running on `ws://127.0.0.1:8548` and //! a local Ethereum node on `http://localhost:8545`. Ensure that these endpoints are configured correctly. -#[allow(clippy::ignored_unit_patterns)] #[cfg(test)] mod tests { use alloy_sol_types::{sol, SolCall}; diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index 83457fbf..cc1b10ac 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -63,16 +63,6 @@ impl BlockchainConfig { metadata: None, } } - - #[must_use] - pub fn node_url(&self) -> String { - self.node_uri.with_host("rosetta.analog.one").to_string() - } - - #[must_use] - pub fn connector_url(&self) -> String { - format!("http://rosetta.analog.one:{}", self.connector_port) - } } #[derive(Clone, Debug, PartialEq, Eq)] From e045d16f308909423014c0206041355712b4c9eb Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 20 Jan 2024 19:28:34 -0300 Subject: [PATCH 13/26] Add transaction to rosetta-core --- chains/astar/server/src/lib.rs | 2 ++ chains/ethereum/server/src/lib.rs | 2 ++ chains/polkadot/server/src/lib.rs | 4 +++- rosetta-client/src/client.rs | 8 ++++++++ rosetta-core/src/lib.rs | 4 ++++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/chains/astar/server/src/lib.rs b/chains/astar/server/src/lib.rs index 547702b2..adf4f215 100644 --- a/chains/astar/server/src/lib.rs +++ b/chains/astar/server/src/lib.rs @@ -202,6 +202,8 @@ impl BlockchainClient for AstarClient { type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; + type Transaction = rosetta_config_ethereum::SignedTransaction; + fn config(&self) -> &BlockchainConfig { self.client.config() } diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index 74f50ddc..ec22ff1a 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -100,6 +100,8 @@ impl BlockchainClient for MaybeWsEthereumClient { type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; + type Transaction = rosetta_config_ethereum::SignedTransaction; + fn config(&self) -> &BlockchainConfig { match self { Self::Http(http_client) => http_client.config(), diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index 97ac4164..e1ffb993 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -154,6 +154,8 @@ impl BlockchainClient for PolkadotClient { type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; + type Transaction = Vec; + fn config(&self) -> &BlockchainConfig { &self.config } @@ -209,8 +211,8 @@ impl BlockchainClient for PolkadotClient { .parse() .map_err(|err| anyhow::anyhow!("{}", err)) .context("invalid address")?; - let signer = PairSigner::::new(AccountKeyring::Alice.pair()); + let signer = PairSigner::::new(AccountKeyring::Alice.pair()); let tx = westend_dev_metadata::tx().balances().transfer_keep_alive(address.into(), value); let hash = self .client diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index e894815b..a0b1e5c5 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -135,6 +135,12 @@ pub enum GenericBlockIdentifier { Polkadot(::BlockIdentifier), } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum GenericTransaction { + Ethereum(::Transaction), + Polkadot(::Transaction), +} + macro_rules! dispatch { ($self:tt$($method:tt)+) => { match $self { @@ -156,6 +162,8 @@ impl BlockchainClient for GenericClient { type AtBlock = GenericAtBlock; type BlockIdentifier = GenericBlockIdentifier; + type Transaction = GenericTransaction; + fn config(&self) -> &BlockchainConfig { dispatch!(self.config()) } diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index cc1b10ac..029c243e 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -110,6 +110,8 @@ pub trait BlockchainClient: Sized + Send + Sync + 'static { type AtBlock: Clone + Send + Sync + Sized + Eq + From + 'static; type BlockIdentifier: Clone + Send + Sync + Sized + Eq + 'static; + type Transaction: Clone + Send + Sync + Sized + Eq + 'static; + fn config(&self) -> &BlockchainConfig; fn genesis_block(&self) -> Self::BlockIdentifier; async fn node_version(&self) -> Result; @@ -151,6 +153,8 @@ where type AtBlock = ::AtBlock; type BlockIdentifier = ::BlockIdentifier; + type Transaction = ::Transaction; + fn config(&self) -> &BlockchainConfig { BlockchainClient::config(Self::as_ref(self)) } From 6ada030fce2bd54ffdedea7b73e3390170b33ba4 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sat, 20 Jan 2024 20:42:48 -0300 Subject: [PATCH 14/26] Remove unused client method --- chains/astar/server/README.md | 8 - chains/astar/server/src/lib.rs | 10 +- chains/ethereum/server/src/client.rs | 32 +-- chains/ethereum/server/src/eth_types.rs | 8 - chains/ethereum/server/src/lib.rs | 13 +- chains/ethereum/server/src/utils.rs | 338 +----------------------- chains/polkadot/server/README.md | 8 - chains/polkadot/server/src/lib.rs | 31 +-- rosetta-client/src/client.rs | 23 +- rosetta-client/src/wallet.rs | 28 +- rosetta-core/src/lib.rs | 17 +- 11 files changed, 19 insertions(+), 497 deletions(-) diff --git a/chains/astar/server/README.md b/chains/astar/server/README.md index 752b93ff..368d8188 100644 --- a/chains/astar/server/README.md +++ b/chains/astar/server/README.md @@ -13,7 +13,6 @@ Methods implemented are: - `metadata` - `submit` - `block` -- `block_transaction` - `call` ### `config`: @@ -42,13 +41,6 @@ Fetches account balance from on chain and returns it. It takes two arguments: This function takes `PartialBlockIdentifier` which contains a block index or hash and returns block transaction and operations happened in that transaction. -### `block_transaction`: - -This function takes: -`block`: Which is a block identifier of block from which we want to fetch transaction from. -`tx`: Transaction identifier of transaction we want to fetch. -And returns a specific transaction and its operations within specified block. - ### `faucet`: This method is used to fund an account with some amount of tokens in testnet. It takes two arguments: diff --git a/chains/astar/server/src/lib.rs b/chains/astar/server/src/lib.rs index adf4f215..199443b7 100644 --- a/chains/astar/server/src/lib.rs +++ b/chains/astar/server/src/lib.rs @@ -13,7 +13,7 @@ use rosetta_core::{ address::{Address, AddressFormat}, PublicKey, }, - types::{Block, BlockIdentifier, PartialBlockIdentifier, Transaction, TransactionIdentifier}, + types::{Block, BlockIdentifier, PartialBlockIdentifier}, BlockchainClient, BlockchainConfig, }; use rosetta_server::ws::default_client; @@ -288,14 +288,6 @@ impl BlockchainClient for AstarClient { self.client.block(block_identifier).await } - async fn block_transaction( - &self, - block_identifier: &Self::BlockIdentifier, - tx: &TransactionIdentifier, - ) -> Result { - self.client.block_transaction(block_identifier, tx).await - } - async fn call(&self, req: &EthQuery) -> Result { self.client.call(req).await } diff --git a/chains/ethereum/server/src/client.rs b/chains/ethereum/server/src/client.rs index 6b334e81..0e6028af 100644 --- a/chains/ethereum/server/src/client.rs +++ b/chains/ethereum/server/src/client.rs @@ -17,17 +17,12 @@ use rosetta_config_ethereum::{ }; use rosetta_core::{ crypto::{address::Address, PublicKey}, - types::{ - Block, BlockIdentifier, Coin, PartialBlockIdentifier, Transaction, TransactionIdentifier, - }, + types::{Block, BlockIdentifier, Coin, PartialBlockIdentifier}, BlockchainConfig, }; -use std::{ - str::FromStr, - sync::{ - atomic::{self, Ordering}, - Arc, - }, +use std::sync::{ + atomic::{self, Ordering}, + Arc, }; /// Strategy used to determine the finalized block @@ -327,25 +322,6 @@ where }) } - #[allow(clippy::missing_errors_doc)] - pub async fn block_transaction( - &self, - block: &BlockIdentifier, - tx: &TransactionIdentifier, - ) -> Result { - let tx_id = H256::from_str(&tx.hash)?; - let block = self - .client - .get_block(BlockId::Hash(H256(block.hash))) - .await? - .context("block not found")?; - let transaction = - self.client.get_transaction(tx_id).await?.context("transaction not found")?; - let transaction = - crate::utils::get_transaction(&self.client, self.config(), block, &transaction).await?; - Ok(transaction) - } - #[allow(clippy::too_many_lines, clippy::missing_errors_doc)] pub async fn call(&self, req: &EthQuery) -> Result { let result = match req { diff --git a/chains/ethereum/server/src/eth_types.rs b/chains/ethereum/server/src/eth_types.rs index e7954782..faec28bf 100644 --- a/chains/ethereum/server/src/eth_types.rs +++ b/chains/ethereum/server/src/eth_types.rs @@ -1,21 +1,13 @@ use ethers::types::{Bytes, H160, U256, U64}; use serde::{Deserialize, Serialize}; -pub const FEE_OP_TYPE: &str = "FEE"; -pub const CALL_OP_TYPE: &str = "CALL"; pub const MINING_REWARD_OP_TYPE: &str = "MINER_REWARD"; pub const UNCLE_REWARD_OP_TYPE: &str = "UNCLE_REWARD"; pub const _CALL_CODE_OP_TYPE: &str = "CALLCODE"; pub const _DELEGATE_CALL_OP_TYPE: &str = "DELEGATECALL"; pub const _STATIC_CALL_OP_TYPE: &str = "STATICCALL"; -pub const SELF_DESTRUCT_OP_TYPE: &str = "SELFDESTRUCT"; -pub const DESTRUCT_OP_TYPE: &str = "DESTRUCT"; - -pub const CREATE_OP_TYPE: &str = "CREATE"; -pub const CREATE2_OP_TYPE: &str = "CREATE2"; pub const SUCCESS_STATUS: &str = "SUCCESS"; -pub const FAILURE_STATUS: &str = "FAILURE"; pub const UNCLE_REWARD_MULTIPLIER: u64 = 32; pub const MAX_UNCLE_DEPTH: u64 = 8; diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index ec22ff1a..81d79ce3 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -6,7 +6,7 @@ pub use rosetta_config_ethereum::{ }; use rosetta_core::{ crypto::{address::Address, PublicKey}, - types::{Block, BlockIdentifier, PartialBlockIdentifier, Transaction, TransactionIdentifier}, + types::{Block, BlockIdentifier, PartialBlockIdentifier}, BlockchainClient, BlockchainConfig, }; use rosetta_server::ws::{default_client, DefaultClient}; @@ -177,17 +177,6 @@ impl BlockchainClient for MaybeWsEthereumClient { } } - async fn block_transaction( - &self, - block: &Self::BlockIdentifier, - tx: &TransactionIdentifier, - ) -> Result { - match self { - Self::Http(http_client) => http_client.block_transaction(block, tx).await, - Self::Ws(ws_client) => ws_client.block_transaction(block, tx).await, - } - } - async fn call(&self, req: &EthQuery) -> Result { match self { Self::Http(http_client) => http_client.call(req).await, diff --git a/chains/ethereum/server/src/utils.rs b/chains/ethereum/server/src/utils.rs index 3bfc42e0..9e6d4f3d 100644 --- a/chains/ethereum/server/src/utils.rs +++ b/chains/ethereum/server/src/utils.rs @@ -1,29 +1,23 @@ use crate::eth_types::{ - FlattenTrace, Trace, BYZANTIUM_BLOCK_REWARD, CALL_OP_TYPE, CONSTANTINOPLE_BLOCK_REWARD, - CREATE2_OP_TYPE, CREATE_OP_TYPE, DESTRUCT_OP_TYPE, FAILURE_STATUS, FEE_OP_TYPE, - FRONTIER_BLOCK_REWARD, MAX_UNCLE_DEPTH, MINING_REWARD_OP_TYPE, SELF_DESTRUCT_OP_TYPE, - SUCCESS_STATUS, TESTNET_CHAIN_CONFIG, UNCLE_REWARD_MULTIPLIER, UNCLE_REWARD_OP_TYPE, + BYZANTIUM_BLOCK_REWARD, CONSTANTINOPLE_BLOCK_REWARD, FRONTIER_BLOCK_REWARD, MAX_UNCLE_DEPTH, + MINING_REWARD_OP_TYPE, SUCCESS_STATUS, TESTNET_CHAIN_CONFIG, UNCLE_REWARD_MULTIPLIER, + UNCLE_REWARD_OP_TYPE, }; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use ethers::{ prelude::*, providers::Middleware, - types::{Block, Transaction, TransactionReceipt, H160, H256, U256, U64}, + types::{Block, Transaction, H256, U64}, utils::to_checksum, }; use rosetta_core::{ types::{ - self as rosetta_types, AccountIdentifier, Amount, BlockIdentifier, Currency, Operation, + self as rosetta_types, AccountIdentifier, Amount, BlockIdentifier, Operation, OperationIdentifier, TransactionIdentifier, }, BlockchainConfig, }; -use serde_json::json; -use std::{ - collections::{HashMap, VecDeque}, - str::FromStr, - sync::Arc, -}; +use std::sync::Arc; /// A block that is not pending, so it must have a valid hash and number. /// This allow skipping duplicated checks in the code @@ -75,324 +69,6 @@ where Ok(Some(block)) } -pub async fn get_transaction( - client: &Provider

, - config: &BlockchainConfig, - block: Block, - tx: &Transaction, -) -> Result { - let Some(block_hash) = block.hash else { - anyhow::bail!("Block must have a hash"); - }; - let Some(block_number) = block.number else { - anyhow::bail!("Block must have a number"); - }; - - let tx_receipt = client - .get_transaction_receipt(tx.hash) - .await? - .context("Transaction receipt not found")?; - - if tx_receipt.block_hash.context("Block hash not found in tx receipt")? != block_hash { - bail!("Transaction receipt block hash does not match block hash"); - } - let currency = config.currency(); - - let mut operations = vec![]; - let fee_ops = get_fee_operations(&block, tx, &tx_receipt, ¤cy)?; - operations.extend(fee_ops); - - let tx_trace = if block_number.is_zero() { - None - } else { - let trace = get_transaction_trace(&tx.hash, client).await?; - let trace_ops = get_trace_operations( - trace.clone(), - i64::try_from(operations.len()).context("operations overflow")?, - ¤cy, - )?; - operations.extend(trace_ops); - Some(trace) - }; - - Ok(rosetta_types::Transaction { - transaction_identifier: TransactionIdentifier { hash: hex::encode(tx.hash) }, - operations, - related_transactions: None, - metadata: Some(json!({ - "gas_limit" : tx.gas, - "gas_price": tx.gas_price, - "receipt": tx_receipt, - "trace": tx_trace, - })), - }) -} - -fn get_fee_operations( - block: &Block, - tx: &Transaction, - receipt: &TransactionReceipt, - currency: &Currency, -) -> Result> { - let miner = block.author.context("block has no author")?; - let base_fee = block.base_fee_per_gas.context("block has no base fee")?; - let tx_type = tx.transaction_type.context("transaction type unavailable")?; - let tx_gas_price = tx.gas_price.context("gas price is not available")?; - let tx_max_priority_fee_per_gas = tx.max_priority_fee_per_gas.unwrap_or_default(); - let gas_used = receipt.gas_used.context("gas used is not available")?; - let gas_price = - if tx_type.as_u64() == 2 { base_fee + tx_max_priority_fee_per_gas } else { tx_gas_price }; - let fee_amount = gas_used * gas_price; - let fee_burned = gas_used * base_fee; - let miner_earned_reward = fee_amount - fee_burned; - - let mut operations = vec![]; - - let first_op = Operation { - operation_identifier: OperationIdentifier { index: 0, network_index: None }, - related_operations: None, - r#type: FEE_OP_TYPE.into(), - status: Some(SUCCESS_STATUS.into()), - account: Some(AccountIdentifier { - address: to_checksum(&tx.from, None), - sub_account: None, - metadata: None, - }), - amount: Some(Amount { - value: format!("-{miner_earned_reward}"), - currency: currency.clone(), - metadata: None, - }), - coin_change: None, - metadata: None, - }; - - let second_op = Operation { - operation_identifier: OperationIdentifier { index: 1, network_index: None }, - related_operations: Some(vec![OperationIdentifier { index: 0, network_index: None }]), - r#type: FEE_OP_TYPE.into(), - status: Some(SUCCESS_STATUS.into()), - account: Some(AccountIdentifier { - address: to_checksum(&miner, None), - sub_account: None, - metadata: None, - }), - amount: Some(Amount { - value: format!("{miner_earned_reward}"), - currency: currency.clone(), - metadata: None, - }), - coin_change: None, - metadata: None, - }; - - operations.push(first_op); - operations.push(second_op); - - if fee_burned != U256::from(0) { - let burned_operation = Operation { - operation_identifier: OperationIdentifier { index: 2, network_index: None }, - related_operations: None, - r#type: FEE_OP_TYPE.into(), - status: Some(SUCCESS_STATUS.into()), - account: Some(AccountIdentifier { - address: to_checksum(&tx.from, None), - sub_account: None, - metadata: None, - }), - amount: Some(Amount { - value: format!("-{fee_burned}"), - currency: currency.clone(), - metadata: None, - }), - coin_change: None, - metadata: None, - }; - - operations.push(burned_operation); - } - Ok(operations) -} - -async fn get_transaction_trace( - hash: &H256, - client: &Provider

, -) -> Result { - let params = json!([ - hash, - { - "tracer": "callTracer" - } - ]); - Ok(client.request("debug_traceTransaction", params).await?) -} - -#[allow(clippy::too_many_lines)] -fn get_trace_operations(trace: Trace, op_len: i64, currency: &Currency) -> Result> { - let mut traces = VecDeque::new(); - traces.push_back(trace); - let mut flatten_traces = vec![]; - while let Some(mut trace) = traces.pop_front() { - for mut child in std::mem::take(&mut trace.calls) { - if trace.revert { - child.revert = true; - if child.error_message.is_empty() { - child.error_message = trace.error_message.clone(); - } - } - traces.push_back(child); - } - flatten_traces.push(FlattenTrace::from(trace)); - } - let traces = flatten_traces; - - let mut operations: Vec = vec![]; - let mut destroyed_accs: HashMap = HashMap::new(); - - if traces.is_empty() { - return Ok(operations); - } - - for trace in traces { - let operation_status = if trace.revert { FAILURE_STATUS } else { SUCCESS_STATUS }; - - let zero_value = trace.value.is_zero(); - - let should_add = !(zero_value && trace.trace_type == CALL_OP_TYPE); - - let from = to_checksum(&trace.from, None); - let to = to_checksum(&trace.to, None); - - if should_add { - let mut from_operation = Operation { - operation_identifier: OperationIdentifier { - index: op_len + - i64::try_from(operations.len()).context("operation.index overflow")?, - network_index: None, - }, - related_operations: None, - r#type: trace.trace_type.clone(), - status: Some(operation_status.into()), - account: Some(AccountIdentifier { - address: from.clone(), - sub_account: None, - metadata: None, - }), - amount: Some(Amount { - value: format!("-{}", trace.value), - currency: currency.clone(), - metadata: None, - }), - coin_change: None, - metadata: None, - }; - - if zero_value { - from_operation.amount = None; - } else if let Some(d_from) = destroyed_accs.get(&from) { - if operation_status == SUCCESS_STATUS { - let amount = d_from - trace.value.as_u64(); - destroyed_accs.insert(from.clone(), amount); - } - } - - operations.push(from_operation); - } - - if trace.trace_type == SELF_DESTRUCT_OP_TYPE { - //assigning destroyed from to an empty number - if from == to { - continue; - } - } - - if to.is_empty() { - continue; - } - - // If the account is resurrected, we remove it from - // the destroyed accounts map. - if trace.trace_type == CREATE_OP_TYPE || trace.trace_type == CREATE2_OP_TYPE { - destroyed_accs.remove(&to); - } - - if should_add { - let last_op_index = operations[operations.len() - 1].operation_identifier.index; - let mut to_op = Operation { - operation_identifier: OperationIdentifier { - index: last_op_index + 1, - network_index: None, - }, - related_operations: Some(vec![OperationIdentifier { - index: last_op_index, - network_index: None, - }]), - r#type: trace.trace_type, - status: Some(operation_status.into()), - account: Some(AccountIdentifier { - address: to.clone(), - sub_account: None, - metadata: None, - }), - amount: Some(Amount { - value: format!("{}", trace.value), - currency: currency.clone(), - metadata: None, - }), - coin_change: None, - metadata: None, - }; - - if zero_value { - to_op.amount = None; - } else if let Some(d_to) = destroyed_accs.get(&to) { - if operation_status == SUCCESS_STATUS { - let amount = d_to + trace.value.as_u64(); - destroyed_accs.insert(to.clone(), amount); - } - } - - operations.push(to_op); - } - - for (k, v) in &destroyed_accs { - if v == &0 { - continue; - } - - if v < &0 { - //throw some error - } - - let operation = Operation { - operation_identifier: OperationIdentifier { - index: operations[operations.len() - 1].operation_identifier.index + 1, - network_index: None, - }, - related_operations: None, - r#type: DESTRUCT_OP_TYPE.into(), - status: Some(SUCCESS_STATUS.into()), - account: Some(AccountIdentifier { - address: to_checksum(&H160::from_str(k)?, None), - sub_account: None, - metadata: None, - }), - amount: Some(Amount { - value: format!("-{v}"), - currency: currency.clone(), - metadata: None, - }), - coin_change: None, - metadata: None, - }; - - operations.push(operation); - } - } - - Ok(operations) -} - pub async fn block_reward_transaction( client: &Provider

, config: &BlockchainConfig, diff --git a/chains/polkadot/server/README.md b/chains/polkadot/server/README.md index 752b93ff..368d8188 100644 --- a/chains/polkadot/server/README.md +++ b/chains/polkadot/server/README.md @@ -13,7 +13,6 @@ Methods implemented are: - `metadata` - `submit` - `block` -- `block_transaction` - `call` ### `config`: @@ -42,13 +41,6 @@ Fetches account balance from on chain and returns it. It takes two arguments: This function takes `PartialBlockIdentifier` which contains a block index or hash and returns block transaction and operations happened in that transaction. -### `block_transaction`: - -This function takes: -`block`: Which is a block identifier of block from which we want to fetch transaction from. -`tx`: Transaction identifier of transaction we want to fetch. -And returns a specific transaction and its operations within specified block. - ### `faucet`: This method is used to fund an account with some amount of tokens in testnet. It takes two arguments: diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index e1ffb993..cc76c0d7 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -4,10 +4,7 @@ use rosetta_config_polkadot::metadata::westend::dev as westend_dev_metadata; pub use rosetta_config_polkadot::{PolkadotMetadata, PolkadotMetadataParams}; use rosetta_core::{ crypto::{address::Address, PublicKey}, - types::{ - Block, BlockIdentifier, CallRequest, PartialBlockIdentifier, Transaction, - TransactionIdentifier, - }, + types::{Block, BlockIdentifier, CallRequest, PartialBlockIdentifier}, BlockchainClient, BlockchainConfig, EmptyEventStream, }; use rosetta_server::ws::default_client; @@ -25,12 +22,11 @@ use subxt::{ blocks::BlockRef, config::{ substrate::{H256, U256}, - Hasher, Header, + Header, }, // dynamic::Value as SubtxValue, tx::{PairSigner, SubmittableExtrinsic}, utils::{AccountId32, MultiAddress}, - Config, OnlineClient, PolkadotConfig, }; @@ -308,29 +304,6 @@ impl BlockchainClient for PolkadotClient { }) } - async fn block_transaction( - &self, - block_identifier: &BlockIdentifier, - transaction_identifier: &TransactionIdentifier, - ) -> Result { - let block_hash = ::Hash::from(block_identifier.hash); - let transaction_hash = transaction_identifier.hash.parse()?; - let block = self.client.blocks().at(block_hash).await?; - let extrinsic = block - .extrinsics() - .await? - .iter() - .filter_map(Result::ok) - .find(|extrinsic| { - ::Hasher::hash_of(&extrinsic.bytes()) == transaction_hash - }) - .context("transaction not found")?; - - let identifier = crate::block::get_transaction_identifier(&extrinsic); - let events = extrinsic.events().await?; - crate::block::get_transaction(self.config(), identifier, &events) - } - async fn call(&self, request: &CallRequest) -> Result { let call_details = request.method.split('-').collect::>(); if call_details.len() != 3 { diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index a0b1e5c5..722bb596 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -1,7 +1,7 @@ #![allow(missing_docs)] use crate::{ crypto::{address::Address, PublicKey}, - types::{Block, CallRequest, Transaction, TransactionIdentifier}, + types::{Block, CallRequest}, Blockchain, BlockchainConfig, }; use anyhow::Result; @@ -272,27 +272,6 @@ impl BlockchainClient for GenericClient { } } - async fn block_transaction( - &self, - block: &Self::BlockIdentifier, - tx: &TransactionIdentifier, - ) -> Result { - match self { - Self::Ethereum(client) => match block { - Self::BlockIdentifier::Ethereum(block) => client.block_transaction(block, tx).await, - Self::BlockIdentifier::Polkadot(_) => anyhow::bail!("invalid block identifier"), - }, - Self::Astar(client) => match block { - Self::BlockIdentifier::Ethereum(block) => client.block_transaction(block, tx).await, - Self::BlockIdentifier::Polkadot(_) => anyhow::bail!("invalid block identifier"), - }, - Self::Polkadot(client) => match block { - Self::BlockIdentifier::Polkadot(block) => client.block_transaction(block, tx).await, - Self::BlockIdentifier::Ethereum(_) => anyhow::bail!("invalid block identifier"), - }, - } - } - async fn call(&self, req: &GenericCall) -> Result { let result = match self { Self::Ethereum(client) => match req { diff --git a/rosetta-client/src/wallet.rs b/rosetta-client/src/wallet.rs index fdaa87c5..49628433 100644 --- a/rosetta-client/src/wallet.rs +++ b/rosetta-client/src/wallet.rs @@ -4,12 +4,12 @@ use crate::{ mnemonic::MnemonicStore, signer::{RosettaAccount, RosettaPublicKey, Signer}, tx_builder::GenericTransactionBuilder, - types::{AccountIdentifier, Amount, BlockIdentifier, PublicKey, TransactionIdentifier}, + types::{AccountIdentifier, Amount, BlockIdentifier, PublicKey}, Blockchain, BlockchainConfig, }; use anyhow::Result; use rosetta_core::{ - types::{Block, PartialBlockIdentifier, Transaction}, + types::{Block, PartialBlockIdentifier}, BlockchainClient, RosettaAlgorithm, }; use rosetta_server_ethereum::config::{ @@ -167,30 +167,6 @@ impl Wallet { } } - /// Returns transactions included in a block - /// Parameters: - /// 1. `block_identifier`: `BlockIdentifier` containing block number and hash - /// 2. `tx_identifier`: `TransactionIdentifier` containing hash of transaction - #[allow(clippy::missing_errors_doc)] - pub async fn block_transaction( - &self, - block_identifer: BlockIdentifier, - tx_identifier: TransactionIdentifier, - ) -> Result { - match &self.client { - GenericClient::Astar(client) => { - client.block_transaction(&block_identifer, &tx_identifier).await - }, - GenericClient::Ethereum(client) => { - client.block_transaction(&block_identifer, &tx_identifier).await - }, - GenericClient::Polkadot(client) => { - client.block_transaction(&block_identifer, &tx_identifier).await - }, - } - // self.client.block_transaction(&block_identifer, &tx_identifier).await - } - /// Returns the on chain metadata. /// Parameters: /// - `metadata_params`: the metadata parameters which we got from transaction builder. diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index 029c243e..b0ade86e 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -6,10 +6,7 @@ use crate::{ address::{Address, AddressFormat}, Algorithm, PublicKey, SecretKey, }, - types::{ - Block, BlockIdentifier, Currency, CurveType, NetworkIdentifier, SignatureType, Transaction, - TransactionIdentifier, - }, + types::{Block, BlockIdentifier, Currency, CurveType, NetworkIdentifier, SignatureType}, }; use anyhow::Result; use async_trait::async_trait; @@ -126,11 +123,6 @@ pub trait BlockchainClient: Sized + Send + Sync + 'static { ) -> Result; async fn submit(&self, transaction: &[u8]) -> Result>; async fn block(&self, block: &Self::AtBlock) -> Result; - async fn block_transaction( - &self, - block: &Self::BlockIdentifier, - tx: &TransactionIdentifier, - ) -> Result; async fn call(&self, req: &Self::Call) -> Result; /// Return a stream of events, return None if the blockchain doesn't support events. @@ -189,13 +181,6 @@ where async fn block(&self, block: &Self::AtBlock) -> Result { BlockchainClient::block(Self::as_ref(self), block).await } - async fn block_transaction( - &self, - block: &Self::BlockIdentifier, - tx: &TransactionIdentifier, - ) -> Result { - BlockchainClient::block_transaction(Self::as_ref(self), block, tx).await - } async fn call(&self, req: &Self::Call) -> Result { BlockchainClient::call(Self::as_ref(self), req).await } From 165698336efd51cff589b2b7da44727ce1517c8c Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sun, 21 Jan 2024 13:29:01 -0300 Subject: [PATCH 15/26] Remove comment --- rosetta-docker/src/lib.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/rosetta-docker/src/lib.rs b/rosetta-docker/src/lib.rs index 21930627..ca0348dd 100644 --- a/rosetta-docker/src/lib.rs +++ b/rosetta-docker/src/lib.rs @@ -418,22 +418,6 @@ pub mod tests { Ok(()) } - // === - // // Check if the finalized block is consistent - // let expected_finalized = client.finalized_block().await?; - // let actual_finalized = client - // .block(&PartialBlockIdentifier { - // index: None, - // hash: Some(expected_finalized.hash.clone()), - // }) - // .await? - // .block_identifier; - // assert_eq!(expected_finalized, actual_finalized); - // Ok(()) - // }) - // .await; - // >>> master - #[allow( clippy::missing_panics_doc, clippy::unwrap_used, From e8839408b04aa8f9a51b27008fe61662d6f622d5 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sun, 21 Jan 2024 19:11:45 -0300 Subject: [PATCH 16/26] Add query to polkadot and ethereum --- Cargo.lock | 14 ++-- chains/astar/server/src/lib.rs | 1 + chains/ethereum/config/src/types.rs | 4 + chains/ethereum/server/src/lib.rs | 1 + chains/polkadot/config/src/lib.rs | 10 ++- chains/polkadot/server/src/lib.rs | 26 +++++++ chains/polkadot/server/src/types.rs | 117 ++++++++++++++++++++++++++++ rosetta-client/src/client.rs | 1 + rosetta-core/Cargo.toml | 2 +- rosetta-core/src/lib.rs | 2 + rosetta-core/src/traits.rs | 13 ++-- 11 files changed, 176 insertions(+), 15 deletions(-) create mode 100644 chains/polkadot/server/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index c61a6b96..f5c19dfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4738,9 +4738,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -4937,13 +4937,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.4", "regex-syntax 0.8.2", ] @@ -4958,9 +4958,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" dependencies = [ "aho-corasick", "memchr", diff --git a/chains/astar/server/src/lib.rs b/chains/astar/server/src/lib.rs index 199443b7..54eccd05 100644 --- a/chains/astar/server/src/lib.rs +++ b/chains/astar/server/src/lib.rs @@ -202,6 +202,7 @@ impl BlockchainClient for AstarClient { type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; + type Query = rosetta_config_ethereum::Query; type Transaction = rosetta_config_ethereum::SignedTransaction; fn config(&self) -> &BlockchainConfig { diff --git a/chains/ethereum/config/src/types.rs b/chains/ethereum/config/src/types.rs index 49dbb314..9383aebf 100644 --- a/chains/ethereum/config/src/types.rs +++ b/chains/ethereum/config/src/types.rs @@ -205,6 +205,10 @@ pub enum Query { ChainId, } +impl rosetta_core::traits::Query for Query { + type Result = QueryResult; +} + /// The result of contract call execution #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))] diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index 81d79ce3..0d698c54 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -100,6 +100,7 @@ impl BlockchainClient for MaybeWsEthereumClient { type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; + type Query = EthQuery; type Transaction = rosetta_config_ethereum::SignedTransaction; fn config(&self) -> &BlockchainConfig { diff --git a/chains/polkadot/config/src/lib.rs b/chains/polkadot/config/src/lib.rs index b93e6d0a..337763e7 100644 --- a/chains/polkadot/config/src/lib.rs +++ b/chains/polkadot/config/src/lib.rs @@ -15,13 +15,19 @@ use subxt::ext::sp_core::crypto::Ss58AddressFormat; pub mod metadata { #[cfg(feature = "polkadot-metadata")] pub mod polkadot { - #[subxt::subxt(runtime_metadata_path = "res/polkadot-v1000001.scale")] + #[subxt::subxt( + runtime_metadata_path = "res/polkadot-v1000001.scale", + derive_for_all_types = "Clone, Eq, PartialEq" + )] pub mod dev {} } #[cfg(feature = "westend-metadata")] pub mod westend { - #[subxt::subxt(runtime_metadata_path = "res/westend-dev-v1.5.0.scale")] + #[subxt::subxt( + runtime_metadata_path = "res/westend-dev-v1.5.0.scale", + derive_for_all_types = "Clone, Eq, PartialEq" + )] pub mod dev {} } } diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index cc76c0d7..6f65485f 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -33,6 +33,7 @@ use subxt::{ mod block; mod call; +mod types; pub struct PolkadotClient { config: BlockchainConfig, @@ -139,6 +140,30 @@ impl PolkadotClient { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WestendDevConfig; + +impl types::ClientConfig for WestendDevConfig { + type Hash = ::Hash; + + type AccountId = ::AccountId; + + type Address = ::Address; + + type Signature = ::Signature; + + type Hasher = ::Hasher; + + type Header = ::Header; + + type ExtrinsicParams = + subxt::config::polkadot::PolkadotExtrinsicParams>; + + type AssetId = ::AssetId; + + type AccountInfo = (); +} + #[async_trait::async_trait] impl BlockchainClient for PolkadotClient { type MetadataParams = PolkadotMetadataParams; @@ -150,6 +175,7 @@ impl BlockchainClient for PolkadotClient { type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; + type Query = types::Query; type Transaction = Vec; fn config(&self) -> &BlockchainConfig { diff --git a/chains/polkadot/server/src/types.rs b/chains/polkadot/server/src/types.rs new file mode 100644 index 00000000..584ed4bb --- /dev/null +++ b/chains/polkadot/server/src/types.rs @@ -0,0 +1,117 @@ +use rosetta_core::traits::Member; +use std::{fmt::Debug, marker::PhantomData}; +// use rosetta_config_polkadot::metadata::westend::dev as westend_dev_metadata; +use subxt::{ + config::{ExtrinsicParams, Hasher, Header}, + ext::{codec::Encode, scale_decode::DecodeAsType, scale_encode::EncodeAsType}, + Config as SubxtConfig, +}; + +pub trait ClientConfig: Debug + Clone + PartialEq + Eq + Sized + Send + Sync + 'static { + /// The output of the `Hasher` function. + type Hash: subxt::config::BlockHash; + + /// The account ID type. + type AccountId: Member + Encode; + + /// The address type. + type Address: Member + Encode + From; + + /// The signature type. + type Signature: Member + Encode; + + /// The hashing system (algorithm) being used in the runtime (e.g. Blake2). + type Hasher: Debug + Hasher; + + /// The block header. + type Header: Member + Header + Send + serde::de::DeserializeOwned; + + /// This type defines the extrinsic extra and additional parameters. + type ExtrinsicParams: ExtrinsicParams>; + + /// This is used to identify an asset in the `ChargeAssetTxPayment` signed extension. + type AssetId: Debug + Clone + Encode + DecodeAsType + EncodeAsType; + + type AccountInfo: Member; +} + +pub struct SubxtConfigAdapter(PhantomData); + +impl SubxtConfig for SubxtConfigAdapter +where + T: ClientConfig, +{ + type Hash = ::Hash; + + type AccountId = ::AccountId; + + type Address = ::Address; + + type Signature = ::Signature; + + type Hasher = ::Hasher; + + type Header = ::Header; + + type ExtrinsicParams = ::ExtrinsicParams; + + type AssetId = ::AssetId; +} + +// // westend_dev_metadata::runtime_types::westend_runtime:: + +// /// A concrete storage address. This can be created from static values (ie those generated +// /// via the `subxt` macro) or dynamic values via [`dynamic`]. +// #[derive(Derivative)] +// pub struct Address { +// pallet_name: Cow<'static, str>, +// entry_name: Cow<'static, str>, +// storage_entry_keys: Vec, +// validation_hash: Option<[u8; 32]>, +// _marker: std::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>, +// } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StorageQuery { + /// Version of the runtime specification. + spec_version: Option, + // Raw storage-key + address: Vec, +} + +// type AccountId = ::subxt::utils::AccountId32; +// type AccountData = +// westend_dev_metadata::runtime_types::pallet_balances::types::AccountData; type AccountInfo +// = westend_dev_metadata::runtime_types::frame_system::AccountInfo; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BlockIdentifier { + Number(u64), + Hash(BlockHash), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Query { + AccountInfo(T::AccountId), + GetBlock(BlockIdentifier), + Storage(StorageQuery), +} + +impl rosetta_core::traits::Query for Query { + type Result = QueryResult; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum QueryResult { + AccountInfo(T::AccountInfo), + GetBlock(u32), + Storage(Vec), +} + +// pub fn teste() { +// let res = westend_dev_metadata::storage().balances().account(0); +// westend_dev_metadata::apis(). +// ::subxt::storage::address::Address; + +// ::subxt::storage::address::StaticStorageMapKey; +// } diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index 722bb596..8d6a5315 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -162,6 +162,7 @@ impl BlockchainClient for GenericClient { type AtBlock = GenericAtBlock; type BlockIdentifier = GenericBlockIdentifier; + type Query = (); type Transaction = GenericTransaction; fn config(&self) -> &BlockchainConfig { diff --git a/rosetta-core/Cargo.toml b/rosetta-core/Cargo.toml index 3fd1ef6b..9a3efbb8 100644 --- a/rosetta-core/Cargo.toml +++ b/rosetta-core/Cargo.toml @@ -9,7 +9,7 @@ description = "Provides traits and definitions shared by the server and client c [dependencies] anyhow = "1.0" async-trait = "0.1" -fluent-uri = "0.1.4" +fluent-uri = "0.1" futures-util = "0.3" rosetta-crypto.workspace = true rosetta-types.workspace = true diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index b0ade86e..facaa7a6 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -107,6 +107,7 @@ pub trait BlockchainClient: Sized + Send + Sync + 'static { type AtBlock: Clone + Send + Sync + Sized + Eq + From + 'static; type BlockIdentifier: Clone + Send + Sync + Sized + Eq + 'static; + type Query: traits::Query; type Transaction: Clone + Send + Sync + Sized + Eq + 'static; fn config(&self) -> &BlockchainConfig; @@ -145,6 +146,7 @@ where type AtBlock = ::AtBlock; type BlockIdentifier = ::BlockIdentifier; + type Query = ::Query; type Transaction = ::Transaction; fn config(&self) -> &BlockchainConfig { diff --git a/rosetta-core/src/traits.rs b/rosetta-core/src/traits.rs index af1c3a01..0d361a4d 100644 --- a/rosetta-core/src/traits.rs +++ b/rosetta-core/src/traits.rs @@ -44,8 +44,8 @@ impl_maybe_marker!( ); /// A type that can be used in runtime structures. -pub trait Member: Send + Sync + Sized + Debug + Eq + PartialEq + Clone + 'static {} -impl Member for T {} +pub trait Member: Debug + Send + Sync + Sized + PartialEq + Eq + Clone + 'static {} +impl Member for T {} /// Super trait with all the attributes for a hashing output. pub trait HashOutput: @@ -151,9 +151,12 @@ pub trait BlockchainConfig { type UnsignedTransaction: Clone + Send + Sync + 'static; } -pub trait Query { - type Params: Send + Sync + 'static; - type Result: Send + Sync + 'static; +pub trait Query: Member { + type Result: Member; +} + +impl Query for () { + type Result = (); } // pub enum QueryEnum { From 763a9164b9d79e252f45c098ab948bd01494be3b Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Sun, 21 Jan 2024 19:39:14 -0300 Subject: [PATCH 17/26] Remove node_version, add query --- chains/astar/server/src/lib.rs | 11 +++++++---- chains/ethereum/server/src/client.rs | 5 ----- chains/ethereum/server/src/lib.rs | 17 ++++++++++------- chains/polkadot/server/src/client.rs | 8 ++++++++ chains/polkadot/server/src/lib.rs | 13 +++++++++---- rosetta-client/src/client.rs | 11 +++++++---- rosetta-core/src/lib.rs | 10 ++++++---- 7 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 chains/polkadot/server/src/client.rs diff --git a/chains/astar/server/src/lib.rs b/chains/astar/server/src/lib.rs index 54eccd05..47d65897 100644 --- a/chains/astar/server/src/lib.rs +++ b/chains/astar/server/src/lib.rs @@ -205,6 +205,13 @@ impl BlockchainClient for AstarClient { type Query = rosetta_config_ethereum::Query; type Transaction = rosetta_config_ethereum::SignedTransaction; + async fn query( + &self, + query: Self::Query, + ) -> Result<::Result> { + self.client.query(query).await + } + fn config(&self) -> &BlockchainConfig { self.client.config() } @@ -213,10 +220,6 @@ impl BlockchainClient for AstarClient { self.client.genesis_block() } - async fn node_version(&self) -> Result { - self.client.node_version().await - } - async fn current_block(&self) -> Result { self.client.current_block().await } diff --git a/chains/ethereum/server/src/client.rs b/chains/ethereum/server/src/client.rs index 0e6028af..bdc6d1a3 100644 --- a/chains/ethereum/server/src/client.rs +++ b/chains/ethereum/server/src/client.rs @@ -108,11 +108,6 @@ where self.genesis_block.identifier.clone() } - #[allow(clippy::missing_errors_doc)] - pub async fn node_version(&self) -> Result { - Ok(self.client.client_version().await?) - } - #[allow(clippy::missing_errors_doc)] pub async fn current_block(&self) -> Result { let index = self.client.get_block_number().await?.as_u64(); diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index 0d698c54..9b4e1573 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -103,6 +103,16 @@ impl BlockchainClient for MaybeWsEthereumClient { type Query = EthQuery; type Transaction = rosetta_config_ethereum::SignedTransaction; + async fn query( + &self, + query: Self::Query, + ) -> Result<::Result> { + match self { + Self::Http(http_client) => http_client.call(&query).await, + Self::Ws(ws_client) => ws_client.call(&query).await, + } + } + fn config(&self) -> &BlockchainConfig { match self { Self::Http(http_client) => http_client.config(), @@ -117,13 +127,6 @@ impl BlockchainClient for MaybeWsEthereumClient { } } - async fn node_version(&self) -> Result { - match self { - Self::Http(http_client) => http_client.node_version().await, - Self::Ws(ws_client) => ws_client.node_version().await, - } - } - async fn current_block(&self) -> Result { match self { Self::Http(http_client) => http_client.current_block().await, diff --git a/chains/polkadot/server/src/client.rs b/chains/polkadot/server/src/client.rs new file mode 100644 index 00000000..80194e0e --- /dev/null +++ b/chains/polkadot/server/src/client.rs @@ -0,0 +1,8 @@ +use crate::types::ClientConfig; + +pub struct PolkadotClient { + config: BlockchainConfig, + client: OnlineClient, + rpc_methods: LegacyRpcMethods, + genesis_block: BlockIdentifier, +} diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index 6f65485f..b2aad89f 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -33,6 +33,7 @@ use subxt::{ mod block; mod call; +// mod client; mod types; pub struct PolkadotClient { @@ -178,6 +179,14 @@ impl BlockchainClient for PolkadotClient { type Query = types::Query; type Transaction = Vec; + async fn query( + &self, + _query: Self::Query, + ) -> Result<::Result> { + + anyhow::bail!("unsupported query"); + } + fn config(&self) -> &BlockchainConfig { &self.config } @@ -186,10 +195,6 @@ impl BlockchainClient for PolkadotClient { self.genesis_block.clone() } - async fn node_version(&self) -> Result { - self.rpc_methods.system_version().await.map_err(anyhow::Error::from) - } - async fn current_block(&self) -> Result { let block = self.rpc_methods.chain_get_block(None).await?.context("no current block")?; let index = u64::from(block.block.header.number); diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index 8d6a5315..9c059aed 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -165,6 +165,13 @@ impl BlockchainClient for GenericClient { type Query = (); type Transaction = GenericTransaction; + async fn query( + &self, + _query: Self::Query, + ) -> Result<::Result> { + anyhow::bail!("unsupported query"); + } + fn config(&self) -> &BlockchainConfig { dispatch!(self.config()) } @@ -178,10 +185,6 @@ impl BlockchainClient for GenericClient { } } - async fn node_version(&self) -> Result { - dispatch!(self.node_version().await) - } - async fn current_block(&self) -> Result { // dispatch!(self.current_block().await) match self { diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index facaa7a6..e3a16fdd 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -110,9 +110,10 @@ pub trait BlockchainClient: Sized + Send + Sync + 'static { type Query: traits::Query; type Transaction: Clone + Send + Sync + Sized + Eq + 'static; + async fn query(&self, query: Self::Query) -> Result<::Result>; + fn config(&self) -> &BlockchainConfig; fn genesis_block(&self) -> Self::BlockIdentifier; - async fn node_version(&self) -> Result; async fn current_block(&self) -> Result; async fn finalized_block(&self) -> Result; async fn balance(&self, address: &Address, block: &Self::AtBlock) -> Result; @@ -149,15 +150,16 @@ where type Query = ::Query; type Transaction = ::Transaction; + async fn query(&self, query: Self::Query) -> Result<::Result> { + BlockchainClient::query(Self::as_ref(self), query).await + } + fn config(&self) -> &BlockchainConfig { BlockchainClient::config(Self::as_ref(self)) } fn genesis_block(&self) -> Self::BlockIdentifier { BlockchainClient::genesis_block(Self::as_ref(self)) } - async fn node_version(&self) -> Result { - BlockchainClient::node_version(Self::as_ref(self)).await - } async fn current_block(&self) -> Result { BlockchainClient::current_block(Self::as_ref(self)).await } From 6e5611e0302203f1cb821e80ee9665676e1c178d Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 02:47:47 -0300 Subject: [PATCH 18/26] Create generic substrate client --- chains/polkadot/server/Cargo.toml | 2 +- chains/polkadot/server/src/chains.rs | 6 + chains/polkadot/server/src/chains/polkadot.rs | 60 +++++++++ chains/polkadot/server/src/chains/westend.rs | 60 +++++++++ chains/polkadot/server/src/client.rs | 117 +++++++++++++++++- chains/polkadot/server/src/lib.rs | 32 +---- chains/polkadot/server/src/types.rs | 62 +++++----- 7 files changed, 275 insertions(+), 64 deletions(-) create mode 100644 chains/polkadot/server/src/chains.rs create mode 100644 chains/polkadot/server/src/chains/polkadot.rs create mode 100644 chains/polkadot/server/src/chains/westend.rs diff --git a/chains/polkadot/server/Cargo.toml b/chains/polkadot/server/Cargo.toml index 541d89e2..e1dbc52e 100644 --- a/chains/polkadot/server/Cargo.toml +++ b/chains/polkadot/server/Cargo.toml @@ -11,7 +11,7 @@ anyhow = "1.0" async-trait = "0.1" hex = "0.4" parity-scale-codec = { workspace = true, features = ["derive"] } -rosetta-config-polkadot.workspace = true +rosetta-config-polkadot = { workspace = true, features = ["polkadot-metadata", "westend-metadata"] } rosetta-core.workspace = true rosetta-server = { workspace = true, default-features = false, features = ["ws", "webpki-tls"] } scale-info.workspace = true diff --git a/chains/polkadot/server/src/chains.rs b/chains/polkadot/server/src/chains.rs new file mode 100644 index 00000000..7925a590 --- /dev/null +++ b/chains/polkadot/server/src/chains.rs @@ -0,0 +1,6 @@ +mod polkadot; +mod westend; + +#[allow(unused_imports)] +pub use polkadot::PolkadotConfig; +pub use westend::WestendDevConfig; diff --git a/chains/polkadot/server/src/chains/polkadot.rs b/chains/polkadot/server/src/chains/polkadot.rs new file mode 100644 index 00000000..863a8c2f --- /dev/null +++ b/chains/polkadot/server/src/chains/polkadot.rs @@ -0,0 +1,60 @@ +use crate::types::{ClientConfig, SubxtConfigAdapter}; +use rosetta_config_polkadot::metadata::polkadot::dev; +use std::borrow::Borrow; +use subxt::{ + config::polkadot, + storage::address, + utils::{AccountId32, MultiAddress}, +}; + +pub type Config = SubxtConfigAdapter; +pub type ExtrinsicParams = polkadot::PolkadotExtrinsicParams; +pub type OtherParams = >::OtherParams; +pub type PairSigner = subxt::tx::PairSigner; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PolkadotConfig; + +impl ClientConfig for PolkadotConfig { + type Hash = ::Hash; + type AccountId = ::AccountId; + type Address = ::Address; + type Signature = ::Signature; + type Hasher = ::Hasher; + type Header = ::Header; + type OtherParams = OtherParams; + type ExtrinsicParams = ExtrinsicParams; + type AssetId = ::AssetId; + + type AccountInfo = dev::runtime_types::frame_system::AccountInfo< + u32, + dev::runtime_types::pallet_balances::types::AccountData, + >; + + type TransferKeepAlive = dev::balances::calls::types::TransferKeepAlive; + + type Pair = PairSigner; + + fn account_info( + account: impl Borrow, + ) -> address::Address< + address::StaticStorageMapKey, + Self::AccountInfo, + address::Yes, + address::Yes, + (), + > { + dev::storage().system().account(account) + } + + fn transfer_keep_alive( + dest: MultiAddress, + value: u128, + ) -> ::subxt::tx::Payload { + dev::tx().balances().transfer_keep_alive(dest, value) + } + + fn other_params() -> OtherParams { + OtherParams::default() + } +} diff --git a/chains/polkadot/server/src/chains/westend.rs b/chains/polkadot/server/src/chains/westend.rs new file mode 100644 index 00000000..26503e94 --- /dev/null +++ b/chains/polkadot/server/src/chains/westend.rs @@ -0,0 +1,60 @@ +use crate::types::{ClientConfig, SubxtConfigAdapter}; +use rosetta_config_polkadot::metadata::westend::dev; +use std::borrow::Borrow; +use subxt::{ + config::{polkadot::PolkadotExtrinsicParams, PolkadotConfig}, + storage::address, + utils::{AccountId32, MultiAddress}, +}; + +pub type Config = SubxtConfigAdapter; +pub type ExtrinsicParams = PolkadotExtrinsicParams; +pub type OtherParams = >::OtherParams; +pub type PairSigner = subxt::tx::PairSigner; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WestendDevConfig; + +impl ClientConfig for WestendDevConfig { + type Hash = ::Hash; + type AccountId = ::AccountId; + type Address = ::Address; + type Signature = ::Signature; + type Hasher = ::Hasher; + type Header = ::Header; + type OtherParams = OtherParams; + type ExtrinsicParams = ExtrinsicParams; + type AssetId = ::AssetId; + + type AccountInfo = dev::runtime_types::frame_system::AccountInfo< + u32, + dev::runtime_types::pallet_balances::types::AccountData, + >; + + type TransferKeepAlive = dev::balances::calls::types::TransferKeepAlive; + + type Pair = PairSigner; + + fn account_info( + account: impl Borrow, + ) -> address::Address< + address::StaticStorageMapKey, + Self::AccountInfo, + address::Yes, + address::Yes, + (), + > { + dev::storage().system().account(account) + } + + fn transfer_keep_alive( + dest: MultiAddress, + value: u128, + ) -> ::subxt::tx::Payload { + dev::tx().balances().transfer_keep_alive(dest, value) + } + + fn other_params() -> OtherParams { + OtherParams::default() + } +} diff --git a/chains/polkadot/server/src/client.rs b/chains/polkadot/server/src/client.rs index 80194e0e..9270b610 100644 --- a/chains/polkadot/server/src/client.rs +++ b/chains/polkadot/server/src/client.rs @@ -1,8 +1,113 @@ -use crate::types::ClientConfig; +#![allow(dead_code)] +use crate::types::{BlockIdentifier, ClientConfig, SubxtConfigAdapter}; +use std::{borrow::Borrow, future::Future, sync::Arc}; +use subxt::{ + backend::{ + rpc::{RpcClient, RpcClientT}, + RuntimeVersion, + }, + blocks::BlockRef, + metadata::Metadata, + utils::AccountId32, +}; -pub struct PolkadotClient { - config: BlockchainConfig, - client: OnlineClient, - rpc_methods: LegacyRpcMethods, - genesis_block: BlockIdentifier, +type OnlineClient = subxt::OnlineClient>; +type LegacyRpcMethods = subxt::backend::legacy::LegacyRpcMethods>; +type LegacyBackend = subxt::backend::legacy::LegacyBackend>; +type PairSigner = subxt::tx::PairSigner, ::Pair>; +type Block = subxt::backend::legacy::rpc_methods::Block>; +type BlockDetails = subxt::backend::legacy::rpc_methods::BlockDetails>; + +pub struct SubstrateClient { + client: OnlineClient, + rpc_methods: LegacyRpcMethods, +} + +impl SubstrateClient { + /// Creates a new polkadot client using the provided `config` and connects to `addr` + /// + /// # Errors + /// Will return `Err` when the network is invalid, or when the provided `addr` is unreacheable. + pub async fn from_client(client: C) -> anyhow::Result { + let rpc_client = RpcClient::new(client); + let rpc_methods = LegacyRpcMethods::::new(rpc_client.clone()); + let backend = LegacyBackend::::new(rpc_client); + let client = OnlineClient::::from_backend(Arc::new(backend)).await?; + Ok(Self { client, rpc_methods }) + } + + pub const fn rpc_methods(&self) -> &LegacyRpcMethods { + &self.rpc_methods + } + + pub const fn client(&self) -> &OnlineClient { + &self.client + } + + pub fn account_info( + &self, + account: impl Borrow, + block_ref: BlockRef, + ) -> impl Future> + Sized + Send + '_ { + let account = account.borrow(); + let tx = T::account_info(account); + async move { + let account = self.client.storage().at(block_ref).fetch_or_default(&tx).await?; + Ok(account) + } + } + + pub async fn block( + &self, + block_identifier: BlockIdentifier, + ) -> anyhow::Result>> { + use subxt::backend::legacy::rpc_methods::BlockNumber; + let block_hash = match block_identifier { + BlockIdentifier::Hash(block_hash) => block_hash, + BlockIdentifier::Number(block_number) => { + let Some(block_hash) = self + .rpc_methods + .chain_get_block_hash(Some(BlockNumber::Number(block_number))) + .await? + else { + return Ok(None); + }; + block_hash + }, + }; + self.rpc_methods + .chain_get_block(Some(block_hash)) + .await + .map_err(anyhow::Error::from) + } + + async fn faucet( + &self, + signer: T::Pair, + dest: subxt::utils::MultiAddress, + value: u128, + ) -> anyhow::Result { + let tx = T::transfer_keep_alive(dest, value); + let hash = self + .client + .tx() + .sign_and_submit_then_watch(&tx, &signer, T::other_params()) + .await? + .wait_for_finalized_success() + .await? + .extrinsic_hash(); + Ok(hash) + } + + fn runtime_version(&self) -> RuntimeVersion { + self.client.runtime_version() + } + + fn metadata(&self) -> Metadata { + self.client.metadata() + } + + fn genesis_hash(&self) -> T::Hash { + self.client.genesis_hash() + } } diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index b2aad89f..49275901 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -33,7 +33,8 @@ use subxt::{ mod block; mod call; -// mod client; +mod chains; +mod client; mod types; pub struct PolkadotClient { @@ -141,30 +142,6 @@ impl PolkadotClient { } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct WestendDevConfig; - -impl types::ClientConfig for WestendDevConfig { - type Hash = ::Hash; - - type AccountId = ::AccountId; - - type Address = ::Address; - - type Signature = ::Signature; - - type Hasher = ::Hasher; - - type Header = ::Header; - - type ExtrinsicParams = - subxt::config::polkadot::PolkadotExtrinsicParams>; - - type AssetId = ::AssetId; - - type AccountInfo = (); -} - #[async_trait::async_trait] impl BlockchainClient for PolkadotClient { type MetadataParams = PolkadotMetadataParams; @@ -176,14 +153,13 @@ impl BlockchainClient for PolkadotClient { type AtBlock = PartialBlockIdentifier; type BlockIdentifier = BlockIdentifier; - type Query = types::Query; + type Query = types::Query; type Transaction = Vec; async fn query( &self, _query: Self::Query, ) -> Result<::Result> { - anyhow::bail!("unsupported query"); } @@ -236,7 +212,7 @@ impl BlockchainClient for PolkadotClient { let address: AccountId32 = address .address() .parse() - .map_err(|err| anyhow::anyhow!("{}", err)) + .map_err(|err| anyhow::anyhow!("{err}")) .context("invalid address")?; let signer = PairSigner::::new(AccountKeyring::Alice.pair()); diff --git a/chains/polkadot/server/src/types.rs b/chains/polkadot/server/src/types.rs index 584ed4bb..c6912dc3 100644 --- a/chains/polkadot/server/src/types.rs +++ b/chains/polkadot/server/src/types.rs @@ -1,9 +1,10 @@ use rosetta_core::traits::Member; -use std::{fmt::Debug, marker::PhantomData}; +use std::{borrow::Borrow, fmt::Debug, marker::PhantomData}; // use rosetta_config_polkadot::metadata::westend::dev as westend_dev_metadata; use subxt::{ config::{ExtrinsicParams, Hasher, Header}, ext::{codec::Encode, scale_decode::DecodeAsType, scale_encode::EncodeAsType}, + utils::{AccountId32, MultiAddress}, Config as SubxtConfig, }; @@ -26,13 +27,42 @@ pub trait ClientConfig: Debug + Clone + PartialEq + Eq + Sized + Send + Sync + ' /// The block header. type Header: Member + Header + Send + serde::de::DeserializeOwned; + /// These parameters can be provided to the constructor along with + /// some default parameters that `subxt` understands, in order to + /// help construct your [`Self::ExtrinsicParams`] object. + type OtherParams: Default + Send + Sync + 'static; + /// This type defines the extrinsic extra and additional parameters. - type ExtrinsicParams: ExtrinsicParams>; + type ExtrinsicParams: ExtrinsicParams, OtherParams = Self::OtherParams>; /// This is used to identify an asset in the `ChargeAssetTxPayment` signed extension. type AssetId: Debug + Clone + Encode + DecodeAsType + EncodeAsType; - type AccountInfo: Member; + type AccountInfo: Member + subxt::metadata::DecodeWithMetadata; + + type TransferKeepAlive: Member + + subxt::blocks::StaticExtrinsic + + subxt::ext::scale_encode::EncodeAsFields; + + type Pair: subxt::tx::Signer> + Send + Sync + 'static; + + fn account_info( + account: impl Borrow, + ) -> ::subxt::storage::address::Address< + ::subxt::storage::address::StaticStorageMapKey, + Self::AccountInfo, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + >; + + fn transfer_keep_alive( + dest: MultiAddress, + value: u128, + ) -> ::subxt::tx::Payload; + + fn other_params( + ) -> >>::OtherParams; } pub struct SubxtConfigAdapter(PhantomData); @@ -58,19 +88,6 @@ where type AssetId = ::AssetId; } -// // westend_dev_metadata::runtime_types::westend_runtime:: - -// /// A concrete storage address. This can be created from static values (ie those generated -// /// via the `subxt` macro) or dynamic values via [`dynamic`]. -// #[derive(Derivative)] -// pub struct Address { -// pallet_name: Cow<'static, str>, -// entry_name: Cow<'static, str>, -// storage_entry_keys: Vec, -// validation_hash: Option<[u8; 32]>, -// _marker: std::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>, -// } - #[derive(Debug, Clone, PartialEq, Eq)] pub struct StorageQuery { /// Version of the runtime specification. @@ -79,11 +96,6 @@ pub struct StorageQuery { address: Vec, } -// type AccountId = ::subxt::utils::AccountId32; -// type AccountData = -// westend_dev_metadata::runtime_types::pallet_balances::types::AccountData; type AccountInfo -// = westend_dev_metadata::runtime_types::frame_system::AccountInfo; - #[derive(Debug, Clone, PartialEq, Eq)] pub enum BlockIdentifier { Number(u64), @@ -107,11 +119,3 @@ pub enum QueryResult { GetBlock(u32), Storage(Vec), } - -// pub fn teste() { -// let res = westend_dev_metadata::storage().balances().account(0); -// westend_dev_metadata::apis(). -// ::subxt::storage::address::Address; - -// ::subxt::storage::address::StaticStorageMapKey; -// } From 4b144395affcd6e0f415015941fb56151fa879eb Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 04:00:22 -0300 Subject: [PATCH 19/26] Support westend and polkadot --- chains/polkadot/server/src/call.rs | 10 +- chains/polkadot/server/src/client.rs | 98 +++++++++----- chains/polkadot/server/src/lib.rs | 186 +++++++-------------------- chains/polkadot/server/src/types.rs | 50 +++++-- 4 files changed, 160 insertions(+), 184 deletions(-) diff --git a/chains/polkadot/server/src/call.rs b/chains/polkadot/server/src/call.rs index d7eef74e..51d8453a 100644 --- a/chains/polkadot/server/src/call.rs +++ b/chains/polkadot/server/src/call.rs @@ -9,11 +9,11 @@ use subxt::{ dynamic::Value as SubxtValue, ext::scale_value::{self, scale::TypeId, BitSequence, ValueDef}, metadata::types::StorageEntryType, - OnlineClient, PolkadotConfig as GenericConfig, + OnlineClient, }; -pub fn dynamic_constant_req( - subxt: &OnlineClient, +pub fn dynamic_constant_req( + subxt: &OnlineClient, pallet_name: &str, constant_name: &str, ) -> Result { @@ -24,8 +24,8 @@ pub fn dynamic_constant_req( Ok(serde_val) } -pub async fn dynamic_storage_req( - subxt: &OnlineClient, +pub async fn dynamic_storage_req( + subxt: &OnlineClient, pallet_name: &str, storage_name: &str, params: Value, diff --git a/chains/polkadot/server/src/client.rs b/chains/polkadot/server/src/client.rs index 9270b610..dbeb22f4 100644 --- a/chains/polkadot/server/src/client.rs +++ b/chains/polkadot/server/src/client.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] use crate::types::{BlockIdentifier, ClientConfig, SubxtConfigAdapter}; +use anyhow::Context; use std::{borrow::Borrow, future::Future, sync::Arc}; use subxt::{ backend::{ @@ -11,12 +12,13 @@ use subxt::{ utils::AccountId32, }; -type OnlineClient = subxt::OnlineClient>; -type LegacyRpcMethods = subxt::backend::legacy::LegacyRpcMethods>; -type LegacyBackend = subxt::backend::legacy::LegacyBackend>; -type PairSigner = subxt::tx::PairSigner, ::Pair>; -type Block = subxt::backend::legacy::rpc_methods::Block>; -type BlockDetails = subxt::backend::legacy::rpc_methods::BlockDetails>; +type Config = SubxtConfigAdapter; +type OnlineClient = subxt::OnlineClient>; +type LegacyRpcMethods = subxt::backend::legacy::LegacyRpcMethods>; +type LegacyBackend = subxt::backend::legacy::LegacyBackend>; +type PairSigner = subxt::tx::PairSigner, ::Pair>; +type Block = subxt::blocks::Block, OnlineClient>; +type BlockDetails = subxt::backend::legacy::rpc_methods::BlockDetails>; pub struct SubstrateClient { client: OnlineClient, @@ -44,23 +46,10 @@ impl SubstrateClient { &self.client } - pub fn account_info( - &self, - account: impl Borrow, - block_ref: BlockRef, - ) -> impl Future> + Sized + Send + '_ { - let account = account.borrow(); - let tx = T::account_info(account); - async move { - let account = self.client.storage().at(block_ref).fetch_or_default(&tx).await?; - Ok(account) - } - } - - pub async fn block( + async fn block_identifier_to_hash( &self, block_identifier: BlockIdentifier, - ) -> anyhow::Result>> { + ) -> anyhow::Result { use subxt::backend::legacy::rpc_methods::BlockNumber; let block_hash = match block_identifier { BlockIdentifier::Hash(block_hash) => block_hash, @@ -70,18 +59,67 @@ impl SubstrateClient { .chain_get_block_hash(Some(BlockNumber::Number(block_number))) .await? else { - return Ok(None); + anyhow::bail!("block not found: {block_identifier:?}"); }; block_hash }, + BlockIdentifier::Latest => self + .rpc_methods + .chain_get_block_hash(None) + .await? + .context("latest block not found")?, + BlockIdentifier::Finalized => self.rpc_methods.chain_get_finalized_head().await?, }; - self.rpc_methods - .chain_get_block(Some(block_hash)) - .await - .map_err(anyhow::Error::from) + Ok(block_hash) + } + + pub fn account_info( + &self, + account: impl Borrow, + block_identifier: impl Into>, + ) -> impl Future> + Sized + Send + '_ { + let account = account.borrow(); + let tx = T::account_info(account); + let block_identifier = block_identifier.into(); + async move { + let block_hash = self.block_identifier_to_hash(block_identifier).await?; + let account = self + .client + .storage() + .at(BlockRef::from_hash(block_hash)) + .fetch_or_default(&tx) + .await?; + Ok(account) + } + } + + pub fn block( + &self, + block_identifier: impl Into> + Send, + ) -> impl Future>> + Sized + Send + '_ { + let block_identifier = block_identifier.into(); + async move { + let block_hash = self.block_identifier_to_hash(block_identifier).await?; + let block = self.client.blocks().at(BlockRef::from_hash(block_hash)).await?; + Ok(block) + } + } + + pub fn block_details( + &self, + block_identifier: impl Into> + Send, + ) -> impl Future>>> + Sized + Send + '_ { + let block_identifier = block_identifier.into(); + async move { + let block_hash = self.block_identifier_to_hash(block_identifier).await?; + self.rpc_methods + .chain_get_block(Some(block_hash)) + .await + .map_err(anyhow::Error::from) + } } - async fn faucet( + pub async fn faucet( &self, signer: T::Pair, dest: subxt::utils::MultiAddress, @@ -99,15 +137,15 @@ impl SubstrateClient { Ok(hash) } - fn runtime_version(&self) -> RuntimeVersion { + pub fn runtime_version(&self) -> RuntimeVersion { self.client.runtime_version() } - fn metadata(&self) -> Metadata { + pub fn metadata(&self) -> Metadata { self.client.metadata() } - fn genesis_hash(&self) -> T::Hash { + pub fn genesis_hash(&self) -> T::Hash { self.client.genesis_hash() } } diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index 49275901..1a140c00 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use chains::WestendDevConfig; use parity_scale_codec::{Decode, Encode}; use rosetta_config_polkadot::metadata::westend::dev as westend_dev_metadata; pub use rosetta_config_polkadot::{PolkadotMetadata, PolkadotMetadataParams}; @@ -12,23 +13,10 @@ use serde_json::Value; use sp_keyring::AccountKeyring; use std::time::Duration; use subxt::{ - backend::{ - legacy::{ - rpc_methods::{BlockNumber, NumberOrHex}, - LegacyRpcMethods, - }, - rpc::RpcClient, - }, - blocks::BlockRef, - config::{ - substrate::{H256, U256}, - Header, - }, + config::Header, // dynamic::Value as SubtxValue, tx::{PairSigner, SubmittableExtrinsic}, utils::{AccountId32, MultiAddress}, - OnlineClient, - PolkadotConfig, }; mod block; @@ -39,8 +27,7 @@ mod types; pub struct PolkadotClient { config: BlockchainConfig, - client: OnlineClient, - rpc_methods: LegacyRpcMethods, + client: client::SubstrateClient, genesis_block: BlockIdentifier, } @@ -59,86 +46,11 @@ impl PolkadotClient { /// # Errors /// Will return `Err` when the network is invalid, or when the provided `addr` is unreacheable. pub async fn from_config(config: BlockchainConfig, addr: &str) -> Result { - let (client, rpc_methods) = { - let ws_client = default_client(addr, None).await?; - let rpc_client = RpcClient::new(ws_client); - let rpc_methods = LegacyRpcMethods::::new(rpc_client.clone()); - let backend = subxt::backend::legacy::LegacyBackend::new(rpc_client); - let client = - OnlineClient::::from_backend(std::sync::Arc::new(backend)).await?; - (client, rpc_methods) - }; + let ws_client = default_client(addr, None).await?; + let client = client::SubstrateClient::::from_client(ws_client).await?; let genesis = client.genesis_hash(); let genesis_block = BlockIdentifier { index: 0, hash: genesis.0 }; - Ok(Self { config, client, rpc_methods, genesis_block }) - } - - async fn account_info( - &self, - address: &Address, - maybe_block: Option, - ) -> Result> { - let account: AccountId32 = address - .address() - .parse() - .map_err(|err| anyhow::anyhow!("{}", err)) - .context("invalid address")?; - - // Build a dynamic storage query to iterate account information. - // let storage_query = - // subxt::dynamic::storage("System", "Account", vec![SubtxValue::from_bytes(account)]); - let storage_query = westend_dev_metadata::storage().system().account(account); - - // Convert `BlockNumber` to `BlockRef` - let block_hash = match maybe_block { - Some(NumberOrHex::Hex(block_hash)) => { - // Convert U256 to BlockRef - let mut bytes = [0u8; 32]; - block_hash.to_big_endian(&mut bytes); - let block_hash = H256(bytes); - BlockRef::from_hash(block_hash) - }, - Some(NumberOrHex::Number(block_number)) => { - // Get block hash by number - self.rpc_methods - .chain_get_block_hash(Some(BlockNumber::Number(block_number))) - .await? - .map(BlockRef::from_hash) - .ok_or_else(|| anyhow::anyhow!("no block hash found"))? - }, - None => { - // Get latest block hash - self.rpc_methods - .chain_get_block_hash(None) - .await? - .map(BlockRef::from_hash) - .ok_or_else(|| anyhow::anyhow!("[report this bug] latest block not found"))? - }, - }; - self.client.storage().at(block_hash).fetch(&storage_query).await?.map_or_else( - || { - Ok(AccountInfo { - nonce: 0, - consumers: 0, - providers: 0, - sufficients: 0, - data: AccountData { free: 0, reserved: 0, frozen: 0 }, - }) - }, - |account_info| { - Ok(AccountInfo { - nonce: account_info.nonce, - consumers: account_info.consumers, - providers: account_info.providers, - sufficients: account_info.sufficients, - data: AccountData { - free: account_info.data.free, - reserved: account_info.data.reserved, - frozen: account_info.data.frozen, - }, - }) - }, - ) + Ok(Self { config, client, genesis_block }) } } @@ -172,19 +84,22 @@ impl BlockchainClient for PolkadotClient { } async fn current_block(&self) -> Result { - let block = self.rpc_methods.chain_get_block(None).await?.context("no current block")?; + let block = self + .client + .block_details(types::BlockIdentifier::<_>::Latest) + .await? + .context("no current block")?; + // let block = self.rpc_methods.chain_get_block(None).await?.context("no current block")?; let index = u64::from(block.block.header.number); let hash = block.block.header.hash(); Ok(BlockIdentifier { index, hash: hash.0 }) } async fn finalized_block(&self) -> Result { - let finalized_head = self.rpc_methods.chain_get_finalized_head().await?; - let block = self - .rpc_methods - .chain_get_block(Some(finalized_head)) - .await? - .context("no finalized block")?; + let Some(block) = self.client.block_details(types::BlockIdentifier::<_>::Finalized).await? + else { + return Ok(self.genesis_block.clone()); + }; let index = u64::from(block.block.header.number); let hash = block.block.header.hash(); Ok(BlockIdentifier { index, hash: hash.0 }) @@ -195,16 +110,12 @@ impl BlockchainClient for PolkadotClient { address: &Address, block_identifier: &PartialBlockIdentifier, ) -> Result { - let block_number = match block_identifier { - PartialBlockIdentifier { hash: Some(block_bash), .. } => { - Some(BlockNumber::Hex(U256::from_big_endian(block_bash))) - }, - PartialBlockIdentifier { index: Some(block_number), .. } => { - Some(BlockNumber::Number(*block_number)) - }, - PartialBlockIdentifier { hash: None, index: None } => None, - }; - let account_info = self.account_info(address, block_number).await?; + let account: AccountId32 = address + .address() + .parse() + .map_err(|err| anyhow::anyhow!("{}", err)) + .context("invalid address")?; + let account_info = self.client.account_info(account, block_identifier).await?; Ok(account_info.data.free) } @@ -214,17 +125,8 @@ impl BlockchainClient for PolkadotClient { .parse() .map_err(|err| anyhow::anyhow!("{err}")) .context("invalid address")?; - - let signer = PairSigner::::new(AccountKeyring::Alice.pair()); - let tx = westend_dev_metadata::tx().balances().transfer_keep_alive(address.into(), value); - let hash = self - .client - .tx() - .sign_and_submit_then_watch_default(&tx, &signer) - .await? - .wait_for_finalized_success() - .await? - .extrinsic_hash(); + let signer = PairSigner::<_, _>::new(AccountKeyring::Alice.pair()); + let hash = self.client.faucet(signer, address.into(), value).await?; Ok(hash.0.to_vec()) } @@ -234,7 +136,15 @@ impl BlockchainClient for PolkadotClient { params: &Self::MetadataParams, ) -> Result { let address = public_key.to_address(self.config().address_format); - let account_info = self.account_info(&address, None).await?; + + let account: AccountId32 = address + .address() + .parse() + .map_err(|err| anyhow::anyhow!("{err}")) + .context("invalid address")?; + + let account_info = + self.client.account_info(&account, types::BlockIdentifier::<_>::Latest).await?; let runtime = self.client.runtime_version(); let metadata = self.client.metadata(); let pallet = metadata @@ -261,26 +171,18 @@ impl BlockchainClient for PolkadotClient { } async fn submit(&self, transaction: &[u8]) -> Result> { - let hash = SubmittableExtrinsic::from_bytes(self.client.clone(), transaction.to_vec()) - .submit_and_watch() - .await? - .wait_for_finalized_success() - .await? - .extrinsic_hash(); + let hash = + SubmittableExtrinsic::from_bytes(self.client.client().clone(), transaction.to_vec()) + .submit_and_watch() + .await? + .wait_for_finalized_success() + .await? + .extrinsic_hash(); Ok(hash.0.to_vec()) } async fn block(&self, block_identifier: &PartialBlockIdentifier) -> Result { - let block_hash = if let Some(hash) = block_identifier.hash.as_ref() { - H256(*hash) - } else { - self.rpc_methods - .chain_get_block_hash(block_identifier.index.map(BlockNumber::from)) - .await? - .context("block not found")? - }; - - let block = self.client.blocks().at(block_hash).await?; + let block = self.client.block(block_identifier).await?; let extrinsics = block.extrinsics().await?; // Build timestamp query @@ -320,10 +222,12 @@ impl BlockchainClient for PolkadotClient { let call_name = call_details[1]; let query_type = call_details[2]; match query_type.to_lowercase().as_str() { - "constant" => crate::call::dynamic_constant_req(&self.client, pallet_name, call_name), + "constant" => { + crate::call::dynamic_constant_req(self.client.client(), pallet_name, call_name) + }, "storage" => { crate::call::dynamic_storage_req( - &self.client, + self.client.client(), pallet_name, call_name, request.parameters.clone(), diff --git a/chains/polkadot/server/src/types.rs b/chains/polkadot/server/src/types.rs index c6912dc3..fe7509b9 100644 --- a/chains/polkadot/server/src/types.rs +++ b/chains/polkadot/server/src/types.rs @@ -1,4 +1,4 @@ -use rosetta_core::traits::Member; +use rosetta_core::{traits::Member, types::PartialBlockIdentifier}; use std::{borrow::Borrow, fmt::Debug, marker::PhantomData}; // use rosetta_config_polkadot::metadata::westend::dev as westend_dev_metadata; use subxt::{ @@ -72,19 +72,12 @@ where T: ClientConfig, { type Hash = ::Hash; - type AccountId = ::AccountId; - type Address = ::Address; - type Signature = ::Signature; - type Hasher = ::Hasher; - type Header = ::Header; - type ExtrinsicParams = ::ExtrinsicParams; - type AssetId = ::AssetId; } @@ -100,6 +93,47 @@ pub struct StorageQuery { pub enum BlockIdentifier { Number(u64), Hash(BlockHash), + Latest, + Finalized, +} + +impl From for BlockIdentifier +where + T: From<[u8; 32]>, +{ + fn from(block_identifier: rosetta_core::types::BlockIdentifier) -> Self { + Self::Hash(T::from(block_identifier.hash)) + } +} + +impl From for BlockIdentifier +where + T: From<[u8; 32]>, +{ + fn from(block_identifier: PartialBlockIdentifier) -> Self { + match block_identifier { + PartialBlockIdentifier { hash: Some(block_hash), .. } => { + Self::Hash((block_hash).into()) + }, + PartialBlockIdentifier { index: Some(block_number), .. } => Self::Number(block_number), + PartialBlockIdentifier { hash: None, index: None } => Self::Latest, + } + } +} + +impl From<&PartialBlockIdentifier> for BlockIdentifier +where + T: From<[u8; 32]>, +{ + fn from(block_identifier: &PartialBlockIdentifier) -> Self { + match block_identifier { + PartialBlockIdentifier { hash: Some(block_hash), .. } => { + Self::Hash((*block_hash).into()) + }, + PartialBlockIdentifier { index: Some(block_number), .. } => Self::Number(*block_number), + PartialBlockIdentifier { hash: None, index: None } => Self::Latest, + } + } } #[derive(Debug, Clone, PartialEq, Eq)] From 0a06c0319dce9aa35475e0838ad6b77f191c25b0 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 04:56:39 -0300 Subject: [PATCH 20/26] Remove block method --- .../rosetta-testing-arbitrum/src/lib.rs | 31 ++- chains/astar/server/src/lib.rs | 28 ++- chains/ethereum/config/src/lib.rs | 2 +- chains/ethereum/config/src/types.rs | 10 +- chains/ethereum/server/src/client.rs | 87 ++++---- chains/ethereum/server/src/eth_types.rs | 22 --- chains/ethereum/server/src/lib.rs | 9 +- chains/ethereum/server/src/utils.rs | 113 +---------- chains/polkadot/server/src/block.rs | 187 ------------------ chains/polkadot/server/src/client.rs | 31 ++- chains/polkadot/server/src/lib.rs | 38 +--- chains/polkadot/server/src/types.rs | 1 - rosetta-client/src/client.rs | 19 +- rosetta-client/src/wallet.rs | 16 +- rosetta-core/src/lib.rs | 4 - rosetta-docker/src/lib.rs | 40 ++-- rosetta-server/src/ws/reconnect_impl.rs | 1 - 17 files changed, 113 insertions(+), 526 deletions(-) delete mode 100644 chains/polkadot/server/src/block.rs diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs index f29ba724..0e9cbc8e 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs @@ -50,7 +50,7 @@ mod tests { use hex_literal::hex; use rosetta_client::Wallet; use rosetta_config_ethereum::{AtBlock, CallResult}; - use rosetta_core::{types::PartialBlockIdentifier, BlockchainClient}; + use rosetta_core::BlockchainClient; use rosetta_server_ethereum::MaybeWsEthereumClient; use sha3::Digest; use std::{collections::BTreeMap, future::Future, path::Path, time::Duration}; @@ -254,29 +254,20 @@ mod tests { .await .expect("Error creating client"); // Check if the genesis is consistent - let expected_genesis = client.genesis_block().clone(); - let actual_genesis = client - .block(&PartialBlockIdentifier { index: Some(0), hash: None }) - .await - .unwrap() - .block_identifier; + let genesis_block = client.genesis_block(); + assert_eq!(genesis_block.index, 0); - assert_eq!(expected_genesis, actual_genesis); // Check if the current block is consistent - let expected_current = client.current_block().await.unwrap(); - let actual_current = client - .block(&PartialBlockIdentifier { index: None, hash: Some(expected_current.hash) }) - .await - .unwrap(); - assert_eq!(expected_current, actual_current.block_identifier); + let current_block = client.current_block().await.unwrap(); + if current_block.index > 0 { + assert_ne!(current_block.hash, genesis_block.hash); + } else { + assert_eq!(current_block.hash, genesis_block.hash); + } // Check if the finalized block is consistent - let expected_finalized = client.finalized_block().await.unwrap(); - let actual_finalized = client - .block(&PartialBlockIdentifier { index: None, hash: Some(expected_finalized.hash) }) - .await - .unwrap(); - assert_eq!(expected_finalized, actual_finalized.block_identifier); + let finalized_block = client.finalized_block().await.unwrap(); + assert!(finalized_block.index >= genesis_block.index); }) .await; } diff --git a/chains/astar/server/src/lib.rs b/chains/astar/server/src/lib.rs index 47d65897..b5a8a4a9 100644 --- a/chains/astar/server/src/lib.rs +++ b/chains/astar/server/src/lib.rs @@ -13,7 +13,7 @@ use rosetta_core::{ address::{Address, AddressFormat}, PublicKey, }, - types::{Block, BlockIdentifier, PartialBlockIdentifier}, + types::{BlockIdentifier, PartialBlockIdentifier}, BlockchainClient, BlockchainConfig, }; use rosetta_server::ws::default_client; @@ -94,15 +94,17 @@ impl AstarClient { // If a hash if provided, we don't know if it's a ethereum block hash or substrate // block hash. We try to fetch the block using ethereum first, and // if it fails, we try to fetch it using substrate. - let ethereum_block = self - .client - .block(&PartialBlockIdentifier { index: None, hash: Some(*block_hash) }) - .await; - - if let Ok(ethereum_block) = ethereum_block { + let ethereum_block = + self.client.call(&EthQuery::GetBlockByHash(H256(*block_hash))).await.map( + |result| match result { + EthQueryResult::GetBlockByHash(block) => block, + _ => unreachable!(), + }, + ); + + if let Ok(Some(ethereum_block)) = ethereum_block { // Convert ethereum block to substrate block by fetching the block by number. - let substrate_block_number = - BlockNumber::Number(ethereum_block.block_identifier.index); + let substrate_block_number = BlockNumber::Number(ethereum_block.header.number); let substrate_block_hash = self .rpc_methods .chain_get_block_hash(Some(substrate_block_number)) @@ -130,12 +132,12 @@ impl AstarClient { // Verify if the ethereum block hash matches the provided ethereum block hash. // TODO: compute the block hash if U256(actual_eth_block.header.number.0) != - U256::from(ethereum_block.block_identifier.index) + U256::from(ethereum_block.header.number) { anyhow::bail!("ethereum block hash mismatch"); } if actual_eth_block.header.parent_hash.as_fixed_bytes() != - ðereum_block.parent_block_identifier.hash + ðereum_block.header.parent_hash.0 { anyhow::bail!("ethereum block hash mismatch"); } @@ -288,10 +290,6 @@ impl BlockchainClient for AstarClient { self.client.submit(transaction).await } - async fn block(&self, block_identifier: &Self::AtBlock) -> Result { - self.client.block(block_identifier).await - } - async fn call(&self, req: &EthQuery) -> Result { self.client.call(req).await } diff --git a/chains/ethereum/config/src/lib.rs b/chains/ethereum/config/src/lib.rs index c74a92f5..cd6b2383 100644 --- a/chains/ethereum/config/src/lib.rs +++ b/chains/ethereum/config/src/lib.rs @@ -116,7 +116,7 @@ impl rosetta_core::traits::Header for types::header::Header { } } -impl rosetta_core::traits::Block for types::FullBlock { +impl rosetta_core::traits::Block for types::BlockFull { type Transaction = types::SignedTransaction; type Header = types::header::Header; type Hash = BlockHash; diff --git a/chains/ethereum/config/src/types.rs b/chains/ethereum/config/src/types.rs index 9383aebf..e3da9b68 100644 --- a/chains/ethereum/config/src/types.rs +++ b/chains/ethereum/config/src/types.rs @@ -9,7 +9,7 @@ use ethereum_types::{Address, Bloom, H256, U256}; use crate::serde_utils::{bytes_to_hex, uint_to_hex}; pub type SignedTransaction = transaction::SignedTransaction; -pub type FullBlock = block::Block; +pub type BlockFull = block::Block; pub type BlockRef = block::Block; #[derive(Clone, Debug)] @@ -199,6 +199,10 @@ pub enum Query { /// from is not tampered with. #[cfg_attr(feature = "serde", serde(rename = "eth_getProof"))] GetProof(GetProof), + /// Returns information about a block whose hash is in the request, or null when no block was + /// found. + #[cfg_attr(feature = "serde", serde(rename = "eth_getblockbyhash"))] + GetBlockByHash(H256), /// Returns the currently configured chain ID, a value used in replay-protected transaction /// signing as introduced by EIP-155 #[cfg_attr(feature = "serde", serde(rename = "eth_chainId"))] @@ -258,6 +262,10 @@ pub enum QueryResult { /// from is not tampered with. #[cfg_attr(feature = "serde", serde(rename = "eth_getProof"))] GetProof(EIP1186ProofResponse), + /// Returns information about a block whose hash is in the request, or null when no block was + /// found. + #[cfg_attr(feature = "serde", serde(rename = "eth_getblockbyhash"))] + GetBlockByHash(Option), /// Returns the account and storage values of the specified account including the /// Merkle-proof. This call can be used to verify that the data you are pulling /// from is not tampered with. diff --git a/chains/ethereum/server/src/client.rs b/chains/ethereum/server/src/client.rs index bdc6d1a3..a147e367 100644 --- a/chains/ethereum/server/src/client.rs +++ b/chains/ethereum/server/src/client.rs @@ -11,13 +11,13 @@ use ethers::{ utils::{keccak256, rlp::Encodable}, }; use rosetta_config_ethereum::{ - AtBlock, CallContract, CallResult, EIP1186ProofResponse, EthereumMetadata, + header::Header, AtBlock, CallContract, CallResult, EIP1186ProofResponse, EthereumMetadata, EthereumMetadataParams, GetBalance, GetProof, GetStorageAt, GetTransactionReceipt, Log, Query as EthQuery, QueryResult as EthQueryResult, StorageProof, TransactionReceipt, }; use rosetta_core::{ crypto::{address::Address, PublicKey}, - types::{Block, BlockIdentifier, Coin, PartialBlockIdentifier}, + types::{BlockIdentifier, Coin, PartialBlockIdentifier}, BlockchainConfig, }; use std::sync::{ @@ -280,43 +280,6 @@ where .to_vec()) } - #[allow(clippy::missing_errors_doc)] - pub async fn block(&self, block_identifier: &PartialBlockIdentifier) -> Result { - let block_id = block_identifier.hash.as_ref().map_or_else( - || { - let index = block_identifier - .index - .map_or(BlockNumber::Latest, |index| BlockNumber::Number(U64::from(index))); - BlockId::Number(index) - }, - |hash| BlockId::Hash(H256(*hash)), - ); - let block = self - .client - .get_block_with_txs(block_id) - .await - .map_err(|error| { - anyhow::anyhow!("Failed to get block with transactions: {}", error.to_string()) - })? - .context("block not found")?; - let block_number = block.number.context("Unable to fetch block number")?; - let block_hash = block.hash.context("Unable to fetch block hash")?; - let mut transactions = vec![]; - let block_reward_transaction = - crate::utils::block_reward_transaction(&self.client, self.config(), &block).await?; - transactions.push(block_reward_transaction); - Ok(Block { - block_identifier: BlockIdentifier { index: block_number.as_u64(), hash: block_hash.0 }, - parent_block_identifier: BlockIdentifier { - index: block_number.as_u64().saturating_sub(1), - hash: block.parent_hash.0, - }, - timestamp: i64::try_from(block.timestamp.as_u64()).context("timestamp overflow")?, - transactions, - metadata: None, - }) - } - #[allow(clippy::too_many_lines, clippy::missing_errors_doc)] pub async fn call(&self, req: &EthQuery) -> Result { let result = match req { @@ -442,6 +405,52 @@ where .collect(), }) }, + EthQuery::GetBlockByHash(block_hash) => { + use rosetta_config_ethereum::BlockFull; + let Some(block) = self.client.get_block(*block_hash).await? else { + return Ok(EthQueryResult::GetBlockByHash(None)); + }; + let block = BlockFull { + hash: *block_hash, + header: Header { + parent_hash: block.parent_hash, + ommers_hash: block.uncles_hash, + beneficiary: block.author.unwrap_or_default(), + state_root: block.state_root, + transactions_root: block.transactions_root, + receipts_root: block.receipts_root, + logs_bloom: block.logs_bloom.unwrap_or_default(), + difficulty: block.difficulty, + number: block.number.map(|n| n.as_u64()).unwrap_or_default(), + gas_limit: block.gas_limit.try_into().unwrap_or(u64::MAX), + gas_used: block.gas_used.try_into().unwrap_or(u64::MAX), + timestamp: block.timestamp.try_into().unwrap_or(u64::MAX), + extra_data: block.extra_data.to_vec(), + mix_hash: block.mix_hash.unwrap_or_default(), + nonce: block + .nonce + .map(|n| u64::from_be_bytes(n.to_fixed_bytes())) + .unwrap_or_default(), + base_fee_per_gas: block + .base_fee_per_gas + .map(|n| u64::try_from(n).unwrap_or(u64::MAX)), + withdrawals_root: block.withdrawals_root, + blob_gas_used: block + .blob_gas_used + .map(|n| u64::try_from(n).unwrap_or(u64::MAX)), + excess_blob_gas: block + .excess_blob_gas + .map(|n| u64::try_from(n).unwrap_or(u64::MAX)), + parent_beacon_block_root: block.parent_beacon_block_root, + }, + total_difficulty: block.total_difficulty, + seal_fields: Vec::new(), + transactions: Vec::new(), + uncles: Vec::new(), + size: block.size.map(|n| u64::try_from(n).unwrap_or(u64::MAX)), + }; + EthQueryResult::GetBlockByHash(Some(block)) + }, EthQuery::ChainId => { let chain_id = self.client.get_chainid().await?.as_u64(); EthQueryResult::ChainId(chain_id) diff --git a/chains/ethereum/server/src/eth_types.rs b/chains/ethereum/server/src/eth_types.rs index faec28bf..5034b57b 100644 --- a/chains/ethereum/server/src/eth_types.rs +++ b/chains/ethereum/server/src/eth_types.rs @@ -1,34 +1,12 @@ use ethers::types::{Bytes, H160, U256, U64}; use serde::{Deserialize, Serialize}; -pub const MINING_REWARD_OP_TYPE: &str = "MINER_REWARD"; -pub const UNCLE_REWARD_OP_TYPE: &str = "UNCLE_REWARD"; pub const _CALL_CODE_OP_TYPE: &str = "CALLCODE"; pub const _DELEGATE_CALL_OP_TYPE: &str = "DELEGATECALL"; pub const _STATIC_CALL_OP_TYPE: &str = "STATICCALL"; -pub const SUCCESS_STATUS: &str = "SUCCESS"; - -pub const UNCLE_REWARD_MULTIPLIER: u64 = 32; -pub const MAX_UNCLE_DEPTH: u64 = 8; - pub const _TRANSFER_GAS_LIMIT: u64 = 21000; -pub const FRONTIER_BLOCK_REWARD: u64 = 5_000_000_000_000_000_000; -pub const BYZANTIUM_BLOCK_REWARD: u64 = 3_000_000_000_000_000_000; -pub const CONSTANTINOPLE_BLOCK_REWARD: u64 = 2_000_000_000_000_000_000; - -pub struct ChainConfig { - pub byzantium_block: u64, - pub constantinople_block: u64, -} - -pub const _MAINNET_CHAIN_CONFIG: ChainConfig = - ChainConfig { byzantium_block: 4_370_000, constantinople_block: 7_280_000 }; - -pub const TESTNET_CHAIN_CONFIG: ChainConfig = - ChainConfig { byzantium_block: 0, constantinople_block: 0 }; - #[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)] pub struct Trace { pub from: H160, diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index 9b4e1573..cf9e0f7a 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -6,7 +6,7 @@ pub use rosetta_config_ethereum::{ }; use rosetta_core::{ crypto::{address::Address, PublicKey}, - types::{Block, BlockIdentifier, PartialBlockIdentifier}, + types::{BlockIdentifier, PartialBlockIdentifier}, BlockchainClient, BlockchainConfig, }; use rosetta_server::ws::{default_client, DefaultClient}; @@ -174,13 +174,6 @@ impl BlockchainClient for MaybeWsEthereumClient { } } - async fn block(&self, block_identifier: &Self::AtBlock) -> Result { - match self { - Self::Http(http_client) => http_client.block(block_identifier).await, - Self::Ws(ws_client) => ws_client.block(block_identifier).await, - } - } - async fn call(&self, req: &EthQuery) -> Result { match self { Self::Http(http_client) => http_client.call(req).await, diff --git a/chains/ethereum/server/src/utils.rs b/chains/ethereum/server/src/utils.rs index 9e6d4f3d..5b9c61bc 100644 --- a/chains/ethereum/server/src/utils.rs +++ b/chains/ethereum/server/src/utils.rs @@ -1,22 +1,5 @@ -use crate::eth_types::{ - BYZANTIUM_BLOCK_REWARD, CONSTANTINOPLE_BLOCK_REWARD, FRONTIER_BLOCK_REWARD, MAX_UNCLE_DEPTH, - MINING_REWARD_OP_TYPE, SUCCESS_STATUS, TESTNET_CHAIN_CONFIG, UNCLE_REWARD_MULTIPLIER, - UNCLE_REWARD_OP_TYPE, -}; -use anyhow::{Context, Result}; -use ethers::{ - prelude::*, - providers::Middleware, - types::{Block, Transaction, H256, U64}, - utils::to_checksum, -}; -use rosetta_core::{ - types::{ - self as rosetta_types, AccountIdentifier, Amount, BlockIdentifier, Operation, - OperationIdentifier, TransactionIdentifier, - }, - BlockchainConfig, -}; +use ethers::{prelude::*, providers::Middleware, types::H256}; +use rosetta_core::types::BlockIdentifier; use std::sync::Arc; /// A block that is not pending, so it must have a valid hash and number. @@ -48,7 +31,7 @@ impl TryFrom> for NonPendingBlock { pub async fn get_non_pending_block( client: Arc>, block_id: ID, -) -> Result> +) -> anyhow::Result> where C: JsonRpcClient + 'static, ID: Into + Send + Sync, @@ -68,93 +51,3 @@ where }; Ok(Some(block)) } - -pub async fn block_reward_transaction( - client: &Provider

, - config: &BlockchainConfig, - block: &Block, -) -> Result { - let block_number = block.number.context("missing block number")?.as_u64(); - let block_hash = block.hash.context("missing block hash")?; - let block_id = BlockId::Hash(block_hash); - let miner = block.author.context("missing block author")?; - - let mut uncles = vec![]; - for (i, _) in block.uncles.iter().enumerate() { - let uncle = client - .get_uncle(block_id, U64::from(i)) - .await? - .context("Uncle block now found")?; - uncles.push(uncle); - } - - let chain_config = TESTNET_CHAIN_CONFIG; - let mut mining_reward = if chain_config.constantinople_block <= block_number { - CONSTANTINOPLE_BLOCK_REWARD - } else if chain_config.byzantium_block <= block_number { - BYZANTIUM_BLOCK_REWARD - } else { - FRONTIER_BLOCK_REWARD - }; - if !uncles.is_empty() { - mining_reward += (mining_reward / UNCLE_REWARD_MULTIPLIER) * mining_reward; - } - - let mut operations = vec![]; - let mining_reward_operation = Operation { - operation_identifier: OperationIdentifier { index: 0, network_index: None }, - related_operations: None, - r#type: MINING_REWARD_OP_TYPE.into(), - status: Some(SUCCESS_STATUS.into()), - account: Some(AccountIdentifier { - address: to_checksum(&miner, None), - sub_account: None, - metadata: None, - }), - amount: Some(Amount { - value: mining_reward.to_string(), - currency: config.currency(), - metadata: None, - }), - coin_change: None, - metadata: None, - }; - operations.push(mining_reward_operation); - - for block in uncles { - let uncle_miner = block.author.context("Uncle block has no author")?; - let uncle_number = block.number.context("Uncle block has no number")?; - let uncle_block_reward = - (uncle_number + MAX_UNCLE_DEPTH - block_number) * (mining_reward / MAX_UNCLE_DEPTH); - - let operation = Operation { - operation_identifier: OperationIdentifier { - index: i64::try_from(operations.len()).context("operation.index overflow")?, - network_index: None, - }, - related_operations: None, - r#type: UNCLE_REWARD_OP_TYPE.into(), - status: Some(SUCCESS_STATUS.into()), - account: Some(AccountIdentifier { - address: to_checksum(&uncle_miner, None), - sub_account: None, - metadata: None, - }), - amount: Some(Amount { - value: uncle_block_reward.to_string(), - currency: config.currency(), - metadata: None, - }), - coin_change: None, - metadata: None, - }; - operations.push(operation); - } - - Ok(rosetta_types::Transaction { - transaction_identifier: TransactionIdentifier { hash: hex::encode(block_hash) }, - related_transactions: None, - operations, - metadata: None, - }) -} diff --git a/chains/polkadot/server/src/block.rs b/chains/polkadot/server/src/block.rs deleted file mode 100644 index fc45b6f2..00000000 --- a/chains/polkadot/server/src/block.rs +++ /dev/null @@ -1,187 +0,0 @@ -use anyhow::{Context, Result}; -use rosetta_core::{ - crypto::address::Address, - types::{ - AccountIdentifier, Amount, Operation, OperationIdentifier, Transaction, - TransactionIdentifier, - }, - BlockchainConfig, -}; -use serde_json::{json, Value}; -use subxt::{ - blocks::{ExtrinsicDetails, ExtrinsicEvents}, - config::Hasher, - events::EventDetails, - ext::scale_value::{scale::TypeId, Composite, Primitive, ValueDef}, - utils::H256, - Config, OnlineClient, -}; - -pub fn get_transaction_identifier>( - extrinsic: &ExtrinsicDetails>, -) -> TransactionIdentifier { - TransactionIdentifier { hash: hex::encode(T::Hasher::hash_of(&extrinsic.bytes())) } -} - -pub fn get_transaction + Send>( - config: &BlockchainConfig, - transaction_identifier: TransactionIdentifier, - events: &ExtrinsicEvents, -) -> Result { - // let transaction_identifier = TransactionIdentifier { - // hash: hex::encode(T::Hasher::hash_of(&extrinsic.bytes())), - // }; - // let events = extrinsic.events().await?; - let mut operations = vec![]; - for (event_index, event_data) in events.iter().enumerate() { - let event = event_data?; - let event_parsed_data = get_operation_data(config, &event)?; - - let mut fields = vec![]; - for field in &event.event_metadata().variant.fields { - fields.push(json!({"name": field.name, "type": field.type_name})); - } - let op_metadata = Value::Array(fields); - - let op_from: Option = event_parsed_data - .from - .map(|address| AccountIdentifier { address, sub_account: None, metadata: None }); - - let op_neg_amount: Option = event_parsed_data.amount.as_ref().map(|amount| { - Amount { value: format!("-{amount}"), currency: config.currency(), metadata: None } - }); - - let operation = Operation { - operation_identifier: OperationIdentifier { - index: i64::try_from(event_index).context("event_index overflow")?, - network_index: None, - }, - related_operations: None, - r#type: event_parsed_data.event_type.clone(), - status: None, - account: op_from, - amount: op_neg_amount, - coin_change: None, - metadata: Some(op_metadata.clone()), - }; - operations.push(operation); - - if let (Some(to), Some(amount)) = (event_parsed_data.to, event_parsed_data.amount) { - operations.push(Operation { - operation_identifier: OperationIdentifier { - index: i64::try_from(event_index).context("event_index overflow")?, - network_index: None, - }, - related_operations: None, - r#type: event_parsed_data.event_type, - status: None, - account: Some(AccountIdentifier { address: to, sub_account: None, metadata: None }), - amount: Some(Amount { value: amount, currency: config.currency(), metadata: None }), - coin_change: None, - metadata: Some(op_metadata), - }); - } - } - Ok(Transaction { - transaction_identifier, - operations, - related_transactions: None, - metadata: None, - }) -} - -fn get_operation_data>( - config: &BlockchainConfig, - event: &EventDetails, -) -> Result { - let pallet_name = event.pallet_name(); - let event_name = event.variant_name(); - - let call_type = format!("{pallet_name}.{event_name}"); - - let event_fields = event.field_values()?; - let parsed_data = match event_fields { - Composite::Named(value) => { - let mut from_data = - value.iter().filter(|(k, _)| k == "from" || k == "who" || k == "account"); - - let sender_address: Option = if let Some(data) = from_data.next() { - let address = generate_address(config, &data.1.value)?; - Some(address) - } else { - None - }; - - let amount: Option = if let Some(value) = - value.iter().find(|(k, _)| k == "amount" || k == "actual_fee") - { - match &value.1.value { - ValueDef::Primitive(Primitive::U128(amount)) => Some(amount.to_string()), - _ => { - anyhow::bail!("invalid operation"); - }, - } - } else { - None - }; - - let to_address: Option = - if let Some(data) = value.iter().find(|(k, _)| k == "to") { - let address = generate_address(config, &data.1.value)?; - Some(address) - } else { - None - }; - - (sender_address, amount, to_address) - }, - Composite::Unnamed(_) => { - anyhow::bail!("invalid operation"); - }, - }; - - Ok(TransactionOperationStatus { - event_type: call_type, - from: parsed_data.0, - amount: parsed_data.1, - to: parsed_data.2, - }) -} - -struct TransactionOperationStatus { - event_type: String, - from: Option, - to: Option, - amount: Option, -} - -fn generate_address(config: &BlockchainConfig, val: &ValueDef) -> Result { - let mut addr_array = vec![]; - match val { - ValueDef::Composite(Composite::Unnamed(unamed_data)) => { - for value_data in unamed_data { - match &value_data.value { - ValueDef::Composite(data) => { - for data in data.values() { - match data.value { - ValueDef::Primitive(Primitive::U128(val)) => { - let Ok(val) = u8::try_from(val) else { - tracing::error!("overflow: {val} > 255"); - anyhow::bail!("overflow: {val} > 255"); - }; - addr_array.push(val); - }, - _ => anyhow::bail!("invalid operation"), - } - } - }, - _ => anyhow::bail!("invalid operation"), - } - } - }, - _ => anyhow::bail!("invalid operation"), - } - - let address = Address::from_public_key_bytes(config.address_format, &addr_array); - Ok(address.address().to_string()) -} diff --git a/chains/polkadot/server/src/client.rs b/chains/polkadot/server/src/client.rs index dbeb22f4..bc751046 100644 --- a/chains/polkadot/server/src/client.rs +++ b/chains/polkadot/server/src/client.rs @@ -1,4 +1,3 @@ -#![allow(dead_code)] use crate::types::{BlockIdentifier, ClientConfig, SubxtConfigAdapter}; use anyhow::Context; use std::{borrow::Borrow, future::Future, sync::Arc}; @@ -16,8 +15,8 @@ type Config = SubxtConfigAdapter; type OnlineClient = subxt::OnlineClient>; type LegacyRpcMethods = subxt::backend::legacy::LegacyRpcMethods>; type LegacyBackend = subxt::backend::legacy::LegacyBackend>; -type PairSigner = subxt::tx::PairSigner, ::Pair>; -type Block = subxt::blocks::Block, OnlineClient>; +// type PairSigner = subxt::tx::PairSigner, ::Pair>; +// type Block = subxt::blocks::Block, OnlineClient>; type BlockDetails = subxt::backend::legacy::rpc_methods::BlockDetails>; pub struct SubstrateClient { @@ -38,10 +37,6 @@ impl SubstrateClient { Ok(Self { client, rpc_methods }) } - pub const fn rpc_methods(&self) -> &LegacyRpcMethods { - &self.rpc_methods - } - pub const fn client(&self) -> &OnlineClient { &self.client } @@ -93,17 +88,17 @@ impl SubstrateClient { } } - pub fn block( - &self, - block_identifier: impl Into> + Send, - ) -> impl Future>> + Sized + Send + '_ { - let block_identifier = block_identifier.into(); - async move { - let block_hash = self.block_identifier_to_hash(block_identifier).await?; - let block = self.client.blocks().at(BlockRef::from_hash(block_hash)).await?; - Ok(block) - } - } + // pub fn block( + // &self, + // block_identifier: impl Into> + Send, + // ) -> impl Future>> + Sized + Send + '_ { + // let block_identifier = block_identifier.into(); + // async move { + // let block_hash = self.block_identifier_to_hash(block_identifier).await?; + // let block = self.client.blocks().at(BlockRef::from_hash(block_hash)).await?; + // Ok(block) + // } + // } pub fn block_details( &self, diff --git a/chains/polkadot/server/src/lib.rs b/chains/polkadot/server/src/lib.rs index 1a140c00..88b9d1c8 100644 --- a/chains/polkadot/server/src/lib.rs +++ b/chains/polkadot/server/src/lib.rs @@ -1,25 +1,21 @@ use anyhow::{Context, Result}; use chains::WestendDevConfig; use parity_scale_codec::{Decode, Encode}; -use rosetta_config_polkadot::metadata::westend::dev as westend_dev_metadata; pub use rosetta_config_polkadot::{PolkadotMetadata, PolkadotMetadataParams}; use rosetta_core::{ crypto::{address::Address, PublicKey}, - types::{Block, BlockIdentifier, CallRequest, PartialBlockIdentifier}, + types::{BlockIdentifier, CallRequest, PartialBlockIdentifier}, BlockchainClient, BlockchainConfig, EmptyEventStream, }; use rosetta_server::ws::default_client; use serde_json::Value; use sp_keyring::AccountKeyring; -use std::time::Duration; use subxt::{ config::Header, - // dynamic::Value as SubtxValue, tx::{PairSigner, SubmittableExtrinsic}, utils::{AccountId32, MultiAddress}, }; -mod block; mod call; mod chains; mod client; @@ -181,38 +177,6 @@ impl BlockchainClient for PolkadotClient { Ok(hash.0.to_vec()) } - async fn block(&self, block_identifier: &PartialBlockIdentifier) -> Result { - let block = self.client.block(block_identifier).await?; - let extrinsics = block.extrinsics().await?; - - // Build timestamp query - let timestamp_now_query = westend_dev_metadata::storage().timestamp().now(); - let timestamp = block.storage().fetch_or_default(×tamp_now_query).await?; - - let mut transactions = vec![]; - for extrinsic in extrinsics.iter().filter_map(Result::ok) { - let transaction_identifier = crate::block::get_transaction_identifier(&extrinsic); - let events = extrinsic.events().await?; - let transaction = - crate::block::get_transaction(self.config(), transaction_identifier, &events)?; - transactions.push(transaction); - } - Ok(Block { - block_identifier: BlockIdentifier { - index: u64::from(block.number()), - hash: block.hash().to_fixed_bytes(), - }, - parent_block_identifier: BlockIdentifier { - index: u64::from(block.number().saturating_sub(1)), - hash: block.header().parent_hash.to_fixed_bytes(), - }, - timestamp: i64::try_from(Duration::from_millis(timestamp).as_nanos()) - .context("timestamp overflow")?, - transactions, - metadata: None, - }) - } - async fn call(&self, request: &CallRequest) -> Result { let call_details = request.method.split('-').collect::>(); if call_details.len() != 3 { diff --git a/chains/polkadot/server/src/types.rs b/chains/polkadot/server/src/types.rs index fe7509b9..d90beaf2 100644 --- a/chains/polkadot/server/src/types.rs +++ b/chains/polkadot/server/src/types.rs @@ -1,6 +1,5 @@ use rosetta_core::{traits::Member, types::PartialBlockIdentifier}; use std::{borrow::Borrow, fmt::Debug, marker::PhantomData}; -// use rosetta_config_polkadot::metadata::westend::dev as westend_dev_metadata; use subxt::{ config::{ExtrinsicParams, Hasher, Header}, ext::{codec::Encode, scale_decode::DecodeAsType, scale_encode::EncodeAsType}, diff --git a/rosetta-client/src/client.rs b/rosetta-client/src/client.rs index 9c059aed..36576c2c 100644 --- a/rosetta-client/src/client.rs +++ b/rosetta-client/src/client.rs @@ -1,7 +1,7 @@ #![allow(missing_docs)] use crate::{ crypto::{address::Address, PublicKey}, - types::{Block, CallRequest}, + types::CallRequest, Blockchain, BlockchainConfig, }; use anyhow::Result; @@ -259,23 +259,6 @@ impl BlockchainClient for GenericClient { dispatch!(self.submit(transaction).await) } - async fn block(&self, block: &GenericAtBlock) -> Result { - match self { - Self::Ethereum(client) => match block { - GenericAtBlock::Ethereum(at_block) => client.block(at_block).await, - GenericAtBlock::Polkadot(_) => anyhow::bail!("invalid block identifier"), - }, - Self::Astar(client) => match block { - GenericAtBlock::Ethereum(at_block) => client.block(at_block).await, - GenericAtBlock::Polkadot(_) => anyhow::bail!("invalid block identifier"), - }, - Self::Polkadot(client) => match block { - GenericAtBlock::Polkadot(at_block) => client.block(at_block).await, - GenericAtBlock::Ethereum(_) => anyhow::bail!("invalid block identifier"), - }, - } - } - async fn call(&self, req: &GenericCall) -> Result { let result = match self { Self::Ethereum(client) => match req { diff --git a/rosetta-client/src/wallet.rs b/rosetta-client/src/wallet.rs index 49628433..b0346a1e 100644 --- a/rosetta-client/src/wallet.rs +++ b/rosetta-client/src/wallet.rs @@ -8,10 +8,7 @@ use crate::{ Blockchain, BlockchainConfig, }; use anyhow::Result; -use rosetta_core::{ - types::{Block, PartialBlockIdentifier}, - BlockchainClient, RosettaAlgorithm, -}; +use rosetta_core::{types::PartialBlockIdentifier, BlockchainClient, RosettaAlgorithm}; use rosetta_server_ethereum::config::{ ethereum_types::{self, Address as EthAddress, H256, U256}, AtBlock, CallContract, CallResult, EIP1186ProofResponse, GetProof, GetStorageAt, @@ -156,17 +153,6 @@ impl Wallet { self.client.listen().await } - /// Returns block data - /// Takes `PartialBlockIdentifier` - #[allow(clippy::missing_errors_doc)] - pub async fn block(&self, at_block: PartialBlockIdentifier) -> Result { - match &self.client { - GenericClient::Astar(client) => client.block(&at_block).await, - GenericClient::Ethereum(client) => client.block(&at_block).await, - GenericClient::Polkadot(client) => client.block(&at_block).await, - } - } - /// Returns the on chain metadata. /// Parameters: /// - `metadata_params`: the metadata parameters which we got from transaction builder. diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index e3a16fdd..112ee0f1 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -124,7 +124,6 @@ pub trait BlockchainClient: Sized + Send + Sync + 'static { params: &Self::MetadataParams, ) -> Result; async fn submit(&self, transaction: &[u8]) -> Result>; - async fn block(&self, block: &Self::AtBlock) -> Result; async fn call(&self, req: &Self::Call) -> Result; /// Return a stream of events, return None if the blockchain doesn't support events. @@ -182,9 +181,6 @@ where async fn submit(&self, transaction: &[u8]) -> Result> { BlockchainClient::submit(Self::as_ref(self), transaction).await } - async fn block(&self, block: &Self::AtBlock) -> Result { - BlockchainClient::block(Self::as_ref(self), block).await - } async fn call(&self, req: &Self::Call) -> Result { BlockchainClient::call(Self::as_ref(self), req).await } diff --git a/rosetta-docker/src/lib.rs b/rosetta-docker/src/lib.rs index ca0348dd..ce16ca37 100644 --- a/rosetta-docker/src/lib.rs +++ b/rosetta-docker/src/lib.rs @@ -381,38 +381,20 @@ pub mod tests { let client = env.node(); // Check if the genesis is consistent - let expected_genesis = client.genesis_block().clone(); - let actual_genesis = client - .block(&PartialBlockIdentifier { index: Some(0), hash: None }) - .await - .unwrap() - .block_identifier; - assert_eq!(expected_genesis, actual_genesis); - // Check if the current block is consistent - let expected_current = client.current_block().await.unwrap(); - let actual_current = client - .block(&PartialBlockIdentifier { index: None, hash: Some(expected_current.hash) }) - .await - .unwrap() - .block_identifier; - assert_eq!(expected_current, actual_current); + let genesis_block = client.genesis_block(); + assert_eq!(genesis_block.index, 0); + // Check if the current block is consistent - let expected_current = client.current_block().await.unwrap(); - let actual_current = client - .block(&PartialBlockIdentifier { index: None, hash: Some(expected_current.hash) }) - .await - .unwrap() - .block_identifier; - assert_eq!(expected_current, actual_current); + let current_block = client.current_block().await.unwrap(); + if current_block.index > 0 { + assert_ne!(current_block.hash, genesis_block.hash); + } else { + assert_eq!(current_block.hash, genesis_block.hash); + } // Check if the finalized block is consistent - let expected_finalized = client.finalized_block().await.unwrap(); - let actual_finalized = client - .block(&PartialBlockIdentifier { index: None, hash: Some(expected_finalized.hash) }) - .await - .unwrap() - .block_identifier; - assert_eq!(expected_finalized, actual_finalized); + let finalized_block = client.finalized_block().await.unwrap(); + assert!(finalized_block.index >= genesis_block.index); }) .await; Ok(()) diff --git a/rosetta-server/src/ws/reconnect_impl.rs b/rosetta-server/src/ws/reconnect_impl.rs index 773f30a0..e1286f88 100644 --- a/rosetta-server/src/ws/reconnect_impl.rs +++ b/rosetta-server/src/ws/reconnect_impl.rs @@ -1,4 +1,3 @@ -#![allow(dead_code)] use super::reconnect::Reconnect; use futures_timer::Delay; use futures_util::{ From ab62670fabd4824246c595bb1cfa5b809bdb44d3 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 05:15:28 -0300 Subject: [PATCH 21/26] Remove currency and amount types --- .../rosetta-testing-arbitrum/src/lib.rs | 4 +- chains/ethereum/server/src/eth_types.rs | 59 --------------- chains/ethereum/server/src/lib.rs | 1 - rosetta-client/src/traits.rs | 75 ------------------- rosetta-client/src/wallet.rs | 10 +-- rosetta-core/src/lib.rs | 11 +-- rosetta-docker/src/lib.rs | 14 ++-- 7 files changed, 11 insertions(+), 163 deletions(-) delete mode 100644 chains/ethereum/server/src/eth_types.rs delete mode 100644 rosetta-client/src/traits.rs diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs index 0e9cbc8e..b62d2e0f 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs @@ -295,9 +295,7 @@ mod tests { let value = 10 * u128::pow(10, client.config().currency_decimals); let _ = wallet.faucet(value).await; let amount = wallet.balance().await.unwrap(); - assert_eq!((amount.value), (value).to_string()); - assert_eq!(amount.currency, client.config().currency()); - assert!(amount.metadata.is_none()); + assert_eq!(amount, value); }) .await; } diff --git a/chains/ethereum/server/src/eth_types.rs b/chains/ethereum/server/src/eth_types.rs deleted file mode 100644 index 5034b57b..00000000 --- a/chains/ethereum/server/src/eth_types.rs +++ /dev/null @@ -1,59 +0,0 @@ -use ethers::types::{Bytes, H160, U256, U64}; -use serde::{Deserialize, Serialize}; - -pub const _CALL_CODE_OP_TYPE: &str = "CALLCODE"; -pub const _DELEGATE_CALL_OP_TYPE: &str = "DELEGATECALL"; -pub const _STATIC_CALL_OP_TYPE: &str = "STATICCALL"; - -pub const _TRANSFER_GAS_LIMIT: u64 = 21000; - -#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)] -pub struct Trace { - pub from: H160, - pub gas: U64, - #[serde(rename = "gasUsed")] - pub gas_used: U64, - pub input: Bytes, - pub output: Bytes, - pub to: H160, - #[serde(rename = "type")] - pub ty: String, - pub value: U256, - #[serde(default)] - pub revert: bool, - #[serde(rename = "error", default)] - pub error_message: String, - #[serde(default)] - pub calls: Vec, -} - -#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)] -pub struct FlattenTrace { - pub from: H160, - pub gas: U64, - pub gas_used: U64, - pub input: Bytes, - pub output: Bytes, - pub to: H160, - pub trace_type: String, - pub value: U256, - pub revert: bool, - pub error_message: String, -} - -impl From for FlattenTrace { - fn from(trace: Trace) -> Self { - Self { - from: trace.from, - gas: trace.gas, - gas_used: trace.gas_used, - input: trace.input, - output: trace.output, - to: trace.to, - trace_type: trace.ty, - value: trace.value, - revert: trace.revert, - error_message: trace.error_message, - } - } -} diff --git a/chains/ethereum/server/src/lib.rs b/chains/ethereum/server/src/lib.rs index cf9e0f7a..0ebb7331 100644 --- a/chains/ethereum/server/src/lib.rs +++ b/chains/ethereum/server/src/lib.rs @@ -13,7 +13,6 @@ use rosetta_server::ws::{default_client, DefaultClient}; use url::Url; mod client; -mod eth_types; mod event_stream; mod proof; mod utils; diff --git a/rosetta-client/src/traits.rs b/rosetta-client/src/traits.rs deleted file mode 100644 index 4effe4c8..00000000 --- a/rosetta-client/src/traits.rs +++ /dev/null @@ -1,75 +0,0 @@ -#![allow(dead_code)] - -use serde::de::DeserializeOwned; -use serde::Serialize; -use std::future::Future; - -pub trait Query { - type Params: Serialize + Clone; - type Response: DeserializeOwned + Sized + Send + Sync + 'static; - type Error: DeserializeOwned + Sized + Send + Sync + 'static; - - fn query(&self, request: &Self::Params) -> Result; -} - -pub trait DefaultQuery { - type Balance: Query; -} - -/// Base Ledger Config -pub trait BaseConfig { - type Address; - type Transaction; - - /// The native currency of the blockchain - type MainCurrency: FungibleAssetConfig; -} - -pub enum AtBlock { - /// The latest block with at least 1 confirmation - Latest, - /// The earliest block - Earliest, - /// The pending block, may not yet included in the blockchain - Pending, - /// The block with the given height - Number(T::BlockNumber), - /// The block with the given unique identifier - At(T::BlockIdentifier), -} - -/// A blockchain have the concept of blocks -pub trait BlockchainConfig: BaseConfig { - type BlockIdentifier; - type BlockNumber: num_traits::Unsigned + num_traits::Bounded; - - /// The genesis block identifier - const GENESIS_BLOCK_IDENTIFIER: Self::BlockIdentifier; - - /// The forks of the blockchain, empty if there is no fork - const FORKED_BLOCKS: [(Self::BlockNumber, Self::BlockIdentifier)]; -} - -/// The blockchain have a native currency -pub trait FungibleAssetConfig { - const SYMBOL: &'static str; - const DECIMALS: u8; - type Balance: num_traits::Unsigned + num_traits::Bounded; -} - -pub trait BlockchainClient { - type Error; - - type InspectBalance: InspectBalance; -} - -/// Trait for providing balance-inspection access to a fungible asset. -pub trait InspectBalance: Sized { - type Error: Into; - - type Future: Future::Balance, Self::Error>> - + Unpin; - - /// Returns the balance of the given account. - fn balance_of(&self, account: T::Address, at: AtBlock) -> Self::Future; -} diff --git a/rosetta-client/src/wallet.rs b/rosetta-client/src/wallet.rs index b0346a1e..f153b59a 100644 --- a/rosetta-client/src/wallet.rs +++ b/rosetta-client/src/wallet.rs @@ -4,7 +4,7 @@ use crate::{ mnemonic::MnemonicStore, signer::{RosettaAccount, RosettaPublicKey, Signer}, tx_builder::GenericTransactionBuilder, - types::{AccountIdentifier, Amount, BlockIdentifier, PublicKey}, + types::{AccountIdentifier, BlockIdentifier, PublicKey}, Blockchain, BlockchainConfig, }; use anyhow::Result; @@ -108,7 +108,7 @@ impl Wallet { /// Returns the balance of the wallet. #[allow(clippy::missing_errors_doc)] - pub async fn balance(&self) -> Result { + pub async fn balance(&self) -> Result { let block = self.client.current_block().await?; let address = Address::new(self.client.config().address_format, self.account.address.clone()); @@ -138,11 +138,7 @@ impl Wallet { }, }, }; - Ok(Amount { - value: format!("{balance}"), - currency: self.client.config().currency(), - metadata: None, - }) + Ok(balance) } /// Return a stream of events, return None if the blockchain doesn't support events. diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index 112ee0f1..e380359a 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -6,7 +6,7 @@ use crate::{ address::{Address, AddressFormat}, Algorithm, PublicKey, SecretKey, }, - types::{Block, BlockIdentifier, Currency, CurveType, NetworkIdentifier, SignatureType}, + types::{Block, BlockIdentifier, CurveType, NetworkIdentifier, SignatureType}, }; use anyhow::Result; use async_trait::async_trait; @@ -51,15 +51,6 @@ impl BlockchainConfig { sub_network_identifier: None, } } - - #[must_use] - pub fn currency(&self) -> Currency { - Currency { - symbol: self.currency_symbol.into(), - decimals: self.currency_decimals, - metadata: None, - } - } } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/rosetta-docker/src/lib.rs b/rosetta-docker/src/lib.rs index ce16ca37..7885627b 100644 --- a/rosetta-docker/src/lib.rs +++ b/rosetta-docker/src/lib.rs @@ -418,10 +418,8 @@ pub mod tests { let value = 100 * u128::pow(10, config.currency_decimals); let wallet = env.ephemeral_wallet().await.unwrap(); wallet.faucet(value).await.unwrap(); - let amount = wallet.balance().await.unwrap(); - assert_eq!(amount.value, value.to_string()); - assert_eq!(amount.currency, config.currency()); - assert!(amount.metadata.is_none()); + let balance = wallet.balance().await.unwrap(); + assert_eq!(balance, value); }) .await; Ok(()) @@ -452,19 +450,19 @@ pub mod tests { // Alice and bob have no balance let balance = alice.balance().await.unwrap(); - assert_eq!(balance.value, "0"); + assert_eq!(balance, 0); let balance = bob.balance().await.unwrap(); - assert_eq!(balance.value, "0"); + assert_eq!(balance, 0); // Transfer faucets to alice alice.faucet(faucet).await.unwrap(); let balance = alice.balance().await.unwrap(); - assert_eq!(balance.value, faucet.to_string()); + assert_eq!(balance, faucet); // Alice transfers to bob alice.transfer(bob.account(), value).await.unwrap(); let amount = bob.balance().await.unwrap(); - assert_eq!(amount.value, value.to_string()); + assert_eq!(amount, value); }) .await; Ok(()) From b9c52d55dc320f008be298781fcd90423801b82f Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 05:33:27 -0300 Subject: [PATCH 22/26] Prepare to remove rosetta-types --- chains/ethereum/server/src/client.rs | 7 +- rosetta-core/src/lib.rs | 3 +- rosetta-core/src/types.rs | 129 +++++++++++++++++---------- 3 files changed, 83 insertions(+), 56 deletions(-) diff --git a/chains/ethereum/server/src/client.rs b/chains/ethereum/server/src/client.rs index a147e367..c0b1f89d 100644 --- a/chains/ethereum/server/src/client.rs +++ b/chains/ethereum/server/src/client.rs @@ -17,7 +17,7 @@ use rosetta_config_ethereum::{ }; use rosetta_core::{ crypto::{address::Address, PublicKey}, - types::{BlockIdentifier, Coin, PartialBlockIdentifier}, + types::{BlockIdentifier, PartialBlockIdentifier}, BlockchainConfig, }; use std::sync::{ @@ -173,11 +173,6 @@ where Ok(self.client.get_balance(address, Some(block_id)).await?.as_u128()) } - #[allow(clippy::unused_async, clippy::missing_errors_doc)] - pub async fn coins(&self, _address: &Address, _block: &BlockIdentifier) -> Result> { - anyhow::bail!("not a utxo chain"); - } - #[allow(clippy::single_match_else, clippy::missing_errors_doc)] pub async fn faucet(&self, address: &Address, param: u128) -> Result> { match self.private_key { diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index e380359a..ce554fcd 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -1,5 +1,6 @@ mod node_uri; pub mod traits; +pub mod types; use crate::{ crypto::{ @@ -18,7 +19,7 @@ use futures_util::stream::Empty; pub use node_uri::{NodeUri, NodeUriError}; pub use rosetta_crypto as crypto; -pub use rosetta_types as types; +// pub use rosetta_types as types; type NodeCommand = Arc Vec + Send + Sync + 'static>; diff --git a/rosetta-core/src/types.rs b/rosetta-core/src/types.rs index 377169c2..bf3525cf 100644 --- a/rosetta-core/src/types.rs +++ b/rosetta-core/src/types.rs @@ -7,15 +7,11 @@ use core::{ use serde::{Deserialize, Serialize}; pub use rosetta_types::{ - AccountIdentifier, Amount, BlockIdentifier, NetworkIdentifier, Operation, OperationIdentifier, - PartialBlockIdentifier, SignatureType, TransactionIdentifier, + AccountIdentifier, Amount, CallRequest, CurveType, NetworkIdentifier, Operation, + OperationIdentifier, PublicKey, SignatureType, TransactionIdentifier, }; -#[cfg(feature = "std")] -use std::{string::ToString, vec::Vec}; - -#[cfg(not(feature = "std"))] -use alloc::{string::ToString, vec::Vec}; +use std::vec::Vec; /// Block : Blocks contain an array of Transactions that occurred at a particular `BlockIdentifier`. /// A hard requirement for blocks returned by Rosetta implementations is that they MUST be @@ -38,59 +34,94 @@ pub struct Block { pub metadata: Option, } -/// `Currency` is composed of a canonical Symbol and Decimals. This Decimals value is used to -/// convert an Amount.Value from atomic units (Satoshis) to standard units (Bitcoins). +/// `BlockIdentifier` : The `block_identifier` uniquely identifies a block in a particular network. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct Currency { - /// Canonical symbol associated with a currency. - pub symbol: &'static str, - /// Number of decimal places in the standard unit representation of the amount. For example, - /// BTC has 8 decimals. Note that it is not possible to represent the value of some currency in - /// atomic units that is not base 10. - pub decimals: u32, +pub struct BlockIdentifier { + /// This is also known as the block height. + #[serde(rename = "index")] + pub index: u64, + /// This should be normalized according to the case specified in the block_hash_case network + /// options. + #[serde(skip_serializing)] + pub hash: [u8; 32], +} + +impl BlockIdentifier { + /// The `block_identifier` uniquely identifies a block in a particular network. + #[must_use] + pub const fn new(index: u64, hash: [u8; 32]) -> Self { + Self { index, hash } + } } -/// `CurveType` is the type of cryptographic curve associated with a `PublicKey`. -/// * [secp256k1: SEC compressed - `33 bytes`](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) -/// * [secp256r1: SEC compressed - `33 bytes`](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) -/// * [edwards25519: `y (255-bits) || x-sign-bit (1-bit)` - `32 bytes`](https://ed25519.cr.yp.to/ed25519-20110926.pdf) -/// * [tweedle: 1st pk : Fq.t (32 bytes) || 2nd pk : Fq.t (32 bytes)](https://github.com/CodaProtocol/coda/blob/develop/rfcs/0038-rosetta-construction-api.md#marshal-keys) -/// * [pallas: `x (255 bits) || y-parity-bit (1-bit) - 32 bytes`](https://github.com/zcash/pasta) -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] -pub enum CurveType { - #[serde(rename = "secp256k1")] - Secp256k1, - #[serde(rename = "secp256r1")] - Secp256r1, - #[serde(rename = "edwards25519")] - Edwards25519, - #[serde(rename = "tweedle")] - Tweedle, - #[serde(rename = "pallas")] - Pallas, - #[serde(rename = "schnorrkel")] - Schnorrkel, +/// `PartialBlockIdentifier` : When fetching data by `BlockIdentifier`, it may be possible to only +/// specify the index or hash. If neither property is specified, it is assumed that the client is +/// making a request at the current block. +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +pub struct PartialBlockIdentifier { + #[serde(rename = "index", skip_serializing_if = "Option::is_none")] + pub index: Option, + #[serde(skip_serializing)] + pub hash: Option<[u8; 32]>, } -impl ToString for CurveType { - fn to_string(&self) -> String { - match self { - Self::Secp256k1 => String::from("secp256k1"), - Self::Secp256r1 => String::from("secp256r1"), - Self::Edwards25519 => String::from("edwards25519"), - Self::Tweedle => String::from("tweedle"), - Self::Pallas => String::from("pallas"), - Self::Schnorrkel => String::from("schnorrkel"), - } +impl From for PartialBlockIdentifier { + fn from(block_identifier: BlockIdentifier) -> Self { + Self { index: Some(block_identifier.index), hash: Some(block_identifier.hash) } } } -impl Default for CurveType { - fn default() -> Self { - Self::Secp256k1 +impl PartialBlockIdentifier { + /// When fetching data by `BlockIdentifier`, it may be possible to only specify the index or + /// hash. If neither property is specified, it is assumed that the client is making a request at + /// the current block. + #[must_use] + pub const fn new() -> Self { + Self { index: None, hash: None } } } +// /// `CurveType` is the type of cryptographic curve associated with a `PublicKey`. +// /// * [secp256k1: SEC compressed - `33 bytes`](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) +// /// * [secp256r1: SEC compressed - `33 bytes`](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) +// /// * [edwards25519: `y (255-bits) || x-sign-bit (1-bit)` - `32 bytes`](https://ed25519.cr.yp.to/ed25519-20110926.pdf) +// /// * [tweedle: 1st pk : Fq.t (32 bytes) || 2nd pk : Fq.t (32 bytes)](https://github.com/CodaProtocol/coda/blob/develop/rfcs/0038-rosetta-construction-api.md#marshal-keys) +// /// * [pallas: `x (255 bits) || y-parity-bit (1-bit) - 32 bytes`](https://github.com/zcash/pasta) +// #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +// pub enum CurveType { +// #[serde(rename = "secp256k1")] +// Secp256k1, +// #[serde(rename = "secp256r1")] +// Secp256r1, +// #[serde(rename = "edwards25519")] +// Edwards25519, +// #[serde(rename = "tweedle")] +// Tweedle, +// #[serde(rename = "pallas")] +// Pallas, +// #[serde(rename = "schnorrkel")] +// Schnorrkel, +// } + +// impl ToString for CurveType { +// fn to_string(&self) -> String { +// match self { +// Self::Secp256k1 => String::from("secp256k1"), +// Self::Secp256r1 => String::from("secp256r1"), +// Self::Edwards25519 => String::from("edwards25519"), +// Self::Tweedle => String::from("tweedle"), +// Self::Pallas => String::from("pallas"), +// Self::Schnorrkel => String::from("schnorrkel"), +// } +// } +// } + +// impl Default for CurveType { +// fn default() -> Self { +// Self::Secp256k1 +// } +// } + /// `Transaction` contain an array of Operations that are attributable to the same /// `TransactionIdentifier`. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] From 3f08e7089e5a11dedd4a0a5c10ce385d951cdcd6 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 06:07:04 -0300 Subject: [PATCH 23/26] Delete more unused types --- rosetta-core/src/lib.rs | 13 +-- rosetta-core/src/types.rs | 45 +-------- rosetta-types/src/account_balance_request.rs | 40 -------- rosetta-types/src/account_balance_response.rs | 42 --------- rosetta-types/src/account_coins_request.rs | 40 -------- rosetta-types/src/account_coins_response.rs | 37 -------- rosetta-types/src/account_faucet_request.rs | 24 ----- rosetta-types/src/allow.rs | 92 ------------------- rosetta-types/src/balance_exemption.rs | 47 ---------- rosetta-types/src/block_request.rs | 9 +- .../src/block_transaction_request.rs | 34 ------- .../src/block_transaction_response.rs | 25 ----- rosetta-types/src/call_request.rs | 5 +- .../src/construction_combine_request.rs | 10 +- .../src/construction_derive_request.rs | 9 +- .../src/construction_hash_request.rs | 9 +- .../src/construction_metadata_request.rs | 40 -------- .../src/construction_metadata_response.rs | 35 ------- .../src/construction_parse_request.rs | 10 +- .../src/construction_payloads_request.rs | 9 +- .../src/construction_preprocess_request.rs | 9 +- .../src/construction_submit_request.rs | 9 +- rosetta-types/src/events_blocks_request.rs | 6 +- rosetta-types/src/lib.rs | 50 ---------- rosetta-types/src/mempool_response.rs | 26 ------ .../src/mempool_transaction_request.rs | 29 ------ .../src/mempool_transaction_response.rs | 28 ------ rosetta-types/src/network_identifier.rs | 31 ------- rosetta-types/src/network_list_response.rs | 26 ------ rosetta-types/src/network_options_response.rs | 28 ------ rosetta-types/src/network_request.rs | 28 ------ rosetta-types/src/network_status_response.rs | 69 -------------- rosetta-types/src/operator.rs | 36 -------- rosetta-types/src/related_transaction.rs | 4 +- .../src/search_transactions_request.rs | 79 ---------------- .../src/search_transactions_response.rs | 41 --------- rosetta-types/src/sub_network_identifier.rs | 29 ------ rosetta-types/src/sync_status.rs | 51 ---------- .../src/transaction_identifier_response.rs | 29 ------ 39 files changed, 23 insertions(+), 1160 deletions(-) delete mode 100644 rosetta-types/src/account_balance_request.rs delete mode 100644 rosetta-types/src/account_balance_response.rs delete mode 100644 rosetta-types/src/account_coins_request.rs delete mode 100644 rosetta-types/src/account_coins_response.rs delete mode 100644 rosetta-types/src/account_faucet_request.rs delete mode 100644 rosetta-types/src/allow.rs delete mode 100644 rosetta-types/src/balance_exemption.rs delete mode 100644 rosetta-types/src/block_transaction_request.rs delete mode 100644 rosetta-types/src/block_transaction_response.rs delete mode 100644 rosetta-types/src/construction_metadata_request.rs delete mode 100644 rosetta-types/src/construction_metadata_response.rs delete mode 100644 rosetta-types/src/mempool_response.rs delete mode 100644 rosetta-types/src/mempool_transaction_request.rs delete mode 100644 rosetta-types/src/mempool_transaction_response.rs delete mode 100644 rosetta-types/src/network_identifier.rs delete mode 100644 rosetta-types/src/network_list_response.rs delete mode 100644 rosetta-types/src/network_options_response.rs delete mode 100644 rosetta-types/src/network_request.rs delete mode 100644 rosetta-types/src/network_status_response.rs delete mode 100644 rosetta-types/src/operator.rs delete mode 100644 rosetta-types/src/search_transactions_request.rs delete mode 100644 rosetta-types/src/search_transactions_response.rs delete mode 100644 rosetta-types/src/sub_network_identifier.rs delete mode 100644 rosetta-types/src/sync_status.rs delete mode 100644 rosetta-types/src/transaction_identifier_response.rs diff --git a/rosetta-core/src/lib.rs b/rosetta-core/src/lib.rs index ce554fcd..6ee421e0 100644 --- a/rosetta-core/src/lib.rs +++ b/rosetta-core/src/lib.rs @@ -7,7 +7,7 @@ use crate::{ address::{Address, AddressFormat}, Algorithm, PublicKey, SecretKey, }, - types::{Block, BlockIdentifier, CurveType, NetworkIdentifier, SignatureType}, + types::{Block, BlockIdentifier, CurveType, SignatureType}, }; use anyhow::Result; use async_trait::async_trait; @@ -43,17 +43,6 @@ pub struct BlockchainConfig { pub testnet: bool, } -impl BlockchainConfig { - #[must_use] - pub fn network(&self) -> NetworkIdentifier { - NetworkIdentifier { - blockchain: self.blockchain.into(), - network: self.network.into(), - sub_network_identifier: None, - } - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum BlockOrIdentifier { Identifier(BlockIdentifier), diff --git a/rosetta-core/src/types.rs b/rosetta-core/src/types.rs index bf3525cf..7453d7ca 100644 --- a/rosetta-core/src/types.rs +++ b/rosetta-core/src/types.rs @@ -7,8 +7,8 @@ use core::{ use serde::{Deserialize, Serialize}; pub use rosetta_types::{ - AccountIdentifier, Amount, CallRequest, CurveType, NetworkIdentifier, Operation, - OperationIdentifier, PublicKey, SignatureType, TransactionIdentifier, + AccountIdentifier, CallRequest, CurveType, Operation, OperationIdentifier, PublicKey, + SignatureType, TransactionIdentifier, }; use std::vec::Vec; @@ -81,47 +81,6 @@ impl PartialBlockIdentifier { } } -// /// `CurveType` is the type of cryptographic curve associated with a `PublicKey`. -// /// * [secp256k1: SEC compressed - `33 bytes`](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) -// /// * [secp256r1: SEC compressed - `33 bytes`](https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) -// /// * [edwards25519: `y (255-bits) || x-sign-bit (1-bit)` - `32 bytes`](https://ed25519.cr.yp.to/ed25519-20110926.pdf) -// /// * [tweedle: 1st pk : Fq.t (32 bytes) || 2nd pk : Fq.t (32 bytes)](https://github.com/CodaProtocol/coda/blob/develop/rfcs/0038-rosetta-construction-api.md#marshal-keys) -// /// * [pallas: `x (255 bits) || y-parity-bit (1-bit) - 32 bytes`](https://github.com/zcash/pasta) -// #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] -// pub enum CurveType { -// #[serde(rename = "secp256k1")] -// Secp256k1, -// #[serde(rename = "secp256r1")] -// Secp256r1, -// #[serde(rename = "edwards25519")] -// Edwards25519, -// #[serde(rename = "tweedle")] -// Tweedle, -// #[serde(rename = "pallas")] -// Pallas, -// #[serde(rename = "schnorrkel")] -// Schnorrkel, -// } - -// impl ToString for CurveType { -// fn to_string(&self) -> String { -// match self { -// Self::Secp256k1 => String::from("secp256k1"), -// Self::Secp256r1 => String::from("secp256r1"), -// Self::Edwards25519 => String::from("edwards25519"), -// Self::Tweedle => String::from("tweedle"), -// Self::Pallas => String::from("pallas"), -// Self::Schnorrkel => String::from("schnorrkel"), -// } -// } -// } - -// impl Default for CurveType { -// fn default() -> Self { -// Self::Secp256k1 -// } -// } - /// `Transaction` contain an array of Operations that are attributable to the same /// `TransactionIdentifier`. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] diff --git a/rosetta-types/src/account_balance_request.rs b/rosetta-types/src/account_balance_request.rs deleted file mode 100644 index 317c2135..00000000 --- a/rosetta-types/src/account_balance_request.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `AccountBalanceRequest` : An `AccountBalanceRequest` is utilized to make a balance request on -/// the /account/balance endpoint. If the `block_identifier` is populated, a historical balance -/// query should be performed. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct AccountBalanceRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, - #[serde(rename = "account_identifier")] - pub account_identifier: crate::AccountIdentifier, - #[serde(rename = "block_identifier", skip_serializing_if = "Option::is_none")] - pub block_identifier: Option, - /// In some cases, the caller may not want to retrieve all available balances for an - /// AccountIdentifier. If the currencies field is populated, only balances for the specified - /// currencies will be returned. If not populated, all available balances will be returned. - #[serde(rename = "currencies", skip_serializing_if = "Option::is_none")] - pub currencies: Option>, -} - -impl AccountBalanceRequest { - /// An `AccountBalanceRequest` is utilized to make a balance request on the /account/balance - /// endpoint. If the `block_identifier` is populated, a historical balance query should be - /// performed. - #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - account_identifier: crate::AccountIdentifier, - ) -> Self { - Self { network_identifier, account_identifier, block_identifier: None, currencies: None } - } -} diff --git a/rosetta-types/src/account_balance_response.rs b/rosetta-types/src/account_balance_response.rs deleted file mode 100644 index b4f5599d..00000000 --- a/rosetta-types/src/account_balance_response.rs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `AccountBalanceResponse` : An `AccountBalanceResponse` is returned on the /account/balance -/// endpoint. If an account has a balance for each `AccountIdentifier` describing it (ex: an ERC-20 -/// token balance on a few smart contracts), an account balance request must be made with each -/// `AccountIdentifier`. The `coins` field was removed and replaced by by `/account/coins` in -/// `v1.4.7`. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct AccountBalanceResponse { - #[serde(rename = "block_identifier")] - pub block_identifier: crate::BlockIdentifier, - /// A single account may have a balance in multiple currencies. - #[serde(rename = "balances")] - pub balances: Vec, - /// Account-based blockchains that utilize a nonce or sequence number should include that - /// number in the metadata. This number could be unique to the identifier or global across the - /// account address. - #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -impl AccountBalanceResponse { - /// An `AccountBalanceResponse` is returned on the /account/balance endpoint. If an account has - /// a balance for each `AccountIdentifier` describing it (ex: an ERC-20 token balance on a few - /// smart contracts), an account balance request must be made with each `AccountIdentifier`. - /// The `coins` field was removed and replaced by by `/account/coins` in `v1.4.7`. - #[must_use] - pub const fn new( - block_identifier: crate::BlockIdentifier, - balances: Vec, - ) -> Self { - Self { block_identifier, balances, metadata: None } - } -} diff --git a/rosetta-types/src/account_coins_request.rs b/rosetta-types/src/account_coins_request.rs deleted file mode 100644 index cdf908e4..00000000 --- a/rosetta-types/src/account_coins_request.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `AccountCoinsRequest` : `AccountCoinsRequest` is utilized to make a request on the -/// /account/coins endpoint. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct AccountCoinsRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, - #[serde(rename = "account_identifier")] - pub account_identifier: crate::AccountIdentifier, - /// Include state from the mempool when looking up an account's unspent coins. Note, using this - /// functionality breaks any guarantee of idempotency. - #[serde(rename = "include_mempool")] - pub include_mempool: bool, - /// In some cases, the caller may not want to retrieve coins for all currencies for an - /// AccountIdentifier. If the currencies field is populated, only coins for the specified - /// currencies will be returned. If not populated, all unspent coins will be returned. - #[serde(rename = "currencies", skip_serializing_if = "Option::is_none")] - pub currencies: Option>, -} - -impl AccountCoinsRequest { - /// `AccountCoinsRequest` is utilized to make a request on the /account/coins endpoint. - #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - account_identifier: crate::AccountIdentifier, - include_mempool: bool, - ) -> Self { - Self { network_identifier, account_identifier, include_mempool, currencies: None } - } -} diff --git a/rosetta-types/src/account_coins_response.rs b/rosetta-types/src/account_coins_response.rs deleted file mode 100644 index a89ac5af..00000000 --- a/rosetta-types/src/account_coins_response.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `AccountCoinsResponse` : `AccountCoinsResponse` is returned on the /account/coins endpoint and -/// includes all unspent Coins owned by an `AccountIdentifier`. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct AccountCoinsResponse { - #[serde(rename = "block_identifier")] - pub block_identifier: crate::BlockIdentifier, - /// If a blockchain is UTXO-based, all unspent Coins owned by an account_identifier should be - /// returned alongside the balance. It is highly recommended to populate this field so that - /// users of the Rosetta API implementation don't need to maintain their own indexer to track - /// their UTXOs. - #[serde(rename = "coins")] - pub coins: Vec, - /// Account-based blockchains that utilize a nonce or sequence number should include that - /// number in the metadata. This number could be unique to the identifier or global across the - /// account address. - #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -impl AccountCoinsResponse { - /// `AccountCoinsResponse` is returned on the /account/coins endpoint and includes all unspent - /// Coins owned by an `AccountIdentifier`. - #[must_use] - pub const fn new(block_identifier: crate::BlockIdentifier, coins: Vec) -> Self { - Self { block_identifier, coins, metadata: None } - } -} diff --git a/rosetta-types/src/account_faucet_request.rs b/rosetta-types/src/account_faucet_request.rs deleted file mode 100644 index b91c9528..00000000 --- a/rosetta-types/src/account_faucet_request.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{AccountIdentifier, NetworkIdentifier}; - -/// `AccountFaucetRequest` : `AccountFaucetRequest` is sent for faucet on an account. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct AccountFaucetRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: NetworkIdentifier, - #[serde(rename = "account_identifier")] - pub account_identifier: AccountIdentifier, - #[serde(rename = "faucet_parameter")] - pub faucet_parameter: u128, -} - -impl AccountFaucetRequest { - /// `AccountCoinsRequest` is utilized to make a request on the /account/coins endpoint. - #[must_use] - pub const fn new( - network_identifier: NetworkIdentifier, - account_identifier: AccountIdentifier, - faucet_parameter: u128, - ) -> Self { - Self { network_identifier, account_identifier, faucet_parameter } - } -} diff --git a/rosetta-types/src/allow.rs b/rosetta-types/src/allow.rs deleted file mode 100644 index e638658c..00000000 --- a/rosetta-types/src/allow.rs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// Allow : Allow specifies supported Operation status, Operation types, and all possible error -/// statuses. This Allow object is used by clients to validate the correctness of a Rosetta Server -/// implementation. It is expected that these clients will error if they receive some response that -/// contains any of the above information that is not specified here. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct Allow { - /// All Operation.Status this implementation supports. Any status that is returned during - /// parsing that is not listed here will cause client validation to error. - #[serde(rename = "operation_statuses")] - pub operation_statuses: Vec, - /// All Operation.Type this implementation supports. Any type that is returned during parsing - /// that is not listed here will cause client validation to error. - #[serde(rename = "operation_types")] - pub operation_types: Vec, - /// All Errors that this implementation could return. Any error that is returned during parsing - /// that is not listed here will cause client validation to error. - #[serde(rename = "errors")] - pub errors: Vec, - /// Any Rosetta implementation that supports querying the balance of an account at any height - /// in the past should set this to true. - #[serde(rename = "historical_balance_lookup")] - pub historical_balance_lookup: bool, - /// If populated, `timestamp_start_index` indicates the first block index where block - /// timestamps are considered valid (i.e. all blocks less than `timestamp_start_index` could - /// have invalid timestamps). This is useful when the genesis block (or blocks) of a network - /// have timestamp 0. If not populated, block timestamps are assumed to be valid for all - /// available blocks. - #[serde(rename = "timestamp_start_index", skip_serializing_if = "Option::is_none")] - pub timestamp_start_index: Option, - /// All methods that are supported by the /call endpoint. Communicating which parameters should - /// be provided to /call is the responsibility of the implementer (this is en lieu of defining - /// an entire type system and requiring the implementer to define that in Allow). - #[serde(rename = "call_methods")] - pub call_methods: Option>, - /// BalanceExemptions is an array of BalanceExemption indicating which account balances could - /// change without a corresponding Operation. BalanceExemptions should be used sparingly as - /// they may introduce significant complexity for integrators that attempt to reconcile all - /// account balance changes. If your implementation relies on any BalanceExemptions, you MUST - /// implement historical balance lookup (the ability to query an account balance at any - /// BlockIdentifier). - #[serde(rename = "balance_exemptions")] - pub balance_exemptions: Option>, - /// Any Rosetta implementation that can update an AccountIdentifier's unspent coins based on - /// the contents of the mempool should populate this field as true. If false, requests to - /// `/account/coins` that set `include_mempool` as true will be automatically rejected. - #[serde(rename = "mempool_coins")] - pub mempool_coins: bool, - #[serde(rename = "block_hash_case", skip_serializing_if = "Option::is_none")] - pub block_hash_case: Option, - #[serde(rename = "transaction_hash_case", skip_serializing_if = "Option::is_none")] - pub transaction_hash_case: Option, -} - -impl Allow { - /// Allow specifies supported Operation status, Operation types, and all possible error - /// statuses. This Allow object is used by clients to validate the correctness of a Rosetta - /// Server implementation. It is expected that these clients will error if they receive some - /// response that contains any of the above information that is not specified here. - #[must_use] - pub const fn new( - operation_statuses: Vec, - operation_types: Vec, - errors: Vec, - historical_balance_lookup: bool, - call_methods: Option>, - balance_exemptions: Option>, - mempool_coins: bool, - ) -> Self { - Self { - operation_statuses, - operation_types, - errors, - historical_balance_lookup, - timestamp_start_index: None, - call_methods, - balance_exemptions, - mempool_coins, - block_hash_case: None, - transaction_hash_case: None, - } - } -} diff --git a/rosetta-types/src/balance_exemption.rs b/rosetta-types/src/balance_exemption.rs deleted file mode 100644 index cbaf9f9f..00000000 --- a/rosetta-types/src/balance_exemption.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `BalanceExemption` : `BalanceExemption` indicates that the balance for an exempt account could -/// change without a corresponding Operation. This typically occurs with staking rewards, vesting -/// balances, and Currencies with a dynamic supply. Currently, it is possible to exempt an account -/// from strict reconciliation by SubAccountIdentifier.Address or by Currency. This means that any -/// account with SubAccountIdentifier.Address would be exempt or any balance of a particular -/// Currency would be exempt, respectively. `BalanceExemptions` should be used sparingly as they -/// may introduce significant complexity for integrators that attempt to reconcile all account -/// balance changes. If your implementation relies on any `BalanceExemptions`, you MUST implement -/// historical balance lookup (the ability to query an account balance at any `BlockIdentifier`). -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct BalanceExemption { - /// SubAccountAddress is the SubAccountIdentifier.Address that the BalanceExemption applies to - /// (regardless of the value of SubAccountIdentifier.Metadata). - #[serde(rename = "sub_account_address", skip_serializing_if = "Option::is_none")] - pub sub_account_address: Option, - #[serde(rename = "currency", skip_serializing_if = "Option::is_none")] - pub currency: Option, - #[serde(rename = "exemption_type", skip_serializing_if = "Option::is_none")] - pub exemption_type: Option, -} - -impl BalanceExemption { - /// `BalanceExemption` indicates that the balance for an exempt account could change without a - /// corresponding Operation. This typically occurs with staking rewards, vesting balances, and - /// Currencies with a dynamic supply. Currently, it is possible to exempt an account from - /// strict reconciliation by SubAccountIdentifier.Address or by Currency. This means that any - /// account with SubAccountIdentifier.Address would be exempt or any balance of a particular - /// Currency would be exempt, respectively. `BalanceExemptions` should be used sparingly as - /// they may introduce significant complexity for integrators that attempt to reconcile all - /// account balance changes. If your implementation relies on any `BalanceExemptions`, you MUST - /// implement historical balance lookup (the ability to query an account balance at any - /// `BlockIdentifier`). - #[must_use] - pub const fn new() -> Self { - Self { sub_account_address: None, currency: None, exemption_type: None } - } -} diff --git a/rosetta-types/src/block_request.rs b/rosetta-types/src/block_request.rs index 2dc41918..58404c11 100644 --- a/rosetta-types/src/block_request.rs +++ b/rosetta-types/src/block_request.rs @@ -11,8 +11,6 @@ /// `BlockRequest` : A `BlockRequest` is utilized to make a block request on the /block endpoint. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct BlockRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, #[serde(rename = "block_identifier")] pub block_identifier: crate::PartialBlockIdentifier, } @@ -20,10 +18,7 @@ pub struct BlockRequest { impl BlockRequest { /// A `BlockRequest` is utilized to make a block request on the /block endpoint. #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - block_identifier: crate::PartialBlockIdentifier, - ) -> Self { - Self { network_identifier, block_identifier } + pub const fn new(block_identifier: crate::PartialBlockIdentifier) -> Self { + Self { block_identifier } } } diff --git a/rosetta-types/src/block_transaction_request.rs b/rosetta-types/src/block_transaction_request.rs deleted file mode 100644 index f78ace76..00000000 --- a/rosetta-types/src/block_transaction_request.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `BlockTransactionRequest` : A `BlockTransactionRequest` is used to fetch a Transaction included -/// in a block that is not returned in a `BlockResponse`. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct BlockTransactionRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, - #[serde(rename = "block_identifier")] - pub block_identifier: crate::BlockIdentifier, - #[serde(rename = "transaction_identifier")] - pub transaction_identifier: crate::TransactionIdentifier, -} - -impl BlockTransactionRequest { - /// A `BlockTransactionRequest` is used to fetch a Transaction included in a block that is not - /// returned in a `BlockResponse`. - #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - block_identifier: crate::BlockIdentifier, - transaction_identifier: crate::TransactionIdentifier, - ) -> Self { - Self { network_identifier, block_identifier, transaction_identifier } - } -} diff --git a/rosetta-types/src/block_transaction_response.rs b/rosetta-types/src/block_transaction_response.rs deleted file mode 100644 index a382c968..00000000 --- a/rosetta-types/src/block_transaction_response.rs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `BlockTransactionResponse` : A `BlockTransactionResponse` contains information about a block -/// transaction. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct BlockTransactionResponse { - #[serde(rename = "transaction")] - pub transaction: crate::Transaction, -} - -impl BlockTransactionResponse { - /// A `BlockTransactionResponse` contains information about a block transaction. - #[must_use] - pub const fn new(transaction: crate::Transaction) -> Self { - Self { transaction } - } -} diff --git a/rosetta-types/src/call_request.rs b/rosetta-types/src/call_request.rs index c20eb126..e8555afe 100644 --- a/rosetta-types/src/call_request.rs +++ b/rosetta-types/src/call_request.rs @@ -11,8 +11,6 @@ /// `CallRequest` : `CallRequest` is the input to the `/call` endpoint. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct CallRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, /// Method is some network-specific procedure call. This method could map to a network-specific /// RPC endpoint, a method in an SDK generated from a smart contract, or some hybrid of the /// two. The implementation must define all available methods in the Allow object. However, it @@ -31,11 +29,10 @@ impl CallRequest { /// `CallRequest` is the input to the `/call` endpoint. #[must_use] pub const fn new( - network_identifier: crate::NetworkIdentifier, method: String, parameters: serde_json::Value, block_identifier: Option, ) -> Self { - Self { network_identifier, method, parameters, block_identifier } + Self { method, parameters, block_identifier } } } diff --git a/rosetta-types/src/construction_combine_request.rs b/rosetta-types/src/construction_combine_request.rs index 69d2f8d9..4d8380d4 100644 --- a/rosetta-types/src/construction_combine_request.rs +++ b/rosetta-types/src/construction_combine_request.rs @@ -13,8 +13,6 @@ /// `/construction/payloads` and all required signatures to create a network transaction. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct ConstructionCombineRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, #[serde(rename = "unsigned_transaction")] pub unsigned_transaction: String, #[serde(rename = "signatures")] @@ -26,11 +24,7 @@ impl ConstructionCombineRequest { /// contains the unsigned transaction blob returned by `/construction/payloads` and all required /// signatures to create a network transaction. #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - unsigned_transaction: String, - signatures: Vec, - ) -> Self { - Self { network_identifier, unsigned_transaction, signatures } + pub const fn new(unsigned_transaction: String, signatures: Vec) -> Self { + Self { unsigned_transaction, signatures } } } diff --git a/rosetta-types/src/construction_derive_request.rs b/rosetta-types/src/construction_derive_request.rs index e5040cda..c60451b0 100644 --- a/rosetta-types/src/construction_derive_request.rs +++ b/rosetta-types/src/construction_derive_request.rs @@ -15,8 +15,6 @@ /// vs normal accounts). #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct ConstructionDeriveRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, #[serde(rename = "public_key")] pub public_key: crate::PublicKey, #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] @@ -29,10 +27,7 @@ impl ConstructionDeriveRequest { /// different networks. Metadata is provided in the request because some blockchains allow for /// multiple address types (i.e. different address for validators vs normal accounts). #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - public_key: crate::PublicKey, - ) -> Self { - Self { network_identifier, public_key, metadata: None } + pub const fn new(public_key: crate::PublicKey) -> Self { + Self { public_key, metadata: None } } } diff --git a/rosetta-types/src/construction_hash_request.rs b/rosetta-types/src/construction_hash_request.rs index 5905315b..7965286a 100644 --- a/rosetta-types/src/construction_hash_request.rs +++ b/rosetta-types/src/construction_hash_request.rs @@ -12,8 +12,6 @@ /// endpoint. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct ConstructionHashRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, #[serde(rename = "signed_transaction")] pub signed_transaction: String, } @@ -21,10 +19,7 @@ pub struct ConstructionHashRequest { impl ConstructionHashRequest { /// `ConstructionHashRequest` is the input to the `/construction/hash` endpoint. #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - signed_transaction: String, - ) -> Self { - Self { network_identifier, signed_transaction } + pub const fn new(signed_transaction: String) -> Self { + Self { signed_transaction } } } diff --git a/rosetta-types/src/construction_metadata_request.rs b/rosetta-types/src/construction_metadata_request.rs deleted file mode 100644 index f57bddd5..00000000 --- a/rosetta-types/src/construction_metadata_request.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `ConstructionMetadataRequest` : A `ConstructionMetadataRequest` is utilized to get information -/// required to construct a transaction. The Options object used to specify which metadata to -/// return is left purposely unstructured to allow flexibility for implementers. Options is not -/// required in the case that there is network-wide metadata of interest. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct ConstructionMetadataRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, - /// Some blockchains require different metadata for different types of transaction construction - /// (ex: delegation versus a transfer). Instead of requiring a blockchain node to return all - /// possible types of metadata for construction (which may require multiple node fetches), the - /// client can populate an options object to limit the metadata returned to only the subset - /// required. - #[serde(rename = "options", skip_serializing_if = "Option::is_none")] - pub options: Option, - - #[serde(rename = "public_keys", skip_serializing_if = "Vec::is_empty")] - pub public_keys: Vec, -} - -impl ConstructionMetadataRequest { - /// A `ConstructionMetadataRequest` is utilized to get information required to construct a - /// transaction. The Options object used to specify which metadata to return is left purposely - /// unstructured to allow flexibility for implementers. Options is not required in the case that - /// there is network-wide metadata of interest. - #[must_use] - pub const fn new(network_identifier: crate::NetworkIdentifier) -> Self { - Self { network_identifier, options: None, public_keys: vec![] } - } -} diff --git a/rosetta-types/src/construction_metadata_response.rs b/rosetta-types/src/construction_metadata_response.rs deleted file mode 100644 index 5622adb9..00000000 --- a/rosetta-types/src/construction_metadata_response.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `ConstructionMetadataResponse` : The `ConstructionMetadataResponse` returns network-specific -/// metadata used for transaction construction. Optionally, the implementer can return the -/// suggested fee associated with the transaction being constructed. The caller may use this info to -/// adjust the intent of the transaction or to create a transaction with a different account that -/// can pay the suggested fee. Suggested fee is an array in case fee payment must occur in multiple -/// currencies. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct ConstructionMetadataResponse { - #[serde(rename = "metadata")] - pub metadata: serde_json::Value, - #[serde(rename = "suggested_fee", skip_serializing_if = "Option::is_none")] - pub suggested_fee: Option>, -} - -impl ConstructionMetadataResponse { - /// The `ConstructionMetadataResponse` returns network-specific metadata used for transaction - /// construction. Optionally, the implementer can return the suggested fee associated with the - /// transaction being constructed. The caller may use this info to adjust the intent of the - /// transaction or to create a transaction with a different account that can pay the suggested - /// fee. Suggested fee is an array in case fee payment must occur in multiple currencies. - #[must_use] - pub const fn new(metadata: serde_json::Value) -> Self { - Self { metadata, suggested_fee: None } - } -} diff --git a/rosetta-types/src/construction_parse_request.rs b/rosetta-types/src/construction_parse_request.rs index 60d38252..e77b38ef 100644 --- a/rosetta-types/src/construction_parse_request.rs +++ b/rosetta-types/src/construction_parse_request.rs @@ -13,8 +13,6 @@ /// transaction. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct ConstructionParseRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, /// Signed is a boolean indicating whether the transaction is signed. #[serde(rename = "signed")] pub signed: bool, @@ -28,11 +26,7 @@ impl ConstructionParseRequest { /// `ConstructionParseRequest` is the input to the `/construction/parse` endpoint. It allows the /// caller to parse either an unsigned or signed transaction. #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - signed: bool, - transaction: String, - ) -> Self { - Self { network_identifier, signed, transaction } + pub const fn new(signed: bool, transaction: String) -> Self { + Self { signed, transaction } } } diff --git a/rosetta-types/src/construction_payloads_request.rs b/rosetta-types/src/construction_payloads_request.rs index ff40a481..53c1b789 100644 --- a/rosetta-types/src/construction_payloads_request.rs +++ b/rosetta-types/src/construction_payloads_request.rs @@ -14,8 +14,6 @@ /// associated with the `AccountIdentifiers` returned in `ConstructionPreprocessResponse`. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct ConstructionPayloadsRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, #[serde(rename = "operations")] pub operations: Vec, #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] @@ -30,10 +28,7 @@ impl ConstructionPayloadsRequest { /// `/construction/metadata`. Optionally, the request can also include an array of `PublicKeys` /// associated with the `AccountIdentifiers` returned in `ConstructionPreprocessResponse`. #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - operations: Vec, - ) -> Self { - Self { network_identifier, operations, metadata: None, public_keys: None } + pub const fn new(operations: Vec) -> Self { + Self { operations, metadata: None, public_keys: None } } } diff --git a/rosetta-types/src/construction_preprocess_request.rs b/rosetta-types/src/construction_preprocess_request.rs index 1f41281e..812eb423 100644 --- a/rosetta-types/src/construction_preprocess_request.rs +++ b/rosetta-types/src/construction_preprocess_request.rs @@ -16,8 +16,6 @@ /// `/construction/metadata`. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct ConstructionPreprocessRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, #[serde(rename = "operations")] pub operations: Vec, #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] @@ -32,10 +30,7 @@ impl ConstructionPreprocessRequest { /// populate required Metadata). If live data is required for construction, it MUST be fetched /// in the call to `/construction/metadata`. #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - operations: Vec, - ) -> Self { - Self { network_identifier, operations, metadata: None } + pub const fn new(operations: Vec) -> Self { + Self { operations, metadata: None } } } diff --git a/rosetta-types/src/construction_submit_request.rs b/rosetta-types/src/construction_submit_request.rs index f4164f92..d64d390e 100644 --- a/rosetta-types/src/construction_submit_request.rs +++ b/rosetta-types/src/construction_submit_request.rs @@ -11,8 +11,6 @@ /// `ConstructionSubmitRequest` : The transaction submission request includes a signed transaction. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct ConstructionSubmitRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, #[serde(rename = "signed_transaction")] pub signed_transaction: String, } @@ -20,10 +18,7 @@ pub struct ConstructionSubmitRequest { impl ConstructionSubmitRequest { /// The transaction submission request includes a signed transaction. #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - signed_transaction: String, - ) -> Self { - Self { network_identifier, signed_transaction } + pub const fn new(signed_transaction: String) -> Self { + Self { signed_transaction } } } diff --git a/rosetta-types/src/events_blocks_request.rs b/rosetta-types/src/events_blocks_request.rs index 8b333d80..24b45482 100644 --- a/rosetta-types/src/events_blocks_request.rs +++ b/rosetta-types/src/events_blocks_request.rs @@ -12,8 +12,6 @@ /// indicating which blocks were added and removed from storage to reach the current state. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct EventsBlocksRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, /// offset is the offset into the event stream to sync events from. If this field is not /// populated, we return the limit events backwards from tip. If this is set to 0, we start /// from the beginning. @@ -29,7 +27,7 @@ impl EventsBlocksRequest { /// `EventsBlocksRequest` is utilized to fetch a sequence of `BlockEvents` indicating which /// blocks were added and removed from storage to reach the current state. #[must_use] - pub const fn new(network_identifier: crate::NetworkIdentifier) -> Self { - Self { network_identifier, offset: None, limit: None } + pub const fn new() -> Self { + Self { offset: None, limit: None } } } diff --git a/rosetta-types/src/lib.rs b/rosetta-types/src/lib.rs index 10634a96..cb267bf6 100644 --- a/rosetta-types/src/lib.rs +++ b/rosetta-types/src/lib.rs @@ -3,24 +3,10 @@ #[macro_use] extern crate serde; -pub mod account_balance_request; -pub use self::account_balance_request::AccountBalanceRequest; -pub mod account_balance_response; -pub use self::account_balance_response::AccountBalanceResponse; -pub mod account_coins_request; -pub use self::account_coins_request::AccountCoinsRequest; -pub mod account_coins_response; -pub use self::account_coins_response::AccountCoinsResponse; -pub mod account_faucet_request; -pub use self::account_faucet_request::AccountFaucetRequest; pub mod account_identifier; pub use self::account_identifier::AccountIdentifier; -pub mod allow; -pub use self::allow::Allow; pub mod amount; pub use self::amount::Amount; -pub mod balance_exemption; -pub use self::balance_exemption::BalanceExemption; pub mod block; pub use self::block::Block; pub mod block_event; @@ -35,10 +21,6 @@ pub mod block_response; pub use self::block_response::BlockResponse; pub mod block_transaction; pub use self::block_transaction::BlockTransaction; -pub mod block_transaction_request; -pub use self::block_transaction_request::BlockTransactionRequest; -pub mod block_transaction_response; -pub use self::block_transaction_response::BlockTransactionResponse; pub mod call_request; pub use self::call_request::CallRequest; pub mod call_response; @@ -63,10 +45,6 @@ pub mod construction_derive_response; pub use self::construction_derive_response::ConstructionDeriveResponse; pub mod construction_hash_request; pub use self::construction_hash_request::ConstructionHashRequest; -pub mod construction_metadata_request; -pub use self::construction_metadata_request::ConstructionMetadataRequest; -pub mod construction_metadata_response; -pub use self::construction_metadata_response::ConstructionMetadataResponse; pub mod construction_parse_request; pub use self::construction_parse_request::ConstructionParseRequest; pub mod construction_parse_response; @@ -95,32 +73,14 @@ pub mod events_blocks_response; pub use self::events_blocks_response::EventsBlocksResponse; pub mod exemption_type; pub use self::exemption_type::ExemptionType; -pub mod mempool_response; -pub use self::mempool_response::MempoolResponse; -pub mod mempool_transaction_request; -pub use self::mempool_transaction_request::MempoolTransactionRequest; -pub mod mempool_transaction_response; -pub use self::mempool_transaction_response::MempoolTransactionResponse; pub mod metadata_request; pub use self::metadata_request::MetadataRequest; -pub mod network_identifier; -pub use self::network_identifier::NetworkIdentifier; -pub mod network_list_response; -pub use self::network_list_response::NetworkListResponse; -pub mod network_options_response; -pub use self::network_options_response::NetworkOptionsResponse; -pub mod network_request; -pub use self::network_request::NetworkRequest; -pub mod network_status_response; -pub use self::network_status_response::NetworkStatusResponse; pub mod operation; pub use self::operation::Operation; pub mod operation_identifier; pub use self::operation_identifier::OperationIdentifier; pub mod operation_status; pub use self::operation_status::OperationStatus; -pub mod operator; -pub use self::operator::Operator; pub mod partial_block_identifier; pub use self::partial_block_identifier::PartialBlockIdentifier; pub mod peer; @@ -129,10 +89,6 @@ pub mod public_key; pub use self::public_key::PublicKey; pub mod related_transaction; pub use self::related_transaction::RelatedTransaction; -pub mod search_transactions_request; -pub use self::search_transactions_request::SearchTransactionsRequest; -pub mod search_transactions_response; -pub use self::search_transactions_response::SearchTransactionsResponse; pub mod signature; pub use self::signature::Signature; pub mod signature_type; @@ -141,15 +97,9 @@ pub mod signing_payload; pub use self::signing_payload::SigningPayload; pub mod sub_account_identifier; pub use self::sub_account_identifier::SubAccountIdentifier; -pub mod sub_network_identifier; -pub use self::sub_network_identifier::SubNetworkIdentifier; -pub mod sync_status; -pub use self::sync_status::SyncStatus; pub mod transaction; pub use self::transaction::Transaction; pub mod transaction_identifier; pub use self::transaction_identifier::TransactionIdentifier; -pub mod transaction_identifier_response; -pub use self::transaction_identifier_response::TransactionIdentifierResponse; pub mod version; pub use self::version::Version; diff --git a/rosetta-types/src/mempool_response.rs b/rosetta-types/src/mempool_response.rs deleted file mode 100644 index 64d0a1d8..00000000 --- a/rosetta-types/src/mempool_response.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// A `MempoolResponse` contains all transaction identifiers in the mempool for a particular -/// `network_identifier`. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct MempoolResponse { - #[serde(rename = "transaction_identifiers")] - pub transaction_identifiers: Vec, -} - -impl MempoolResponse { - /// A `MempoolResponse` contains all transaction identifiers in the mempool for a particular - /// `network_identifier`. - #[must_use] - pub const fn new(transaction_identifiers: Vec) -> Self { - Self { transaction_identifiers } - } -} diff --git a/rosetta-types/src/mempool_transaction_request.rs b/rosetta-types/src/mempool_transaction_request.rs deleted file mode 100644 index 4846a103..00000000 --- a/rosetta-types/src/mempool_transaction_request.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// A `MempoolTransactionRequest` is utilized to retrieve a transaction from the mempool. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct MempoolTransactionRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, - #[serde(rename = "transaction_identifier")] - pub transaction_identifier: crate::TransactionIdentifier, -} - -impl MempoolTransactionRequest { - /// A `MempoolTransactionRequest` is utilized to retrieve a transaction from the mempool. - #[must_use] - pub const fn new( - network_identifier: crate::NetworkIdentifier, - transaction_identifier: crate::TransactionIdentifier, - ) -> Self { - Self { network_identifier, transaction_identifier } - } -} diff --git a/rosetta-types/src/mempool_transaction_response.rs b/rosetta-types/src/mempool_transaction_response.rs deleted file mode 100644 index e3940063..00000000 --- a/rosetta-types/src/mempool_transaction_response.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// A `MempoolTransactionResponse` contains an estimate of a mempool transaction. It may not be -/// possible to know the full impact of a transaction in the mempool (ex: fee paid). -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct MempoolTransactionResponse { - #[serde(rename = "transaction")] - pub transaction: crate::Transaction, - #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -impl MempoolTransactionResponse { - /// A `MempoolTransactionResponse` contains an estimate of a mempool transaction. It may not be - /// possible to know the full impact of a transaction in the mempool (ex: fee paid). - #[must_use] - pub const fn new(transaction: crate::Transaction) -> Self { - Self { transaction, metadata: None } - } -} diff --git a/rosetta-types/src/network_identifier.rs b/rosetta-types/src/network_identifier.rs deleted file mode 100644 index d3da20c0..00000000 --- a/rosetta-types/src/network_identifier.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// The `network_identifier` specifies which network a particular object is associated with. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct NetworkIdentifier { - #[serde(rename = "blockchain")] - pub blockchain: String, - /// If a blockchain has a specific chain-id or network identifier, it should go in this field. - /// It is up to the client to determine which network-specific identifier is mainnet or - /// testnet. - #[serde(rename = "network")] - pub network: String, - #[serde(rename = "sub_network_identifier", skip_serializing_if = "Option::is_none")] - pub sub_network_identifier: Option, -} - -impl NetworkIdentifier { - /// The `network_identifier` specifies which network a particular object is associated with. - #[must_use] - pub const fn new(blockchain: String, network: String) -> Self { - Self { blockchain, network, sub_network_identifier: None } - } -} diff --git a/rosetta-types/src/network_list_response.rs b/rosetta-types/src/network_list_response.rs deleted file mode 100644 index 48470d39..00000000 --- a/rosetta-types/src/network_list_response.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// A `NetworkListResponse` contains all `NetworkIdentifiers` that the node can serve information -/// for. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct NetworkListResponse { - #[serde(rename = "network_identifiers")] - pub network_identifiers: Vec, -} - -impl NetworkListResponse { - /// A `NetworkListResponse` contains all `NetworkIdentifiers` that the node can serve - /// information for. - #[must_use] - pub const fn new(network_identifiers: Vec) -> Self { - Self { network_identifiers } - } -} diff --git a/rosetta-types/src/network_options_response.rs b/rosetta-types/src/network_options_response.rs deleted file mode 100644 index af45801b..00000000 --- a/rosetta-types/src/network_options_response.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `NetworkOptionsResponse` contains information about the versioning of the node and the allowed -/// operation statuses, operation types, and errors. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct NetworkOptionsResponse { - #[serde(rename = "version")] - pub version: crate::Version, - #[serde(rename = "allow")] - pub allow: Option, -} - -impl NetworkOptionsResponse { - /// `NetworkOptionsResponse` contains information about the versioning of the node and the - /// allowed operation statuses, operation types, and errors. - #[must_use] - pub const fn new(version: crate::Version) -> Self { - Self { version, allow: None } - } -} diff --git a/rosetta-types/src/network_request.rs b/rosetta-types/src/network_request.rs deleted file mode 100644 index d4adf22a..00000000 --- a/rosetta-types/src/network_request.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// A `NetworkRequest` is utilized to retrieve some data specific exclusively to a -/// `NetworkIdentifier`. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct NetworkRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, - #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -impl NetworkRequest { - /// A `NetworkRequest` is utilized to retrieve some data specific exclusively to a - /// `NetworkIdentifier`. - #[must_use] - pub const fn new(network_identifier: crate::NetworkIdentifier) -> Self { - Self { network_identifier, metadata: None } - } -} diff --git a/rosetta-types/src/network_status_response.rs b/rosetta-types/src/network_status_response.rs deleted file mode 100644 index 1947901a..00000000 --- a/rosetta-types/src/network_status_response.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `NetworkStatusResponse` contains basic information about the node's view of a blockchain -/// network. It is assumed that any BlockIdentifier.Index less than or equal to -/// CurrentBlockIdentifier.Index can be queried. If a Rosetta implementation prunes historical -/// state, it should populate the optional `oldest_block_identifier` field with the oldest block -/// available to query. If this is not populated, it is assumed that the `genesis_block_identifier` -/// is the oldest queryable block. If a Rosetta implementation performs some pre-sync before it is -/// possible to query blocks, `sync_status` should be populated so that clients can still monitor -/// healthiness. Without this field, it may appear that the implementation is stuck syncing and -/// needs to be terminated. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct NetworkStatusResponse { - #[serde(rename = "current_block_identifier")] - pub current_block_identifier: crate::BlockIdentifier, - /// The timestamp of the block in milliseconds since the Unix Epoch. The timestamp is stored in - /// milliseconds because some blockchains produce blocks more often than once a second. - #[serde(rename = "current_block_timestamp")] - pub current_block_timestamp: i64, - // TODO: genesis_block_identifier is not nullable. rosetta-ethereum can't determine - // it on dev net. - #[serde(rename = "genesis_block_identifier")] - pub genesis_block_identifier: Option, - #[serde(rename = "oldest_block_identifier", skip_serializing_if = "Option::is_none")] - pub oldest_block_identifier: Option, - #[serde(rename = "latest_finalized_block_identifier")] - pub latest_finalized_block_identifier: crate::BlockIdentifier, - #[serde(rename = "sync_status", skip_serializing_if = "Option::is_none")] - pub sync_status: Option, - #[serde(rename = "peers", skip_serializing_if = "Option::is_none")] - pub peers: Option>, -} - -impl NetworkStatusResponse { - /// `NetworkStatusResponse` contains basic information about the node's view of a blockchain - /// network. It is assumed that any BlockIdentifier.Index less than or equal to - /// CurrentBlockIdentifier.Index can be queried. If a Rosetta implementation prunes historical - /// state, it should populate the optional `oldest_block_identifier` field with the oldest block - /// available to query. If this is not populated, it is assumed that the - /// `genesis_block_identifier` is the oldest queryable block. If a Rosetta implementation - /// performs some pre-sync before it is possible to query blocks, `sync_status` should be - /// populated so that clients can still monitor healthiness. Without this field, it may appear - /// that the implementation is stuck syncing and needs to be terminated. - #[must_use] - pub const fn new( - current_block_identifier: crate::BlockIdentifier, - current_block_timestamp: i64, - genesis_block_identifier: crate::BlockIdentifier, - latest_finalized_block_identifier: crate::BlockIdentifier, - ) -> Self { - Self { - current_block_identifier, - current_block_timestamp, - genesis_block_identifier: Some(genesis_block_identifier), - oldest_block_identifier: None, - latest_finalized_block_identifier, - sync_status: None, - peers: None, - } - } -} diff --git a/rosetta-types/src/operator.rs b/rosetta-types/src/operator.rs deleted file mode 100644 index ae40e0d8..00000000 --- a/rosetta-types/src/operator.rs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// Operator : Operator is used by query-related endpoints to determine how to apply conditions. If -/// this field is not populated, the default `and` value will be used. Operator is used by -/// query-related endpoints to determine how to apply conditions. If this field is not populated, -/// the default `and` value will be used. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] -pub enum Operator { - #[serde(rename = "or")] - Or, - #[serde(rename = "and")] - And, -} - -impl ToString for Operator { - fn to_string(&self) -> String { - match self { - Self::Or => String::from("or"), - Self::And => String::from("and"), - } - } -} - -impl Default for Operator { - fn default() -> Self { - Self::Or - } -} diff --git a/rosetta-types/src/related_transaction.rs b/rosetta-types/src/related_transaction.rs index 97a520a2..9d688a51 100644 --- a/rosetta-types/src/related_transaction.rs +++ b/rosetta-types/src/related_transaction.rs @@ -13,8 +13,6 @@ /// is on the same network. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct RelatedTransaction { - #[serde(rename = "network_identifier", skip_serializing_if = "Option::is_none")] - pub network_identifier: Option, #[serde(rename = "transaction_identifier")] pub transaction_identifier: crate::TransactionIdentifier, #[serde(rename = "direction")] @@ -30,6 +28,6 @@ impl RelatedTransaction { transaction_identifier: crate::TransactionIdentifier, direction: crate::Direction, ) -> Self { - Self { network_identifier: None, transaction_identifier, direction } + Self { transaction_identifier, direction } } } diff --git a/rosetta-types/src/search_transactions_request.rs b/rosetta-types/src/search_transactions_request.rs deleted file mode 100644 index c8ea27fe..00000000 --- a/rosetta-types/src/search_transactions_request.rs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `SearchTransactionsRequest` : `SearchTransactionsRequest` is used to search for transactions -/// matching a set of provided conditions in canonical blocks. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct SearchTransactionsRequest { - #[serde(rename = "network_identifier")] - pub network_identifier: crate::NetworkIdentifier, - #[serde(rename = "operator", skip_serializing_if = "Option::is_none")] - pub operator: Option, - /// max_block is the largest block index to consider when searching for transactions. If this - /// field is not populated, the current block is considered the max_block. If you do not - /// specify a max_block, it is possible a newly synced block will interfere with paginated - /// transaction queries (as the offset could become invalid with newly added rows). - #[serde(rename = "max_block", skip_serializing_if = "Option::is_none")] - pub max_block: Option, - /// offset is the offset into the query result to start returning transactions. If any search - /// conditions are changed, the query offset will change and you must restart your search - /// iteration. - #[serde(rename = "offset", skip_serializing_if = "Option::is_none")] - pub offset: Option, - /// limit is the maximum number of transactions to return in one call. The implementation may - /// return <= limit transactions. - #[serde(rename = "limit", skip_serializing_if = "Option::is_none")] - pub limit: Option, - #[serde(rename = "transaction_identifier", skip_serializing_if = "Option::is_none")] - pub transaction_identifier: Option, - #[serde(rename = "account_identifier", skip_serializing_if = "Option::is_none")] - pub account_identifier: Option, - #[serde(rename = "coin_identifier", skip_serializing_if = "Option::is_none")] - pub coin_identifier: Option, - #[serde(rename = "currency", skip_serializing_if = "Option::is_none")] - pub currency: Option, - /// status is the network-specific operation type. - #[serde(rename = "status", skip_serializing_if = "Option::is_none")] - pub status: Option, - /// type is the network-specific operation type. - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub r#type: Option, - /// address is AccountIdentifier.Address. This is used to get all transactions related to an - /// AccountIdentifier.Address, regardless of SubAccountIdentifier. - #[serde(rename = "address", skip_serializing_if = "Option::is_none")] - pub address: Option, - /// success is a synthetic condition populated by parsing network-specific operation statuses - /// (using the mapping provided in `/network/options`). - #[serde(rename = "success", skip_serializing_if = "Option::is_none")] - pub success: Option, -} - -impl SearchTransactionsRequest { - /// `SearchTransactionsRequest` is used to search for transactions matching a set of provided - /// conditions in canonical blocks. - #[must_use] - pub const fn new(network_identifier: crate::NetworkIdentifier) -> Self { - Self { - network_identifier, - operator: None, - max_block: None, - offset: None, - limit: None, - transaction_identifier: None, - account_identifier: None, - coin_identifier: None, - currency: None, - status: None, - r#type: None, - address: None, - success: None, - } - } -} diff --git a/rosetta-types/src/search_transactions_response.rs b/rosetta-types/src/search_transactions_response.rs deleted file mode 100644 index 5db368cc..00000000 --- a/rosetta-types/src/search_transactions_response.rs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `SearchTransactionsResponse` : `SearchTransactionsResponse` contains an ordered collection of -/// `BlockTransactions` that match the query in `SearchTransactionsRequest`. These -/// `BlockTransactions` are sorted from most recent block to oldest block. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct SearchTransactionsResponse { - /// transactions is an array of BlockTransactions sorted by most recent BlockIdentifier - /// (meaning that transactions in recent blocks appear first). If there are many transactions - /// for a particular search, transactions may not contain all matching transactions. It is up - /// to the caller to paginate these transactions using the max_block field. - #[serde(rename = "transactions")] - pub transactions: Vec, - /// total_count is the number of results for a given search. Callers typically use this value - /// to concurrently fetch results by offset or to display a virtual page number associated with - /// results. - #[serde(rename = "total_count")] - pub total_count: i64, - /// next_offset is the next offset to use when paginating through transaction results. If this - /// field is not populated, there are no more transactions to query. - #[serde(rename = "next_offset", skip_serializing_if = "Option::is_none")] - pub next_offset: Option, -} - -impl SearchTransactionsResponse { - /// `SearchTransactionsResponse` contains an ordered collection of `BlockTransactions` that - /// match the query in `SearchTransactionsRequest`. These `BlockTransactions` are sorted from - /// most recent block to oldest block. - #[must_use] - pub const fn new(transactions: Vec, total_count: i64) -> Self { - Self { transactions, total_count, next_offset: None } - } -} diff --git a/rosetta-types/src/sub_network_identifier.rs b/rosetta-types/src/sub_network_identifier.rs deleted file mode 100644 index 1ca9de7f..00000000 --- a/rosetta-types/src/sub_network_identifier.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `SubNetworkIdentifier` : In blockchains with sharded state, the `SubNetworkIdentifier` is -/// required to query some object on a specific shard. This identifier is optional for all -/// non-sharded blockchains. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct SubNetworkIdentifier { - #[serde(rename = "network")] - pub network: String, - #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -impl SubNetworkIdentifier { - /// In blockchains with sharded state, the `SubNetworkIdentifier` is required to query some - /// object on a specific shard. This identifier is optional for all non-sharded blockchains. - #[must_use] - pub const fn new(network: String) -> Self { - Self { network, metadata: None } - } -} diff --git a/rosetta-types/src/sync_status.rs b/rosetta-types/src/sync_status.rs deleted file mode 100644 index ce2ba0af..00000000 --- a/rosetta-types/src/sync_status.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `SyncStatus` : `SyncStatus` is used to provide additional context about an implementation's sync -/// status. This object is often used by implementations to indicate healthiness when block data -/// cannot be queried until some sync phase completes or cannot be determined by comparing the -/// timestamp of the most recent block with the current time. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct SyncStatus { - /// CurrentIndex is the index of the last synced block in the current stage. This is a - /// separate field from current_block_identifier in NetworkStatusResponse because blocks with - /// indices up to and including the current_index may not yet be queryable by the caller. To - /// reiterate, all indices up to and including current_block_identifier in - /// NetworkStatusResponse must be queryable via the /block endpoint (excluding indices less - /// than oldest_block_identifier). - #[serde(rename = "current_index", skip_serializing_if = "Option::is_none")] - pub current_index: Option, - /// TargetIndex is the index of the block that the implementation is attempting to sync to in - /// the current stage. - #[serde(rename = "target_index", skip_serializing_if = "Option::is_none")] - pub target_index: Option, - /// Stage is the phase of the sync process. - #[serde(rename = "stage", skip_serializing_if = "Option::is_none")] - pub stage: Option, - /// synced is a boolean that indicates if an implementation has synced up to the most recent - /// block. If this field is not populated, the caller should rely on a traditional tip - /// timestamp comparison to determine if an implementation is synced. This field is - /// particularly useful for quiescent blockchains (blocks only produced when there are pending - /// transactions). In these blockchains, the most recent block could have a timestamp far - /// behind the current time but the node could be healthy and at tip. - #[serde(rename = "synced", skip_serializing_if = "Option::is_none")] - pub synced: Option, -} - -impl SyncStatus { - /// `SyncStatus` is used to provide additional context about an implementation's sync status. - /// This object is often used by implementations to indicate healthiness when block data cannot - /// be queried until some sync phase completes or cannot be determined by comparing the - /// timestamp of the most recent block with the current time. - #[must_use] - pub const fn new() -> Self { - Self { current_index: None, target_index: None, stage: None, synced: None } - } -} diff --git a/rosetta-types/src/transaction_identifier_response.rs b/rosetta-types/src/transaction_identifier_response.rs deleted file mode 100644 index 6e08c987..00000000 --- a/rosetta-types/src/transaction_identifier_response.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `TransactionIdentifierResponse` : `TransactionIdentifierResponse` contains the -/// `transaction_identifier` of a transaction that was submitted to either `/construction/hash` or -/// `/construction/submit`. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct TransactionIdentifierResponse { - #[serde(rename = "transaction_identifier")] - pub transaction_identifier: crate::TransactionIdentifier, - #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -impl TransactionIdentifierResponse { - /// `TransactionIdentifierResponse` contains the `transaction_identifier` of a transaction that - /// was submitted to either `/construction/hash` or `/construction/submit`. - #[must_use] - pub const fn new(transaction_identifier: crate::TransactionIdentifier) -> Self { - Self { transaction_identifier, metadata: None } - } -} From aa8f98e90ae23efbec037bc9be8095800318ebd3 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 06:16:12 -0300 Subject: [PATCH 24/26] Remove more unused types --- rosetta-types/src/block_event.rs | 38 ---------------- rosetta-types/src/block_event_type.rs | 34 -------------- rosetta-types/src/block_request.rs | 24 ---------- rosetta-types/src/block_response.rs | 44 ------------------- rosetta-types/src/block_transaction.rs | 31 ------------- rosetta-types/src/case.rs | 39 ---------------- .../src/construction_preprocess_request.rs | 36 --------------- .../src/construction_preprocess_response.rs | 39 ---------------- .../src/construction_submit_request.rs | 24 ---------- rosetta-types/src/events_blocks_request.rs | 33 -------------- rosetta-types/src/events_blocks_response.rs | 32 -------------- rosetta-types/src/lib.rs | 22 ---------- 12 files changed, 396 deletions(-) delete mode 100644 rosetta-types/src/block_event.rs delete mode 100644 rosetta-types/src/block_event_type.rs delete mode 100644 rosetta-types/src/block_request.rs delete mode 100644 rosetta-types/src/block_response.rs delete mode 100644 rosetta-types/src/block_transaction.rs delete mode 100644 rosetta-types/src/case.rs delete mode 100644 rosetta-types/src/construction_preprocess_request.rs delete mode 100644 rosetta-types/src/construction_preprocess_response.rs delete mode 100644 rosetta-types/src/construction_submit_request.rs delete mode 100644 rosetta-types/src/events_blocks_request.rs delete mode 100644 rosetta-types/src/events_blocks_response.rs diff --git a/rosetta-types/src/block_event.rs b/rosetta-types/src/block_event.rs deleted file mode 100644 index 0ded5762..00000000 --- a/rosetta-types/src/block_event.rs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `BlockEvent` : `BlockEvent` represents the addition or removal of a `BlockIdentifier` from -/// storage. Streaming `BlockEvents` allows lightweight clients to update their own state without -/// needing to implement their own syncing logic. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct BlockEvent { - /// sequence is the unique identifier of a BlockEvent within the context of a - /// NetworkIdentifier. - #[serde(rename = "sequence")] - pub sequence: i64, - #[serde(rename = "block_identifier")] - pub block_identifier: crate::BlockIdentifier, - #[serde(rename = "type")] - pub r#type: crate::BlockEventType, -} - -impl BlockEvent { - /// `BlockEvent` represents the addition or removal of a `BlockIdentifier` from storage. - /// Streaming `BlockEvents` allows lightweight clients to update their own state without needing - /// to implement their own syncing logic. - #[must_use] - pub const fn new( - sequence: i64, - block_identifier: crate::BlockIdentifier, - r#type: crate::BlockEventType, - ) -> Self { - Self { sequence, block_identifier, r#type } - } -} diff --git a/rosetta-types/src/block_event_type.rs b/rosetta-types/src/block_event_type.rs deleted file mode 100644 index 19c6c5bf..00000000 --- a/rosetta-types/src/block_event_type.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `BlockEventType` : `BlockEventType` determines if a `BlockEvent` represents the addition or -/// removal of a block. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] -pub enum BlockEventType { - #[serde(rename = "block_added")] - Added, - #[serde(rename = "block_removed")] - Removed, -} - -impl ToString for BlockEventType { - fn to_string(&self) -> String { - match self { - Self::Added => String::from("block_added"), - Self::Removed => String::from("block_removed"), - } - } -} - -impl Default for BlockEventType { - fn default() -> Self { - Self::Added - } -} diff --git a/rosetta-types/src/block_request.rs b/rosetta-types/src/block_request.rs deleted file mode 100644 index 58404c11..00000000 --- a/rosetta-types/src/block_request.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `BlockRequest` : A `BlockRequest` is utilized to make a block request on the /block endpoint. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct BlockRequest { - #[serde(rename = "block_identifier")] - pub block_identifier: crate::PartialBlockIdentifier, -} - -impl BlockRequest { - /// A `BlockRequest` is utilized to make a block request on the /block endpoint. - #[must_use] - pub const fn new(block_identifier: crate::PartialBlockIdentifier) -> Self { - Self { block_identifier } - } -} diff --git a/rosetta-types/src/block_response.rs b/rosetta-types/src/block_response.rs deleted file mode 100644 index 10fb0a4e..00000000 --- a/rosetta-types/src/block_response.rs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `BlockResponse` : A `BlockResponse` includes a fully-populated block or a partially-populated -/// block with a list of other transactions to fetch (`other_transactions`). As a result of the -/// consensus algorithm of some blockchains, blocks can be omitted (i.e. certain block indices can -/// be skipped). If a query for one of these omitted indices is made, the response should not -/// include a `Block` object. It is VERY important to note that blocks MUST still form a canonical, -/// connected chain of blocks where each block has a unique index. In other words, the -/// `PartialBlockIdentifier` of a block after an omitted block should reference the last non-omitted -/// block. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct BlockResponse { - #[serde(rename = "block", skip_serializing_if = "Option::is_none")] - pub block: Option, - /// Some blockchains may require additional transactions to be fetched that weren't returned in - /// the block response (ex: block only returns transaction hashes). For blockchains with a lot - /// of transactions in each block, this can be very useful as consumers can concurrently fetch - /// all transactions returned. - #[serde(rename = "other_transactions", skip_serializing_if = "Option::is_none")] - pub other_transactions: Option>, -} - -impl BlockResponse { - /// A `BlockResponse` includes a fully-populated block or a partially-populated block with a - /// list of other transactions to fetch (`other_transactions`). As a result of the consensus - /// algorithm of some blockchains, blocks can be omitted (i.e. certain block indices can be - /// skipped). If a query for one of these omitted indices is made, the response should not - /// include a `Block` object. It is VERY important to note that blocks MUST still form a - /// canonical, connected chain of blocks where each block has a unique index. In other words, - /// the `PartialBlockIdentifier` of a block after an omitted block should reference the last - /// non-omitted block. - #[must_use] - pub const fn new() -> Self { - Self { block: None, other_transactions: None } - } -} diff --git a/rosetta-types/src/block_transaction.rs b/rosetta-types/src/block_transaction.rs deleted file mode 100644 index 905ca87a..00000000 --- a/rosetta-types/src/block_transaction.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `BlockTransaction` : `BlockTransaction` contains a populated Transaction and the -/// `BlockIdentifier` that contains it. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct BlockTransaction { - #[serde(rename = "block_identifier")] - pub block_identifier: crate::BlockIdentifier, - #[serde(rename = "transaction")] - pub transaction: crate::Transaction, -} - -impl BlockTransaction { - /// `BlockTransaction` contains a populated Transaction and the `BlockIdentifier` that contains - /// it. - #[must_use] - pub const fn new( - block_identifier: crate::BlockIdentifier, - transaction: crate::Transaction, - ) -> Self { - Self { block_identifier, transaction } - } -} diff --git a/rosetta-types/src/case.rs b/rosetta-types/src/case.rs deleted file mode 100644 index 4695afc4..00000000 --- a/rosetta-types/src/case.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `Case` specifies the expected case for strings and hashes. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] -pub enum Case { - #[serde(rename = "upper_case")] - UpperCase, - #[serde(rename = "lower_case")] - LowerCase, - #[serde(rename = "case_sensitive")] - CaseSensitive, - #[serde(rename = "null")] - Null, -} - -impl ToString for Case { - fn to_string(&self) -> String { - match self { - Self::UpperCase => String::from("upper_case"), - Self::LowerCase => String::from("lower_case"), - Self::CaseSensitive => String::from("case_sensitive"), - Self::Null => String::from("null"), - } - } -} - -impl Default for Case { - fn default() -> Self { - Self::UpperCase - } -} diff --git a/rosetta-types/src/construction_preprocess_request.rs b/rosetta-types/src/construction_preprocess_request.rs deleted file mode 100644 index 812eb423..00000000 --- a/rosetta-types/src/construction_preprocess_request.rs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `ConstructionPreprocessRequest` is passed to the `/construction/preprocess` endpoint so that a -/// Rosetta implementation can determine which metadata it needs to request for construction. -/// Metadata provided in this object should NEVER be a product of live data (i.e. the caller must -/// follow some network-specific data fetching strategy outside of the Construction API to populate -/// required Metadata). If live data is required for construction, it MUST be fetched in the call to -/// `/construction/metadata`. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct ConstructionPreprocessRequest { - #[serde(rename = "operations")] - pub operations: Vec, - #[serde(rename = "metadata", skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -impl ConstructionPreprocessRequest { - /// `ConstructionPreprocessRequest` is passed to the `/construction/preprocess` endpoint so that - /// a Rosetta implementation can determine which metadata it needs to request for construction. - /// Metadata provided in this object should NEVER be a product of live data (i.e. the caller - /// must follow some network-specific data fetching strategy outside of the Construction API to - /// populate required Metadata). If live data is required for construction, it MUST be fetched - /// in the call to `/construction/metadata`. - #[must_use] - pub const fn new(operations: Vec) -> Self { - Self { operations, metadata: None } - } -} diff --git a/rosetta-types/src/construction_preprocess_response.rs b/rosetta-types/src/construction_preprocess_response.rs deleted file mode 100644 index d1006da2..00000000 --- a/rosetta-types/src/construction_preprocess_response.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `ConstructionPreprocessResponse` contains `options` that will be sent unmodified to -/// `/construction/metadata`. If it is not necessary to make a request to `/construction/metadata`, -/// `options` should be omitted. Some blockchains require the `PublicKey` of particular -/// `AccountIdentifiers` to construct a valid transaction. To fetch these `PublicKeys`, populate -/// `required_public_keys` with the `AccountIdentifiers` associated with the desired `PublicKeys`. -/// If it is not necessary to retrieve any `PublicKeys` for construction, `required_public_keys` -/// should be omitted. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct ConstructionPreprocessResponse { - /// The options that will be sent directly to `/construction/metadata` by the caller. - #[serde(rename = "options", skip_serializing_if = "Option::is_none")] - pub options: Option, - #[serde(rename = "required_public_keys", skip_serializing_if = "Option::is_none")] - pub required_public_keys: Option>, -} - -impl ConstructionPreprocessResponse { - /// `ConstructionPreprocessResponse` contains `options` that will be sent unmodified to - /// `/construction/metadata`. If it is not necessary to make a request to - /// `/construction/metadata`, `options` should be omitted. Some blockchains require the - /// `PublicKey` of particular `AccountIdentifiers` to construct a valid transaction. To fetch - /// these `PublicKeys`, populate `required_public_keys` with the `AccountIdentifiers` associated - /// with the desired `PublicKeys`. If it is not necessary to retrieve any `PublicKeys` for - /// construction, `required_public_keys` should be omitted. - #[must_use] - pub const fn new() -> Self { - Self { options: None, required_public_keys: None } - } -} diff --git a/rosetta-types/src/construction_submit_request.rs b/rosetta-types/src/construction_submit_request.rs deleted file mode 100644 index d64d390e..00000000 --- a/rosetta-types/src/construction_submit_request.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `ConstructionSubmitRequest` : The transaction submission request includes a signed transaction. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct ConstructionSubmitRequest { - #[serde(rename = "signed_transaction")] - pub signed_transaction: String, -} - -impl ConstructionSubmitRequest { - /// The transaction submission request includes a signed transaction. - #[must_use] - pub const fn new(signed_transaction: String) -> Self { - Self { signed_transaction } - } -} diff --git a/rosetta-types/src/events_blocks_request.rs b/rosetta-types/src/events_blocks_request.rs deleted file mode 100644 index 24b45482..00000000 --- a/rosetta-types/src/events_blocks_request.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `EventsBlocksRequest` : `EventsBlocksRequest` is utilized to fetch a sequence of `BlockEvents` -/// indicating which blocks were added and removed from storage to reach the current state. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct EventsBlocksRequest { - /// offset is the offset into the event stream to sync events from. If this field is not - /// populated, we return the limit events backwards from tip. If this is set to 0, we start - /// from the beginning. - #[serde(rename = "offset", skip_serializing_if = "Option::is_none")] - pub offset: Option, - /// limit is the maximum number of events to fetch in one call. The implementation may return - /// <= limit events. - #[serde(rename = "limit", skip_serializing_if = "Option::is_none")] - pub limit: Option, -} - -impl EventsBlocksRequest { - /// `EventsBlocksRequest` is utilized to fetch a sequence of `BlockEvents` indicating which - /// blocks were added and removed from storage to reach the current state. - #[must_use] - pub const fn new() -> Self { - Self { offset: None, limit: None } - } -} diff --git a/rosetta-types/src/events_blocks_response.rs b/rosetta-types/src/events_blocks_response.rs deleted file mode 100644 index fab246c8..00000000 --- a/rosetta-types/src/events_blocks_response.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Rosetta - * - * Build Once. Integrate Your Blockchain Everywhere. - * - * The version of the OpenAPI document: 1.4.13 - * - * Generated by: https://openapi-generator.tech - */ - -/// `EventsBlocksResponse` contains an ordered collection of `BlockEvents` and the max retrievable -/// sequence. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct EventsBlocksResponse { - /// max_sequence is the maximum available sequence number to fetch. - #[serde(rename = "max_sequence")] - pub max_sequence: i64, - /// events is an array of BlockEvents indicating the order to add and remove blocks to maintain - /// a canonical view of blockchain state. Lightweight clients can use this event stream to - /// update state without implementing their own block syncing logic. - #[serde(rename = "events")] - pub events: Vec, -} - -impl EventsBlocksResponse { - /// `EventsBlocksResponse` contains an ordered collection of `BlockEvents` and the max - /// retrievable sequence. - #[must_use] - pub const fn new(max_sequence: i64, events: Vec) -> Self { - Self { max_sequence, events } - } -} diff --git a/rosetta-types/src/lib.rs b/rosetta-types/src/lib.rs index cb267bf6..da98957b 100644 --- a/rosetta-types/src/lib.rs +++ b/rosetta-types/src/lib.rs @@ -9,24 +9,12 @@ pub mod amount; pub use self::amount::Amount; pub mod block; pub use self::block::Block; -pub mod block_event; -pub use self::block_event::BlockEvent; -pub mod block_event_type; -pub use self::block_event_type::BlockEventType; pub mod block_identifier; pub use self::block_identifier::BlockIdentifier; -pub mod block_request; -pub use self::block_request::BlockRequest; -pub mod block_response; -pub use self::block_response::BlockResponse; -pub mod block_transaction; -pub use self::block_transaction::BlockTransaction; pub mod call_request; pub use self::call_request::CallRequest; pub mod call_response; pub use self::call_response::CallResponse; -pub mod case; -pub use self::case::Case; pub mod coin; pub use self::coin::Coin; pub mod coin_action; @@ -53,12 +41,6 @@ pub mod construction_payloads_request; pub use self::construction_payloads_request::ConstructionPayloadsRequest; pub mod construction_payloads_response; pub use self::construction_payloads_response::ConstructionPayloadsResponse; -pub mod construction_preprocess_request; -pub use self::construction_preprocess_request::ConstructionPreprocessRequest; -pub mod construction_preprocess_response; -pub use self::construction_preprocess_response::ConstructionPreprocessResponse; -pub mod construction_submit_request; -pub use self::construction_submit_request::ConstructionSubmitRequest; pub mod currency; pub use self::currency::Currency; pub mod curve_type; @@ -67,10 +49,6 @@ pub mod direction; pub use self::direction::Direction; pub mod error; pub use self::error::Error; -pub mod events_blocks_request; -pub use self::events_blocks_request::EventsBlocksRequest; -pub mod events_blocks_response; -pub use self::events_blocks_response::EventsBlocksResponse; pub mod exemption_type; pub use self::exemption_type::ExemptionType; pub mod metadata_request; From 0d471126c35b6a762dd0dbd0665ecc86a65b8789 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 06:20:11 -0300 Subject: [PATCH 25/26] Remove commented code --- .../config/src/types/transaction/signature.rs | 2 - rosetta-core/src/traits.rs | 42 ------------------- 2 files changed, 44 deletions(-) diff --git a/chains/ethereum/config/src/types/transaction/signature.rs b/chains/ethereum/config/src/types/transaction/signature.rs index d0f1f46d..917bee96 100644 --- a/chains/ethereum/config/src/types/transaction/signature.rs +++ b/chains/ethereum/config/src/types/transaction/signature.rs @@ -27,8 +27,6 @@ impl Signature { pub fn to_raw_signature(&self, output: &mut [u8; 65]) { self.r.to_big_endian(&mut output[0..32]); self.s.to_big_endian(&mut output[32..64]); - // output[0..32].copy_from_slice(self.r.as_fixed_bytes()); - // output[32..64].copy_from_slice(self.s.as_fixed_bytes()); output[64] = self.v.y_parity() as u8; } } diff --git a/rosetta-core/src/traits.rs b/rosetta-core/src/traits.rs index 0d361a4d..23ef8354 100644 --- a/rosetta-core/src/traits.rs +++ b/rosetta-core/src/traits.rs @@ -158,45 +158,3 @@ pub trait Query: Member { impl Query for () { type Result = (); } - -// pub enum QueryEnum { -// BlockHeaderByHash(::Hash), -// BlockByHash(T::Block), -// Query(CUSTOM), -// } - -// pub enum SubscribeEnum { -// NewHeads, -// FinalizedHeads, -// Subscribe(CUSTOM), -// } - -// pub trait BlockchainClient { -// type Config: BlockchainConfig; -// type Error: std::error::Error + Send + Sync + 'static; -// type TxParams: Send + Sync + 'static; - -// type QueryFuture<'a, Q>: core::future::Future> -// + Send -// + 'a -// where -// Q: Query, -// Self: 'a; - -// fn query(&self, params: Q::Params) -> Self::QueryFuture<'_, Q>; - -// fn build_transaction( -// &self, -// params: Self::TxParams, -// ) -> ::Transaction; - -// fn submit_transaction( -// &self, -// tx: ::Transaction, -// ) -> Result<(), Self::Error>; - -// fn poll_next_event( -// &self, -// tx: ::Transaction, -// ) -> Result<(), Self::Error>; -// } From 0c48861fe9bdb257dd44f0c99119100a492a9a80 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 22 Jan 2024 13:56:27 -0300 Subject: [PATCH 26/26] Fix clippy warnings --- chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs index b62d2e0f..a105c802 100644 --- a/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs +++ b/chains/arbitrum/testing/rosetta-testing-arbitrum/src/lib.rs @@ -34,6 +34,7 @@ //! Note: The code assumes a local Arbitrum Nitro Testnet node running on `ws://127.0.0.1:8548` and //! a local Ethereum node on `http://localhost:8545`. Ensure that these endpoints are configured correctly. +#[allow(clippy::ignored_unit_patterns)] #[cfg(test)] mod tests { use alloy_sol_types::{sol, SolCall};