diff --git a/.gitignore b/.gitignore index d79576072..cc74be73e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ target .env .vscode +.trunk bin config.json node_modules @@ -14,4 +15,4 @@ hfuzz_workspace *.code-workspace evm_loader/solidity/artifacts evm_loader/solidity/cache -test-ledger \ No newline at end of file +test-ledger diff --git a/Dockerfile b/Dockerfile index 0d44a9d57..636b6c3fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,13 +19,13 @@ ENV NEON_REVISION=${REVISION} RUN cargo fmt --check && \ cargo clippy --release && \ cargo build --release && \ - cargo build-bpf --features devnet && cp target/deploy/evm_loader.so target/deploy/evm_loader-devnet.so && \ - cargo build-bpf --features testnet && cp target/deploy/evm_loader.so target/deploy/evm_loader-testnet.so && \ - cargo build-bpf --features govertest && cp target/deploy/evm_loader.so target/deploy/evm_loader-govertest.so && \ - cargo build-bpf --features govertest,emergency && cp target/deploy/evm_loader.so target/deploy/evm_loader-govertest-emergency.so && \ - cargo build-bpf --features mainnet && cp target/deploy/evm_loader.so target/deploy/evm_loader-mainnet.so && \ - cargo build-bpf --features mainnet,emergency && cp target/deploy/evm_loader.so target/deploy/evm_loader-mainnet-emergency.so && \ - cargo build-bpf --features ci --dump + cargo build-bpf --manifest-path program/Cargo.toml --features devnet && cp target/deploy/evm_loader.so target/deploy/evm_loader-devnet.so && \ + cargo build-bpf --manifest-path program/Cargo.toml --features testnet && cp target/deploy/evm_loader.so target/deploy/evm_loader-testnet.so && \ + cargo build-bpf --manifest-path program/Cargo.toml --features govertest && cp target/deploy/evm_loader.so target/deploy/evm_loader-govertest.so && \ + cargo build-bpf --manifest-path program/Cargo.toml --features govertest,emergency && cp target/deploy/evm_loader.so target/deploy/evm_loader-govertest-emergency.so && \ + cargo build-bpf --manifest-path program/Cargo.toml --features mainnet && cp target/deploy/evm_loader.so target/deploy/evm_loader-mainnet.so && \ + cargo build-bpf --manifest-path program/Cargo.toml --features mainnet,emergency && cp target/deploy/evm_loader.so target/deploy/evm_loader-mainnet-emergency.so && \ + cargo build-bpf --manifest-path program/Cargo.toml --features ci --dump # Add neon_test_invoke_program to the genesis @@ -42,6 +42,8 @@ COPY --from=evm-loader-builder /opt/neon-evm/evm_loader/target/release/neon-cli COPY --from=evm-loader-builder /opt/neon-evm/evm_loader/target/release/neon-api /opt/ COPY --from=neon_test_programs /opt/deploy/ /opt/deploy/ +COPY --from=evm-loader-builder /opt/neon-evm/evm_loader/target/release/neon-rpc /opt/ +COPY --from=evm-loader-builder /opt/neon-evm/evm_loader/target/release/libneon_lib.so /opt/libs/current/ COPY ci/wait-for-solana.sh \ ci/wait-for-neon.sh \ diff --git a/ci/docker-compose-ci.yml b/ci/docker-compose-ci.yml index a6351e0be..07b6549f2 100644 --- a/ci/docker-compose-ci.yml +++ b/ci/docker-compose-ci.yml @@ -8,7 +8,7 @@ services: - CI=true hostname: solana ports: - - "8899" + - "8899" expose: - "8899" ulimits: @@ -43,11 +43,42 @@ services: networks: - net + neon-core-rpc: + restart: unless-stopped + hostname: neon_core_rpc + entrypoint: /opt/neon-rpc /opt/libs/current + environment: + RUST_BACKTRACE: full + RUST_LOG: neon=debug + NEON_API_LISTENER_ADDR: 0.0.0.0:3100 + SOLANA_URL: http://solana:8899 + EVM_LOADER: 53DfF883gyixYNXnM7s5xhdeyV8mVk9T4i2hGV9vG9io + NEON_TOKEN_MINT: HPsV9Deocecw3GeZv1FkAPNCBRfuVyfw9MMwjwRe1xaU + NEON_CHAIN_ID: 111 + COMMITMENT: confirmed + NEON_DB_CLICKHOUSE_URLS: "http://45.250.253.36:8123;http://45.250.253.38:8123" + NEON_DB_INDEXER_HOST: 45.250.253.32 + NEON_DB_INDEXER_PORT: 5432 + NEON_DB_INDEXER_DATABASE: indexer + NEON_DB_INDEXER_USER: postgres + NEON_DB_INDEXER_PASSWORD: "vUlpDyAP0gA98R5Bu" + KEYPAIR: /opt/operator-keypairs/id.json + FEEPAIR: /opt/operator-keypairs/id.json + SOLANA_KEY_FOR_CONFIG: BMp6gEnveANdvSvspESJUrNczuHz1GF5UQKjVLCkAZih + image: ${EVM_LOADER_IMAGE} + ports: + - "3100" + expose: + - "3100" + networks: + - net + tests: image: ${NEON_TESTS_IMAGE} environment: - SOLANA_URL=http://solana:8899 - NEON_CORE_API_URL=http://neon_api:8085/api + - NEON_CORE_API_RPC_URL=http://neon_core_rpc:3100 hostname: tests command: sleep infinity networks: diff --git a/ci/docker-compose-test.yml b/ci/docker-compose-test.yml index 3577e8fc5..3f6cf6447 100644 --- a/ci/docker-compose-test.yml +++ b/ci/docker-compose-test.yml @@ -45,11 +45,42 @@ services: - "8085" networks: - net + + neon-core-rpc: + restart: unless-stopped + container_name: neon-core-rpc + hostname: neon_core_rpc + entrypoint: /opt/neon-rpc /opt/libs/current + environment: + RUST_BACKTRACE: full + RUST_LOG: neon=debug + NEON_API_LISTENER_ADDR: 0.0.0.0:3100 + SOLANA_URL: http://solana:8899 + EVM_LOADER: 53DfF883gyixYNXnM7s5xhdeyV8mVk9T4i2hGV9vG9io + NEON_TOKEN_MINT: HPsV9Deocecw3GeZv1FkAPNCBRfuVyfw9MMwjwRe1xaU + NEON_CHAIN_ID: 111 + COMMITMENT: confirmed + NEON_DB_CLICKHOUSE_URLS: "http://45.250.253.36:8123;http://45.250.253.38:8123" + NEON_DB_INDEXER_HOST: 45.250.253.32 + NEON_DB_INDEXER_PORT: 5432 + NEON_DB_INDEXER_DATABASE: indexer + NEON_DB_INDEXER_USER: postgres + NEON_DB_INDEXER_PASSWORD: "vUlpDyAP0gA98R5Bu" + KEYPAIR: /opt/operator-keypairs/id.json + FEEPAIR: /opt/operator-keypairs/id.json + image: ${EVM_LOADER_IMAGE} + ports: + - "3100:3100" + expose: + - "3100" + networks: + - net tests: image: ${NEON_TESTS_IMAGE} environment: - SOLANA_URL=http://solana:8899 - NEON_CORE_API_URL=http://neon_api:8085/api + - NEON_CORE_API_RPC_URL=http://neon_core_rpc:3100 hostname: tests command: sleep infinity networks: diff --git a/evm_loader/Cargo.lock b/evm_loader/Cargo.lock index 83f9c73a9..61bbf2cd9 100644 --- a/evm_loader/Cargo.lock +++ b/evm_loader/Cargo.lock @@ -12,6 +12,54 @@ dependencies = [ "regex", ] +[[package]] +name = "abi_stable" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d6512d3eb05ffe5004c59c206de7f99c34951504056ce23fc953842f12c445" +dependencies = [ + "abi_stable_derive", + "abi_stable_shared", + "const_panic", + "core_extensions", + "crossbeam-channel", + "generational-arena", + "libloading", + "lock_api", + "parking_lot", + "paste", + "repr_offset", + "rustc_version", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "abi_stable_derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7178468b407a4ee10e881bc7a328a65e739f0863615cca4429d43916b05e898" +dependencies = [ + "abi_stable_shared", + "as_derive_utils", + "core_extensions", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", + "typed-arena", +] + +[[package]] +name = "abi_stable_shared" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b5df7688c123e63f4d4d649cba63f2967ba7f7861b1664fca3f77d3dad2b63" +dependencies = [ + "core_extensions", +] + [[package]] name = "actix-codec" version = "0.5.1" @@ -466,6 +514,18 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as_derive_utils" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff3c96645900a44cf11941c111bd08a6573b0e2f9f69bc9264b179d8fae753c4" +dependencies = [ + "core_extensions", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ascii" version = "0.9.3" @@ -542,6 +602,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-ffi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed5a937a789391ebc1c77d3a15b060e0d100e6d7c483a2af3f250d6b8dc2a23" +dependencies = [ + "abi_stable", +] + [[package]] name = "async-mutex" version = "1.4.0" @@ -618,6 +687,15 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "1.3.3" @@ -1251,6 +1329,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "const_panic" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -1290,6 +1374,21 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "core_extensions" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c71dc07c9721607e7a16108336048ee978c3a8b129294534272e8bac96c0ee" +dependencies = [ + "core_extensions_proc_macros", +] + +[[package]] +name = "core_extensions_proc_macros" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f3b219d28b6e3b4ac87bc1fc522e0803ab22e055da177bff0068c4150c61a6" + [[package]] name = "cpufeatures" version = "0.2.6" @@ -1812,6 +1911,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde", +] + [[package]] name = "errno" version = "0.3.8" @@ -1923,6 +2031,15 @@ dependencies = [ "toml 0.8.0", ] +[[package]] +name = "extensions" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258f70bd2b060d448403a66d420e81dcac3e5247a4928a887404a5e03715e2e0" +dependencies = [ + "fxhash", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -2124,6 +2241,24 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generational-arena" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7" +dependencies = [ + "cfg-if", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2463,7 +2598,9 @@ dependencies = [ "futures-util", "http", "hyper", + "log", "rustls", + "rustls-native-certs", "tokio", "tokio-rustls", ] @@ -2698,6 +2835,76 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonrpc-v2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759affe8550a30591c68f5e85d1784f24dc65217d2cca765949857f844fcecb0" +dependencies = [ + "actix-service", + "actix-web", + "async-trait", + "bytes", + "erased-serde", + "extensions", + "futures", + "serde", + "serde_json", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2327ba8df2fdbd5e897e2b5ed25ce7f299d345b9736b6828814c3dbd1fd47b" +dependencies = [ + "anyhow", + "async-trait", + "beef", + "futures-util", + "hyper", + "jsonrpsee-types", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f80c17f62c7653ce767e3d7288b793dfec920f97067ceb189ebdd3570f2bc20" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be0be325642e850ed0bdff426674d2e66b2b7117c9be23a7caef68a2902b7d9" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "keccak" version = "0.1.3" @@ -2737,6 +2944,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libsecp256k1" version = "0.6.0" @@ -3112,20 +3329,25 @@ dependencies = [ name = "neon-lib" version = "1.11.1" dependencies = [ + "abi_stable", "anyhow", + "async-ffi", "async-trait", "base64 0.21.4", "bincode", "bs58", "build-info", "build-info-build", + "clap 2.34.0", "clickhouse", "enum_dispatch", "ethnum", "evm-loader", "goblin 0.6.1", "hex", + "lazy_static", "log", + "neon-lib-interface", "rand 0.8.5", "scroll", "serde", @@ -3140,12 +3362,63 @@ dependencies = [ "solana-transaction-status", "spl-associated-token-account", "spl-token", + "strum 0.25.0", + "strum_macros 0.25.3", "thiserror", "tokio", "tracing", "web3", ] +[[package]] +name = "neon-lib-interface" +version = "0.1.0" +dependencies = [ + "abi_stable", + "async-ffi", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "neon-rpc" +version = "0.1.0" +dependencies = [ + "actix-web", + "build-info", + "build-info-build", + "clap 2.34.0", + "jsonrpc-v2", + "neon-lib", + "neon-lib-interface", + "semver", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + +[[package]] +name = "neon-rpc-client" +version = "0.1.0" +dependencies = [ + "async-trait", + "build-info", + "jsonrpc-v2", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-types", + "neon-lib", + "serde", + "serde_json", + "thiserror", + "tokio", +] + [[package]] name = "nix" version = "0.26.4" @@ -4050,6 +4323,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "repr_offset" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1070755bd29dffc19d0971cab794e607839ba2ef4b69a9e6fbc8733c1b72ea" +dependencies = [ + "tstr", +] + [[package]] name = "reqwest" version = "0.11.20" @@ -4755,8 +5037,8 @@ dependencies = [ "solana-system-program", "solana-vote-program", "static_assertions", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "tar", "tempfile", "thiserror", @@ -5571,8 +5853,8 @@ dependencies = [ "solana-zk-token-proof-program", "solana-zk-token-sdk", "static_assertions", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "symlink", "tar", "tempfile", @@ -6175,9 +6457,15 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros", + "strum_macros 0.24.3", ] +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + [[package]] name = "strum_macros" version = "0.24.3" @@ -6191,6 +6479,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.50", +] + [[package]] name = "subtle" version = "2.4.1" @@ -6647,7 +6948,9 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ + "futures-core", "futures-util", + "pin-project", "pin-project-lite", "tokio", "tower-layer", @@ -6760,6 +7063,21 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tstr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca3264971090dec0feef3b455a3c178f02762f7550cf4592991ac64b3be2d7e" +dependencies = [ + "tstr_proc_macros", +] + +[[package]] +name = "tstr_proc_macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a" + [[package]] name = "tungstenite" version = "0.20.1" @@ -6781,6 +7099,12 @@ dependencies = [ "webpki-roots 0.24.0", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.16.0" diff --git a/evm_loader/Cargo.toml b/evm_loader/Cargo.toml index 5081e7429..c52ac9726 100644 --- a/evm_loader/Cargo.toml +++ b/evm_loader/Cargo.toml @@ -4,6 +4,9 @@ members = [ 'api', 'cli', 'lib', + 'lib-interface', + 'rpc', + 'rpc-client', 'program', - 'program-macro' + 'program-macro', ] diff --git a/evm_loader/api/src/api_server/mod.rs b/evm_loader/api/src/api_server/mod.rs index 53b4478d2..c3d449565 100644 --- a/evm_loader/api/src/api_server/mod.rs +++ b/evm_loader/api/src/api_server/mod.rs @@ -1,2 +1 @@ pub mod handlers; -pub mod state; diff --git a/evm_loader/api/src/main.rs b/evm_loader/api/src/main.rs index e52524e72..077e7cf7d 100644 --- a/evm_loader/api/src/main.rs +++ b/evm_loader/api/src/main.rs @@ -9,6 +9,7 @@ use actix_web::web; use actix_web::App; use actix_web::HttpServer; use api_server::handlers::NeonApiError; +use neon_lib::abi::state::State; pub use neon_lib::commands; pub use neon_lib::config; pub use neon_lib::errors; @@ -33,7 +34,7 @@ use tracing::info; use tracing_subscriber::EnvFilter; type NeonApiResult = Result; -type NeonApiState = Data; +type NeonApiState = Data; #[actix_web::main] async fn main() -> NeonApiResult<()> { @@ -51,8 +52,8 @@ async fn main() -> NeonApiResult<()> { info!("{}", get_build_info()); - let api_config = config::load_api_config_from_enviroment(); - let state: NeonApiState = Data::new(api_server::state::State::new(api_config)); + let api_config = config::load_api_config_from_environment(); + let state: NeonApiState = Data::new(State::new(api_config)); let listener_addr = options .value_of("host") diff --git a/evm_loader/lib-interface/.gitignore b/evm_loader/lib-interface/.gitignore new file mode 100644 index 000000000..2f7896d1d --- /dev/null +++ b/evm_loader/lib-interface/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/evm_loader/lib-interface/Cargo.lock b/evm_loader/lib-interface/Cargo.lock new file mode 100644 index 000000000..7c3935937 --- /dev/null +++ b/evm_loader/lib-interface/Cargo.lock @@ -0,0 +1,474 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "abi_stable" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f69d9465d88d24382d43fa68335a92fe9d3c53a918549c693403ed9a85eff50" +dependencies = [ + "abi_stable_derive", + "abi_stable_shared", + "const_panic", + "core_extensions", + "crossbeam-channel", + "generational-arena", + "libloading", + "lock_api", + "parking_lot", + "paste", + "repr_offset", + "rustc_version", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "abi_stable_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aecd3efa5a5294f5c67913d45f985ccb382b3c93327581529610eeecdf4821a" +dependencies = [ + "abi_stable_shared", + "as_derive_utils", + "core_extensions", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", + "typed-arena", +] + +[[package]] +name = "abi_stable_shared" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b5df7688c123e63f4d4d649cba63f2967ba7f7861b1664fca3f77d3dad2b63" +dependencies = [ + "core_extensions", +] + +[[package]] +name = "as_derive_utils" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff3c96645900a44cf11941c111bd08a6573b0e2f9f69bc9264b179d8fae753c4" +dependencies = [ + "core_extensions", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-ffi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed5a937a789391ebc1c77d3a15b060e0d100e6d7c483a2af3f250d6b8dc2a23" +dependencies = [ + "abi_stable", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const_panic" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" + +[[package]] +name = "core_extensions" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c71dc07c9721607e7a16108336048ee978c3a8b129294534272e8bac96c0ee" +dependencies = [ + "core_extensions_proc_macros", +] + +[[package]] +name = "core_extensions_proc_macros" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f3b219d28b6e3b4ac87bc1fc522e0803ab22e055da177bff0068c4150c61a6" + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "generational-arena" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "libc" +version = "0.2.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "neon-interface" +version = "0.1.0" +dependencies = [ + "abi_stable", + "async-ffi", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "proc-macro2" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "repr_offset" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1070755bd29dffc19d0971cab794e607839ba2ef4b69a9e6fbc8733c1b72ea" +dependencies = [ + "tstr", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.16", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.16", +] + +[[package]] +name = "tstr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca3264971090dec0feef3b455a3c178f02762f7550cf4592991ac64b3be2d7e" +dependencies = [ + "tstr_proc_macros", +] + +[[package]] +name = "tstr_proc_macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a" + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/evm_loader/lib-interface/Cargo.toml b/evm_loader/lib-interface/Cargo.toml new file mode 100644 index 000000000..1a1283d88 --- /dev/null +++ b/evm_loader/lib-interface/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "neon-lib-interface" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +abi_stable = "0.11.1" +thiserror = "1" +async-ffi = { version = "0.4.1", features = ["abi_stable"] } +serde = "1.0.147" +serde_json = "1.0.85" diff --git a/evm_loader/lib-interface/src/lib.rs b/evm_loader/lib-interface/src/lib.rs new file mode 100644 index 000000000..fab07f3f7 --- /dev/null +++ b/evm_loader/lib-interface/src/lib.rs @@ -0,0 +1,59 @@ +#![allow(non_camel_case_types)] + +pub mod types; + +use crate::types::RNeonEVMLibResult; +use std::{collections::HashMap, path::Path}; +use thiserror::Error; + +use abi_stable::{ + library::{LibraryError, RootModule}, + package_version_strings, + std_types::{RStr, RString}, + StableAbi, +}; + +#[repr(C)] +#[derive(StableAbi)] +#[sabi(kind(Prefix(prefix_ref = NeonEVMLib_Ref)))] +#[sabi(missing_field(panic))] +pub struct NeonEVMLib { + pub hash: extern "C" fn() -> RString, + pub get_version: extern "C" fn() -> RString, + pub get_build_info: extern "C" fn() -> RString, + + pub invoke: for<'a> extern "C" fn(RStr<'a>, RStr<'a>) -> RNeonEVMLibResult<'a>, +} + +impl RootModule for NeonEVMLib_Ref { + abi_stable::declare_root_module_statics! {NeonEVMLib_Ref} + + const BASE_NAME: &'static str = "neon-lib-interface"; + const NAME: &'static str = "neon-lib-interface"; + const VERSION_STRINGS: abi_stable::sabi_types::VersionStrings = package_version_strings!(); +} + +#[derive(Error, Debug)] +pub enum NeonEVMLibLoadError { + #[error("abi_stable library error")] + LibraryError(#[from] LibraryError), + #[error("IO error")] + IoError(#[from] std::io::Error), +} + +pub fn load_libraries

( + directory: P, +) -> Result, NeonEVMLibLoadError> +where + P: AsRef, +{ + let paths = std::fs::read_dir(directory)?; + let mut result = HashMap::new(); + for path in paths { + let lib = NeonEVMLib_Ref::load_from_file(&path?.path())?; + let hash = lib.hash()(); + + result.insert(hash.into_string(), lib); + } + Ok(result) +} diff --git a/evm_loader/lib-interface/src/types.rs b/evm_loader/lib-interface/src/types.rs new file mode 100644 index 000000000..c6f8f22fe --- /dev/null +++ b/evm_loader/lib-interface/src/types.rs @@ -0,0 +1,12 @@ +use abi_stable::std_types::{RResult, RString}; +use async_ffi::LocalBorrowingFfiFuture; +use serde::{Deserialize, Serialize}; + +pub type RNeonEVMLibResult<'a> = LocalBorrowingFfiFuture<'a, RResult>; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NeonEVMLibError { + pub code: u32, + pub message: String, + pub data: Option, +} diff --git a/evm_loader/lib/Cargo.toml b/evm_loader/lib/Cargo.toml index 8aeae9605..b2f4d8fbb 100644 --- a/evm_loader/lib/Cargo.toml +++ b/evm_loader/lib/Cargo.toml @@ -37,6 +37,16 @@ async-trait = "0.1.73" build-info = { version = "0.0.31", features = ["serde"] } enum_dispatch = "0.3.12" web3 = "0.19.0" +neon-lib-interface = { path = "../lib-interface" } +abi_stable = "0.11.2" +async-ffi = { version = "0.4.1", features = ["abi_stable"] } +strum = "0.25.0" +strum_macros = "0.25.3" +clap = "2.33.3" +lazy_static = "1.4.0" [build-dependencies] build-info-build = "0.0.31" + +[lib] +crate-type = ["cdylib", "lib"] diff --git a/evm_loader/lib/src/abi/emulate.rs b/evm_loader/lib/src/abi/emulate.rs new file mode 100644 index 000000000..0937068c2 --- /dev/null +++ b/evm_loader/lib/src/abi/emulate.rs @@ -0,0 +1,20 @@ +use super::params_to_neon_error; +use crate::commands::emulate::{self, EmulateResponse}; +use crate::commands::get_config::BuildConfigSimulator; +use crate::config::APIOptions; +use crate::rpc::Rpc; +use crate::tracing::tracers::TracerTypeEnum; +use crate::{types::EmulateApiRequest, NeonResult}; + +pub async fn execute( + rpc: &(impl Rpc + BuildConfigSimulator), + config: &APIOptions, + params: &str, +) -> NeonResult { + let params: EmulateApiRequest = + serde_json::from_str(params).map_err(|_| params_to_neon_error(params))?; + + emulate::execute(rpc, config.evm_loader, params.body, None::) + .await + .map(|(response, _)| response) +} diff --git a/evm_loader/lib/src/abi/get_balance.rs b/evm_loader/lib/src/abi/get_balance.rs new file mode 100644 index 000000000..5258a1c6b --- /dev/null +++ b/evm_loader/lib/src/abi/get_balance.rs @@ -0,0 +1,17 @@ +use super::params_to_neon_error; +use crate::commands::get_balance::{self, GetBalanceResponse}; +use crate::commands::get_config::BuildConfigSimulator; +use crate::config::APIOptions; +use crate::rpc::Rpc; +use crate::{types::GetBalanceRequest, NeonResult}; + +pub async fn execute( + rpc: &(impl Rpc + BuildConfigSimulator), + config: &APIOptions, + params: &str, +) -> NeonResult> { + let params: GetBalanceRequest = + serde_json::from_str(params).map_err(|_| params_to_neon_error(params))?; + + get_balance::execute(rpc, &config.evm_loader, ¶ms.account).await +} diff --git a/evm_loader/lib/src/abi/get_config.rs b/evm_loader/lib/src/abi/get_config.rs new file mode 100644 index 000000000..5ea8714e3 --- /dev/null +++ b/evm_loader/lib/src/abi/get_config.rs @@ -0,0 +1,12 @@ +use crate::commands::get_config::{self, BuildConfigSimulator, GetConfigResponse}; +use crate::config::APIOptions; +use crate::rpc::Rpc; +use crate::NeonResult; + +pub async fn execute( + rpc: &(impl Rpc + BuildConfigSimulator), + config: &APIOptions, + _params: &str, +) -> NeonResult { + get_config::execute(rpc, config.evm_loader).await +} diff --git a/evm_loader/lib/src/abi/get_contract.rs b/evm_loader/lib/src/abi/get_contract.rs new file mode 100644 index 000000000..dc5367596 --- /dev/null +++ b/evm_loader/lib/src/abi/get_contract.rs @@ -0,0 +1,17 @@ +use super::params_to_neon_error; +use crate::commands::get_config::BuildConfigSimulator; +use crate::commands::get_contract::{self, GetContractResponse}; +use crate::config::APIOptions; +use crate::rpc::Rpc; +use crate::{types::GetContractRequest, NeonResult}; + +pub async fn execute( + rpc: &(impl Rpc + BuildConfigSimulator), + config: &APIOptions, + params: &str, +) -> NeonResult> { + let params: GetContractRequest = + serde_json::from_str(params).map_err(|_| params_to_neon_error(params))?; + + get_contract::execute(rpc, &config.evm_loader, ¶ms.contract).await +} diff --git a/evm_loader/lib/src/abi/get_holder.rs b/evm_loader/lib/src/abi/get_holder.rs new file mode 100644 index 000000000..36a47f27b --- /dev/null +++ b/evm_loader/lib/src/abi/get_holder.rs @@ -0,0 +1,17 @@ +use super::params_to_neon_error; +use crate::commands::get_config::BuildConfigSimulator; +use crate::commands::get_holder::{self, GetHolderResponse}; +use crate::config::APIOptions; +use crate::rpc::Rpc; +use crate::{types::GetHolderRequest, NeonResult}; + +pub async fn execute( + rpc: &(impl Rpc + BuildConfigSimulator), + config: &APIOptions, + params: &str, +) -> NeonResult { + let params: GetHolderRequest = + serde_json::from_str(params).map_err(|_| params_to_neon_error(params))?; + + get_holder::execute(rpc, &config.evm_loader, params.pubkey).await +} diff --git a/evm_loader/lib/src/abi/get_storage_at.rs b/evm_loader/lib/src/abi/get_storage_at.rs new file mode 100644 index 000000000..2a0bf51f1 --- /dev/null +++ b/evm_loader/lib/src/abi/get_storage_at.rs @@ -0,0 +1,17 @@ +use super::params_to_neon_error; +use crate::commands::get_config::BuildConfigSimulator; +use crate::commands::get_storage_at::{self, GetStorageAtReturn}; +use crate::config::APIOptions; +use crate::rpc::Rpc; +use crate::{types::GetStorageAtRequest, NeonResult}; + +pub async fn execute( + rpc: &(impl Rpc + BuildConfigSimulator), + config: &APIOptions, + params: &str, +) -> NeonResult { + let params: GetStorageAtRequest = + serde_json::from_str(params).map_err(|_| params_to_neon_error(params))?; + + get_storage_at::execute(rpc, &config.evm_loader, params.contract, params.index).await +} diff --git a/evm_loader/lib/src/abi/mod.rs b/evm_loader/lib/src/abi/mod.rs new file mode 100644 index 000000000..2e8e40e15 --- /dev/null +++ b/evm_loader/lib/src/abi/mod.rs @@ -0,0 +1,137 @@ +mod emulate; +mod get_balance; +mod get_config; +mod get_contract; +mod get_holder; +mod get_storage_at; +pub mod state; +mod trace; + +use crate::{ + abi::state::State, + config::{self, APIOptions}, + types::RequestWithSlot, + LibMethod, NeonError, +}; +use abi_stable::{ + prefix_type::WithMetadata, + sabi_extern_fn, + std_types::{RStr, RString}, +}; +use async_ffi::FutureExt; +use lazy_static::lazy_static; +use neon_lib_interface::{ + types::{NeonEVMLibError, RNeonEVMLibResult}, + NeonEVMLib, +}; +use serde_json::json; + +lazy_static! { + static ref RUNTIME: tokio::runtime::Runtime = tokio::runtime::Runtime::new().unwrap(); + static ref STATE: State = State::new(load_config().unwrap()); +} + +pub const _MODULE_WM_: &WithMetadata = &WithMetadata::new(NeonEVMLib { + hash, + get_version, + get_build_info, + invoke, +}); + +#[sabi_extern_fn] +fn hash() -> RString { + env!("NEON_REVISION").into() +} + +#[sabi_extern_fn] +fn get_version() -> RString { + env!("CARGO_PKG_VERSION").into() +} + +#[sabi_extern_fn] +fn get_build_info() -> RString { + json!(crate::build_info::get_build_info()) + .to_string() + .into() +} + +#[sabi_extern_fn] +fn invoke<'a>(method: RStr<'a>, params: RStr<'a>) -> RNeonEVMLibResult<'a> { + async move { + // Needed for tokio::task::spawn_blocking using thread local storage inside dynamic library + // since dynamic library and executable have different thread local storage namespaces + let _guard = RUNTIME.enter(); + + dispatch(method.as_str(), params.as_str()) + .await + .map(RString::from) + .map_err(neon_error_to_rstring) + .into() + } + .into_local_ffi() +} + +fn load_config() -> Result { + Ok(config::load_api_config_from_environment()) +} + +async fn dispatch(method_str: &str, params_str: &str) -> Result { + let method: LibMethod = method_str.parse()?; + let RequestWithSlot { + slot, + tx_index_in_block, + } = match params_str { + "" => RequestWithSlot { + slot: None, + tx_index_in_block: None, + }, + _ => serde_json::from_str(params_str).map_err(|_| params_to_neon_error(params_str))?, + }; + let state = &STATE; + let config = &state.config; + let rpc = state.build_rpc(slot, tx_index_in_block).await?; + + match method { + LibMethod::Emulate => emulate::execute(&rpc, config, params_str) + .await + .map(|v| serde_json::to_string(&v).unwrap()), + LibMethod::GetStorageAt => get_storage_at::execute(&rpc, config, params_str) + .await + .map(|v| serde_json::to_string(&v).unwrap()), + LibMethod::GetBalance => get_balance::execute(&rpc, config, params_str) + .await + .map(|v| serde_json::to_string(&v).unwrap()), + LibMethod::GetConfig => get_config::execute(&rpc, config, params_str) + .await + .map(|v| serde_json::to_string(&v).unwrap()), + LibMethod::GetContract => get_contract::execute(&rpc, config, params_str) + .await + .map(|v| serde_json::to_string(&v).unwrap()), + LibMethod::GetHolder => get_holder::execute(&rpc, config, params_str) + .await + .map(|v| serde_json::to_string(&v).unwrap()), + LibMethod::Trace => trace::execute(&rpc, config, params_str) + .await + .map(|v| serde_json::to_string(&v).unwrap()), + // _ => Err(NeonError::IncorrectLibMethod), + } +} + +fn params_to_neon_error(params: &str) -> NeonError { + NeonError::EnvironmentError( + crate::commands::init_environment::EnvironmentError::InvalidProgramParameter(params.into()), + ) +} + +fn neon_error_to_neon_lib_error(error: NeonError) -> NeonEVMLibError { + assert!(error.error_code() >= 0); + NeonEVMLibError { + code: error.error_code() as u32, + message: error.to_string(), + data: None, + } +} + +fn neon_error_to_rstring(error: NeonError) -> RString { + RString::from(serde_json::to_string(&neon_error_to_neon_lib_error(error)).unwrap()) +} diff --git a/evm_loader/api/src/api_server/state.rs b/evm_loader/lib/src/abi/state.rs similarity index 83% rename from evm_loader/api/src/api_server/state.rs rename to evm_loader/lib/src/abi/state.rs index 3a9ae8523..bad3cb07d 100644 --- a/evm_loader/api/src/api_server/state.rs +++ b/evm_loader/lib/src/abi/state.rs @@ -1,7 +1,7 @@ -use neon_lib::config::APIOptions; -use neon_lib::rpc::{CallDbClient, CloneRpcClient, RpcEnum}; -use neon_lib::types::TracerDb; -use neon_lib::NeonError; +use crate::config::APIOptions; +use crate::rpc::{CallDbClient, CloneRpcClient, RpcEnum}; +use crate::types::TracerDb; +use crate::NeonError; pub struct State { pub tracer_db: TracerDb, diff --git a/evm_loader/lib/src/abi/trace.rs b/evm_loader/lib/src/abi/trace.rs new file mode 100644 index 000000000..491bf284f --- /dev/null +++ b/evm_loader/lib/src/abi/trace.rs @@ -0,0 +1,19 @@ +use serde_json::Value; + +use super::params_to_neon_error; +use crate::commands::get_config::BuildConfigSimulator; +use crate::commands::trace::{self}; +use crate::config::APIOptions; +use crate::rpc::Rpc; +use crate::{types::EmulateApiRequest, NeonResult}; + +pub async fn execute( + rpc: &(impl Rpc + BuildConfigSimulator), + config: &APIOptions, + params: &str, +) -> NeonResult { + let params: EmulateApiRequest = + serde_json::from_str(params).map_err(|_| params_to_neon_error(params))?; + + trace::trace_transaction(rpc, config.evm_loader, params.body).await +} diff --git a/evm_loader/lib/src/commands/get_config.rs b/evm_loader/lib/src/commands/get_config.rs index a2e87ee6d..019713936 100644 --- a/evm_loader/lib/src/commands/get_config.rs +++ b/evm_loader/lib/src/commands/get_config.rs @@ -24,7 +24,7 @@ use serde_with::{serde_as, DisplayFromStr}; use solana_client::rpc_config::RpcSimulateTransactionConfig; use tokio::sync::{Mutex, MutexGuard, OnceCell}; -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub enum Status { Ok, Emergency, @@ -41,7 +41,7 @@ pub struct ChainInfo { } #[serde_as] -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct GetConfigResponse { pub version: String, pub revision: String, diff --git a/evm_loader/lib/src/commands/get_holder.rs b/evm_loader/lib/src/commands/get_holder.rs index c58adb69f..fd7ae2d28 100644 --- a/evm_loader/lib/src/commands/get_holder.rs +++ b/evm_loader/lib/src/commands/get_holder.rs @@ -5,7 +5,7 @@ use evm_loader::account::{ }, Holder, StateAccount, StateFinalizedAccount, TAG_HOLDER, TAG_STATE, TAG_STATE_FINALIZED, }; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use solana_sdk::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use std::fmt::Display; @@ -13,7 +13,7 @@ use crate::{account_storage::account_info, rpc::Rpc, NeonResult}; use serde_with::{hex::Hex, serde_as, skip_serializing_none, DisplayFromStr}; -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub enum Status { #[default] Empty, @@ -23,9 +23,17 @@ pub enum Status { Finalized, } +#[serde_as] +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct AccountMeta { + pub is_writable: bool, + #[serde_as(as = "DisplayFromStr")] + pub key: Pubkey, +} + #[serde_as] #[skip_serializing_none] -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct GetHolderResponse { pub status: Status, pub len: Option, diff --git a/evm_loader/lib/src/config.rs b/evm_loader/lib/src/config.rs index 499262899..7706bf814 100644 --- a/evm_loader/lib/src/config.rs +++ b/evm_loader/lib/src/config.rs @@ -28,7 +28,7 @@ pub struct APIOptions { /// # Errors #[must_use] -pub fn load_api_config_from_enviroment() -> APIOptions { +pub fn load_api_config_from_environment() -> APIOptions { let solana_cli_config_path: Option = env::var("SOLANA_CLI_CONFIG_PATH").map(Some).unwrap_or(None); @@ -50,7 +50,7 @@ pub fn load_api_config_from_enviroment() -> APIOptions { .and_then(|v| Pubkey::from_str(&v).ok()) .expect("SOLANA_KEY_FOR_CONFIG variable must be a valid pubkey"); - let db_config = load_db_config_from_enviroment(); + let db_config = load_db_config_from_environment(); APIOptions { solana_cli_config_path, @@ -63,7 +63,7 @@ pub fn load_api_config_from_enviroment() -> APIOptions { } /// # Errors -fn load_db_config_from_enviroment() -> ChDbConfig { +fn load_db_config_from_environment() -> ChDbConfig { let clickhouse_url = env::var("NEON_DB_CLICKHOUSE_URLS") .map(|urls| { urls.split(';') diff --git a/evm_loader/lib/src/errors.rs b/evm_loader/lib/src/errors.rs index a7cf3e0be..e6ab476c5 100644 --- a/evm_loader/lib/src/errors.rs +++ b/evm_loader/lib/src/errors.rs @@ -6,6 +6,7 @@ use std::net::AddrParseError; use std::string::FromUtf8Error; use log::error; +use neon_lib_interface::NeonEVMLibLoadError; use solana_cli::cli::CliError as SolanaCliError; use solana_client::client_error::ClientError as SolanaClientError; use solana_client::tpu_client::TpuSenderError as SolanaTpuSenderError; @@ -45,6 +46,8 @@ pub enum NeonError { /// EVM Loader Error #[error("EVM Error. {0}")] EvmError(#[from] evm_loader::error::Error), + #[error("Can't load db config")] + LoadingDBConfigError, /// Need specify evm_loader #[error("EVM loader must be specified.")] EvmLoaderNotSpecified, @@ -114,6 +117,12 @@ pub enum NeonError { TryFromSliceError(#[from] TryFromSliceError), #[error("Solana pubkey for config must be specified.")] SolanaKeyForConfigNotSpecified, + #[error("library interface error")] + NeonEVMLibLoadError(#[from] NeonEVMLibLoadError), + #[error("Incorrect lib method")] + IncorrectLibMethod, + #[error("strum parse error {0:?}")] + StrumParseError(#[from] strum::ParseError), } impl NeonError { @@ -156,6 +165,10 @@ impl NeonError { NeonError::FromUtf8Error(_) => 258, NeonError::TryFromSliceError(_) => 259, NeonError::SolanaKeyForConfigNotSpecified => 260, + NeonError::NeonEVMLibLoadError(_) => 261, + NeonError::LoadingDBConfigError => 262, + NeonError::IncorrectLibMethod => 263, + NeonError::StrumParseError(_) => 264, } } } diff --git a/evm_loader/lib/src/lib.rs b/evm_loader/lib/src/lib.rs index d50c2f17b..57d634521 100644 --- a/evm_loader/lib/src/lib.rs +++ b/evm_loader/lib/src/lib.rs @@ -1,3 +1,4 @@ +pub mod abi; pub mod account_storage; pub mod build_info; pub mod build_info_common; @@ -9,7 +10,37 @@ pub mod rpc; pub mod tracing; pub mod types; +use abi::_MODULE_WM_; +use abi_stable::export_root_module; pub use config::Config; pub use errors::NeonError; +use neon_lib_interface::NeonEVMLib_Ref; pub type NeonResult = Result; + +const MODULE: NeonEVMLib_Ref = NeonEVMLib_Ref(_MODULE_WM_.static_as_prefix()); + +#[export_root_module] +pub fn get_root_module() -> NeonEVMLib_Ref { + MODULE +} + +use strum_macros::{AsRefStr, Display, EnumString, IntoStaticStr}; + +#[derive(Debug, Clone, Copy, PartialEq, Display, EnumString, IntoStaticStr, AsRefStr)] +pub enum LibMethod { + #[strum(serialize = "emulate")] + Emulate, + #[strum(serialize = "get_storage_at")] + GetStorageAt, + #[strum(serialize = "config")] + GetConfig, + #[strum(serialize = "balance")] + GetBalance, + #[strum(serialize = "contract")] + GetContract, + #[strum(serialize = "holder")] + GetHolder, + #[strum(serialize = "trace")] + Trace, +} diff --git a/evm_loader/lib/src/types/mod.rs b/evm_loader/lib/src/types/mod.rs index cf4138198..904ed772f 100644 --- a/evm_loader/lib/src/types/mod.rs +++ b/evm_loader/lib/src/types/mod.rs @@ -174,6 +174,30 @@ pub struct GetStorageAtRequest { pub slot: Option, } +#[derive(Deserialize, Serialize, Debug, Default)] +pub struct CancelTrxRequest { + pub storage_account: Pubkey, +} + +#[derive(Deserialize, Serialize, Debug, Default)] +pub struct RequestWithSlot { + pub slot: Option, + pub tx_index_in_block: Option, +} + +#[derive(Deserialize, Serialize, Debug, Default)] +pub struct GetNeonElfRequest { + pub program_location: Option, +} + +#[derive(Deserialize, Serialize, Debug, Default)] +pub struct InitEnvironmentRequest { + pub send_trx: bool, + pub force: bool, + pub keys_dir: Option, + pub file: Option, +} + #[serde_as] #[derive(Deserialize, Serialize, Debug, Default)] pub struct GetHolderRequest { diff --git a/evm_loader/rpc-client/Cargo.toml b/evm_loader/rpc-client/Cargo.toml new file mode 100644 index 000000000..826081a55 --- /dev/null +++ b/evm_loader/rpc-client/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "neon-rpc-client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = "1.0.189" +serde_json = "1.0.107" +jsonrpc-v2 = "0.13.0" +neon-lib = { path = "../lib" } +thiserror = "1.0.49" +async-trait = "0.1.74" +jsonrpsee-core = "0.20.2" +jsonrpsee-http-client = "0.20.2" +jsonrpsee-types = "0.20.2" +tokio = { version = "1", features = ["full"] } +build-info = { version = "0.0.31", features = ["serde"] } diff --git a/evm_loader/rpc-client/src/config.rs b/evm_loader/rpc-client/src/config.rs new file mode 100644 index 000000000..41a62c1fe --- /dev/null +++ b/evm_loader/rpc-client/src/config.rs @@ -0,0 +1,9 @@ +pub struct NeonRpcClientConfig { + pub url: String, +} + +impl NeonRpcClientConfig { + pub fn new(url: impl Into) -> NeonRpcClientConfig { + NeonRpcClientConfig { url: url.into() } + } +} diff --git a/evm_loader/rpc-client/src/error.rs b/evm_loader/rpc-client/src/error.rs new file mode 100644 index 000000000..2f6564fe3 --- /dev/null +++ b/evm_loader/rpc-client/src/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum NeonRpcClientError { + #[error("Jsonrpc error. {0:?}")] + JsonrpseeError(#[from] jsonrpsee_core::Error), + #[error("serde json error. {0:?}")] + SerdeJsonError(#[from] serde_json::Error), +} diff --git a/evm_loader/rpc-client/src/http.rs b/evm_loader/rpc-client/src/http.rs new file mode 100644 index 000000000..9312d20c7 --- /dev/null +++ b/evm_loader/rpc-client/src/http.rs @@ -0,0 +1,110 @@ +use async_trait::async_trait; +use jsonrpsee_core::{client::ClientT, rpc_params}; +use jsonrpsee_http_client::{HttpClient, HttpClientBuilder}; +use neon_lib::LibMethod; +use neon_lib::{ + commands::{ + emulate::EmulateResponse, get_balance::GetBalanceResponse, get_config::GetConfigResponse, + get_contract::GetContractResponse, get_holder::GetHolderResponse, + get_storage_at::GetStorageAtReturn, + }, + types::{ + EmulateApiRequest, GetBalanceRequest, GetContractRequest, GetHolderRequest, + GetStorageAtRequest, + }, +}; +use serde::de::DeserializeOwned; +use serde::Serialize; + +use crate::{config::NeonRpcClientConfig, NeonRpcClient, NeonRpcClientResult}; + +pub struct NeonRpcHttpClient { + client: HttpClient, +} + +impl NeonRpcHttpClient { + pub async fn new(config: NeonRpcClientConfig) -> NeonRpcClientResult { + Ok(NeonRpcHttpClient { + client: HttpClientBuilder::default().build(config.url)?, + }) + } +} + +pub struct NeonRpcHttpClientBuilder {} + +impl NeonRpcHttpClientBuilder { + pub fn new() -> NeonRpcHttpClientBuilder { + NeonRpcHttpClientBuilder {} + } + + pub async fn build(&self, url: impl Into) -> NeonRpcClientResult { + let config = NeonRpcClientConfig::new(url); + NeonRpcHttpClient::new(config).await + } +} + +impl Default for NeonRpcHttpClientBuilder { + fn default() -> Self { + Self::new() + } +} + +#[async_trait(?Send)] +impl NeonRpcClient for NeonRpcHttpClient { + async fn emulate(&self, params: EmulateApiRequest) -> NeonRpcClientResult { + self.request(LibMethod::Emulate, params).await + } + + async fn balance( + &self, + params: GetBalanceRequest, + ) -> NeonRpcClientResult> { + self.request(LibMethod::GetBalance, params).await + } + + async fn get_contract( + &self, + params: GetContractRequest, + ) -> NeonRpcClientResult> { + self.request(LibMethod::GetContract, params).await + } + + async fn get_config(&self) -> NeonRpcClientResult { + self.request_without_params(LibMethod::GetConfig).await + } + + async fn get_holder(&self, params: GetHolderRequest) -> NeonRpcClientResult { + self.request(LibMethod::GetHolder, params).await + } + + async fn get_storage_at( + &self, + params: GetStorageAtRequest, + ) -> NeonRpcClientResult { + self.request(LibMethod::GetStorageAt, params).await + } + + async fn trace(&self, params: EmulateApiRequest) -> NeonRpcClientResult { + self.request(LibMethod::Trace, params).await + } +} + +impl NeonRpcHttpClient { + async fn request(&self, method: LibMethod, params: P) -> NeonRpcClientResult + where + P: Serialize, + R: DeserializeOwned, + { + Ok(self + .client + .request(method.into(), rpc_params![params]) + .await?) + } + + async fn request_without_params(&self, method: LibMethod) -> NeonRpcClientResult + where + R: DeserializeOwned, + { + Ok(self.client.request(method.into(), rpc_params![]).await?) + } +} diff --git a/evm_loader/rpc-client/src/lib.rs b/evm_loader/rpc-client/src/lib.rs new file mode 100644 index 000000000..609805d17 --- /dev/null +++ b/evm_loader/rpc-client/src/lib.rs @@ -0,0 +1,40 @@ +mod config; +mod error; +pub mod http; + +pub use error::NeonRpcClientError; + +use async_trait::async_trait; +use neon_lib::{ + commands::{ + emulate::EmulateResponse, get_balance::GetBalanceResponse, get_config::GetConfigResponse, + get_contract::GetContractResponse, get_holder::GetHolderResponse, + get_storage_at::GetStorageAtReturn, + }, + types::{ + EmulateApiRequest, GetBalanceRequest, GetContractRequest, GetHolderRequest, + GetStorageAtRequest, + }, +}; + +type NeonRpcClientResult = Result; + +#[async_trait(?Send)] +pub trait NeonRpcClient { + async fn emulate(&self, params: EmulateApiRequest) -> NeonRpcClientResult; + async fn balance( + &self, + params: GetBalanceRequest, + ) -> NeonRpcClientResult>; + async fn get_contract( + &self, + params: GetContractRequest, + ) -> NeonRpcClientResult>; + async fn get_holder(&self, params: GetHolderRequest) -> NeonRpcClientResult; + async fn get_config(&self) -> NeonRpcClientResult; + async fn get_storage_at( + &self, + params: GetStorageAtRequest, + ) -> NeonRpcClientResult; + async fn trace(&self, params: EmulateApiRequest) -> NeonRpcClientResult; +} diff --git a/evm_loader/rpc/.gitignore b/evm_loader/rpc/.gitignore new file mode 100644 index 000000000..e82451797 --- /dev/null +++ b/evm_loader/rpc/.gitignore @@ -0,0 +1,3 @@ +libs +d +keys \ No newline at end of file diff --git a/evm_loader/rpc/Cargo.toml b/evm_loader/rpc/Cargo.toml new file mode 100644 index 000000000..ea03ce5dc --- /dev/null +++ b/evm_loader/rpc/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "neon-rpc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +actix-web = "4.3.1" +clap = "2.33.3" +jsonrpc-v2 = "0.13.0" +neon-lib-interface = { path = "../lib-interface" } +neon-lib = { path = "../lib" } +semver = "1.0.18" +serde = "1.0.188" +serde_json = "1.0.107" +tokio = { version = "1", features = ["full"] } +build-info = { version = "0.0.31", features = ["serde"] } +thiserror = "1.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-appender = "0.2.2" + +[build-dependencies] +build-info-build = "0.0.31" diff --git a/evm_loader/rpc/build.rs b/evm_loader/rpc/build.rs new file mode 100644 index 000000000..d36778f60 --- /dev/null +++ b/evm_loader/rpc/build.rs @@ -0,0 +1,3 @@ +fn main() { + build_info_build::build_script(); +} diff --git a/evm_loader/rpc/src/build_info.rs b/evm_loader/rpc/src/build_info.rs new file mode 100644 index 000000000..85f42da0d --- /dev/null +++ b/evm_loader/rpc/src/build_info.rs @@ -0,0 +1,7 @@ +use neon_lib::build_info_common::SlimBuildInfo; + +build_info::build_info!(fn build_info); + +pub fn get_build_info() -> SlimBuildInfo { + build_info().into() +} diff --git a/evm_loader/rpc/src/context.rs b/evm_loader/rpc/src/context.rs new file mode 100644 index 000000000..614b629a5 --- /dev/null +++ b/evm_loader/rpc/src/context.rs @@ -0,0 +1,6 @@ +use neon_lib_interface::NeonEVMLib_Ref; +use std::collections::HashMap; + +pub struct Context { + pub libraries: HashMap, +} diff --git a/evm_loader/rpc/src/error.rs b/evm_loader/rpc/src/error.rs new file mode 100644 index 000000000..bc7f2818f --- /dev/null +++ b/evm_loader/rpc/src/error.rs @@ -0,0 +1,27 @@ +use neon_lib::errors::NeonError; +use neon_lib_interface::NeonEVMLibLoadError; +use std::net::AddrParseError; + +use thiserror::Error; + +#[allow(clippy::enum_variant_names)] +#[derive(Debug, Error)] +pub enum NeonRPCError { + /// Std IO Error + #[error("Std I/O error. {0:?}")] + StdIoError(#[from] std::io::Error), + #[error("Addr parse error. {0:?}")] + AddrParseError(#[from] AddrParseError), + #[error("Neon error. {0:?}")] + NeonError(#[from] NeonError), + #[error("Neon lib error. {0:?}")] + NeonEVMLibLoadError(#[from] NeonEVMLibLoadError), + #[error("Neon RPC: Incorrect parameters.")] + IncorrectParameters(), +} + +impl From for jsonrpc_v2::Error { + fn from(value: NeonRPCError) -> Self { + jsonrpc_v2::Error::internal(value) + } +} diff --git a/evm_loader/rpc/src/handlers/emulate.rs b/evm_loader/rpc/src/handlers/emulate.rs new file mode 100644 index 000000000..9e068282c --- /dev/null +++ b/evm_loader/rpc/src/handlers/emulate.rs @@ -0,0 +1,17 @@ +use super::invoke; +use crate::{context::Context, error::NeonRPCError}; +use jsonrpc_v2::{Data, Params}; +use neon_lib::{types::EmulateApiRequest, LibMethod}; + +pub async fn handle( + ctx: Data, + Params(params): Params>, +) -> Result { + let param = params.first().ok_or(NeonRPCError::IncorrectParameters())?; + invoke( + LibMethod::Emulate, + ctx, + Some(serde_json::value::to_value(param).unwrap()), + ) + .await +} diff --git a/evm_loader/rpc/src/handlers/get_balance.rs b/evm_loader/rpc/src/handlers/get_balance.rs new file mode 100644 index 000000000..5d78d4b95 --- /dev/null +++ b/evm_loader/rpc/src/handlers/get_balance.rs @@ -0,0 +1,17 @@ +use super::invoke; +use crate::{context::Context, error::NeonRPCError}; +use jsonrpc_v2::{Data, Params}; +use neon_lib::{types::GetBalanceRequest, LibMethod}; + +pub async fn handle( + ctx: Data, + Params(params): Params>, +) -> Result { + let param = params.first().ok_or(NeonRPCError::IncorrectParameters())?; + invoke( + LibMethod::GetBalance, + ctx, + Some(serde_json::value::to_value(param).unwrap()), + ) + .await +} diff --git a/evm_loader/rpc/src/handlers/get_config.rs b/evm_loader/rpc/src/handlers/get_config.rs new file mode 100644 index 000000000..d7f579eb7 --- /dev/null +++ b/evm_loader/rpc/src/handlers/get_config.rs @@ -0,0 +1,8 @@ +use super::invoke; +use crate::context::Context; +use jsonrpc_v2::Data; +use neon_lib::LibMethod; + +pub async fn handle(ctx: Data) -> Result { + invoke(LibMethod::GetConfig, ctx, Option::::None).await +} diff --git a/evm_loader/rpc/src/handlers/get_contract.rs b/evm_loader/rpc/src/handlers/get_contract.rs new file mode 100644 index 000000000..422bd73b4 --- /dev/null +++ b/evm_loader/rpc/src/handlers/get_contract.rs @@ -0,0 +1,17 @@ +use super::invoke; +use crate::{context::Context, error::NeonRPCError}; +use jsonrpc_v2::{Data, Params}; +use neon_lib::{types::GetContractRequest, LibMethod}; + +pub async fn handle( + ctx: Data, + Params(params): Params>, +) -> Result { + let param = params.first().ok_or(NeonRPCError::IncorrectParameters())?; + invoke( + LibMethod::GetContract, + ctx, + Some(serde_json::value::to_value(param).unwrap()), + ) + .await +} diff --git a/evm_loader/rpc/src/handlers/get_holder.rs b/evm_loader/rpc/src/handlers/get_holder.rs new file mode 100644 index 000000000..8140401bf --- /dev/null +++ b/evm_loader/rpc/src/handlers/get_holder.rs @@ -0,0 +1,17 @@ +use super::invoke; +use crate::{context::Context, error::NeonRPCError}; +use jsonrpc_v2::{Data, Params}; +use neon_lib::{types::GetHolderRequest, LibMethod}; + +pub async fn handle( + ctx: Data, + Params(params): Params>, +) -> Result { + let param = params.first().ok_or(NeonRPCError::IncorrectParameters())?; + invoke( + LibMethod::GetHolder, + ctx, + Some(serde_json::value::to_value(param).unwrap()), + ) + .await +} diff --git a/evm_loader/rpc/src/handlers/get_storage_at.rs b/evm_loader/rpc/src/handlers/get_storage_at.rs new file mode 100644 index 000000000..8bf80a4a9 --- /dev/null +++ b/evm_loader/rpc/src/handlers/get_storage_at.rs @@ -0,0 +1,17 @@ +use super::invoke; +use crate::{context::Context, error::NeonRPCError}; +use jsonrpc_v2::{Data, Params}; +use neon_lib::{types::GetStorageAtRequest, LibMethod}; + +pub async fn handle( + ctx: Data, + Params(params): Params>, +) -> Result { + let param = params.first().ok_or(NeonRPCError::IncorrectParameters())?; + invoke( + LibMethod::GetStorageAt, + ctx, + Some(serde_json::value::to_value(param).unwrap()), + ) + .await +} diff --git a/evm_loader/rpc/src/handlers/info.rs b/evm_loader/rpc/src/handlers/info.rs new file mode 100644 index 000000000..afc36bb76 --- /dev/null +++ b/evm_loader/rpc/src/handlers/info.rs @@ -0,0 +1,7 @@ +use serde_json::json; + +use crate::build_info::get_build_info; + +pub async fn handle() -> Result { + Ok(json!(get_build_info())) +} diff --git a/evm_loader/rpc/src/handlers/lib_info.rs b/evm_loader/rpc/src/handlers/lib_info.rs new file mode 100644 index 000000000..1d0f80bdb --- /dev/null +++ b/evm_loader/rpc/src/handlers/lib_info.rs @@ -0,0 +1,7 @@ +use super::lib_build_info; +use crate::context::Context; +use jsonrpc_v2::Data; + +pub async fn handle(ctx: Data) -> Result { + lib_build_info(ctx).await +} diff --git a/evm_loader/rpc/src/handlers/mod.rs b/evm_loader/rpc/src/handlers/mod.rs new file mode 100644 index 000000000..08f6f978d --- /dev/null +++ b/evm_loader/rpc/src/handlers/mod.rs @@ -0,0 +1,82 @@ +pub mod emulate; +pub mod get_balance; +pub mod get_config; +pub mod get_contract; +pub mod get_holder; +pub mod get_storage_at; +pub mod info; +pub mod lib_info; +pub mod trace; + +use crate::context::Context; +use jsonrpc_v2::Data; +use neon_lib::LibMethod; +use neon_lib_interface::{types::NeonEVMLibError, NeonEVMLib_Ref}; +use serde::Serialize; +use serde_json::Value; + +async fn get_library(context: &Data) -> Result<&NeonEVMLib_Ref, jsonrpc_v2::Error> { + // just for testing + let hash = context + .libraries + .keys() + .last() + .ok_or(jsonrpc_v2::Error::internal("library collection is empty"))?; + + let library = context + .libraries + .get(hash) + .ok_or(jsonrpc_v2::Error::internal(format!( + "Library not found for hash {hash}" + )))?; + + tracing::debug!("ver {:?}", library.hash()()); + + Ok(library) +} + +pub async fn invoke( + method: LibMethod, + context: Data, + params: Option, +) -> Result { + let library = get_library(&context).await?; + + let method_str: &str = method.into(); + let mut params_str: String = "".to_string(); + if let Some(params_value) = params { + params_str = serde_json::to_string(¶ms_value).unwrap(); + } + + library.invoke()(method_str.into(), params_str.as_str().into()) + .await + .map(|x| serde_json::from_str::(&x).unwrap()) + .map_err(|s| { + let NeonEVMLibError { + code, + message, + data, + } = serde_json::from_str(s.as_str()).unwrap(); + + jsonrpc_v2::Error::Full { + code: code as i64, + message, + data: Some(Box::new( + data.as_ref() + .and_then(Value::as_str) + .unwrap_or("null") + .to_string(), + )), + } + }) + .into() +} + +pub async fn lib_build_info( + context: Data, +) -> Result { + let library = get_library(&context).await?; + let build_info = library.get_build_info()(); + + Ok(serde_json::from_str::(&build_info).unwrap()) +} diff --git a/evm_loader/rpc/src/handlers/trace.rs b/evm_loader/rpc/src/handlers/trace.rs new file mode 100644 index 000000000..826c65f5c --- /dev/null +++ b/evm_loader/rpc/src/handlers/trace.rs @@ -0,0 +1,17 @@ +use super::invoke; +use crate::{context::Context, error::NeonRPCError}; +use jsonrpc_v2::{Data, Params}; +use neon_lib::{types::EmulateApiRequest, LibMethod}; + +pub async fn handle( + ctx: Data, + Params(params): Params>, +) -> Result { + let param = params.first().ok_or(NeonRPCError::IncorrectParameters())?; + invoke( + LibMethod::Trace, + ctx, + Some(serde_json::value::to_value(param).unwrap()), + ) + .await +} diff --git a/evm_loader/rpc/src/main.rs b/evm_loader/rpc/src/main.rs new file mode 100644 index 000000000..7c0f5a7af --- /dev/null +++ b/evm_loader/rpc/src/main.rs @@ -0,0 +1,75 @@ +// use std::{collections::HashMap, error::Error}; +mod build_info; +mod context; +mod error; +mod handlers; +mod options; +mod rpc; + +use crate::build_info::get_build_info; +use context::Context; +use error::NeonRPCError; +use neon_lib::config; +use std::{env, net::SocketAddr, str::FromStr}; +use tracing::info; +use tracing_appender::non_blocking::NonBlockingBuilder; + +type NeonRPCResult = Result; + +#[actix_web::main] +async fn main() -> NeonRPCResult<()> { + let matches = options::parse(); + + // initialize tracing + let (non_blocking, _guard) = NonBlockingBuilder::default() + .lossy(false) + .finish(std::io::stdout()); + + tracing_subscriber::fmt().with_writer(non_blocking).init(); + + let lib_dir = matches.value_of("LIB-DIR").unwrap(); + let libraries = neon_lib_interface::load_libraries(lib_dir)?; + + info!("BUILD INFO: {}", get_build_info()); + info!( + "LIBRARY DIR: {}, count: {}", + lib_dir, + libraries.keys().len(), + ); + + if libraries.keys().len() > 0 { + info!("=== LIBRARY VERSIONS: ================================================================="); + for library_ver in libraries.keys() { + info!("Lib version: {}", library_ver); + } + info!("=== END LIBRARY VERSIONS =============================================================="); + } + + // check configs + let _api_config = config::load_api_config_from_environment(); + + let ctx = Context { libraries }; + let rpc = rpc::build_rpc(ctx); + + let listener_addr = matches + .value_of("host") + .map(std::borrow::ToOwned::to_owned) + .or_else(|| Some(env::var("NEON_API_LISTENER_ADDR").unwrap_or("0.0.0.0:3100".to_owned()))) + .unwrap(); + + let addr = SocketAddr::from_str(listener_addr.as_str())?; + + actix_web::HttpServer::new(move || { + let rpc = rpc.clone(); + actix_web::App::new().service( + actix_web::web::service("/") + .guard(actix_web::guard::Post()) + .finish(rpc.into_web_service()), + ) + }) + .bind(addr)? + .run() + .await?; + + Ok(()) +} diff --git a/evm_loader/rpc/src/options.rs b/evm_loader/rpc/src/options.rs new file mode 100644 index 000000000..c7d953ce4 --- /dev/null +++ b/evm_loader/rpc/src/options.rs @@ -0,0 +1,26 @@ +use clap::ArgMatches; + +pub fn parse<'a>() -> ArgMatches<'a> { + clap::App::new("Neon Core RPC") + .version(env!("CARGO_PKG_VERSION")) + .author("Neon Labs") + .about("Runs a Neon Core RPC server") + .arg( + clap::Arg::with_name("LIB-DIR") + .env("NEON_LIB_DIR") + .alias("dir") + .help("Directory with neon libraries to load") + .required(true) + .index(1), + ) + .arg( + clap::Arg::with_name("HOST") + .alias("host") + .env("NEON_API_LISTENER_ADDR") + .default_value("0.0.0.0:3100") + .help("RPC host to connect to") + .required(false) + .index(2), + ) + .get_matches() +} diff --git a/evm_loader/rpc/src/rpc.rs b/evm_loader/rpc/src/rpc.rs new file mode 100644 index 000000000..eb49a5042 --- /dev/null +++ b/evm_loader/rpc/src/rpc.rs @@ -0,0 +1,24 @@ +use crate::context::Context; +use crate::handlers::{ + emulate, get_balance, get_config, get_contract, get_holder, get_storage_at, info, lib_info, + trace, +}; + +use jsonrpc_v2::{Data, MapRouter, Server}; +use neon_lib::LibMethod; +use std::sync::Arc; + +pub fn build_rpc(ctx: Context) -> Arc> { + Server::new() + .with_data(Data::new(ctx)) + .with_method("build_info", info::handle) + .with_method("lib_build_info", lib_info::handle) + .with_method(LibMethod::GetStorageAt.to_string(), get_storage_at::handle) + .with_method(LibMethod::Trace.to_string(), trace::handle) + .with_method(LibMethod::Emulate.to_string(), emulate::handle) + .with_method(LibMethod::GetBalance.to_string(), get_balance::handle) + .with_method(LibMethod::GetConfig.to_string(), get_config::handle) + .with_method(LibMethod::GetHolder.to_string(), get_holder::handle) + .with_method(LibMethod::GetContract.to_string(), get_contract::handle) + .finish() +}