├── .cargo └── config.toml ├── .circleci └── config.yml ├── .clippy.toml ├── .dockerignore ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── book.yml │ ├── ci.yml │ ├── dependencies.yml │ ├── monthly_block_root_update.yml │ ├── release.yml │ └── stale.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── Cross.toml ├── LICENSE ├── Makefile ├── README.md ├── bin ├── e2hs-writer │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── cli.rs │ │ ├── main.rs │ │ └── subcommands │ │ ├── full_block.rs │ │ ├── head_generator │ │ ├── e2hs_builder.rs │ │ ├── mod.rs │ │ └── s3_bucket.rs │ │ ├── mod.rs │ │ └── single_generator │ │ ├── e2hs_builder.rs │ │ ├── era_binary_search.rs │ │ ├── mod.rs │ │ └── provider.rs ├── portal-bridge │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── bridge │ │ ├── beacon.rs │ │ ├── constants.rs │ │ ├── e2hs.rs │ │ ├── ephemeral_history │ │ │ ├── ephemeral_bundle.rs │ │ │ ├── gossiper.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── offer_report.rs │ │ └── state.rs │ │ ├── census │ │ ├── mod.rs │ │ ├── network.rs │ │ ├── peer.rs │ │ ├── peers.rs │ │ └── scoring.rs │ │ ├── cli.rs │ │ ├── constants.rs │ │ ├── handle.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── types │ │ ├── mod.rs │ │ ├── mode.rs │ │ └── range.rs │ │ └── utils.rs ├── trin-execution │ ├── Cargo.toml │ ├── README.md │ ├── metrics │ │ ├── README.md │ │ ├── compose.yaml │ │ ├── grafana │ │ │ ├── dashboard.yaml │ │ │ ├── dashboards │ │ │ │ └── trin-execution-main.json │ │ │ └── datasource.yml │ │ └── prometheus │ │ │ ├── prometheus.yml │ │ │ └── rules.yml │ ├── resources │ │ └── genesis │ │ │ └── mainnet.json │ ├── src │ │ ├── cli.rs │ │ ├── config.rs │ │ ├── content.rs │ │ ├── e2hs │ │ │ ├── beacon.rs │ │ │ ├── manager.rs │ │ │ ├── mod.rs │ │ │ ├── types.rs │ │ │ └── utils.rs │ │ ├── engine │ │ │ ├── mod.rs │ │ │ └── rpc.rs │ │ ├── evm │ │ │ ├── block_executor.rs │ │ │ ├── dao_fork.rs │ │ │ ├── mod.rs │ │ │ ├── post_block_beneficiaries.rs │ │ │ └── pre_block_contracts.rs │ │ ├── execution.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── metrics.rs │ │ ├── storage │ │ │ ├── account_db.rs │ │ │ ├── error.rs │ │ │ ├── evm_db.rs │ │ │ ├── execution_position.rs │ │ │ ├── mod.rs │ │ │ ├── trie_db.rs │ │ │ └── utils.rs │ │ ├── subcommands │ │ │ ├── e2ss │ │ │ │ ├── export.rs │ │ │ │ ├── import.rs │ │ │ │ ├── mod.rs │ │ │ │ └── utils.rs │ │ │ └── mod.rs │ │ ├── trie_walker │ │ │ ├── db.rs │ │ │ ├── filter.rs │ │ │ └── mod.rs │ │ ├── types │ │ │ ├── block_to_trace.rs │ │ │ ├── mod.rs │ │ │ └── trie_proof.rs │ │ └── utils.rs │ └── tests │ │ ├── content_generation.rs │ │ └── export_import.rs └── trin │ ├── Cargo.toml │ └── src │ ├── cli.rs │ ├── handle.rs │ ├── lib.rs │ ├── main.rs │ └── run.rs ├── book ├── .gitignore ├── README.md ├── book.toml ├── mermaid-init.js └── src │ ├── SUMMARY.md │ ├── concepts │ ├── README.md │ └── portal_vs_standard.md │ ├── developers │ ├── README.md │ ├── architecture │ │ ├── README.md │ │ ├── database.md │ │ ├── process_flow.md │ │ ├── testing.md │ │ └── workspaces.md │ ├── contributing │ │ ├── README.md │ │ ├── book.md │ │ ├── build_instructions.md │ │ ├── build_instructions │ │ │ ├── linux.md │ │ │ ├── mac_os.md │ │ │ ├── raspberry_pi.md │ │ │ └── windows.md │ │ ├── git │ │ │ ├── README.md │ │ │ ├── code_review.md │ │ │ ├── commits.md │ │ │ ├── fetching_pull_requests.md │ │ │ ├── merging.md │ │ │ ├── pull_requests.md │ │ │ ├── rebasing.md │ │ │ ├── release_notes.md │ │ │ └── submodules.md │ │ ├── releases │ │ │ ├── README.md │ │ │ ├── deployment.md │ │ │ └── release_checklist.md │ │ ├── rotation │ │ │ └── index.md │ │ ├── rust │ │ │ ├── README.md │ │ │ ├── comments.md │ │ │ ├── error_handling.md │ │ │ ├── imports.md │ │ │ ├── logging.md │ │ │ └── style.md │ │ └── tests.md │ ├── core_concepts │ │ ├── README.md │ │ ├── archive_nodes.md │ │ ├── bridge.md │ │ ├── chain_tip.md │ │ ├── cryptographic_accumulator.md │ │ └── finding_peers.md │ ├── developer_stories.md │ ├── goals.md │ ├── progress_status.md │ ├── protocols │ │ ├── README.md │ │ └── json_rpc.md │ └── quick_setup.md │ ├── introduction │ ├── README.md │ ├── portal_network.md │ └── quickstart.md │ └── users │ ├── README.md │ ├── cli.md │ ├── faq.md │ ├── monitoring.md │ ├── problems.md │ ├── requirements.md │ ├── startup.md │ └── use │ ├── README.md │ ├── ethereum_data.md │ ├── making_queries.md │ ├── portal_network_data.md │ └── remote_access.md ├── crates ├── e2store │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── bin │ │ └── e2ss-stats.rs │ │ ├── e2hs.rs │ │ ├── e2ss.rs │ │ ├── e2store │ │ ├── memory.rs │ │ ├── mod.rs │ │ ├── stream.rs │ │ └── types.rs │ │ ├── entry_types.rs │ │ ├── era.rs │ │ ├── era1.rs │ │ ├── lib.rs │ │ ├── types.rs │ │ └── utils.rs ├── ethereum-rpc-client │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── consensus │ │ ├── constants.rs │ │ ├── event.rs │ │ ├── mod.rs │ │ └── rpc_types.rs │ │ ├── constants.rs │ │ ├── execution.rs │ │ ├── http_client.rs │ │ └── lib.rs ├── ethportal-api │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── build.rs │ └── src │ │ ├── assets │ │ └── test │ │ │ ├── block_body_14764013.bin │ │ │ ├── fluffy_epoch_acc.bin │ │ │ ├── receipts_14764013.bin │ │ │ └── ultralight_testEpoch.hex │ │ ├── beacon.rs │ │ ├── discv5.rs │ │ ├── eth.rs │ │ ├── history.rs │ │ ├── lib.rs │ │ ├── state.rs │ │ ├── test_utils │ │ ├── constants.rs │ │ ├── mod.rs │ │ └── types.rs │ │ ├── types │ │ ├── accept_code.rs │ │ ├── bytes.rs │ │ ├── client_type.rs │ │ ├── consensus │ │ │ ├── beacon_block.rs │ │ │ ├── beacon_state.rs │ │ │ ├── body.rs │ │ │ ├── consolidation_request.rs │ │ │ ├── constants.rs │ │ │ ├── deposit_request.rs │ │ │ ├── execution_payload.rs │ │ │ ├── execution_requests.rs │ │ │ ├── fork.rs │ │ │ ├── header.rs │ │ │ ├── historical_summaries.rs │ │ │ ├── kzg_commitment.rs │ │ │ ├── light_client │ │ │ │ ├── bootstrap.rs │ │ │ │ ├── finality_update.rs │ │ │ │ ├── header.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── optimistic_update.rs │ │ │ │ ├── store.rs │ │ │ │ └── update.rs │ │ │ ├── mod.rs │ │ │ ├── participation_flags.rs │ │ │ ├── pending_balance_deposit.rs │ │ │ ├── pending_consolidation.rs │ │ │ ├── pending_partial_withdrawal.rs │ │ │ ├── proof.rs │ │ │ ├── pubkey.rs │ │ │ ├── serde.rs │ │ │ ├── signature.rs │ │ │ ├── sync_committee.rs │ │ │ └── withdrawal_request.rs │ │ ├── content_key │ │ │ ├── beacon.rs │ │ │ ├── error.rs │ │ │ ├── history.rs │ │ │ ├── mod.rs │ │ │ ├── overlay.rs │ │ │ └── state.rs │ │ ├── content_value │ │ │ ├── beacon.rs │ │ │ ├── constants.rs │ │ │ ├── error.rs │ │ │ ├── history.rs │ │ │ ├── mod.rs │ │ │ └── state.rs │ │ ├── discv5.rs │ │ ├── distance.rs │ │ ├── enr.rs │ │ ├── execution │ │ │ ├── accumulator.rs │ │ │ ├── block_body.rs │ │ │ ├── builders │ │ │ │ ├── block.rs │ │ │ │ ├── header.rs │ │ │ │ └── mod.rs │ │ │ ├── ephermeral_header.rs │ │ │ ├── header_with_proof.rs │ │ │ ├── mod.rs │ │ │ ├── receipts.rs │ │ │ └── ssz_header.rs │ │ ├── jsonrpc │ │ │ ├── endpoints.rs │ │ │ ├── json_rpc_mock.rs │ │ │ ├── mod.rs │ │ │ ├── params.rs │ │ │ └── request.rs │ │ ├── mod.rs │ │ ├── network.rs │ │ ├── network_spec.rs │ │ ├── node_id.rs │ │ ├── ping_extensions │ │ │ ├── consts.rs │ │ │ ├── decode.rs │ │ │ ├── extension_types.rs │ │ │ ├── extensions │ │ │ │ ├── mod.rs │ │ │ │ ├── type_0.rs │ │ │ │ ├── type_1.rs │ │ │ │ ├── type_2.rs │ │ │ │ └── type_65535.rs │ │ │ └── mod.rs │ │ ├── portal.rs │ │ ├── portal_wire.rs │ │ ├── protocol_versions.rs │ │ ├── query_trace.rs │ │ └── state_trie │ │ │ ├── account_state.rs │ │ │ ├── mod.rs │ │ │ ├── nibbles.rs │ │ │ ├── trie_traversal.rs │ │ │ └── utils.rs │ │ ├── utils │ │ ├── bytes.rs │ │ ├── mod.rs │ │ └── serde │ │ │ ├── hex_fixed_vec.rs │ │ │ ├── hex_var_list.rs │ │ │ └── mod.rs │ │ ├── version.rs │ │ └── web3.rs ├── evm │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── async_db.rs │ │ ├── lib.rs │ │ ├── spec_id.rs │ │ └── tx_env_modifier.rs ├── light-client │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ ├── client.rs │ │ ├── config │ │ │ ├── base.rs │ │ │ ├── checkpoints.rs │ │ │ ├── cli.rs │ │ │ ├── client_config.rs │ │ │ ├── mod.rs │ │ │ ├── networks.rs │ │ │ └── types.rs │ │ ├── consensus │ │ │ ├── consensus_client.rs │ │ │ ├── constants.rs │ │ │ ├── errors.rs │ │ │ ├── mod.rs │ │ │ ├── rpc │ │ │ │ ├── mock_rpc.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── nimbus_rpc.rs │ │ │ │ └── portal_rpc.rs │ │ │ ├── types.rs │ │ │ └── utils.rs │ │ ├── database.rs │ │ ├── errors.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── node.rs │ │ ├── rpc.rs │ │ ├── utils.rs │ │ └── watch.rs │ ├── testdata │ │ ├── bootstrap.json │ │ ├── finality.json │ │ ├── optimistic.json │ │ └── updates.json │ └── tests │ │ └── sync.rs ├── metrics │ ├── Cargo.toml │ └── src │ │ ├── bridge.rs │ │ ├── chain_head.rs │ │ ├── labels.rs │ │ ├── lib.rs │ │ ├── overlay.rs │ │ ├── portalnet.rs │ │ ├── storage.rs │ │ └── timer.rs ├── portalnet │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ ├── accept_queue.rs │ │ ├── bootnodes.rs │ │ ├── config.rs │ │ ├── constants.rs │ │ ├── discovery.rs │ │ ├── events.rs │ │ ├── find │ │ │ ├── iterators │ │ │ │ ├── findcontent.rs │ │ │ │ ├── findnodes.rs │ │ │ │ ├── mod.rs │ │ │ │ └── query.rs │ │ │ ├── mod.rs │ │ │ ├── query_info.rs │ │ │ └── query_pool.rs │ │ ├── gossip │ │ │ ├── mod.rs │ │ │ ├── neighborhood.rs │ │ │ └── random.rs │ │ ├── lib.rs │ │ ├── overlay │ │ │ ├── command.rs │ │ │ ├── config.rs │ │ │ ├── errors.rs │ │ │ ├── mod.rs │ │ │ ├── ping_extensions.rs │ │ │ ├── protocol.rs │ │ │ ├── request.rs │ │ │ └── service │ │ │ │ ├── find_content.rs │ │ │ │ ├── find_nodes.rs │ │ │ │ ├── manager.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── offer.rs │ │ │ │ ├── ping │ │ │ │ ├── handlers.rs │ │ │ │ └── mod.rs │ │ │ │ └── utils.rs │ │ ├── put_content.rs │ │ ├── socket.rs │ │ ├── types │ │ │ ├── kbucket.rs │ │ │ ├── mod.rs │ │ │ └── node.rs │ │ ├── utils │ │ │ ├── db.rs │ │ │ ├── mod.rs │ │ │ └── portal_wire.rs │ │ └── utp │ │ │ ├── controller.rs │ │ │ ├── mod.rs │ │ │ └── timed_semaphore.rs │ └── tests │ │ └── overlay.rs ├── rpc │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── beacon_rpc.rs │ │ ├── builder.rs │ │ ├── config.rs │ │ ├── cors.rs │ │ ├── discv5_rpc.rs │ │ ├── errors.rs │ │ ├── eth_rpc.rs │ │ ├── evm_state.rs │ │ ├── fetch.rs │ │ ├── history_rpc.rs │ │ ├── lib.rs │ │ ├── ping_extension.rs │ │ ├── rpc_server.rs │ │ ├── serde.rs │ │ ├── state_rpc.rs │ │ └── web3_rpc.rs ├── storage │ ├── Cargo.toml │ └── src │ │ ├── config.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── sql.rs │ │ ├── test_utils.rs │ │ ├── utils.rs │ │ └── versioned │ │ ├── ephemeral_v1 │ │ ├── config.rs │ │ ├── mod.rs │ │ ├── sql.rs │ │ └── store.rs │ │ ├── id_indexed_v1 │ │ ├── config.rs │ │ ├── mod.rs │ │ ├── pruning_strategy.rs │ │ ├── sql.rs │ │ └── store.rs │ │ ├── mod.rs │ │ ├── sql.rs │ │ ├── store.rs │ │ ├── usage_stats.rs │ │ └── utils.rs ├── subnetworks │ ├── beacon │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── assets │ │ │ └── trusted_block_root.txt │ │ │ ├── events.rs │ │ │ ├── jsonrpc.rs │ │ │ ├── lib.rs │ │ │ ├── network.rs │ │ │ ├── ping_extensions.rs │ │ │ ├── storage.rs │ │ │ ├── sync.rs │ │ │ ├── test_utils.rs │ │ │ └── validation.rs │ ├── history │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ ├── events.rs │ │ │ ├── jsonrpc.rs │ │ │ ├── lib.rs │ │ │ ├── network.rs │ │ │ ├── ping_extensions.rs │ │ │ ├── storage.rs │ │ │ └── validation.rs │ └── state │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ ├── events.rs │ │ ├── jsonrpc.rs │ │ ├── lib.rs │ │ ├── network.rs │ │ ├── ping_extensions.rs │ │ ├── storage.rs │ │ └── validation │ │ ├── error.rs │ │ ├── mod.rs │ │ ├── trie.rs │ │ └── validator.rs ├── utils │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── cli.rs │ │ ├── dir.rs │ │ ├── lib.rs │ │ ├── log.rs │ │ ├── submodules.rs │ │ └── testing.rs └── validation │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── accumulator.rs │ ├── assets │ ├── chain_head │ │ ├── README.md │ │ ├── pectra_historical_summaries.ssz │ │ └── pectra_light_client_header.yaml │ ├── epoch_accs │ │ ├── 0x5ec1ffb8c3b146f42606c74ced973dc16ec5a107c0345858c343fc94780b4218.bin │ │ ├── 0xcddbda3fd6f764602c06803ff083dbfc73f2bb396df17a31e5457329b9a0f38d.bin │ │ ├── 0xe6ebe562c89bc8ecb94dc9b2889a27a816ec05d3d6bd1625acad72227071e721.bin │ │ └── 0xf216a28afb2212269b634b9b44ff327a4a79f261640ff967f7e3283e3a184c70.bin │ ├── historical_roots.ssz │ └── merge_macc.bin │ ├── chain_head │ ├── mod.rs │ └── store.rs │ ├── constants.rs │ ├── header_validator.rs │ ├── historical_roots_acc.rs │ ├── historical_summaries_provider.rs │ ├── lib.rs │ ├── merkle │ ├── mod.rs │ ├── proof.rs │ └── safe_arith.rs │ ├── oracle.rs │ └── validator.rs ├── docker ├── Dockerfile.bridge ├── Dockerfile.e2hs-writer ├── Dockerfile.trin ├── Dockerfile.trin-execution └── docker-compose.yml ├── metrics ├── README.md ├── compose.yaml ├── grafana │ ├── dashboard.yaml │ ├── dashboards │ │ ├── trin-bench-trin-metrics-dashboard.json.template │ │ └── trin-metrics.json │ └── datasource.yaml └── prometheus │ └── prometheus.yml ├── rust-toolchain.toml ├── rustfmt.toml ├── test_assets ├── beacon │ ├── bellatrix │ │ └── ValidSignedBeaconBlock │ │ │ └── signed_beacon_block_15537397.ssz │ └── deneb │ │ ├── LightClientBootstrap │ │ └── ssz_random │ │ │ └── case_0 │ │ │ └── serialized.ssz_snappy │ │ ├── LightClientFinalityUpdate │ │ └── ssz_random │ │ │ └── case_0 │ │ │ └── serialized.ssz_snappy │ │ ├── LightClientOptimisticUpdate │ │ └── ssz_random │ │ │ └── case_0 │ │ │ └── serialized.ssz_snappy │ │ └── LightClientUpdate │ │ └── ssz_random │ │ ├── case_0 │ │ └── serialized.ssz_snappy │ │ └── case_1 │ │ └── serialized.ssz_snappy ├── e2hs │ └── mainnet-00000-a6860fef.e2hs ├── era1 │ ├── mainnet-00000-5ec1ffb8.era1 │ ├── mainnet-00001-a5364e9a.era1 │ ├── mainnet-00010-5f5d4516.era1 │ ├── test-mainnet-01600-xxxxxxxx.era1 │ └── test-mainnet-01896-xxxxxx.era1 ├── geth_batch │ └── headers.json ├── infura_batch │ ├── receipts-1114271.json │ ├── receipts-15573637.json │ ├── receipts-19433902.json │ └── receipts-19433903.json └── mainnet │ ├── block_14764013_value.json │ ├── block_17034871_value.json │ ├── block_17034873_value.json │ ├── block_19433902_value.json │ ├── block_19433903_value.json │ ├── block_body_14764013.bin │ ├── block_body_17139055.bin │ ├── large_content │ ├── 15040641 │ │ ├── body.bin │ │ ├── header.bin │ │ └── receipts.bin │ ├── 15040708 │ │ ├── body.bin │ │ ├── header.bin │ │ └── receipts.bin │ └── README.txt │ └── receipts_14764013.bin └── testing ├── ef-tests ├── Cargo.toml ├── README.md ├── src │ ├── lib.rs │ └── macros.rs └── tests │ ├── build_proofs.rs │ └── tests.rs ├── ethportal-peertest ├── Cargo.toml ├── README.md ├── src │ ├── lib.rs │ ├── scenarios │ │ ├── basic.rs │ │ ├── eth_rpc.rs │ │ ├── find.rs │ │ ├── mod.rs │ │ ├── offer_accept.rs │ │ ├── paginate.rs │ │ ├── ping.rs │ │ ├── put_content.rs │ │ ├── state.rs │ │ ├── utp.rs │ │ └── validation.rs │ └── utils.rs └── tests │ ├── rpc_server.rs │ ├── self_peertest.rs │ └── utils │ └── mod.rs └── utp ├── Cargo.toml ├── README.md ├── docker ├── Dockerfile ├── circleci │ └── Dockerfile ├── docker-compose.yml ├── run_endpoint.sh └── setup.sh └── src ├── bin ├── test_app.rs └── test_suite.rs ├── cli.rs ├── lib.rs └── rpc.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [net] 2 | git-fetch-with-cli = true 3 | -------------------------------------------------------------------------------- /.clippy.toml: -------------------------------------------------------------------------------- 1 | # This feature is in Rust nightly, as of v1.62. The line is added in anticipation. 2 | # Once the feature stabilizes, remove the #[allow(clippy::unwrap_used)] lines in front of every `mod test` 3 | allow-unwrap-in-tests = true 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # exclude everything 2 | * 3 | 4 | # include source files 5 | !Cargo.lock 6 | !Cargo.toml 7 | !/bin 8 | !/crates 9 | !/testing 10 | 11 | # include for vergen constants 12 | !/.git 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What was wrong? 2 | 3 | ### How was it fixed? 4 | 5 | ### To-Do 6 | 7 | [//]: # (Stay ahead of things, add list items here!) 8 | - [ ] Clean up commit history and use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). 9 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | jobs: 6 | close-issues: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - uses: actions/stale@v5 13 | with: 14 | close-issue-reason: "not_planned" 15 | days-before-issue-stale: 180 16 | days-before-issue-close: 14 17 | stale-issue-label: "stale" 18 | stale-issue-message: "This issue is stale because it has been open for 180 days with no activity." 19 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 20 | days-before-pr-stale: -1 21 | days-before-pr-close: -1 22 | exempt-issue-labels: "shelf-stable,good-first-issue,flamingo,good-community-issue" 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.swp 3 | .idea 4 | .python-version 5 | .DS_Store 6 | *rusty-tags.vi 7 | .vscode/ 8 | # Ignore downloaded consensus specs test data 9 | testing/ef-tests/mainnet* 10 | 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "portal-spec-tests"] 2 | path = portal-spec-tests 3 | url = https://github.com/ethereum/portal-spec-tests 4 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | pre-build = [ 3 | "apt-get update && apt-get install --assume-yes llvm-dev clang-6.0", 4 | ] 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2025 Trin Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trin 2 | 3 | Trin is a Rust implementation of a [Portal Network](https://github.com/ethereum/portal-network-specs) client. 4 | 5 | ## How to use Trin 6 | 7 | Check out the [Trin book](https://ethereum.github.io/trin) to quickly get up and running with Trin. 8 | 9 | > **NOTE**: This project uses [Git Submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). If you just cloned the project, be sure to run: `git submodule update --init`. See our dedicated [page](https://ethereum.github.io/trin/developers/contributing/git/submodules.html) for more info. 10 | 11 | ## Want to help? 12 | 13 | Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our 14 | guidelines for contributing in the [Trin book](https://ethereum.github.io/trin), 15 | then check out issues that are labeled 16 | [Good First Issue](https://github.com/ethereum/trin/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). 17 | 18 | Join our [Discord](https://discord.gg/JrwTY7FEf4) to participate in the conversation! 19 | 20 | -------------------------------------------------------------------------------- /bin/e2hs-writer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "e2hs-writer" 3 | authors.workspace = true 4 | categories.workspace = true 5 | edition.workspace = true 6 | keywords.workspace = true 7 | license.workspace = true 8 | readme = "README.md" 9 | repository.workspace = true 10 | rust-version.workspace = true 11 | version.workspace = true 12 | 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | alloy-hardforks.workspace = true 17 | anyhow.workspace = true 18 | async-stream = "0.3.6" 19 | aws-config = { version = "1.6", features = ["behavior-version-latest"] } 20 | aws-sdk-s3 = "1.83.0" 21 | clap.workspace = true 22 | e2store.workspace = true 23 | ethereum-rpc-client.workspace = true 24 | ethereum_ssz.workspace = true 25 | ethportal-api.workspace = true 26 | futures.workspace = true 27 | humanize-duration.workspace = true 28 | reqwest.workspace = true 29 | rstest.workspace = true 30 | revm-primitives.workspace = true 31 | serde_yaml.workspace = true 32 | ssz_types.workspace = true 33 | tempfile.workspace = true 34 | tokio.workspace = true 35 | tracing.workspace = true 36 | tree_hash.workspace = true 37 | trin-evm.workspace = true 38 | trin-execution.workspace = true 39 | trin-utils.workspace = true 40 | trin-validation.workspace = true 41 | url.workspace = true 42 | -------------------------------------------------------------------------------- /bin/e2hs-writer/README.md: -------------------------------------------------------------------------------- 1 | # E2HS Writer 2 | 3 | ## What? 4 | A WIP CLI program to generate [E2HS](https://github.com/eth-clients/e2store-format-specs/blob/main/formats/e2hs.md) files. 5 | 6 | 7 | ## Mode: `single-generator` 8 | Generates a single E2HS file for a given index. 9 | 10 | #### Data Sources 11 | - Pre-Merge: The program will use era1 files to source data. 12 | - Post-Merge: The program will use a combination of era files (headers / bodies) and a live provider (receipts) which can be configured via cli flags (defaults to pandaops). 13 | 14 | #### How to run Single Generator? 15 | 16 | ```sh 17 | $ cargo run -p e2hs-writer --release -- single-generator --target-dir --index --el-provider 18 | ``` 19 | 20 | ## Mode: `head-generator` 21 | Head Generator maintains a s3 bucket of E2HS files at the head of the chain, Head Generator will backfill the last 3 months of E2HS files if some files are missing. Head Generator will exit earlier if the backfill is longer then 3 months. It is recommended to use Single Generator if more then 3 months of E2HS files must be generated as the Single Generator is more efficient for generating individual files. 22 | 23 | #### Data Sources 24 | - Execution Layer JSON-RPC: used to get receipts. 25 | - Consensus Layer Beacon API: used to get Beacon Blocks and the Beacon State, which can be used to derive `HeaderWithProof` and bodies. 26 | 27 | 28 | #### How to run Head Generator? 29 | 30 | S3 Environment Variables Required to be set: 31 | - AWS_ACCESS_KEY_ID 32 | - AWS_SECRET_ACCESS_KEY 33 | - AWS_REGION=auto 34 | - AWS_ENDPOINT_URL 35 | 36 | ```sh 37 | $ cargo run -p e2hs-writer --release -- head-generator --el-provider --cl-provider --s3-bucket 38 | ``` 39 | -------------------------------------------------------------------------------- /bin/e2hs-writer/src/main.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | #![warn(clippy::uninlined_format_args)] 3 | 4 | pub mod cli; 5 | pub mod subcommands; 6 | 7 | use clap::Parser; 8 | use cli::E2HSWriterSubCommands; 9 | use subcommands::{head_generator::HeadGenerator, single_generator}; 10 | use tracing::info; 11 | use trin_utils::log::init_tracing_logger; 12 | 13 | use crate::single_generator::single_generator; 14 | 15 | #[tokio::main] 16 | async fn main() -> anyhow::Result<()> { 17 | init_tracing_logger(); 18 | info!("Running E2HS writer"); 19 | let config = cli::WriterConfig::parse(); 20 | info!("With configuration: {config:?}"); 21 | 22 | match config.command { 23 | E2HSWriterSubCommands::SingleGenerator(config) => { 24 | single_generator(config).await?; 25 | } 26 | E2HSWriterSubCommands::HeadGenerator(config) => { 27 | let mut head_generator = HeadGenerator::new(config).await?; 28 | head_generator.run().await?; 29 | } 30 | } 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /bin/e2hs-writer/src/subcommands/full_block.rs: -------------------------------------------------------------------------------- 1 | use anyhow::ensure; 2 | use e2store::{ 3 | e2hs::{BlockTuple, HeaderWithProofEntry}, 4 | era1::{BodyEntry, ReceiptsEntry}, 5 | }; 6 | use ethportal_api::{types::execution::header_with_proof::HeaderWithProof, BlockBody, Receipts}; 7 | use trin_validation::header_validator::HeaderValidator; 8 | 9 | pub struct FullBlock { 10 | pub header_with_proof: HeaderWithProof, 11 | pub body: BlockBody, 12 | pub receipts: Receipts, 13 | } 14 | 15 | impl FullBlock { 16 | pub async fn validate_block(&self, header_validator: &HeaderValidator) -> anyhow::Result<()> { 17 | let header_with_proof = &self.header_with_proof; 18 | header_validator 19 | .validate_header_with_proof(header_with_proof) 20 | .await?; 21 | self.body 22 | .validate_against_header(&header_with_proof.header)?; 23 | ensure!( 24 | self.receipts.root() == header_with_proof.header.receipts_root, 25 | "Receipts root mismatch" 26 | ); 27 | Ok(()) 28 | } 29 | } 30 | 31 | impl From for BlockTuple { 32 | fn from(value: FullBlock) -> Self { 33 | Self { 34 | header_with_proof: HeaderWithProofEntry { 35 | header_with_proof: value.header_with_proof, 36 | }, 37 | body: BodyEntry { body: value.body }, 38 | receipts: ReceiptsEntry { 39 | receipts: value.receipts, 40 | }, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /bin/e2hs-writer/src/subcommands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod full_block; 2 | pub mod head_generator; 3 | pub mod single_generator; 4 | -------------------------------------------------------------------------------- /bin/e2hs-writer/src/subcommands/single_generator/mod.rs: -------------------------------------------------------------------------------- 1 | mod e2hs_builder; 2 | mod era_binary_search; 3 | mod provider; 4 | 5 | use std::time::Instant; 6 | 7 | use e2hs_builder::E2HSBuilder; 8 | use humanize_duration::{prelude::DurationExt, Truncate}; 9 | use tracing::info; 10 | 11 | use crate::cli::SingleGeneratorConfig; 12 | 13 | pub async fn single_generator(config: SingleGeneratorConfig) -> anyhow::Result<()> { 14 | info!("Running E2HS single writer"); 15 | let start = Instant::now(); 16 | let e2hs_builder = E2HSBuilder::new( 17 | config.index, 18 | config.portal_accumulator_path, 19 | config.el_provider, 20 | ) 21 | .await?; 22 | info!( 23 | "Time taken to download Era/Era1 and Receipts {}", 24 | start.elapsed().human(Truncate::Second) 25 | ); 26 | 27 | let start = Instant::now(); 28 | e2hs_builder 29 | .build_e2hs_file(config.target_dir, config.index) 30 | .await?; 31 | info!( 32 | "Time taken to finished writing blocks {}", 33 | start.elapsed().human(Truncate::Second) 34 | ); 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /bin/portal-bridge/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "portal-bridge" 3 | description = "Bridge node for the Portal Network" 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme = "README.md" 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | anyhow.workspace = true 17 | bytes.workspace = true 18 | chrono.workspace = true 19 | clap.workspace = true 20 | delay_map.workspace = true 21 | discv5.workspace = true 22 | e2store.workspace = true 23 | eth_trie.workspace = true 24 | ethereum-rpc-client.workspace = true 25 | ethportal-api.workspace = true 26 | futures.workspace = true 27 | humanize-duration.workspace = true 28 | itertools.workspace = true 29 | lru.workspace = true 30 | portalnet.workspace = true 31 | prometheus_exporter.workspace = true 32 | rand.workspace = true 33 | reqwest.workspace = true 34 | revm.workspace = true 35 | revm-primitives.workspace = true 36 | ssz_types.workspace = true 37 | thiserror.workspace = true 38 | tokio.workspace = true 39 | tracing.workspace = true 40 | tree_hash.workspace = true 41 | trin.workspace = true 42 | trin-beacon.workspace = true 43 | trin-history.workspace = true 44 | trin-state.workspace = true 45 | trin-storage.workspace = true 46 | trin-evm.workspace = true 47 | trin-execution.workspace = true 48 | trin-metrics.workspace = true 49 | trin-utils.workspace = true 50 | trin-validation.workspace = true 51 | url.workspace = true 52 | 53 | [dev-dependencies] 54 | env_logger.workspace = true 55 | rstest.workspace = true 56 | test-log.workspace = true 57 | -------------------------------------------------------------------------------- /bin/portal-bridge/src/bridge/constants.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | pub const HEADER_SATURATION_DELAY: Duration = Duration::from_secs(2); 4 | pub const SERVE_BLOCK_TIMEOUT: Duration = Duration::from_secs(120); 5 | -------------------------------------------------------------------------------- /bin/portal-bridge/src/bridge/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod beacon; 2 | pub mod constants; 3 | pub mod e2hs; 4 | pub mod ephemeral_history; 5 | pub mod offer_report; 6 | pub mod state; 7 | -------------------------------------------------------------------------------- /bin/portal-bridge/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | #![warn(clippy::uninlined_format_args)] 3 | 4 | pub mod bridge; 5 | pub mod census; 6 | pub mod cli; 7 | pub mod constants; 8 | pub mod handle; 9 | pub mod types; 10 | pub mod utils; 11 | -------------------------------------------------------------------------------- /bin/portal-bridge/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mode; 2 | pub mod range; 3 | -------------------------------------------------------------------------------- /bin/trin-execution/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-execution" 3 | keywords = ["ethereum", "execution-layer"] 4 | description = "Trin's execution used for gossiping state and soon an execution layer client for Ethereum?" 5 | authors.workspace = true 6 | categories.workspace = true 7 | edition.workspace = true 8 | license.workspace = true 9 | readme = "README.md" 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy = { workspace = true, features = ["eips", "rpc-types-engine", "serde"] } 16 | alloy-rlp.workspace = true 17 | alloy-rpc-types-engine = { version = "1.0", default-features = false, features = ["serde"] } 18 | anyhow.workspace = true 19 | clap.workspace = true 20 | ethportal-api.workspace = true 21 | e2store.workspace = true 22 | eth_trie.workspace = true 23 | futures-util.workspace = true 24 | hashbrown = "0.15" 25 | humanize-duration.workspace = true 26 | jsonrpsee = { workspace = true, features = ["async-client", "client", "macros", "server"]} 27 | lazy_static.workspace = true 28 | parking_lot.workspace = true 29 | prometheus_exporter.workspace = true 30 | rand.workspace = true 31 | rayon.workspace = true 32 | reqwest = { workspace = true, features = ["stream"] } 33 | revm.workspace = true 34 | revm-inspectors = "0.23" 35 | revm-primitives.workspace = true 36 | rocksdb = "0.23" 37 | serde = { workspace = true, features = ["rc"] } 38 | serde_json.workspace = true 39 | thiserror.workspace = true 40 | tokio.workspace = true 41 | tracing.workspace = true 42 | trin-evm.workspace = true 43 | trin-utils.workspace = true 44 | 45 | [dev-dependencies] 46 | test-log.workspace = true 47 | -------------------------------------------------------------------------------- /bin/trin-execution/README.md: -------------------------------------------------------------------------------- 1 | # Trin Execution 2 | 3 | Trin execution has it's origins in trying to gossip all of Ethereum's Merkle Patrica archival state onto the Portal State Network. To do this we execute all of the blocks to the head of the chain. If we can execute to the head of the chain, as a byproduct we just built an Ethereum Execution Layer client. 4 | 5 | ## Priorities 6 | 7 | Currently the main priority is executing to the head of the chain so we can gossip all of Ethereum's state data. After we achieve this goal we can open Trin Execution as an Execution layer client. Trin Execution is the first execution layer client being built without relying on devp2p. 8 | 9 | 10 | ## How to run 11 | ```bash 12 | cargo run --release -p trin-execution 13 | ``` 14 | 15 | ### Want to get a trace of the EVM's execution? 16 | EVM traces are useful for debugging as they will give you the trace of every opcode executed during a transaction. Traces will be saved in Trin Execution's working directory, under the `evm_traces` folder 17 | 18 | The tracer we use is from [EIP-3155](https://eips.ethereum.org/EIPS/eip-3155) 19 | 20 | #### To trace all blocks 21 | ```bash 22 | cargo run -p trin-execution -- --block-to-trace=all 23 | ``` 24 | 25 | #### To trace a specific block by block number 26 | ```bash 27 | cargo run -p trin-execution -- --block-to-trace=block: 28 | ``` 29 | 30 | ### Want to export prometheus metrics 31 | ```base 32 | cargo run -p trin-execution -- --enable-metrics-with-url=127.0.0.1:9091 33 | ``` 34 | 35 | ### Run with an ephemeral database 36 | ```base 37 | cargo run -p trin-execution -- --ephemeral 38 | ``` 39 | 40 | ## Want to see metrics of how Trin Execution is performing 41 | Go to [metrics/README.md](metrics/README.md) to find out more 42 | -------------------------------------------------------------------------------- /bin/trin-execution/metrics/README.md: -------------------------------------------------------------------------------- 1 | # Trin Execution Metrics 2 | 3 | You can run this docker compose file it will run 4 | - prometheus 5 | - grafana 6 | - setup the metrics page 7 | 8 | Metrics are useful for telling how Trin Execution is performing and what is slow. 9 | 10 | The default username and password is `admin`. 11 | 12 | ### How to run 13 | ```sh 14 | docker compose up 15 | ``` 16 | 17 | ### View the dashboard at 18 | http://localhost:3000 19 | 20 | ***WARNING***: don't forget to run `Trin Execution` with metric exporting on! 21 | 22 | **Example** 23 | ```bash 24 | cargo run -p trin-execution -- --ephemeral --enable-metrics-with-url=127.0.0.1:9091 25 | ``` 26 | -------------------------------------------------------------------------------- /bin/trin-execution/metrics/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | prometheus: 3 | image: prom/prometheus 4 | container_name: prometheus 5 | command: 6 | - '--config.file=/etc/prometheus/prometheus.yml' 7 | network_mode: "host" 8 | restart: unless-stopped 9 | volumes: 10 | - ./prometheus:/etc/prometheus 11 | - prom_data:/prometheus 12 | grafana: 13 | image: grafana/grafana 14 | container_name: grafana 15 | network_mode: "host" 16 | restart: unless-stopped 17 | volumes: 18 | - ./grafana/datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml 19 | - ./grafana/dashboard.yaml:/etc/grafana/provisioning/dashboards/main.yaml 20 | - ./grafana/dashboards:/var/lib/grafana/dashboards 21 | volumes: 22 | prom_data: 23 | -------------------------------------------------------------------------------- /bin/trin-execution/metrics/grafana/dashboard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: "Dashboard provider" 5 | orgId: 1 6 | type: file 7 | disableDeletion: false 8 | updateIntervalSeconds: 10 9 | allowUiUpdates: false 10 | options: 11 | path: /var/lib/grafana/dashboards 12 | foldersFromFilesStructure: true 13 | -------------------------------------------------------------------------------- /bin/trin-execution/metrics/grafana/datasource.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: Prometheus 5 | type: prometheus 6 | url: http://localhost:9090 7 | isDefault: true 8 | access: proxy 9 | editable: true 10 | -------------------------------------------------------------------------------- /bin/trin-execution/metrics/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | scrape_timeout: 10s 4 | evaluation_interval: 15s 5 | 6 | # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. 7 | rule_files: 8 | - "rules.yml" 9 | 10 | # scrape configuration for trin hosts and the prometheus/grafana instance itself 11 | scrape_configs: 12 | # Node exporter metrics 13 | - job_name: "node" 14 | static_configs: 15 | - targets: 16 | - host.docker.internal:9091 17 | - localhost:9091 18 | labels: 19 | instance: local_node 20 | 21 | -------------------------------------------------------------------------------- /bin/trin-execution/metrics/prometheus/rules.yml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: "InstanceDown" 3 | rules: 4 | - alert: InstanceDown 5 | expr: up == 0 6 | for: 5m 7 | -------------------------------------------------------------------------------- /bin/trin-execution/src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::{cli::TrinExecutionConfig, types::block_to_trace::BlockToTrace}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct StateConfig { 5 | /// This flag when enabled will storage all the trie keys from block execution in a cache, this 6 | /// is needed for gossiping the storage trie's changes. It is also needed for gossiping newly 7 | /// created contracts. 8 | pub cache_contract_changes: bool, 9 | pub block_to_trace: BlockToTrace, 10 | } 11 | 12 | #[allow(clippy::derivable_impls)] 13 | impl Default for StateConfig { 14 | fn default() -> Self { 15 | Self { 16 | cache_contract_changes: false, 17 | block_to_trace: BlockToTrace::None, 18 | } 19 | } 20 | } 21 | 22 | impl From for StateConfig { 23 | fn from(cli_config: TrinExecutionConfig) -> Self { 24 | Self { 25 | cache_contract_changes: false, 26 | block_to_trace: cli_config.block_to_trace, 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bin/trin-execution/src/e2hs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod beacon; 2 | pub mod manager; 3 | pub mod types; 4 | pub mod utils; 5 | -------------------------------------------------------------------------------- /bin/trin-execution/src/engine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod rpc; 2 | -------------------------------------------------------------------------------- /bin/trin-execution/src/evm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block_executor; 2 | pub mod dao_fork; 3 | pub mod post_block_beneficiaries; 4 | pub mod pre_block_contracts; 5 | -------------------------------------------------------------------------------- /bin/trin-execution/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod cli; 2 | pub mod config; 3 | pub mod content; 4 | pub mod e2hs; 5 | pub mod engine; 6 | pub mod evm; 7 | pub mod execution; 8 | pub mod metrics; 9 | pub mod storage; 10 | pub mod subcommands; 11 | pub mod trie_walker; 12 | pub mod types; 13 | pub mod utils; 14 | -------------------------------------------------------------------------------- /bin/trin-execution/src/storage/error.rs: -------------------------------------------------------------------------------- 1 | use revm::context::DBErrorMarker; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Error)] 5 | pub enum EVMError { 6 | #[error("trie error {0}")] 7 | Trie(#[from] eth_trie::TrieError), 8 | 9 | #[error("rlp error {0}")] 10 | RLP(#[from] alloy::rlp::Error), 11 | 12 | #[error("rocksdb error {0}")] 13 | DB(#[from] rocksdb::Error), 14 | 15 | #[error("ethportal error {0}")] 16 | ANYHOW(#[from] anyhow::Error), 17 | 18 | #[error("not found database error {0}")] 19 | NotFound(String), 20 | 21 | #[error("balance error")] 22 | BalanceError, 23 | } 24 | 25 | impl DBErrorMarker for EVMError {} 26 | -------------------------------------------------------------------------------- /bin/trin-execution/src/storage/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod account_db; 2 | pub mod error; 3 | pub mod evm_db; 4 | pub mod execution_position; 5 | pub mod trie_db; 6 | pub mod utils; 7 | -------------------------------------------------------------------------------- /bin/trin-execution/src/storage/trie_db.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use eth_trie::DB; 4 | use rocksdb::DB as RocksDB; 5 | 6 | #[derive(Debug)] 7 | pub struct TrieRocksDB { 8 | // If "light" is true, the data is deleted from the database at the time of submission. 9 | light: bool, 10 | storage: Arc, 11 | } 12 | 13 | impl TrieRocksDB { 14 | pub fn new(light: bool, storage: Arc) -> Self { 15 | TrieRocksDB { light, storage } 16 | } 17 | } 18 | 19 | impl DB for TrieRocksDB { 20 | type Error = rocksdb::Error; 21 | 22 | fn get(&self, key: &[u8]) -> Result>, Self::Error> { 23 | self.storage.get(key) 24 | } 25 | 26 | fn insert(&self, key: &[u8], value: Vec) -> Result<(), Self::Error> { 27 | self.storage.put(key, value) 28 | } 29 | 30 | fn remove(&self, key: &[u8]) -> Result<(), Self::Error> { 31 | if self.light { 32 | self.storage.delete(key)?; 33 | } 34 | Ok(()) 35 | } 36 | 37 | fn flush(&self) -> Result<(), Self::Error> { 38 | Ok(()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /bin/trin-execution/src/storage/utils.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use rocksdb::{Options, DB as RocksDB}; 4 | use tracing::info; 5 | 6 | /// Helper function for opening a RocksDB connection for the radius-constrained db. 7 | pub fn setup_rocksdb(path: &Path) -> anyhow::Result { 8 | let rocksdb_path = path.join("rocksdb"); 9 | info!(path = %rocksdb_path.display(), "Setting up RocksDB"); 10 | 11 | let cache_size = 1024 * 1024 * 1024; // 1GB 12 | 13 | let mut db_opts = Options::default(); 14 | db_opts.create_if_missing(true); 15 | db_opts.set_write_buffer_size(cache_size / 4); 16 | let mut factory = rocksdb::BlockBasedOptions::default(); 17 | factory.set_block_cache(&rocksdb::Cache::new_lru_cache(cache_size / 2)); 18 | db_opts.set_block_based_table_factory(&factory); 19 | 20 | // Set the max number of open files to 150. MacOs has a default limit of 256 open files per 21 | // process. This limit prevents issues with the OS running out of file descriptors. 22 | db_opts.set_max_open_files(150); 23 | Ok(RocksDB::open(&db_opts, rocksdb_path)?) 24 | } 25 | -------------------------------------------------------------------------------- /bin/trin-execution/src/subcommands/e2ss/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod export; 2 | pub mod import; 3 | pub mod utils; 4 | -------------------------------------------------------------------------------- /bin/trin-execution/src/subcommands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod e2ss; 2 | -------------------------------------------------------------------------------- /bin/trin-execution/src/trie_walker/db.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Bytes, B256}; 2 | use anyhow::anyhow; 3 | use eth_trie::DB; 4 | use hashbrown::HashMap; 5 | 6 | use crate::storage::{account_db::AccountDB, trie_db::TrieRocksDB}; 7 | 8 | pub trait TrieWalkerDb { 9 | fn get(&self, key: &[u8]) -> anyhow::Result>; 10 | } 11 | 12 | impl TrieWalkerDb for HashMap> { 13 | fn get(&self, key: &[u8]) -> anyhow::Result> { 14 | Ok(self.get(key).map(|vec| Bytes::copy_from_slice(vec))) 15 | } 16 | } 17 | 18 | impl TrieWalkerDb for TrieRocksDB { 19 | fn get(&self, key: &[u8]) -> anyhow::Result> { 20 | DB::get(self, key) 21 | .map(|result| result.map(Bytes::from)) 22 | .map_err(|err| anyhow!("Failed to read key value from TrieRocksDB {err}")) 23 | } 24 | } 25 | 26 | impl TrieWalkerDb for AccountDB { 27 | fn get(&self, key: &[u8]) -> anyhow::Result> { 28 | DB::get(self, key) 29 | .map(|result| result.map(Bytes::from)) 30 | .map_err(|err| anyhow!("Failed to read key value from TrieRocksDB {err}")) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bin/trin-execution/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block_to_trace; 2 | pub mod trie_proof; 3 | -------------------------------------------------------------------------------- /bin/trin-execution/src/types/trie_proof.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::types::state_trie::{EncodedTrieNode, TrieProof as EncodedTrieProof}; 2 | use revm_primitives::Bytes; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] 6 | pub struct TrieProof { 7 | pub path: Vec, 8 | pub proof: Vec, 9 | } 10 | 11 | impl From<&TrieProof> for EncodedTrieProof { 12 | fn from(trie_proof: &TrieProof) -> Self { 13 | EncodedTrieProof::from( 14 | trie_proof 15 | .proof 16 | .iter() 17 | .map(|bytes| EncodedTrieNode::from(bytes.to_vec())) 18 | .collect::>(), 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /bin/trin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin" 3 | description = "A Rust implementation of the Ethereum Portal Network" 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy = { workspace = true, features = ["eips", "provider-ipc", "provider-ws", "pubsub", "reqwest", "rpc-types"] } 16 | anyhow.workspace = true 17 | clap.workspace = true 18 | discv5.workspace = true 19 | ethportal-api.workspace = true 20 | portalnet.workspace = true 21 | prometheus_exporter.workspace = true 22 | rpc.workspace = true 23 | tokio.workspace = true 24 | tracing.workspace = true 25 | tree_hash.workspace = true 26 | trin-beacon.workspace = true 27 | trin-history.workspace = true 28 | trin-state.workspace = true 29 | trin-storage.workspace = true 30 | trin-utils.workspace = true 31 | trin-validation.workspace = true 32 | utp-rs.workspace = true 33 | url.workspace = true 34 | 35 | [dev-dependencies] 36 | test-log.workspace = true 37 | rstest.workspace = true 38 | -------------------------------------------------------------------------------- /bin/trin/src/handle.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use anyhow::bail; 4 | use rpc::RpcServerHandle; 5 | use trin_beacon::network::BeaconNetwork; 6 | use trin_history::network::HistoryNetwork; 7 | use trin_state::network::StateNetwork; 8 | 9 | pub struct TrinHandle { 10 | pub rpc_server_handle: RpcServerHandle, 11 | pub subnetwork_overlays: SubnetworkOverlays, 12 | } 13 | 14 | #[derive(Default, Clone)] 15 | pub struct SubnetworkOverlays { 16 | pub history: Option>, 17 | pub state: Option>, 18 | pub beacon: Option>, 19 | } 20 | 21 | impl SubnetworkOverlays { 22 | pub fn history(&self) -> anyhow::Result> { 23 | match &self.history { 24 | Some(history) => Ok(history.clone()), 25 | None => bail!("History network is not available"), 26 | } 27 | } 28 | 29 | pub fn state(&self) -> anyhow::Result> { 30 | match &self.state { 31 | Some(state) => Ok(state.clone()), 32 | None => bail!("State network is not available"), 33 | } 34 | } 35 | 36 | pub fn beacon(&self) -> anyhow::Result> { 37 | match &self.beacon { 38 | Some(beacon) => Ok(beacon.clone()), 39 | None => bail!("Beacon network is not available"), 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /bin/trin/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | #![warn(clippy::uninlined_format_args)] 3 | 4 | mod cli; 5 | mod handle; 6 | mod run; 7 | 8 | pub use cli::TrinConfig; 9 | pub use handle::{SubnetworkOverlays, TrinHandle}; 10 | pub use run::{run_trin, run_trin_from_trin_config, run_trin_with_rpc, NodeRuntimeConfig}; 11 | -------------------------------------------------------------------------------- /bin/trin/src/main.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | 3 | use ethportal_api::types::network_spec::set_network_spec; 4 | use tracing::error; 5 | use trin::{run_trin_from_trin_config, TrinConfig}; 6 | use trin_utils::log::init_tracing_logger; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | init_tracing_logger(); 11 | let trin_config = TrinConfig::from_cli(); 12 | set_network_spec(trin_config.network.clone()); 13 | let trin_handle = run_trin_from_trin_config(trin_config).await?; 14 | 15 | tokio::signal::ctrl_c() 16 | .await 17 | .expect("failed to pause until ctrl-c"); 18 | 19 | if let Err(err) = trin_handle.rpc_server_handle.stop() { 20 | error!(err = %err, "Failed to close RPC server") 21 | } 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | mermaid.min.js 3 | mermaid-init.js 4 | 5 | -------------------------------------------------------------------------------- /book/README.md: -------------------------------------------------------------------------------- 1 | ## Using the book 2 | 3 | The book can be built and served locally. 4 | ```sh 5 | cargo install mdbook 6 | ``` 7 | Install support for `mermaid` diagrams: 8 | ```sh 9 | cd book 10 | cargo install mdbook-mermaid 11 | mdbook-mermaid install 12 | ``` 13 | This will create `mermaid.min.js` and `mermaid-init.js` files. 14 | 15 | Then run the book from the book crate: 16 | ```sh 17 | mdbook serve --open 18 | ``` 19 | Or the project root: 20 | ```sh 21 | mdbook serve --open ./book 22 | ``` 23 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | language = "en" 3 | multilingual = false 4 | src = "src" 5 | title = "Trin" 6 | 7 | [output.html] 8 | additional-js = ["mermaid.min.js", "mermaid-init.js"] 9 | 10 | [output.html.fold] 11 | enable = true 12 | 13 | [preprocessor] 14 | 15 | [preprocessor.mermaid] 16 | command = "mdbook-mermaid" 17 | 18 | -------------------------------------------------------------------------------- /book/mermaid-init.js: -------------------------------------------------------------------------------- 1 | mermaid.initialize({startOnLoad:true, theme: 'dark'}); 2 | -------------------------------------------------------------------------------- /book/src/concepts/README.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | 3 | ## Why does Portal Network exist? 4 | 5 | Portal is an important way to support the evolution of the core Ethereum protocol. 6 | 7 | To relieve pressure on Ethereum clients, the core protocol will allow full nodes to forget old data in a 8 | likely future upgrade. 9 | 10 | When that happens, the Portal Network can supply users with that purged data. 11 | 12 | ## How do Portal clients use less space? 13 | 14 | Each Portal Network client stores a user-configurable fraction of the data. The client retrieves any missing data from peers, on demand. Just like a full node, the client can cryptographically prove the data it serves. 15 | 16 | ```mermaid 17 | flowchart TB 18 | subgraph Full node data: on one computer 19 | full[Regular full node] 20 | end 21 | subgraph Full node data: spread amongst computers 22 | p1[Portal node] 23 | p2[Portal node] 24 | p3[Portal node] 25 | p1 <--> p2 26 | p2 <--> p3 27 | p1 <--> p3 28 | 29 | end 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /book/src/developers/README.md: -------------------------------------------------------------------------------- 1 | # Developers 2 | 3 | This part of the book is for understanding Trin, and processes around 4 | building Trin better. 5 | 6 | Where the Trin crates and the Portal Network specification are the source of 7 | truth, this section seeks to offer a quicker "key concepts" for getting started. 8 | 9 | It seeks to answer questions like: 10 | - What do I need to know about the Portal Network? 11 | - How do the different components of Trin work together? 12 | - What sort of data guarantees are made and how are they achieved? 13 | - What things should a new contributor be mindful of? -------------------------------------------------------------------------------- /book/src/developers/architecture/README.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | Trin can be understood from different perspectives. 4 | 5 | - How is code organised? 6 | - How does data flow through trin? 7 | - How is data stored? 8 | - How does testing work? 9 | -------------------------------------------------------------------------------- /book/src/developers/architecture/database.md: -------------------------------------------------------------------------------- 1 | # Database 2 | 3 | The database related code is located in `./portalnet/storage.rs`. 4 | 5 | There are three main database kinds: 6 | 7 | | DB Name |Kind|Location| Purpose |Keys| Values | 8 | |---------|-|-|----------------|-|------------------------------------------| 9 | | Main |SQLite|Disk| Data store |Content ID| Content key, content value, content size | 10 | | Memory |HashMap|Memory| Kademlia cache |Content key| Content data bytes | 11 | 12 | ## Main content database 13 | 14 | This is an SQLite database that stores content data. For a piece of content, this includes 15 | the content ID, content key, content value and the size of the content. It makes assessing the size of 16 | the database quicker by avoiding the need to repeatedly compute the size of each content. 17 | 18 | ## Memory content database 19 | 20 | This uses is an in-memory hashmap to keep content that may not be required for long term 21 | storage. An overlay service uses this database when receiving data from a peer as 22 | part of Kademlia-related actions. If required, data is later moved to disk in the 23 | main content database. 24 | -------------------------------------------------------------------------------- /book/src/developers/architecture/testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | Testing occurs at different levels of abstraction. 4 | 5 | ## Unit testing 6 | 7 | Unit tests are for checking individual data structures and methods. 8 | These tests are included within each workspace, at the bottom the file that contains the 9 | code being tested. Tests are run by CI tasks on pull requests to the Trin repository. 10 | 11 | ## Integration testing 12 | 13 | Tests that involve testing different parts of a crate at the same time are included in a `/tests` 14 | directory within the relevant module or crate. They are also run by CI tasks on pull 15 | requests to the Trin repository. 16 | 17 | ## Network simulation 18 | 19 | The `test-utp` crate is part of continuous integration (CI). This sets up 20 | client and server infrastructure on a single machine to test data streaming with 21 | simulated packet loss. 22 | 23 | ## Hive 24 | 25 | Hive testing runs Trin as a node and challenges it in a peer to peer environment. This 26 | involves creating a docker image with the Trin binary and passing it to Hive. 27 | 28 | Hive itself is a fork of Ethereum hive testing and exists as `portal-hive`, an 29 | external repository ([here](https://github.com/ogenev/portal-hive)). It can be started with docker images of other clients for cross-client testing. 30 | The nodes are started, fed a small amount of data and then challenged with RPC requests 31 | related to that data. 32 | 33 | Testing is automated, using docker configurations in the Trin repository to build test Trin 34 | and other clients at a regular cadence. Results of the latest test are displayed 35 | at [https://portal-hive.ethdevops.io/](https://portal-hive.ethdevops.io/). 36 | -------------------------------------------------------------------------------- /book/src/developers/contributing/README.md: -------------------------------------------------------------------------------- 1 | # Contributor guidelines 2 | 3 | These guidelines are heavily influenced by the [Snake-Charmer Tactical Manual](https://github.com/ethereum/snake-charmers-tactical-manual). While the manual is written with a focus on Python projects, there is tons of relevant information in there for how to effectively contribute to open-source projects, and it's recommended that you look through the manual before contributing. 4 | -------------------------------------------------------------------------------- /book/src/developers/contributing/build_instructions.md: -------------------------------------------------------------------------------- 1 | # Build Instructions 2 | 3 | The following are guides for building Trin on different platforms. 4 | Other methods may be used as appropriate. 5 | 6 | * [Mac OS](build_instructions/mac_os.md) 7 | * [Linux](build_instructions/linux.md) 8 | * [Raspberry Pi](build_instructions/raspberry_pi.md) 9 | * [Windows](build_instructions/windows.md) -------------------------------------------------------------------------------- /book/src/developers/contributing/build_instructions/mac_os.md: -------------------------------------------------------------------------------- 1 | # Mac Os 2 | 3 | Clone trin and run. 4 | ```sh 5 | $ cd ~ 6 | $ git clone https://github.com/ethereum/trin.git 7 | $ cd trin 8 | $ cargo run -p trin --release 9 | ``` 10 | -------------------------------------------------------------------------------- /book/src/developers/contributing/build_instructions/raspberry_pi.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi 2 | 3 | Not yet attempted, but experiments are encouraged. -------------------------------------------------------------------------------- /book/src/developers/contributing/build_instructions/windows.md: -------------------------------------------------------------------------------- 1 | # Windows 2 | 3 | These are instructions for Native and cross compiling Windows builds 4 | 5 | ## Native compilation of Windows 6 | 7 | If you don't already have Rust install it 8 | ```sh 9 | $ winget install Rustlang.Rustup 10 | ``` 11 | 12 | Install clang/llvm as it is required to compile c-kzg 13 | ```sh 14 | $ winget install LLVM.LLVM 15 | ``` 16 | 17 | Add Rust's msvc target 18 | ```sh 19 | $ rustup target add x86_64-pc-windows-msvc 20 | ``` 21 | 22 | Install target toolchain 23 | ```sh 24 | $ rustup toolchain install stable-x86_64-pc-windows-msvc 25 | ``` 26 | 27 | Build Trin 28 | 29 | ```sh 30 | $ cargo build -p trin 31 | ``` 32 | 33 | 34 | ## Cross-compilation for Ubuntu compiling to Windows 35 | 36 | This is assuming you already have rust installed on Linux 37 | 38 | Install required dependencies 39 | ```sh 40 | $ sudo apt update 41 | $ sudo apt upgrade 42 | $ sudo apt install git clang g++-mingw-w64-x86-64-posix 43 | ``` 44 | 45 | Clone trin and build. 46 | ```sh 47 | $ git clone https://github.com/ethereum/trin.git 48 | $ cd trin 49 | $ rustup target add x86_64-pc-windows-gnu 50 | $ rustup toolchain install stable-x86_64-pc-windows-gnu 51 | $ cargo build -p trin --target x86_64-pc-windows-gnu 52 | ``` -------------------------------------------------------------------------------- /book/src/developers/contributing/git/README.md: -------------------------------------------------------------------------------- 1 | # Git 2 | 3 | This section covers guidelines and common scenarios encountered with 4 | using git and github for Trin development. -------------------------------------------------------------------------------- /book/src/developers/contributing/git/commits.md: -------------------------------------------------------------------------------- 1 | # Commit messages 2 | 3 | ## Commit Hygiene 4 | 5 | We do not have any stringent requirements on how you commit your work, however 6 | you should work towards the following with your git habits. 7 | 8 | ## Logical Commits 9 | 10 | This means that each commit contains one logical change to the code. For example: 11 | 12 | - commit `A` introduces new API 13 | - commit `B` deprecates or removes the old API being replaced. 14 | - commit `C` modifies the configuration for CI. 15 | 16 | This approach is sometimes easier to do *after* all of the code has been 17 | written. Once things are complete, you can `git reset master` to unstage all 18 | of the changes you've made, and then re-commit them in small chunks using `git 19 | add -p`. 20 | 21 | ### Commit Messages 22 | 23 | We use conventional commits for our commit messages. This means that your 24 | commit messages should be of the form: 25 | 26 | ```text 27 | [optional scope]: 28 | ``` 29 | 30 | To learn more about conventional commits please check out the [conventional commits website](https://www.conventionalcommits.org/en/v1.0.0/). 31 | 32 | Examples: 33 | 34 | - `fix: Update metrics strategy to support multiple subnetworks` 35 | - `refactor(light-client): Refactor light-client crate to use `ethportal-api` consensus types` 36 | - `feat(rpc): Return header to eth_getBlockByHash` 37 | 38 | One way to test whether you have it right is to complete the following sentence. 39 | 40 | > If you apply this commit it will ________________. 41 | -------------------------------------------------------------------------------- /book/src/developers/contributing/git/fetching_pull_requests.md: -------------------------------------------------------------------------------- 1 | # Fetching a pull request 2 | 3 | We often want or need to run code that someone proposes in a PR. Typically this involves adding the remote of the PR author locally and then fetching their branches. 4 | 5 | Example: 6 | 7 | ```sh 8 | git remote add someone https://github.com/someone/reponame.git 9 | git fetch someone 10 | git checkout someone/branch-name 11 | ``` 12 | 13 | With an increasing number of different contributors this workflow becomes tedious. 14 | Luckily, there's a little trick that greatly improves the workflow as it lets us 15 | pull down any PR without adding another remote. 16 | 17 | To do this, we just have to add the following line in the `[remote "origin"]` 18 | section of the `.git/config` file in our local repository. 19 | 20 | ```sh 21 | fetch = +refs/pull/*/head:refs/remotes/origin/pr/* 22 | ``` 23 | 24 | Then, checking out a PR locally becomes as easy as: 25 | 26 | ```sh 27 | git fetch origin 28 | git checkout origin/pr/ 29 | ``` 30 | 31 | >Replace `origin` ☝ with the actual name (e.g. `upstream`) that we use for the 32 | remote that we want to fetch PRs from. 33 | 34 | Notice that fetching PRs this way is *read-only* which means that in case we do 35 | want to contribute back to the PR (and the author has this enabled), we would 36 | still need to add their remote explicitly. 37 | -------------------------------------------------------------------------------- /book/src/developers/contributing/git/merging.md: -------------------------------------------------------------------------------- 1 | # Merging 2 | 3 | Once your pull request has been *Approved* it may be merged at your discretion. In most cases responsibility for merging is left to the person who opened the pull request, however for simple pull requests it is fine for anyone to merge. 4 | 5 | If substantive changes are made **after** the pull request has been marked *Approved* you should ask for an additional round of review. 6 | -------------------------------------------------------------------------------- /book/src/developers/contributing/git/pull_requests.md: -------------------------------------------------------------------------------- 1 | # Pull requests 2 | 3 | 4 | We are a distributed team. The primary way we communicate about our code is 5 | through github via pull requests. 6 | 7 | * When you start work on something you should have a pull request opened that 8 | same day. 9 | * Mark unfinished pull requests with the "Work in Progress" label. 10 | * Before submitting a pr for review, you should run the following commands 11 | locally and make sure they are passing, otherwise CI will raise an error. 12 | * `cargo +nightly fmt --all -- --check` and `cargo clippy --all --all-targets --all-features -- --deny warnings` for linting checks 13 | * `RUSTFLAGS='-D warnings' cargo test --workspace` to run all tests 14 | * Pull requests **should** always be reviewed by another member of the team 15 | prior to being merged. 16 | * Obvious exceptions include very small pull requests. 17 | * Less obvious examples include things like time-sensitive fixes. 18 | * You should not expect feedback on a pull request that is not passing CI. 19 | * Obvious exceptions include soliciting high-level feedback on your approach. 20 | 21 | 22 | Large pull requests (above 200-400 lines of code changed) cannot be effectively 23 | reviewed. If your pull request exceeds this threshold you **should** make 24 | every effort to divide it into smaller pieces. 25 | 26 | You as the person opening the pull request should assign a reviewer. 27 | 28 | -------------------------------------------------------------------------------- /book/src/developers/contributing/git/rebasing.md: -------------------------------------------------------------------------------- 1 | # Rebasing 2 | 3 | You should be using `git rebase` when there are *upstream* changes that you 4 | need in your branch. You **should not** use `git merge` to pull in these 5 | changes. 6 | -------------------------------------------------------------------------------- /book/src/developers/contributing/git/release_notes.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | 3 | We use conventional commits to generate release notes. 4 | 5 | Check the [commits guide](commits.md) for more information on how to write commit messages. 6 | -------------------------------------------------------------------------------- /book/src/developers/contributing/git/submodules.md: -------------------------------------------------------------------------------- 1 | # Submodules 2 | 3 | This project uses [Git Submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). If you 4 | just cloned the project, be sure to run: 5 | 6 | ```console 7 | $ git submodule update --init 8 | ``` 9 | 10 | This page provides a short overview of the most common use cases. 11 | 12 | ## Pulling in Upstream Changes from the Submodule Remote 13 | 14 | You want to do this when the remote version of submodule is updated. The simplest way to resolve 15 | this is to run: 16 | 17 | ```console 18 | $ git submodule update --remote --rebase 19 | ``` 20 | 21 | > If you modified your local submodule, you might want to use different flags. 22 | 23 | If you run `git status`, you should see that submodule is updated. Commit and push the changes so 24 | others can use the same version. 25 | 26 | ## Pulling Upstream Changes from the Project Remote 27 | 28 | If somebody else updated the submodule and you pulled the changes, you have to update your local 29 | clone as well. The message `"Submodules changed but not updated"` will show when running 30 | `git status`. To update local submodule, run: 31 | 32 | ```console 33 | $ git submodule update --init 34 | ``` 35 | -------------------------------------------------------------------------------- /book/src/developers/contributing/releases/README.md: -------------------------------------------------------------------------------- 1 | # Releases 2 | 3 | This section covers the process of making & deploying a Trin release. 4 | -------------------------------------------------------------------------------- /book/src/developers/contributing/rust/README.md: -------------------------------------------------------------------------------- 1 | # Rust 2 | 3 | Trin is written in Rust. This section includes guidelines for Rust-specific 4 | patterns and principles. -------------------------------------------------------------------------------- /book/src/developers/contributing/rust/comments.md: -------------------------------------------------------------------------------- 1 | # Comments 2 | 3 | Any datatype of significance **should** have an accompanying comment briefly describing its role and responsibilities. Comments are an extremely valuable tool in open-source projects with many different contributors, and can greatly improve development speed. Explain your assumptions clearly so others don't need to dig through the code. 4 | - Rust [doc comments](https://doc.rust-lang.org/rust-by-example/meta/doc.html) are the most best way to comment your code. 5 | -------------------------------------------------------------------------------- /book/src/developers/contributing/rust/error_handling.md: -------------------------------------------------------------------------------- 1 | # Error handling 2 | 3 | - Handle errors. Naked `.unwrap()`s aren't allowed, except for in unit tests. 4 | Exceptions must be accompanied by a note justifying usage. 5 | - In most cases where an exception can be made (E.g., parsing a static value) `.expect()` with a relevant message should be used over a naked unwrap. 6 | - Write descriptive error messages that give context of the problem that occurred. Error messages should be unique, to aid with debugging. 7 | - Meaningful error types should be used in place of `Result< _, String>`. 8 | - General errors should use the [anyhow](https://docs.rs/anyhow/latest/anyhow/) crate. 9 | - Custom / typed errors should derive from the `std::error::Error` trait. The [`thiserror`](https://docs.rs/thiserror/1.0.30/thiserror/) crate provides a useful macro to simplify creating custom error types. 10 | -------------------------------------------------------------------------------- /book/src/developers/contributing/rust/imports.md: -------------------------------------------------------------------------------- 1 | # Imports 2 | 3 | - In `*.rs` files, imports should be split into 3 groups [src](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#StdExternalCrate) and separated by a single line. Within a single group, imported items should be sorted alphabetically. 4 | 1. std, core and alloc, 5 | 2. external crates, 6 | 3. self, super and crate imports. 7 | - Alphabetize imports in `Cargo.toml` 8 | 9 | ```rust,ignore 10 | use alloc::alloc::Layout; 11 | use core::f32; 12 | use std::sync::Arc; 13 | 14 | use broker::database::PooledConnection; 15 | use chrono::Utc; 16 | use juniper::{FieldError, FieldResult}; 17 | use uuid::Uuid; 18 | 19 | use super::schema::{Context, Payload}; 20 | use super::update::convert_publish_payload; 21 | use crate::models::Event; 22 | ``` -------------------------------------------------------------------------------- /book/src/developers/contributing/rust/logging.md: -------------------------------------------------------------------------------- 1 | # Logging 2 | 3 | - All logging should be done with the `log` library and not `println!()` statements. 4 | - Appropriate log levels (`debug`, `warn`, `info`, etc.) should be used with respect to their content. 5 | - Log statements should be declarative, useful, succinct and formatted for readability. 6 | 7 | Bad: 8 | ```sh 9 | Oct 25 23:42:11.079 DEBUG trin_core::portalnet::events: Got discv5 event TalkRequest(TalkRequest { id: RequestId([226, 151, 109, 239, 115, 223, 116, 109]), node_address: NodeAddress { socket_addr: 127.0.0.1:4568, node_id: NodeId { raw: [5, 208, 240, 167, 153, 116, 216, 224, 160, 101, 80, 229, 154, 206, 113, 239, 182, 109, 181, 137, 16, 96, 251, 63, 85, 223, 235, 208, 3, 242, 175, 11] } }, protocol: [115, 116, 97, 116, 101], body: [1, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0], sender: Some(UnboundedSender { chan: Tx { inner: Chan { tx: Tx { block_tail: 0x55c4fe611290, tail_position: 1 }, semaphore: 0, rx_waker: AtomicWaker, tx_count: 2, rx_fields: "..." } } }) }) 10 | ``` 11 | 12 | Good: 13 | ```sh 14 | Oct 25 23:43:02.373 DEBUG trin_core::portalnet::overlay: Received Ping(enr_seq=1, radius=18446744073709551615) 15 | ``` 16 | -------------------------------------------------------------------------------- /book/src/developers/contributing/rust/style.md: -------------------------------------------------------------------------------- 1 | # Style 2 | 3 | ## Clone 4 | 5 | Minimize the amount of `.clone()`s used. Cloning can be a useful mechanism, but should be used with discretion. When leaned upon excessively to [satisfy the borrow checker](https://rust-unofficial.github.io/patterns/anti_patterns/borrow_clone.html) it can lead to unintended consequences. 6 | 7 | ## String interpolation 8 | 9 | Use interpolated string formatting when possible. 10 | - Do `format!("words: {var:?}")` not `format!("words: {:?}", var)` -------------------------------------------------------------------------------- /book/src/developers/contributing/tests.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | Testing is essential to the production of software with minimal flaws. The default should always be writing tests for the code you produce. 4 | 5 | Testing also introduces overhead into our workflow. If a test suite takes a long time to run, it slows down our iteration cycle. This means finding a pragmatic balance between thorough testing, and the speed of our test suite, as well as always iterating on our testing infrastructure. 6 | 7 | Unit test names should unambiguously identify the functionality being tested. Omit any "test" prefix from the name to avoid redundancy. 8 | -------------------------------------------------------------------------------- /book/src/developers/core_concepts/README.md: -------------------------------------------------------------------------------- 1 | # Core concepts 2 | 3 | This section contains specific concepts that are common, important or 4 | that have an interesting facet to understand. -------------------------------------------------------------------------------- /book/src/developers/core_concepts/bridge.md: -------------------------------------------------------------------------------- 1 | # Bridge 2 | 3 | Blocks are produced by Ethereum Execution clients which use a different 4 | network to Portal Network nodes. A Bridge node is responsible for taking data 5 | from the external network and passing it to the Portal Network. 6 | 7 | ```mermaid 8 | flowchart LR 9 | eth[Ethereum Execution node]-->bridge[Portal Network Bridge node] 10 | bridge-->portal[Portal network node] 11 | ``` 12 | This operates as follows: 13 | ```mermaid 14 | sequenceDiagram 15 | Bridge-->>Execution: eth_getBlock 16 | Execution-->>Bridge: block 17 | Bridge-->>Portal: block 18 | ``` 19 | Currently the bridge functionality exists as a separate executable under `portal-bridge`. 20 | -------------------------------------------------------------------------------- /book/src/developers/core_concepts/chain_tip.md: -------------------------------------------------------------------------------- 1 | # Chain tip 2 | 3 | A Trin node can serve information about the chain tip, such as the latest 4 | block number. A Trin node knows about the beacon chain protocol that is 5 | creating the chain tip. 6 | 7 | By listening to activity on the beacon chain 8 | network, it can follow the activities of members of the sync committee. If a certain fraction 9 | of the sync committee have signed off on a certain beacon block, the Trin node can 10 | be confident that this is likely to be the chain tip. 11 | 12 | Beacon blocks contain references to Ethereum blocks, and so the node can see the tip of the 13 | Execution chain. -------------------------------------------------------------------------------- /book/src/developers/developer_stories.md: -------------------------------------------------------------------------------- 1 | # Developer stories 2 | 3 | Trin is under active development. Perhaps you would like to get involved? 4 | 5 | The following are some situations that might resonate. 6 | 7 | ## Issue resolver 8 | 9 | Someone who tried out Trin and found an issue, then worked out 10 | where it was coming from. 11 | 12 | Consider making a pull request to fix the issue. 13 | 14 | ## Ecosystem contributor 15 | 16 | Trin, and the Portal Network more broadly are perhaps more 17 | quiet than other areas of Ethereum development. Maybe you can see 18 | yourself helping out somewhere where you can have a meaningful impact. 19 | 20 | ## Researcher 21 | 22 | Someone looking into the Ethereum protocol upgrade path, and thinking through 23 | the impact of potential upcoming changes. 24 | 25 | There are interesting facets to the Portal network still to be determined. 26 | 27 | Perhaps you can be tempted by: 28 | - Double batched merkle log accumulators 29 | - Topology of content in distributed hash tables 30 | - Adversarial scenario planning and mitigation 31 | 32 | ## Hobbyist 33 | 34 | Someone looking to poke around and run a node on a single board computer or 35 | a mobile device. How small is too small? 36 | 37 | ## Rust developer 38 | 39 | Someone looking to build something meaningful in Rust, with interesting 40 | architecture and crates: 41 | - Cryptography 42 | - Peer to peer networking 43 | - Async runtimes 44 | -------------------------------------------------------------------------------- /book/src/developers/goals.md: -------------------------------------------------------------------------------- 1 | # Goals 2 | 3 | ## Demonstrate feasibility 4 | 5 | Implement the Portal Network and demonstrate its use. Starting 6 | with subset of the whole and then expanding from there. 7 | 8 | ## Prioritise Sub-protocols 9 | 10 | ### Primary 11 | Get the History sub-protocol working. 12 | - Iron out bugs 13 | - Interop with other clients 14 | - Monitor network to see if it retains data 15 | 16 | ### Secondary 17 | 18 | Start work on the State sub-protocol. 19 | - Implementation from the Portal Network specification. 20 | 21 | ### Tertiary 22 | Start work on remaining sub-protocols 23 | - Canonical indices 24 | - Transaction gossip 25 | -------------------------------------------------------------------------------- /book/src/developers/progress_status.md: -------------------------------------------------------------------------------- 1 | # Progress status 2 | 3 | The Portal Network and Trin are under active development so different components 4 | may be in varying stages of progress. 5 | 6 | - Completed, looking for bugs 7 | - Mid-construction 8 | - Future, planned features 9 | 10 | Some methods to get a sense of the state of development are: 11 | - Run `trin -- --help` and see what flags are available. 12 | - Look at recent closed PRs to see what has just been merged. 13 | - Look at recent issues that have been opened to see what active foci are. 14 | - Look at what examples are shown in the setup and usage guides. 15 | - Run trin in the way you think it might work and see what happens. 16 | -------------------------------------------------------------------------------- /book/src/developers/protocols/README.md: -------------------------------------------------------------------------------- 1 | # Protocols 2 | 3 | This section contains summaries of important protocols for the Portal 4 | Network. The purpose is to distill important concepts to quickly 5 | see how Trin works. 6 | 7 | See the relevant specifications for deeper explanations. 8 | -------------------------------------------------------------------------------- /book/src/introduction/README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Trin 2 | 3 | > trin: a fast Rust client for Ethereum access via the Portal Network. 4 | 5 | Trin acts as a json-rpc server, like an Ethereum client. 6 | 7 | Unlike a typical Ethereum client, trin: 8 | - is usable within minutes 9 | - limits storage & CPU usage 10 | 11 | Continue reading to see how to use trin. 12 | 13 | 🏗 This is a living document, subject to substantial change without warning. 14 | -------------------------------------------------------------------------------- /book/src/introduction/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | Trin runs on Linux, MacOS, and Windows. There are two ways to run it: download a binary executable, or install it from source. 4 | 5 | ## Download an executable 6 | 7 | The github repository hosts the binaries. Download the latest release for your platform from the [releases page](https://github.com/ethereum/trin/releases). 8 | 9 | Extract the compressed file to get the `trin` executable. 10 | 11 | ### Extraction on Linux / MacOS 12 | 13 | The binary is compressed in a tarball, so first we need to extract it. 14 | 15 | For example, to extract version 0.1.0: 16 | 17 | ```sh 18 | tar -xzvf trin-v0.1.0-x86_64-unknown-linux-gnu.tar.gz 19 | ``` 20 | 21 | You now have a `trin` executable in the current directory. 22 | 23 | ## Run trin 24 | 25 | Launch the executable with 2GB local disk space: 26 | ```sh 27 | trin --mb 2000 28 | ``` 29 | 30 | ## Load a block from Ethereum history 31 | 32 | Print the block data at height 20,987,654: 33 | 34 | ```sh 35 | BLOCK_NUM=20987654; echo '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x'$(printf "%x" $BLOCK_NUM)'", false],"id":1}' | nc -U /tmp/trin-jsonrpc.ipc | jq 36 | ``` 37 | 38 | For a deeper understanding of how to interact with Ethereum, like invoking a contract function, see the [Ethereum data](../users/use/ethereum_data.md) section. 39 | 40 | ## Alternatively, install from source 41 | 42 | To get the very latest updates, install from source. This path is intended for power users and developers, who want access to the very latest code. 43 | 44 | There are platform-specific [build instructions](../developers/contributing/build_instructions.md). 45 | -------------------------------------------------------------------------------- /book/src/users/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | The following are frequently asked questions or topics that may be of interest 4 | to users. 5 | 6 | New submissions are welcome, if you had a question and found the answer elsewhere, 7 | submit a pull request or an issue describing the question and the answer. 8 | These questions will appear in searches in the book and in the trin repository. 9 | 10 | ## Can I rely on Trin to interact with Ethereum? 11 | 12 | Not at present. Trin and the Portal Network more broadly are under active 13 | development. 14 | 15 | ## Can Trin be used with a VPN? 16 | 17 | Trin should be compatible with VPN use, but if you experience difficulty connecting to the 18 | network we recommend disabling your VPN. 19 | 20 | ## Can Trin be used over TOR? 21 | 22 | No, the Trin uses uTP, which is not supported in the TOR protocol. 23 | 24 | -------------------------------------------------------------------------------- /book/src/users/problems.md: -------------------------------------------------------------------------------- 1 | # Problems 2 | 3 | If you encounter a problem, keep in mind that Trin is under active development. 4 | Some issues may be lower on the priority list. 5 | 6 | ## Search for more information 7 | 8 | Try searching: 9 | - This book 10 | - The Trin repository issues 11 | 12 | ## Document the problem 13 | 14 | If the problem seems new, [raise an issue](https://github.com/ethereum/trin/issues) 15 | in the Trin repository. 16 | Try to record the problem details and include those in the issue. 17 | Include details for how someone else might reproduce the problem you have. 18 | -------------------------------------------------------------------------------- /book/src/users/requirements.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | ## Hardware 4 | 5 | Suitable: 6 | - Processor: x86 or Arm based. Minimum spec TBD. 7 | - RAM: Minimum TBD. 8 | - Disk: 50 MB 9 | 10 | We are eager to hear about a device that is too slow or small to run Trin. The minimum permitted setting for storage usage is technically 1MB. Though the trin binary itself is 41MB at the moment. Tell us if that isn't working for you! 11 | 12 | Testing and reports of performance on the following are welcome: 13 | - RISC-V based processor. 14 | - Resource constrained (CPU/RAM) 15 | 16 | ## Software 17 | 18 | - Linux, MacOS, or Windows 19 | 20 | ## Network 21 | 22 | Testing/reports of low-bandwidth network are welcome. 23 | 24 | Trin should be compatible with VPN use, but if you experience difficulty 25 | connecting to the network we recommend disabling your VPN. 26 | -------------------------------------------------------------------------------- /book/src/users/use/README.md: -------------------------------------------------------------------------------- 1 | # Querying Data 2 | 3 | Once Trin is running, you can access Ethereum data by making requests to its endpoint. 4 | 5 | The interface for these requests is JSON-RPC, which is a standard way to communicate with Ethereum nodes. 6 | 7 | In the following sections, we make queries with: 8 | - hand-coded JSON-RPC 9 | - using a Web3 library 10 | 11 | Serving data for wallets is not covered here. We hope to get there eventually, but the network is not ready quite yet. 12 | -------------------------------------------------------------------------------- /book/src/users/use/making_queries.md: -------------------------------------------------------------------------------- 1 | # Building Queries 2 | 3 | If you want to manually query trin, the following patterns can be used, depending on whether 4 | Trin was started with `--web3-transport` as `http` or `ipc`. It defaults to `ipc`. 5 | 6 | Whatever the transport, the JSON-RPC format is the same. 7 | 8 | ## Query form 9 | A query for JSON-RPC has the following form for a call to `methodname` that accepts two 10 | parameters: `parameter_one` and `parameter_two`. 11 | 12 | Query: 13 | ```json 14 | { 15 | "jsonrpc": "2.0", 16 | "method": "", 17 | "params": ["", ""], 18 | "id":1 19 | } 20 | ``` 21 | Succinctly written: 22 | ```json 23 | {"jsonrpc":"2.0","method":"","params":["", ""],"id":1} 24 | ``` 25 | 26 | In following pages, we'll cover a couple specific examples of queries. 27 | 28 | ## IPC transport 29 | 30 | By default, Trin listens on a Unix domain socket file at `/tmp/trin-jsonrpc.ipc`. This means that you can only access the data from the local machine. 31 | 32 | Example command for `query` (above) to IPC server with socket file located at `/tmp/trin-jsonrpc.ipc`: 33 | ```sh 34 | echo '' | nc -U /tmp/trin-jsonrpc.ipc | jq 35 | ``` 36 | 37 | ## HTTP transport 38 | 39 | If you started Trin with `--web3-transport http`, you can query it over HTTP. 40 | 41 | Command for `query` (above) to HTTP server on it's default port (8545): 42 | ```sh 43 | curl -X POST -H "Content-Type: application/json" -d '' localhost:8545 | jq 44 | ``` 45 | 46 | ## Response 47 | 48 | The "id" will match the request. The result is the data you requested. Alternatively, it may return an error message. 49 | 50 | An example successful response: 51 | ```json 52 | { 53 | "jsonrpc": "2.0", 54 | "id": 1, 55 | "result": "0x1234" 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /book/src/users/use/portal_network_data.md: -------------------------------------------------------------------------------- 1 | # Portal network data 2 | 3 | There are methods for requesting data that are specific to: 4 | - Each sub-protocol (history, state, etc.) 5 | - `portal_history*` 6 | - `portal_state*` 7 | - Discovery protocol 8 | - `discv5_*` 9 | 10 | See the Portal Network JSON-RPC specification 11 | [here](https://github.com/ethereum/portal-network-specs/tree/master/jsonrpc) 12 | for a comprehensive and interactive view of specific methods available. 13 | 14 | ## Designing a Query 15 | One can identify data by its "content key". The following queries ask Trin to speak with 16 | peers, looking for a particular piece of data. 17 | 18 | Let us request the block body for block 21,000,000. 19 | - Block hash: `0xf5e1d15a3e380006bd271e73c8eeed75fafc3ae6942b16f63c21361079bba709` 20 | - Selector for a block body: `0x01` (defined in Portal Network spec under the History sub-protocol). 21 | - Content key: `0x01f5e1d15a3e380006bd271e73c8eeed75fafc3ae6942b16f63c21361079bba709` 22 | - Request: `portal_historyGetContent`, which accepts a content key as a parameter 23 | 24 | ```json 25 | {"jsonrpc":"2.0","method":"portal_historyGetContent","params":["0x01f5e1d15a3e380006bd271e73c8eeed75fafc3ae6942b16f63c21361079bba709"],"id":1} 26 | ``` 27 | 28 | ## IPC 29 | 30 | ```sh 31 | echo '{"jsonrpc":"2.0","method":"portal_historyGetContent","params":["0x01f5e1d15a3e380006bd271e73c8eeed75fafc3ae6942b16f63c21361079bba709"],"id":1}' | nc -U /tmp/trin-jsonrpc.ipc | jq 32 | ``` 33 | 34 | ## HTTP 35 | 36 | If you have started Trin with `--web3-transport http`, you can query it over HTTP from any computer that can reach that port. 37 | 38 | ```sh 39 | curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"portal_historyGetContent","params":["0x01f5e1d15a3e380006bd271e73c8eeed75fafc3ae6942b16f63c21361079bba709"],"id":1}' http://localhost:8545 | jq 40 | ``` 41 | -------------------------------------------------------------------------------- /book/src/users/use/remote_access.md: -------------------------------------------------------------------------------- 1 | # Access trin from different computer 2 | 3 | If you want to run Trin on one computer and access it from another, launch the trin node with an HTTP transport instead of a default IPC tranport: 4 | 5 | ```sh 6 | trin --web3-transport http 7 | ``` 8 | 9 | This endpoint is unprotected. Anyone can make requests to your trin node. (This is why the default is IPC) 10 | 11 | You probably want to restrict access to your trin node. One way to do that is to firewall off the trin port, and use SSH port forwarding. 12 | 13 | ## SSH port forwarding 14 | 15 | Assuming you can SSH into the computer running Trin, you can forward the HTTP port to your local machine, with: 16 | 17 | ```sh 18 | ssh -N -L 8545:127.0.0.1:8545 username@trin-host-computer 19 | ``` 20 | 21 | Now you can query the trin node from your local machine at `http://localhost:8545`, as described in [Building Queries](../use/making_queries.md). 22 | -------------------------------------------------------------------------------- /crates/e2store/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "e2store" 3 | keywords = ["ethereum", "portal-network", "e2store", "era", "era1"] 4 | description = "E2store, era, and era1 implementations for Ethereum" 5 | authors.workspace = true 6 | categories.workspace = true 7 | edition.workspace = true 8 | license.workspace = true 9 | readme = "README.md" 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version = "0.4.1" 13 | 14 | [dependencies] 15 | alloy = { workspace = true, features = ["rlp", "consensus"] } 16 | alloy-rlp.workspace = true 17 | anyhow.workspace = true 18 | clap = { workspace = true, optional = true } 19 | ethereum_ssz.workspace = true 20 | ethereum_ssz_derive.workspace = true 21 | ethportal-api.workspace = true 22 | rand.workspace = true 23 | reqwest.workspace = true 24 | tracing = { workspace = true, optional = true } 25 | tracing-subscriber = { workspace = true, optional = true } 26 | scraper.workspace = true 27 | snap.workspace = true 28 | url.workspace = true 29 | 30 | [dev-dependencies] 31 | rstest.workspace = true 32 | tokio.workspace = true 33 | trin-utils.workspace = true 34 | 35 | [features] 36 | e2ss-stats-binary = ["clap", "tracing", "tracing-subscriber"] 37 | 38 | [[bin]] 39 | name = "e2ss-stats" 40 | required-features = ["e2ss-stats-binary"] 41 | -------------------------------------------------------------------------------- /crates/e2store/src/e2store/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod memory; 2 | pub mod stream; 3 | pub mod types; 4 | -------------------------------------------------------------------------------- /crates/e2store/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod e2hs; 2 | pub mod e2ss; 3 | pub mod e2store; 4 | pub mod entry_types; 5 | pub mod era; 6 | pub mod era1; 7 | pub mod types; 8 | pub mod utils; 9 | -------------------------------------------------------------------------------- /crates/e2store/src/types.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | 3 | use alloy::{consensus::Header, rlp::Decodable}; 4 | use anyhow::ensure; 5 | 6 | use crate::{e2store::types::Entry, entry_types}; 7 | 8 | #[derive(Clone, Eq, PartialEq, Debug)] 9 | pub struct HeaderEntry { 10 | pub header: Header, 11 | } 12 | 13 | impl TryFrom<&Entry> for HeaderEntry { 14 | type Error = anyhow::Error; 15 | 16 | fn try_from(entry: &Entry) -> Result { 17 | ensure!( 18 | entry.header.type_ == entry_types::COMPRESSED_HEADER, 19 | "invalid header entry: incorrect header type" 20 | ); 21 | ensure!( 22 | entry.header.reserved == 0, 23 | "invalid header entry: incorrect header reserved bytes" 24 | ); 25 | let mut decoder = snap::read::FrameDecoder::new(&entry.value[..]); 26 | let mut buf: Vec = vec![]; 27 | decoder.read_to_end(&mut buf)?; 28 | let header = Decodable::decode(&mut buf.as_slice())?; 29 | Ok(Self { header }) 30 | } 31 | } 32 | 33 | impl TryFrom<&HeaderEntry> for Entry { 34 | type Error = std::io::Error; 35 | 36 | fn try_from(value: &HeaderEntry) -> Result { 37 | let rlp_encoded = alloy::rlp::encode(&value.header); 38 | let buf: Vec = vec![]; 39 | let mut encoder = snap::write::FrameEncoder::new(buf); 40 | let _ = encoder.write(&rlp_encoded)?; 41 | let encoded = encoder.into_inner().map_err(|e| e.into_error())?; 42 | Ok(Entry::new(entry_types::COMPRESSED_HEADER, encoded)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/ethereum-rpc-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethereum-rpc-client" 3 | description = "Ethereum RPC client for consensus and execution APIs." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | alloy-hardforks.workspace = true 17 | anyhow.workspace = true 18 | ethereum_ssz.workspace = true 19 | ethereum_ssz_derive.workspace = true 20 | ethportal-api.workspace = true 21 | eventsource-client.workspace = true 22 | futures.workspace = true 23 | reqwest.workspace = true 24 | reqwest-middleware = { version = "0.4", features = ["json"] } 25 | reqwest-retry = "0.7" 26 | serde.workspace = true 27 | serde_json.workspace = true 28 | tokio.workspace = true 29 | tracing.workspace = true 30 | url.workspace = true 31 | -------------------------------------------------------------------------------- /crates/ethereum-rpc-client/README.md: -------------------------------------------------------------------------------- 1 | # ethereum-rpc-client 2 | 3 | This crate provides an Ethereum RPC client for consensus and execution APIs. -------------------------------------------------------------------------------- /crates/ethereum-rpc-client/src/consensus/constants.rs: -------------------------------------------------------------------------------- 1 | use tokio::time::Duration; 2 | 3 | /// The timeout in seconds is applied when requesting the beacon state from the Beacon API 4 | pub const DEFAULT_BEACON_STATE_REQUEST_TIMEOUT: Duration = Duration::from_secs(60); 5 | -------------------------------------------------------------------------------- /crates/ethereum-rpc-client/src/consensus/event.rs: -------------------------------------------------------------------------------- 1 | use alloy::rpc::types::beacon::events::{ 2 | BeaconNodeEventTopic, ChainReorgEvent, FinalizedCheckpointEvent, HeadEvent, 3 | LightClientOptimisticUpdateEvent, 4 | }; 5 | use eventsource_client::Event; 6 | use serde::de::{DeserializeOwned, Error}; 7 | 8 | #[derive(Debug, Clone, PartialEq, Eq)] 9 | pub enum BeaconEvent { 10 | ChainReorg(ChainReorgEvent), 11 | Head(HeadEvent), 12 | LightClientOptimisticUpdate(LightClientOptimisticUpdateEvent), 13 | FinalizedCheckpoint(FinalizedCheckpointEvent), 14 | } 15 | 16 | impl BeaconEvent { 17 | fn from_json( 18 | json: &str, 19 | constructor: impl FnOnce(T) -> Self, 20 | ) -> Result { 21 | serde_json::from_str(json).map(constructor) 22 | } 23 | } 24 | 25 | impl TryFrom for BeaconEvent { 26 | type Error = serde_json::Error; 27 | 28 | fn try_from(event: Event) -> Result { 29 | if event.event_type == BeaconNodeEventTopic::ChainReorg.query_value() { 30 | Self::from_json(&event.data, Self::ChainReorg) 31 | } else if event.event_type == BeaconNodeEventTopic::Head.query_value() { 32 | Self::from_json(&event.data, Self::Head) 33 | } else if event.event_type 34 | == BeaconNodeEventTopic::LightClientOptimisticUpdate.query_value() 35 | { 36 | Self::from_json(&event.data, Self::LightClientOptimisticUpdate) 37 | } else if event.event_type == BeaconNodeEventTopic::FinalizedCheckpoint.query_value() { 38 | Self::from_json(&event.data, Self::FinalizedCheckpoint) 39 | } else { 40 | Err(Self::Error::custom(format!( 41 | "Can't create BeaconEvent: unexpected event type: {}", 42 | event.event_type, 43 | ))) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/ethereum-rpc-client/src/constants.rs: -------------------------------------------------------------------------------- 1 | use tokio::time::Duration; 2 | 3 | /// The timeout in seconds is applied from when the request starts connecting until the response 4 | /// body has finished. Also considered a total deadline. 5 | pub const DEFAULT_TOTAL_REQUEST_TIMEOUT: u64 = 20; 6 | 7 | // Number of seconds to wait before retrying a provider request 8 | pub const FALLBACK_RETRY_AFTER: Duration = Duration::from_secs(5); 9 | 10 | // PANDAOPS refers to the group of clients provisioned by the EF devops team. 11 | // These are only intended to be used by core team members who have access to the nodes. 12 | // 13 | /// Execution layer PandaOps endpoint 14 | // This endpoint points towards an archive node (erigon) and skips dshackle (by using el-cl url 15 | // format), shackle is known to be somewhat buggy has caused some invalid responses. 16 | // Reth's archive node, has also exhibited some problems with the concurrent requests rate we 17 | // currently use. 18 | pub const DEFAULT_BASE_EL_ENDPOINT: &str = "https://geth-lighthouse.mainnet.eu1.ethpandaops.io/"; 19 | pub const FALLBACK_BASE_EL_ENDPOINT: &str = "https://geth-lighthouse.mainnet.eu1.ethpandaops.io/"; 20 | /// Consensus layer PandaOps endpoint 21 | /// We use Nimbus as the CL client, because it supports light client data by default. 22 | pub const DEFAULT_BASE_CL_ENDPOINT: &str = "https://nimbus-geth.mainnet.eu1.ethpandaops.io/"; 23 | pub const FALLBACK_BASE_CL_ENDPOINT: &str = "https://nimbus.mainnet.na1.ethpandaops.io/"; 24 | 25 | // Number of seconds to wait before retrying a provider request for get_receipts 26 | pub const GET_RECEIPTS_RETRY_AFTER: Duration = Duration::from_secs(1); 27 | -------------------------------------------------------------------------------- /crates/ethereum-rpc-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | #![warn(clippy::uninlined_format_args)] 3 | 4 | pub mod consensus; 5 | pub mod constants; 6 | pub mod execution; 7 | pub mod http_client; 8 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/assets/test/block_body_14764013.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/ethportal-api/src/assets/test/block_body_14764013.bin -------------------------------------------------------------------------------- /crates/ethportal-api/src/assets/test/fluffy_epoch_acc.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/ethportal-api/src/assets/test/fluffy_epoch_acc.bin -------------------------------------------------------------------------------- /crates/ethportal-api/src/assets/test/receipts_14764013.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/ethportal-api/src/assets/test/receipts_14764013.bin -------------------------------------------------------------------------------- /crates/ethportal-api/src/eth.rs: -------------------------------------------------------------------------------- 1 | use alloy::{ 2 | primitives::{Address, Bytes, B256, U256}, 3 | rpc::types::{Block, BlockId, BlockNumberOrTag, TransactionRequest}, 4 | }; 5 | use jsonrpsee::{core::RpcResult, proc_macros::rpc}; 6 | 7 | /// Web3 JSON-RPC endpoints 8 | #[rpc(client, server, namespace = "eth")] 9 | pub trait EthApi { 10 | #[method(name = "chainId")] 11 | async fn chain_id(&self) -> RpcResult; 12 | 13 | #[method(name = "getBlockByNumber")] 14 | async fn get_block_by_number( 15 | &self, 16 | block_number_or_tag: BlockNumberOrTag, 17 | hydrated_transactions: bool, 18 | ) -> RpcResult; 19 | 20 | #[method(name = "getBlockByHash")] 21 | async fn get_block_by_hash( 22 | &self, 23 | block_hash: B256, 24 | hydrated_transactions: bool, 25 | ) -> RpcResult; 26 | 27 | #[method(name = "getBalance")] 28 | async fn get_balance(&self, address: Address, block: BlockId) -> RpcResult; 29 | 30 | #[method(name = "getCode")] 31 | async fn get_code(&self, address: Address, block: BlockId) -> RpcResult; 32 | 33 | #[method(name = "getStorageAt")] 34 | async fn get_storage_at(&self, address: Address, slot: U256, block: BlockId) 35 | -> RpcResult; 36 | 37 | #[method(name = "call")] 38 | async fn call(&self, transaction: TransactionRequest, block: BlockId) -> RpcResult; 39 | } 40 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # ethportal-api 2 | //! 3 | //! `ethportal_api` is a collection of Portal Network APIs and types. 4 | #![warn(clippy::unwrap_used)] 5 | #![warn(clippy::uninlined_format_args)] 6 | 7 | extern crate lazy_static; 8 | 9 | mod beacon; 10 | pub mod discv5; 11 | mod eth; 12 | mod history; 13 | mod state; 14 | #[cfg(test)] 15 | mod test_utils; 16 | pub mod types; 17 | pub mod utils; 18 | pub mod version; 19 | mod web3; 20 | 21 | pub use beacon::{BeaconNetworkApiClient, BeaconNetworkApiServer}; 22 | pub use discv5::{Discv5ApiClient, Discv5ApiServer}; 23 | pub use eth::{EthApiClient, EthApiServer}; 24 | pub use history::{HistoryNetworkApiClient, HistoryNetworkApiServer}; 25 | // Re-exports jsonrpsee crate 26 | pub use jsonrpsee; 27 | pub use state::{StateNetworkApiClient, StateNetworkApiServer}; 28 | pub use types::{ 29 | consensus, 30 | consensus::light_client, 31 | content_key::{ 32 | beacon::{BeaconContentKey, LightClientBootstrapKey, LightClientUpdatesByRangeKey}, 33 | error::ContentKeyError, 34 | history::{BlockBodyKey, BlockReceiptsKey, HistoryContentKey}, 35 | overlay::{IdentityContentKey, OverlayContentKey}, 36 | state::StateContentKey, 37 | }, 38 | content_value::{ 39 | beacon::BeaconContentValue, error::ContentValueError, history::HistoryContentValue, 40 | state::StateContentValue, ContentValue, 41 | }, 42 | discv5::*, 43 | enr::*, 44 | execution::{block_body::*, receipts::*}, 45 | node_id::*, 46 | portal::{RawContentKey, RawContentValue}, 47 | }; 48 | pub use web3::{Web3ApiClient, Web3ApiServer}; 49 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/test_utils/constants.rs: -------------------------------------------------------------------------------- 1 | pub const PORTAL_SPEC_TESTS_SUBMODULE_PATH: &str = "../../portal-spec-tests"; 2 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/test_utils/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::{self, File}, 3 | io::{self, BufReader}, 4 | path::{Path, PathBuf}, 5 | }; 6 | 7 | use anyhow::anyhow; 8 | use serde::de::DeserializeOwned; 9 | use ssz::Decode; 10 | 11 | use self::constants::PORTAL_SPEC_TESTS_SUBMODULE_PATH; 12 | 13 | pub mod constants; 14 | pub mod types; 15 | 16 | fn portal_spec_tests_path(path: impl AsRef) -> PathBuf { 17 | PathBuf::from(PORTAL_SPEC_TESTS_SUBMODULE_PATH).join(path) 18 | } 19 | 20 | /// Reads bytes from a "portal-spec-tests" submodule. 21 | pub fn read_binary_portal_spec_tests_file>(path: P) -> io::Result> { 22 | fs::read(portal_spec_tests_path(path)) 23 | } 24 | 25 | /// Reads json file from a "portal-spec-tests" submodule 26 | pub fn read_json_portal_spec_tests_file(path: impl AsRef) -> anyhow::Result 27 | where 28 | T: DeserializeOwned, 29 | { 30 | let reader = BufReader::new(File::open(portal_spec_tests_path(path))?); 31 | Ok(serde_json::from_reader(reader)?) 32 | } 33 | 34 | /// Reads yaml file from a "portal-spec-tests" submodule 35 | pub fn read_yaml_portal_spec_tests_file(path: impl AsRef) -> anyhow::Result 36 | where 37 | T: DeserializeOwned, 38 | { 39 | let reader = BufReader::new(File::open(portal_spec_tests_path(path))?); 40 | Ok(serde_yaml::from_reader(reader)?) 41 | } 42 | 43 | /// Reads ssz file from a "portal-spec-tests" submodule 44 | pub fn read_ssz_portal_spec_tests_file(path: impl AsRef) -> anyhow::Result { 45 | let bytes = read_binary_portal_spec_tests_file(&path)?; 46 | T::from_ssz_bytes(&bytes).map_err(|err| { 47 | anyhow!( 48 | "Error decoding ssz file: {}. Error: {err:?}", 49 | path.as_ref().display() 50 | ) 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/test_utils/types.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | types::execution::header_with_proof::HeaderWithProof, ContentValue, ContentValueError, 5 | HistoryContentKey, HistoryContentValue, OverlayContentKey, RawContentValue, 6 | }; 7 | 8 | /// A common type used in test files. 9 | #[derive(Debug, Clone, Serialize, Deserialize)] 10 | pub struct ContentItem { 11 | pub content_key: K, 12 | #[serde(rename = "content_value")] 13 | pub raw_content_value: RawContentValue, 14 | } 15 | 16 | impl ContentItem { 17 | pub fn content_value>(&self) -> Result { 18 | V::decode(&self.content_key, &self.raw_content_value) 19 | } 20 | } 21 | 22 | impl ContentItem { 23 | /// Decodes content value as HeaderWithProof. 24 | /// 25 | /// Panics if content value is not HeaderWithProof. 26 | pub fn content_value_as_header_with_proof(&self) -> HeaderWithProof { 27 | let HistoryContentValue::BlockHeaderWithProof(header_with_proof) = 28 | self.content_value().unwrap() 29 | else { 30 | panic!( 31 | "Expected BlockHeaderWithProof content value. Actual {}", 32 | self.raw_content_value 33 | ); 34 | }; 35 | header_with_proof 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/bytes.rs: -------------------------------------------------------------------------------- 1 | use ssz_types::{ 2 | typenum::{self, UInt, UTerm, B0, B1}, 3 | VariableList, 4 | }; 5 | 6 | // 1100 in binary is 10001001100 7 | pub type U1100 = UInt< 8 | UInt< 9 | UInt< 10 | UInt, B0>, B0>, B0>, B1>, B0>, B0>, B1>, 11 | B1, 12 | >, 13 | B0, 14 | >, 15 | B0, 16 | >; 17 | 18 | pub type ByteList32 = VariableList; 19 | pub type ByteList1024 = VariableList; 20 | pub type ByteList1100 = VariableList; 21 | pub type ByteList2048 = VariableList; 22 | pub type ByteList32K = VariableList; 23 | pub type ByteList1G = VariableList; 24 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/client_type.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | /// ClientType is not robust and should not be used for any critical logic. 4 | /// It can't be used to reliably identify the client type from ClientInfoRadiusCapabilities, since 5 | /// clients can include amendments to their client name, an example of this is Trin Execution uses 6 | /// the client name "trin-execution", and hence if ClientType is used to parse this it will return 7 | /// unknown. 8 | /// 9 | /// For projects built on Portal like Glados, it is recommended the respective projects maintain 10 | /// their own client type parsing logic. 11 | #[derive(Debug, Clone, PartialEq, Eq)] 12 | pub enum ClientType { 13 | Fluffy, 14 | Trin, 15 | TrinExecution, 16 | Shisui, 17 | Ultralight, 18 | Samba, 19 | Unknown(Option), 20 | } 21 | 22 | impl From<&str> for ClientType { 23 | fn from(value: &str) -> Self { 24 | let value = value.to_lowercase(); 25 | match value.as_str() { 26 | "fluffy" => ClientType::Fluffy, 27 | "trin" => ClientType::Trin, 28 | "trin-execution" => ClientType::TrinExecution, 29 | "shisui" => ClientType::Shisui, 30 | "ultralight" => ClientType::Ultralight, 31 | "samba" => ClientType::Samba, 32 | _ => ClientType::Unknown(Some(value)), 33 | } 34 | } 35 | } 36 | 37 | impl Display for ClientType { 38 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 39 | match self { 40 | ClientType::Unknown(Some(client)) => write!(f, "Unknown({client})"), 41 | ClientType::Unknown(None) => write!(f, "Unknown"), 42 | _ => write!(f, "{self:?}"), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/consolidation_request.rs: -------------------------------------------------------------------------------- 1 | use alloy::{eips::eip7251::CONSOLIDATION_REQUEST_TYPE, primitives::Address}; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | use crate::consensus::pubkey::PubKey; 7 | 8 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 9 | pub struct ConsolidationRequest { 10 | pub source_address: Address, 11 | pub source_pubkey: PubKey, 12 | pub target_pubkey: PubKey, 13 | } 14 | 15 | impl ConsolidationRequest { 16 | pub const REQUEST_TYPE: u8 = CONSOLIDATION_REQUEST_TYPE; 17 | } 18 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/constants.rs: -------------------------------------------------------------------------------- 1 | //! Consensus specs constants. 2 | //! 3 | //! Mostly taken from: 4 | //! https://github.com/ethereum/consensus-specs/blob/d8cfdf2626c1219a40048f8fa3dd103ae8c0b040/presets/mainnet/phase0.yaml 5 | //! or 6 | //! https://github.com/ethereum/consensus-specs/blob/d8cfdf2626c1219a40048f8fa3dd103ae8c0b040/configs/mainnet.yaml 7 | //! 8 | //! These should eventually be part of the Chain configuration parameters. 9 | 10 | use std::time::Duration; 11 | 12 | /// Number of slots per Epoch. 13 | /// 14 | /// 2**5 (= 32) slots 6.4 minutes 15 | pub const SLOTS_PER_EPOCH: u64 = 32; 16 | 17 | /// Number of slots per HistoricalRoot / HistoricalSummary. 18 | /// 19 | /// 2**13 (= 8,192) slots ~27 hours 20 | pub const SLOTS_PER_HISTORICAL_ROOT: u64 = 8192; 21 | 22 | /// Seconds per slot 23 | /// 24 | /// 12 seconds 25 | pub const SECONDS_PER_SLOT: Duration = Duration::from_secs(12); 26 | 27 | /// The Epoch of the mainnet Capella fork. 28 | /// 29 | /// April 12, 2023, 10:27:35pm UTC 30 | pub const CAPELLA_FORK_EPOCH: u64 = 194_048; 31 | 32 | /// The Epoch of the mainnet Deneb fork. 33 | /// 34 | /// March 13, 2024, 01:55:35pm UTC 35 | pub const DENEB_FORK_EPOCH: u64 = 269_568; 36 | 37 | /// The Epoch of the mainnet Electra fork. 38 | /// 39 | /// May 7, 2025, 10:05:11am UTC 40 | pub const ELECTRA_FORK_EPOCH: u64 = 364_032; 41 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/deposit_request.rs: -------------------------------------------------------------------------------- 1 | use alloy::{eips::eip6110::DEPOSIT_REQUEST_TYPE, primitives::B256}; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_this_or_that::as_u64; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::consensus::{pubkey::PubKey, signature::BlsSignature}; 8 | 9 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct DepositRequest { 11 | pub pubkey: PubKey, 12 | pub withdrawal_credentials: B256, 13 | #[serde(deserialize_with = "as_u64")] 14 | pub amount: u64, 15 | pub signature: BlsSignature, 16 | #[serde(deserialize_with = "as_u64")] 17 | pub index: u64, 18 | } 19 | 20 | impl DepositRequest { 21 | pub const REQUEST_TYPE: u8 = DEPOSIT_REQUEST_TYPE; 22 | } 23 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/header.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_this_or_that::as_u64; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | /// Types based off specs @ 8 | /// https://github.com/ethereum/consensus-specs/blob/5970ae56a1cd50ea06049d8aad6bed74093d49d3/specs/phase0/beacon-chain.md 9 | #[derive( 10 | Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize, Encode, Decode, TreeHash, 11 | )] 12 | pub struct BeaconBlockHeader { 13 | #[serde(deserialize_with = "as_u64")] 14 | pub slot: u64, 15 | #[serde(deserialize_with = "as_u64")] 16 | pub proposer_index: u64, 17 | pub parent_root: B256, 18 | pub state_root: B256, 19 | pub body_root: B256, 20 | } 21 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/light_client/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bootstrap; 2 | pub mod finality_update; 3 | pub mod header; 4 | pub mod optimistic_update; 5 | pub mod store; 6 | pub mod update; 7 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/light_client/store.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::consensus::{header::BeaconBlockHeader, sync_committee::SyncCommittee}; 4 | 5 | /// `LightClientStore` object for the light client sync protocol. 6 | #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] 7 | pub struct LightClientStore { 8 | pub finalized_header: BeaconBlockHeader, 9 | pub current_sync_committee: SyncCommittee, 10 | pub next_sync_committee: Option, 11 | pub optimistic_header: BeaconBlockHeader, 12 | pub previous_max_active_participants: u64, 13 | pub current_max_active_participants: u64, 14 | } 15 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod beacon_block; 2 | pub mod beacon_state; 3 | pub mod body; 4 | pub mod consolidation_request; 5 | pub mod constants; 6 | pub mod deposit_request; 7 | pub mod execution_payload; 8 | pub mod execution_requests; 9 | pub mod fork; 10 | pub mod header; 11 | pub mod historical_summaries; 12 | pub mod kzg_commitment; 13 | pub mod light_client; 14 | pub mod participation_flags; 15 | pub mod pending_balance_deposit; 16 | pub mod pending_consolidation; 17 | pub mod pending_partial_withdrawal; 18 | pub mod proof; 19 | pub mod pubkey; 20 | pub mod serde; 21 | pub mod signature; 22 | pub mod sync_committee; 23 | pub mod withdrawal_request; 24 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/pending_balance_deposit.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::B256; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_this_or_that::as_u64; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::consensus::{pubkey::PubKey, signature::BlsSignature}; 8 | 9 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct PendingDeposit { 11 | pub pubkey: PubKey, 12 | pub withdrawal_credentials: B256, 13 | #[serde(deserialize_with = "as_u64")] 14 | pub amount: u64, 15 | pub signature: BlsSignature, 16 | #[serde(deserialize_with = "as_u64")] 17 | pub slot: u64, 18 | } 19 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/pending_consolidation.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_this_or_that::as_u64; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 7 | pub struct PendingConsolidation { 8 | #[serde(deserialize_with = "as_u64")] 9 | pub source_index: u64, 10 | #[serde(deserialize_with = "as_u64")] 11 | pub target_index: u64, 12 | } 13 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/pending_partial_withdrawal.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_this_or_that::as_u64; 3 | use ssz_derive::{Decode, Encode}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | use crate::consensus::beacon_state::Epoch; 7 | 8 | #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 9 | pub struct PendingPartialWithdrawal { 10 | #[serde(deserialize_with = "as_u64")] 11 | pub validator_index: u64, 12 | #[serde(deserialize_with = "as_u64")] 13 | pub amount: u64, 14 | pub withdrawable_epoch: Epoch, 15 | } 16 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/proof.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::B256; 2 | use rs_merkle::{algorithms::Sha256, MerkleTree}; 3 | 4 | pub fn build_merkle_proof_for_index>( 5 | leaves: impl IntoIterator, 6 | index_to_prove: usize, 7 | ) -> Vec { 8 | let leaves = leaves.into_iter(); 9 | // Returns the smallest power of two greater than or equal to self 10 | let full_tree_len = leaves.len().next_power_of_two(); 11 | 12 | // Convert to [u8;32], and append [0;32] until we have full_tree_len 13 | let leaves = leaves 14 | .map(Into::into) 15 | .chain(std::iter::repeat([0; 32])) 16 | .take(full_tree_len) 17 | .collect::>(); 18 | 19 | let merkle_tree = MerkleTree::::from_leaves(&leaves); 20 | let indices_to_prove = vec![index_to_prove]; 21 | let proof = merkle_tree.proof(&indices_to_prove); 22 | proof 23 | .proof_hashes() 24 | .iter() 25 | .map(|hash| B256::from_slice(hash)) 26 | .collect() 27 | } 28 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/serde.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::U256; 2 | use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serializer}; 3 | use serde_json::Value; 4 | use ssz_types::VariableList; 5 | 6 | use super::body::{Transaction, Transactions}; 7 | use crate::utils::bytes::{hex_decode, hex_encode}; 8 | 9 | pub fn se_txs_to_hex(value: &Transactions, serializer: S) -> Result 10 | where 11 | S: Serializer, 12 | { 13 | let mut s = serializer.serialize_seq(Some(value.len()))?; 14 | for val in value { 15 | s.serialize_element(&hex_encode(val.to_vec()))?; 16 | } 17 | s.end() 18 | } 19 | 20 | pub fn se_hex_to_number(value: &U256, serializer: S) -> Result 21 | where 22 | S: Serializer, 23 | { 24 | serializer.serialize_str(&value.to_string()) 25 | } 26 | 27 | pub fn de_number_to_u256<'de, D>(deserializer: D) -> Result 28 | where 29 | D: Deserializer<'de>, 30 | { 31 | let result: Value = Deserialize::deserialize(deserializer)?; 32 | let result = match result.as_str() { 33 | Some(val) => val, 34 | None => return Err(serde::de::Error::custom("Unable to deserialize u256")), 35 | }; 36 | let result = U256::from_str_radix(result, 10).map_err(serde::de::Error::custom)?; 37 | Ok(result) 38 | } 39 | 40 | pub fn de_hex_to_txs<'de, D>(deserializer: D) -> Result 41 | where 42 | D: Deserializer<'de>, 43 | { 44 | let result: Vec = Deserialize::deserialize(deserializer)?; 45 | let mut txs: Transactions = VariableList::empty(); 46 | for r in result { 47 | let tx = hex_decode(&r).map_err(serde::de::Error::custom)?; 48 | let tx = Transaction::from(tx); 49 | if txs.push(tx).is_err() { 50 | return Err(serde::de::Error::custom("Unable to deserialize txs")); 51 | } 52 | } 53 | Ok(txs) 54 | } 55 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/sync_committee.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use ssz_derive::{Decode, Encode}; 3 | use ssz_types::{typenum::U512, FixedVector}; 4 | use tree_hash_derive::TreeHash; 5 | 6 | use crate::types::consensus::pubkey::PubKey; 7 | 8 | type SyncCommitteeSize = U512; 9 | 10 | /// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#synccommittee 11 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 12 | pub struct SyncCommittee { 13 | pub pubkeys: FixedVector, 14 | pub aggregate_pubkey: PubKey, 15 | } 16 | 17 | impl Default for SyncCommittee { 18 | fn default() -> Self { 19 | Self { 20 | pubkeys: FixedVector::from_elem(PubKey::default()), 21 | aggregate_pubkey: PubKey::default(), 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/consensus/withdrawal_request.rs: -------------------------------------------------------------------------------- 1 | use alloy::{eips::eip7002::WITHDRAWAL_REQUEST_TYPE, primitives::Address}; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_this_or_that::as_u64; 4 | use ssz_derive::{Decode, Encode}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | use crate::consensus::pubkey::PubKey; 8 | 9 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)] 10 | pub struct WithdrawalRequest { 11 | pub source_address: Address, 12 | pub validator_pubkey: PubKey, 13 | #[serde(deserialize_with = "as_u64")] 14 | pub amount: u64, 15 | } 16 | 17 | impl WithdrawalRequest { 18 | pub const REQUEST_TYPE: u8 = WITHDRAWAL_REQUEST_TYPE; 19 | } 20 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/content_key/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | use crate::utils::bytes::hex_encode; 4 | 5 | /// An error decoding a portal network content key. 6 | #[derive(Clone, Debug, Error, PartialEq)] 7 | pub enum ContentKeyError { 8 | #[error("Unable to decode key SSZ bytes {input} due to {decode_error:?}")] 9 | DecodeSsz { 10 | decode_error: ssz::DecodeError, 11 | input: String, 12 | }, 13 | 14 | #[error("Input Vec has length {received}, expected {expected})")] 15 | InvalidLength { received: usize, expected: usize }, 16 | } 17 | 18 | impl ContentKeyError { 19 | pub fn from_decode_error>(decode_error: ssz::DecodeError, input: T) -> Self { 20 | Self::DecodeSsz { 21 | decode_error, 22 | input: hex_encode(input), 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/content_key/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod beacon; 2 | pub mod error; 3 | pub mod history; 4 | pub mod overlay; 5 | pub mod state; 6 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/content_value/constants.rs: -------------------------------------------------------------------------------- 1 | /// The length of the Merkle proof for the inclusion of a block header in a particular epoch 2 | /// accumulator. 3 | pub const EPOCH_ACC_PROOF_LEN: usize = 15; 4 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/content_value/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | use crate::types::network::Subnetwork; 4 | 5 | /// An error decoding a portal network content value. 6 | #[derive(Clone, Debug, Error, PartialEq)] 7 | pub enum ContentValueError { 8 | #[error("unable to decode value SSZ bytes {input} due to {decode_error:?}")] 9 | DecodeSsz { 10 | decode_error: ssz::DecodeError, 11 | input: String, 12 | }, 13 | #[error("could not determine content type of {bytes} from {subnetwork:?} subnetwork")] 14 | UnknownContent { 15 | bytes: String, 16 | subnetwork: Subnetwork, 17 | }, 18 | /// The content value is the "0x" absent content message rather than data. 19 | /// 20 | /// This error implies that handling of the "content absent" response was skipped. 21 | #[error("attempted to deserialize the '0x' absent content message")] 22 | DeserializeAbsentContent, 23 | 24 | /// The content value is the "0x" absent content message rather than data. 25 | /// 26 | /// This error implies that handling of the "content absent" response was skipped. 27 | #[error("attempted to decode the '0x' absent content message")] 28 | DecodeAbsentContent, 29 | #[error("could not determine fork digest of {bytes} from {subnetwork:?} subnetwork")] 30 | UnknownForkDigest { 31 | bytes: String, 32 | subnetwork: Subnetwork, 33 | }, 34 | #[error("could not determine fork name of {bytes} from {subnetwork:?} subnetwork")] 35 | UnknownForkName { 36 | bytes: String, 37 | subnetwork: Subnetwork, 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/content_value/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | utils::bytes::{hex_decode, hex_encode}, 3 | ContentValueError, OverlayContentKey, RawContentValue, 4 | }; 5 | 6 | pub mod beacon; 7 | pub mod constants; 8 | pub mod error; 9 | pub mod history; 10 | pub mod state; 11 | 12 | /// An encodable portal network content value. 13 | pub trait ContentValue: Sized { 14 | /// The content key type associated with this content value. 15 | type TContentKey: OverlayContentKey; 16 | 17 | /// Encodes the content value into a byte vector. 18 | /// 19 | /// The [RawContentValue] is better suited than `Vec` for representing content value bytes. 20 | /// For more details, see [RawContentValue] documentation. If `Vec` is still desired, one 21 | /// can obtain it with: `value.to_bytes().to_vec()`. 22 | fn encode(&self) -> RawContentValue; 23 | 24 | /// Decodes `buf` into a content value. 25 | fn decode(key: &Self::TContentKey, buf: &[u8]) -> Result; 26 | 27 | /// Encodes the content as "0x"-prefixed hex string. 28 | fn to_hex(&self) -> String { 29 | hex_encode(self.encode()) 30 | } 31 | 32 | /// Decodes the "0x"-prefixed hex string as a content value. 33 | fn from_hex(key: &Self::TContentKey, data: &str) -> anyhow::Result { 34 | Ok(Self::decode(key, &hex_decode(data)?)?) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/execution/accumulator.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::U256; 2 | use serde::{Deserialize, Serialize}; 3 | use ssz_derive::{Decode, Encode}; 4 | use ssz_types::{typenum, VariableList}; 5 | use tree_hash_derive::TreeHash; 6 | 7 | /// SSZ List[HeaderRecord, max_length = EPOCH_SIZE] 8 | /// List of (block_number, block_hash) for each header in the current epoch. 9 | pub type EpochAccumulator = VariableList; 10 | 11 | /// Individual record for a historical header. 12 | /// Block hash and total difficulty are used to validate whether 13 | /// a header is canonical or not. 14 | /// Every HeaderRecord is 64bytes. 15 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Decode, Encode, Deserialize, Serialize, TreeHash)] 16 | pub struct HeaderRecord { 17 | pub block_hash: tree_hash::Hash256, 18 | pub total_difficulty: U256, 19 | } 20 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/execution/builders/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block; 2 | pub mod header; 3 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/execution/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod accumulator; 2 | pub mod block_body; 3 | pub mod builders; 4 | pub mod ephermeral_header; 5 | pub mod header_with_proof; 6 | pub mod receipts; 7 | pub mod ssz_header; 8 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/execution/ssz_header.rs: -------------------------------------------------------------------------------- 1 | use crate::types::bytes::ByteList2048; 2 | 3 | pub mod encode { 4 | use alloy::consensus::Header; 5 | use ssz::Encode; 6 | 7 | use super::*; 8 | 9 | pub fn is_ssz_fixed_len() -> bool { 10 | ByteList2048::is_ssz_fixed_len() 11 | } 12 | 13 | pub fn ssz_append(header: &Header, buf: &mut Vec) { 14 | let header = alloy::rlp::encode(header); 15 | ByteList2048::from(header).ssz_append(buf); 16 | } 17 | 18 | pub fn ssz_fixed_len() -> usize { 19 | ByteList2048::ssz_fixed_len() 20 | } 21 | 22 | pub fn ssz_bytes_len(header: &Header) -> usize { 23 | // The ssz encoded length is the same as rlp encoded length. 24 | alloy_rlp::Encodable::length(header) 25 | } 26 | } 27 | 28 | pub mod decode { 29 | use alloy::consensus::Header; 30 | use alloy_rlp::Decodable; 31 | use ssz::Decode; 32 | 33 | use super::*; 34 | 35 | pub fn is_ssz_fixed_len() -> bool { 36 | ByteList2048::is_ssz_fixed_len() 37 | } 38 | 39 | pub fn ssz_fixed_len() -> usize { 40 | ByteList2048::ssz_fixed_len() 41 | } 42 | 43 | pub fn from_ssz_bytes(bytes: &[u8]) -> Result { 44 | let rlp_encoded_header = ByteList2048::from_ssz_bytes(bytes)?; 45 | Header::decode(&mut &*rlp_encoded_header).map_err(|_| { 46 | ssz::DecodeError::BytesInvalid("Unable to decode bytes into header.".to_string()) 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/jsonrpc/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod endpoints; 2 | pub mod json_rpc_mock; 3 | pub mod params; 4 | pub mod request; 5 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod accept_code; 2 | pub mod bytes; 3 | pub mod client_type; 4 | pub mod consensus; 5 | pub mod content_key; 6 | pub mod content_value; 7 | pub mod discv5; 8 | pub mod distance; 9 | pub mod enr; 10 | pub mod execution; 11 | pub mod jsonrpc; 12 | pub mod network; 13 | pub mod network_spec; 14 | pub mod node_id; 15 | pub mod ping_extensions; 16 | pub mod portal; 17 | pub mod portal_wire; 18 | pub mod protocol_versions; 19 | pub mod query_trace; 20 | pub mod state_trie; 21 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/ping_extensions/consts.rs: -------------------------------------------------------------------------------- 1 | use super::extension_types::PingExtensionType; 2 | 3 | pub const BEACON_SUPPORTED_EXTENSIONS: &[PingExtensionType] = &[ 4 | PingExtensionType::Capabilities, 5 | PingExtensionType::BasicRadius, 6 | PingExtensionType::Error, 7 | ]; 8 | pub const HISTORY_SUPPORTED_EXTENSIONS: &[PingExtensionType] = &[ 9 | PingExtensionType::Capabilities, 10 | PingExtensionType::HistoryRadius, 11 | PingExtensionType::Error, 12 | ]; 13 | pub const STATE_SUPPORTED_EXTENSIONS: &[PingExtensionType] = &[ 14 | PingExtensionType::Capabilities, 15 | PingExtensionType::BasicRadius, 16 | PingExtensionType::Error, 17 | ]; 18 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/ping_extensions/extensions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod type_0; 2 | pub mod type_1; 3 | pub mod type_2; 4 | pub mod type_65535; 5 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/ping_extensions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod consts; 2 | pub mod decode; 3 | pub mod extension_types; 4 | pub mod extensions; 5 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/state_trie/account_state.rs: -------------------------------------------------------------------------------- 1 | use alloy::{ 2 | consensus::{constants::KECCAK_EMPTY, EMPTY_ROOT_HASH}, 3 | primitives::{B256, U256}, 4 | rlp::{RlpDecodable, RlpEncodable}, 5 | }; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /// The Account State stored in the state trie. 9 | #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize)] 10 | pub struct AccountState { 11 | pub nonce: u64, 12 | pub balance: U256, 13 | pub storage_root: B256, 14 | pub code_hash: B256, 15 | } 16 | 17 | impl Default for AccountState { 18 | fn default() -> Self { 19 | Self { 20 | nonce: 0, 21 | balance: U256::ZERO, 22 | storage_root: EMPTY_ROOT_HASH, 23 | code_hash: KECCAK_EMPTY, 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/types/state_trie/mod.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use alloy::primitives::{keccak256, B256}; 4 | use eth_trie::{decode_node, node::Node, TrieError}; 5 | use serde::{Deserialize, Serialize}; 6 | use ssz_derive::{Decode, Encode}; 7 | use ssz_types::{typenum, VariableList}; 8 | 9 | use super::bytes::{ByteList1024, ByteList32K}; 10 | 11 | pub mod account_state; 12 | pub mod nibbles; 13 | pub mod trie_traversal; 14 | mod utils; 15 | 16 | /// The RLP encoding of a trie node. 17 | #[derive(Clone, Debug, Eq, PartialEq, Decode, Encode, Serialize, Deserialize)] 18 | #[ssz(struct_behaviour = "transparent")] 19 | pub struct EncodedTrieNode(ByteList1024); 20 | 21 | impl EncodedTrieNode { 22 | pub fn node_hash(&self) -> B256 { 23 | keccak256(&self[..]) 24 | } 25 | 26 | pub fn as_trie_node(&self) -> Result { 27 | decode_node(&mut &self.clone()[..]) 28 | } 29 | } 30 | 31 | impl Deref for EncodedTrieNode { 32 | type Target = ByteList1024; 33 | 34 | fn deref(&self) -> &Self::Target { 35 | &self.0 36 | } 37 | } 38 | 39 | impl From> for EncodedTrieNode { 40 | fn from(value: Vec) -> Self { 41 | Self(value.into()) 42 | } 43 | } 44 | 45 | impl TryFrom for Node { 46 | type Error = TrieError; 47 | 48 | fn try_from(value: EncodedTrieNode) -> Result { 49 | value.as_trie_node() 50 | } 51 | } 52 | 53 | impl From<&Node> for EncodedTrieNode { 54 | fn from(node: &Node) -> Self { 55 | Self::from(utils::encode_node(node)) 56 | } 57 | } 58 | 59 | /// The ordered list of trie nodes. Together they make the path in a trie, first node being the 60 | /// root, last node being the node whose inclusion we are proving. 61 | pub type TrieProof = VariableList; 62 | 63 | /// The bytecode of the contract. Current maximum size is 24KB, but we are using 32KB to be safe. 64 | pub type ByteCode = ByteList32K; 65 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bytes; 2 | pub mod serde; 3 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/utils/serde/hex_fixed_vec.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserializer, Serializer}; 2 | use serde_utils::hex::PrefixedHexVisitor; 3 | use ssz_types::{typenum::Unsigned, FixedVector}; 4 | 5 | use crate::utils::bytes::hex_encode; 6 | 7 | pub fn serialize(bytes: &FixedVector, serializer: S) -> Result 8 | where 9 | S: Serializer, 10 | U: Unsigned, 11 | { 12 | serializer.serialize_str(&hex_encode(&bytes[..])) 13 | } 14 | 15 | pub fn deserialize<'de, D, U>(deserializer: D) -> Result, D::Error> 16 | where 17 | D: Deserializer<'de>, 18 | U: Unsigned, 19 | { 20 | let vec = deserializer.deserialize_string(PrefixedHexVisitor)?; 21 | FixedVector::new(vec) 22 | .map_err(|e| serde::de::Error::custom(format!("invalid fixed vector: {e:?}"))) 23 | } 24 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/utils/serde/hex_var_list.rs: -------------------------------------------------------------------------------- 1 | //! Serialize `VariableList` as 0x-prefixed hex string. 2 | 3 | use serde::{Deserializer, Serializer}; 4 | use serde_utils::hex::{self, PrefixedHexVisitor}; 5 | use ssz_types::{typenum::Unsigned, VariableList}; 6 | 7 | pub fn serialize(bytes: &VariableList, serializer: S) -> Result 8 | where 9 | S: Serializer, 10 | N: Unsigned, 11 | { 12 | serializer.serialize_str(&hex::encode(&**bytes)) 13 | } 14 | 15 | pub fn deserialize<'de, D, N>(deserializer: D) -> Result, D::Error> 16 | where 17 | D: Deserializer<'de>, 18 | N: Unsigned, 19 | { 20 | let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?; 21 | VariableList::new(bytes) 22 | .map_err(|e| serde::de::Error::custom(format!("invalid variable list: {e:?}"))) 23 | } 24 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/utils/serde/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod hex_fixed_vec; 2 | pub mod hex_var_list; 3 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/version.rs: -------------------------------------------------------------------------------- 1 | pub const APP_NAME: &str = "trin"; 2 | 3 | /// The latest git commit hash of the build. 4 | pub const TRIN_FULL_COMMIT: &str = env!("VERGEN_GIT_SHA"); 5 | pub const TRIN_SHORT_COMMIT: &str = env!("VERGEN_GIT_SHA_SHORT"); 6 | 7 | /// Trin's version is the same as the git tag. 8 | pub const TRIN_VERSION: &str = env!("TRIN_VERSION"); 9 | 10 | /// The operating system of the build, linux, macos, windows etc. 11 | pub const BUILD_OPERATING_SYSTEM: &str = env!("TRIN_BUILD_OPERATING_SYSTEM"); 12 | 13 | /// The architecture of the build, x86_64, aarch64, etc. 14 | pub const BUILD_ARCHITECTURE: &str = env!("TRIN_BUILD_ARCHITECTURE"); 15 | 16 | // /// The version of the programming language used to build the binary. 17 | pub const PROGRAMMING_LANGUAGE_VERSION: &str = env!("VERGEN_RUSTC_SEMVER"); 18 | 19 | pub const FULL_VERSION: &str = env!("TRIN_FULL_VERSION"); 20 | 21 | /// Returns the trin version and git revision. 22 | pub const fn get_trin_version() -> &'static str { 23 | TRIN_SHORT_COMMIT 24 | } 25 | -------------------------------------------------------------------------------- /crates/ethportal-api/src/web3.rs: -------------------------------------------------------------------------------- 1 | use jsonrpsee::{core::RpcResult, proc_macros::rpc}; 2 | 3 | /// Web3 JSON-RPC endpoints 4 | #[rpc(client, server, namespace = "web3")] 5 | pub trait Web3Api { 6 | #[method(name = "clientVersion")] 7 | async fn client_version(&self) -> RpcResult; 8 | } 9 | -------------------------------------------------------------------------------- /crates/evm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-evm" 3 | keywords = ["ethereum", "execution-layer"] 4 | description = "Common EVM functionality" 5 | authors.workspace = true 6 | categories.workspace = true 7 | edition.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | ethportal-api.workspace = true 17 | revm.workspace = true 18 | revm-primitives.workspace = true 19 | tokio.workspace = true 20 | -------------------------------------------------------------------------------- /crates/evm/README.md: -------------------------------------------------------------------------------- 1 | # Trin EVM 2 | 3 | The collection of common EVM related functionality. 4 | -------------------------------------------------------------------------------- /crates/light-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "light-client" 3 | description = "Beacon chain light client implementation" 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | anyhow.workspace = true 17 | async-trait.workspace = true 18 | chrono.workspace = true 19 | ethereum_ssz.workspace = true 20 | ethportal-api.workspace = true 21 | figment = { version = "0.10.7", features = ["toml", "env"] } 22 | futures.workspace = true 23 | hex.workspace = true 24 | jsonrpsee = { workspace = true, features = ["full"] } 25 | log = "0.4.17" 26 | milagro_bls = { package="snowbridge-milagro-bls", git = "https://github.com/Snowfork/milagro_bls" } 27 | portalnet.workspace = true 28 | reqwest.workspace = true 29 | serde.workspace = true 30 | serde-this-or-that.workspace = true 31 | serde_json.workspace = true 32 | serde_yaml.workspace = true 33 | ssz_types.workspace = true 34 | strum.workspace = true 35 | thiserror.workspace = true 36 | tokio.workspace = true 37 | tracing.workspace = true 38 | tracing-subscriber.workspace = true 39 | tree_hash.workspace = true 40 | tree_hash_derive.workspace = true 41 | trin-validation.workspace = true 42 | 43 | [lib] 44 | name = "light_client" 45 | path = "src/lib.rs" 46 | -------------------------------------------------------------------------------- /crates/light-client/src/config/base.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::B256; 2 | use serde::Serialize; 3 | 4 | use crate::config::{ChainConfig, Forks}; 5 | 6 | /// The base configuration for a network. 7 | #[derive(Serialize, Default)] 8 | pub struct BaseConfig { 9 | pub rpc_port: u16, 10 | pub consensus_rpc: Option, 11 | pub default_checkpoint: B256, 12 | pub chain: ChainConfig, 13 | pub forks: Forks, 14 | pub max_checkpoint_age: u64, 15 | } 16 | -------------------------------------------------------------------------------- /crates/light-client/src/config/cli.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, path::PathBuf}; 2 | 3 | use alloy::primitives::B256; 4 | use figment::{providers::Serialized, value::Value}; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | /// Cli Config 8 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 9 | pub struct CliConfig { 10 | pub consensus_rpc: Option, 11 | pub checkpoint: Option, 12 | pub rpc_port: Option, 13 | pub data_dir: PathBuf, 14 | pub fallback: Option, 15 | pub load_external_fallback: bool, 16 | pub strict_checkpoint_age: bool, 17 | } 18 | 19 | impl CliConfig { 20 | pub fn as_provider(&self, network: &str) -> Serialized> { 21 | let mut user_dict = HashMap::new(); 22 | 23 | if let Some(rpc) = &self.consensus_rpc { 24 | user_dict.insert("consensus_rpc", Value::from(rpc.clone())); 25 | } 26 | 27 | if let Some(checkpoint) = &self.checkpoint { 28 | user_dict.insert("checkpoint", Value::from(hex::encode(checkpoint))); 29 | } 30 | 31 | if let Some(port) = self.rpc_port { 32 | user_dict.insert("rpc_port", Value::from(port)); 33 | } 34 | 35 | user_dict.insert( 36 | "data_dir", 37 | Value::from( 38 | self.data_dir 39 | .to_str() 40 | .expect("data dir path must be a valid UTF-8 str"), 41 | ), 42 | ); 43 | 44 | if let Some(fallback) = &self.fallback { 45 | user_dict.insert("fallback", Value::from(fallback.clone())); 46 | } 47 | 48 | user_dict.insert( 49 | "load_external_fallback", 50 | Value::from(self.load_external_fallback), 51 | ); 52 | 53 | user_dict.insert( 54 | "strict_checkpoint_age", 55 | Value::from(self.strict_checkpoint_age), 56 | ); 57 | 58 | Serialized::from(user_dict, network) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /crates/light-client/src/config/mod.rs: -------------------------------------------------------------------------------- 1 | /// Base Config 2 | pub mod base; 3 | pub use base::*; 4 | 5 | /// Core Config 6 | pub mod client_config; 7 | 8 | /// Checkpoint Config 9 | pub mod checkpoints; 10 | pub use checkpoints::*; 11 | 12 | /// Cli Config 13 | pub mod cli; 14 | pub use cli::*; 15 | 16 | /// Network Configuration 17 | pub mod networks; 18 | pub use networks::*; 19 | 20 | /// Generic Config Types 21 | pub mod types; 22 | pub use types::*; 23 | -------------------------------------------------------------------------------- /crates/light-client/src/config/types.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{FixedBytes, B256}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Default, Clone)] 5 | pub struct ChainConfig { 6 | pub chain_id: u64, 7 | pub genesis_time: u64, 8 | pub genesis_root: B256, 9 | } 10 | 11 | #[derive(Serialize, Deserialize, Debug, Default, Clone)] 12 | pub struct Forks { 13 | pub genesis: Fork, 14 | pub altair: Fork, 15 | pub bellatrix: Fork, 16 | pub capella: Fork, 17 | pub deneb: Fork, 18 | pub electra: Fork, 19 | } 20 | 21 | #[derive(Serialize, Deserialize, Debug, Default, Clone)] 22 | pub struct Fork { 23 | pub epoch: u64, 24 | pub fork_version: FixedBytes<4>, 25 | } 26 | -------------------------------------------------------------------------------- /crates/light-client/src/consensus/constants.rs: -------------------------------------------------------------------------------- 1 | // Consensus constants 2 | 3 | // https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/p2p-interface.md#configuration 4 | pub const MAX_REQUEST_LIGHT_CLIENT_UPDATES: u8 = 128; 5 | -------------------------------------------------------------------------------- /crates/light-client/src/consensus/errors.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::B256; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Error)] 5 | pub enum ConsensusError { 6 | #[error("insufficient participation")] 7 | InsufficientParticipation, 8 | #[error("invalid timestamp")] 9 | InvalidTimestamp, 10 | #[error("invalid sync committee period")] 11 | InvalidPeriod, 12 | #[error("update not relevant")] 13 | NotRelevant, 14 | #[error("invalid finality proof")] 15 | InvalidFinalityProof, 16 | #[error("invalid next sync committee proof")] 17 | InvalidNextSyncCommitteeProof, 18 | #[error("invalid current sync committee proof")] 19 | InvalidCurrentSyncCommitteeProof, 20 | #[error("invalid sync committee signature")] 21 | InvalidSignature, 22 | #[error("invalid header hash found: {0}, expected: {1}")] 23 | InvalidHeaderHash(B256, B256), 24 | #[error("checkpoint is too old")] 25 | CheckpointTooOld, 26 | #[error("consensus rpc is for the incorrect network")] 27 | IncorrectRpcNetwork, 28 | } 29 | -------------------------------------------------------------------------------- /crates/light-client/src/consensus/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod errors; 2 | pub mod rpc; 3 | pub mod types; 4 | 5 | mod consensus_client; 6 | pub use crate::consensus::consensus_client::*; 7 | 8 | mod constants; 9 | mod utils; 10 | -------------------------------------------------------------------------------- /crates/light-client/src/consensus/rpc/mock_rpc.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::read_to_string, path::PathBuf}; 2 | 3 | use alloy::primitives::B256; 4 | use anyhow::Result; 5 | use async_trait::async_trait; 6 | 7 | use super::ConsensusRpc; 8 | use crate::consensus::types::{ 9 | LightClientBootstrapElectra, LightClientFinalityUpdateElectra, 10 | LightClientOptimisticUpdateElectra, LightClientUpdateElectra, 11 | }; 12 | 13 | #[derive(Clone, Debug)] 14 | pub struct MockRpc { 15 | testdata: PathBuf, 16 | } 17 | 18 | #[async_trait] 19 | impl ConsensusRpc for MockRpc { 20 | fn new(path: &str) -> Self { 21 | MockRpc { 22 | testdata: PathBuf::from(path), 23 | } 24 | } 25 | 26 | async fn get_bootstrap(&self, _block_root: B256) -> Result { 27 | let bootstrap = read_to_string(self.testdata.join("bootstrap.json"))?; 28 | Ok(serde_json::from_str(&bootstrap)?) 29 | } 30 | 31 | async fn get_updates(&self, _period: u64, _count: u8) -> Result> { 32 | let updates = read_to_string(self.testdata.join("updates.json"))?; 33 | Ok(serde_json::from_str(&updates)?) 34 | } 35 | 36 | async fn get_finality_update(&self) -> Result { 37 | let finality = read_to_string(self.testdata.join("finality.json"))?; 38 | Ok(serde_json::from_str(&finality)?) 39 | } 40 | 41 | async fn get_optimistic_update(&self) -> Result { 42 | let optimistic = read_to_string(self.testdata.join("optimistic.json"))?; 43 | Ok(serde_json::from_str(&optimistic)?) 44 | } 45 | 46 | async fn chain_id(&self) -> Result { 47 | anyhow::bail!("not implemented") 48 | } 49 | 50 | fn name(&self) -> String { 51 | "mock".to_string() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/light-client/src/consensus/rpc/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mock_rpc; 2 | pub mod nimbus_rpc; 3 | pub mod portal_rpc; 4 | 5 | use alloy::primitives::B256; 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use super::types::{ 10 | LightClientBootstrapElectra, LightClientFinalityUpdateElectra, 11 | LightClientOptimisticUpdateElectra, LightClientUpdateElectra, 12 | }; 13 | 14 | // implements https://github.com/ethereum/beacon-APIs/tree/master/apis/beacon/light_client 15 | #[async_trait] 16 | pub trait ConsensusRpc: Send + Sync + Clone { 17 | fn new(path: &str) -> Self; 18 | async fn get_bootstrap(&self, block_root: B256) -> Result; 19 | async fn get_updates(&self, period: u64, count: u8) -> Result>; 20 | async fn get_finality_update(&self) -> Result; 21 | async fn get_optimistic_update(&self) -> Result; 22 | async fn chain_id(&self) -> Result; 23 | fn name(&self) -> String; 24 | } 25 | -------------------------------------------------------------------------------- /crates/light-client/src/database.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, path::PathBuf}; 2 | 3 | use alloy::primitives::B256; 4 | use anyhow::Result; 5 | 6 | use crate::config::client_config::Config; 7 | 8 | pub trait Database { 9 | fn new(config: &Config) -> Result 10 | where 11 | Self: Sized; 12 | fn save_checkpoint(&self, checkpoint: B256) -> Result<()>; 13 | fn load_checkpoint(&self) -> Result; 14 | } 15 | 16 | #[derive(Clone)] 17 | pub struct FileDB { 18 | data_dir: PathBuf, 19 | default_checkpoint: B256, 20 | } 21 | 22 | impl Database for FileDB { 23 | fn new(config: &Config) -> Result { 24 | if let Some(data_dir) = &config.data_dir { 25 | return Ok(FileDB { 26 | data_dir: data_dir.to_path_buf(), 27 | default_checkpoint: config.default_checkpoint, 28 | }); 29 | } 30 | 31 | anyhow::bail!("data dir not in config") 32 | } 33 | 34 | fn save_checkpoint(&self, checkpoint: B256) -> Result<()> { 35 | fs::create_dir_all(&self.data_dir)?; 36 | fs::write(self.data_dir.join("checkpoint"), checkpoint.as_slice())?; 37 | Ok(()) 38 | } 39 | 40 | fn load_checkpoint(&self) -> Result { 41 | let Ok(bytes) = fs::read(self.data_dir.join("checkpoint")) else { 42 | return Ok(self.default_checkpoint); 43 | }; 44 | Ok(B256::try_from(bytes.as_slice()).unwrap_or(self.default_checkpoint)) 45 | } 46 | } 47 | 48 | pub struct ConfigDB { 49 | checkpoint: B256, 50 | } 51 | 52 | impl Database for ConfigDB { 53 | fn new(config: &Config) -> Result { 54 | Ok(Self { 55 | checkpoint: config.checkpoint.unwrap_or(config.default_checkpoint), 56 | }) 57 | } 58 | 59 | fn load_checkpoint(&self) -> Result { 60 | Ok(self.checkpoint) 61 | } 62 | 63 | fn save_checkpoint(&self, _checkpoint: B256) -> Result<()> { 64 | Ok(()) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /crates/light-client/src/errors.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error as AnyhowError; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Error)] 5 | #[error("rpc error on method: {method}, message: {error}")] 6 | pub struct RpcError { 7 | method: String, 8 | error: E, 9 | } 10 | 11 | impl RpcError { 12 | pub fn new(method: &str, err: E) -> Self { 13 | Self { 14 | method: method.to_string(), 15 | error: err, 16 | } 17 | } 18 | } 19 | 20 | /// Errors that can occur during Node calls 21 | #[derive(Debug, Error)] 22 | pub enum NodeError { 23 | #[error("out of sync: {0} slots behind")] 24 | OutOfSync(u64), 25 | 26 | #[error("consensus client creation error: {0}")] 27 | ConsensusClientCreationError(AnyhowError), 28 | 29 | #[error("consensus advance error: {0}")] 30 | ConsensusAdvanceError(AnyhowError), 31 | 32 | #[error("consensus sync error: {0}")] 33 | ConsensusSyncError(AnyhowError), 34 | } 35 | -------------------------------------------------------------------------------- /crates/light-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::uninlined_format_args)] 2 | #![warn(clippy::unwrap_used)] 3 | mod client; 4 | pub use crate::client::*; 5 | 6 | pub mod config; 7 | pub mod consensus; 8 | pub mod database; 9 | pub mod errors; 10 | pub mod node; 11 | pub mod rpc; 12 | pub mod utils; 13 | pub mod watch; 14 | -------------------------------------------------------------------------------- /crates/light-client/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use alloy::primitives::{b256, B256}; 4 | use anyhow::Result; 5 | use light_client::{ 6 | config::networks, consensus::rpc::nimbus_rpc::NimbusRpc, database::FileDB, Client, 7 | ClientBuilder, 8 | }; 9 | use tracing::info; 10 | 11 | const CONSENSUS_RPC_URL: &str = "http://testing.mainnet.beacon-api.nimbus.team"; 12 | const TRUSTED_CHECKPOINT: B256 = 13 | b256!("0x9c30624f15e4df4e8f819f89db8b930f36b561b7f70905688ea208d22fb0b822"); 14 | const FALLBACK_URL: &str = "https://sync-mainnet.beaconcha.in"; 15 | 16 | #[tokio::main] 17 | async fn main() -> Result<()> { 18 | tracing_subscriber::fmt::init(); 19 | 20 | // Create a new Client Builder 21 | let mut builder = ClientBuilder::new(); 22 | 23 | // Set the network to mainnet 24 | builder = builder.network(networks::Network::Mainnet); 25 | 26 | // Set the consensus rpc url 27 | builder = builder.consensus_rpc(CONSENSUS_RPC_URL); 28 | 29 | // Set the checkpoint to the last known checkpoint 30 | builder = builder.checkpoint(TRUSTED_CHECKPOINT); 31 | 32 | // Set the rpc port 33 | builder = builder.rpc_port(8544); 34 | 35 | // Set the data dir 36 | builder = builder.data_dir(PathBuf::from("/tmp/light-client")); 37 | 38 | // Set the fallback service 39 | builder = builder.fallback(FALLBACK_URL); 40 | 41 | // Enable lazy checkpoints 42 | builder = builder.load_external_fallback(); 43 | 44 | // Build the client 45 | let mut client: Client = builder.build().unwrap(); 46 | 47 | info!("Starting consensus light client..."); 48 | // Run the client 49 | client.start().await.unwrap(); 50 | 51 | tokio::signal::ctrl_c() 52 | .await 53 | .expect("failed to pause until ctrl-c"); 54 | 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /crates/light-client/src/utils.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{FixedBytes, B256}; 2 | use tree_hash::TreeHash; 3 | use tree_hash_derive::TreeHash; 4 | 5 | pub fn u64_to_hex_string(val: u64) -> String { 6 | format!("0x{val:x}") 7 | } 8 | 9 | #[derive(Default, Debug, TreeHash)] 10 | struct ForkData { 11 | current_version: FixedBytes<4>, 12 | genesis_validator_root: B256, 13 | } 14 | 15 | pub fn compute_fork_data_root( 16 | current_version: FixedBytes<4>, 17 | genesis_validator_root: B256, 18 | ) -> B256 { 19 | let fork_data = ForkData { 20 | current_version, 21 | genesis_validator_root, 22 | }; 23 | fork_data.tree_hash_root() 24 | } 25 | -------------------------------------------------------------------------------- /crates/light-client/tests/sync.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use alloy::primitives::b256; 4 | use light_client::{ 5 | config::{client_config::Config, networks}, 6 | consensus::{rpc::mock_rpc::MockRpc, ConsensusLightClient}, 7 | }; 8 | 9 | async fn setup() -> ConsensusLightClient { 10 | let base_config = networks::mainnet(); 11 | let config = Config { 12 | consensus_rpc: String::new(), 13 | chain: base_config.chain, 14 | forks: base_config.forks, 15 | max_checkpoint_age: 123123123, 16 | ..Default::default() 17 | }; 18 | 19 | let checkpoint = b256!("c62aa0de55e6f21230fa63713715e1a6c13e73005e89f6389da271955d819bde"); 20 | 21 | ConsensusLightClient::new("testdata/", checkpoint, Arc::new(config)).unwrap() 22 | } 23 | 24 | #[tokio::test] 25 | #[ignore = "Missing Pectra test vectors"] 26 | async fn test_sync() { 27 | let mut client = setup().await; 28 | client.sync().await.unwrap(); 29 | 30 | let head = client.get_header(); 31 | assert_eq!(head.slot, 7358726); 32 | 33 | let finalized_head = client.get_finalized_header(); 34 | assert_eq!(finalized_head.slot, 7358656); 35 | } 36 | -------------------------------------------------------------------------------- /crates/metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-metrics" 3 | description = "Trin metrics" 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | anyhow.workspace = true 16 | ethportal-api.workspace = true 17 | lazy_static.workspace = true 18 | prometheus_exporter.workspace = true 19 | -------------------------------------------------------------------------------- /crates/metrics/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | #![warn(clippy::uninlined_format_args)] 3 | 4 | pub mod bridge; 5 | pub mod chain_head; 6 | pub mod labels; 7 | pub mod overlay; 8 | pub mod portalnet; 9 | pub mod storage; 10 | pub mod timer; 11 | -------------------------------------------------------------------------------- /crates/metrics/src/portalnet.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use prometheus_exporter::prometheus::default_registry; 3 | 4 | use crate::{ 5 | bridge::BridgeMetrics, chain_head::ChainHeadMetrics, overlay::OverlayMetrics, 6 | storage::StorageMetrics, 7 | }; 8 | 9 | // We use lazy_static to ensure that the metrics registry is initialized only once, for each 10 | // runtime. This is important because the registry is a global singleton, and if it is 11 | // initialized more than once, it will panic when trying to register the same metric for each 12 | // subnetwork. 13 | lazy_static! { 14 | pub static ref PORTALNET_METRICS: PortalnetMetrics = initialize_metrics_registry(); 15 | } 16 | 17 | fn initialize_metrics_registry() -> PortalnetMetrics { 18 | PortalnetMetrics::new().expect("failed to initialize metrics") 19 | } 20 | 21 | pub struct PortalnetMetrics { 22 | bridge: BridgeMetrics, 23 | chain_head: ChainHeadMetrics, 24 | overlay: OverlayMetrics, 25 | storage: StorageMetrics, 26 | } 27 | 28 | impl PortalnetMetrics { 29 | pub fn new() -> anyhow::Result { 30 | let registry = default_registry(); 31 | let bridge = BridgeMetrics::new(registry)?; 32 | let chain_head = ChainHeadMetrics::new(registry)?; 33 | let overlay = OverlayMetrics::new(registry)?; 34 | let storage = StorageMetrics::new(registry)?; 35 | Ok(Self { 36 | bridge, 37 | chain_head, 38 | overlay, 39 | storage, 40 | }) 41 | } 42 | 43 | pub fn bridge(&self) -> BridgeMetrics { 44 | self.bridge.clone() 45 | } 46 | 47 | pub fn chain_head(&self) -> ChainHeadMetrics { 48 | self.chain_head.clone() 49 | } 50 | 51 | pub fn overlay(&self) -> OverlayMetrics { 52 | self.overlay.clone() 53 | } 54 | 55 | pub fn storage(&self) -> StorageMetrics { 56 | self.storage.clone() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crates/portalnet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "portalnet" 3 | description = "Core library for Trin." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | anyhow.workspace = true 17 | async-trait.workspace = true 18 | bytes.workspace = true 19 | crossbeam-channel = "0.5.13" 20 | delay_map.workspace = true 21 | discv5.workspace = true 22 | ethereum_ssz.workspace = true 23 | ethportal-api.workspace = true 24 | fnv = "1.0.7" 25 | futures.workspace = true 26 | hex.workspace = true 27 | igd-next = "0.16" 28 | itertools.workspace = true 29 | lazy_static.workspace = true 30 | leb128 = "0.2.1" 31 | local-ip-address = "0.6" 32 | lru.workspace = true 33 | parking_lot.workspace = true 34 | rand.workspace = true 35 | serde.workspace = true 36 | smallvec = "1.8.0" 37 | ssz_types.workspace = true 38 | stunclient = "0.4.1" 39 | thiserror.workspace = true 40 | tokio.workspace = true 41 | tokio-stream = { version = "0.1.14", features = ["sync"] } 42 | tracing.workspace = true 43 | trin-metrics.workspace = true 44 | trin-storage.workspace = true 45 | trin-utils.workspace = true 46 | trin-validation.workspace = true 47 | utp-rs.workspace = true 48 | 49 | [target.'cfg(windows)'.dependencies] 50 | uds_windows.workspace = true 51 | 52 | [dev-dependencies] 53 | env_logger.workspace = true 54 | quickcheck.workspace = true 55 | rstest.workspace = true 56 | serial_test.workspace = true 57 | tempfile.workspace = true 58 | test-log.workspace = true 59 | tokio-test.workspace = true 60 | tracing-subscriber.workspace = true 61 | -------------------------------------------------------------------------------- /crates/portalnet/README.md: -------------------------------------------------------------------------------- 1 | # Portalnet 2 | An implementation of the base layer of the Portal Network. See trin-history for an example overlay network in practice, storing chain history. 3 | -------------------------------------------------------------------------------- /crates/portalnet/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use alloy::primitives::B256; 4 | use ethportal_api::types::{enr::Enr, network::Network}; 5 | 6 | use crate::{bootnodes::Bootnodes, constants::DEFAULT_UTP_TRANSFER_LIMIT}; 7 | 8 | /// Discv5 session cache capacity. 9 | /// Provides capacity for 1000 nodes, to match Discv5's default session_cache_capacity value. 10 | pub const DISCV5_SESSION_CACHE_CAPACITY: usize = 1000; 11 | 12 | #[derive(Clone)] 13 | pub struct PortalnetConfig { 14 | pub external_addr: Option, 15 | pub private_key: B256, 16 | pub listen_port: u16, 17 | pub bootnodes: Vec, 18 | pub no_stun: bool, 19 | pub no_upnp: bool, 20 | pub discv5_session_cache_capacity: usize, 21 | pub disable_poke: bool, 22 | pub trusted_block_root: Option, 23 | /// the max number of concurrent utp transfers 24 | pub utp_transfer_limit: usize, 25 | } 26 | 27 | // to be used inside test code only 28 | impl Default for PortalnetConfig { 29 | fn default() -> Self { 30 | Self { 31 | external_addr: None, 32 | private_key: B256::random(), 33 | listen_port: 4242, 34 | bootnodes: Bootnodes::default().to_enrs(Network::Mainnet), 35 | no_stun: false, 36 | no_upnp: false, 37 | discv5_session_cache_capacity: DISCV5_SESSION_CACHE_CAPACITY, 38 | disable_poke: false, 39 | trusted_block_root: None, 40 | utp_transfer_limit: DEFAULT_UTP_TRANSFER_LIMIT, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/portalnet/src/constants.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | /// The default timeout for a query. 4 | /// 5 | /// A "query" here refers to the whole process for finding a single piece of Portal content, across 6 | /// all peer interactions. A single RPC request may spawn many queries. Each query will typically 7 | /// spawn many requests to peers. 8 | pub const DEFAULT_QUERY_TIMEOUT: Duration = Duration::from_secs(60); 9 | 10 | pub const DEFAULT_WEB3_IPC_PATH: &str = "/tmp/trin-jsonrpc.ipc"; 11 | pub const DEFAULT_WEB3_HTTP_ADDRESS: &str = "http://127.0.0.1:8545/"; 12 | pub const DEFAULT_WEB3_HTTP_PORT: u16 = 8545; 13 | pub const DEFAULT_WEB3_WS_PORT: u16 = 8546; 14 | pub const DEFAULT_DISCOVERY_PORT: u16 = 9009; 15 | pub const DEFAULT_UTP_TRANSFER_LIMIT: usize = 50; 16 | pub const DEFAULT_NETWORK: &str = "mainnet"; 17 | -------------------------------------------------------------------------------- /crates/portalnet/src/find/iterators/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod findcontent; 2 | pub mod findnodes; 3 | pub mod query; 4 | -------------------------------------------------------------------------------- /crates/portalnet/src/find/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod iterators; 2 | pub mod query_info; 3 | pub mod query_pool; 4 | -------------------------------------------------------------------------------- /crates/portalnet/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | #![warn(clippy::uninlined_format_args)] 3 | 4 | pub mod accept_queue; 5 | pub mod bootnodes; 6 | pub mod config; 7 | pub mod constants; 8 | pub mod discovery; 9 | pub mod events; 10 | pub mod find; 11 | pub mod gossip; 12 | pub mod overlay; 13 | pub mod put_content; 14 | pub mod socket; 15 | pub mod types; 16 | pub mod utils; 17 | pub mod utp; 18 | -------------------------------------------------------------------------------- /crates/portalnet/src/overlay/command.rs: -------------------------------------------------------------------------------- 1 | use discv5::enr::NodeId; 2 | use ethportal_api::types::enr::Enr; 3 | use futures::channel::oneshot; 4 | use tokio::sync::broadcast; 5 | 6 | use super::{config::FindContentConfig, request::OverlayRequest}; 7 | use crate::{events::EventEnvelope, find::query_info::RecursiveFindContentResult}; 8 | 9 | /// A network-based action that the overlay may perform. 10 | /// 11 | /// The overlay performs network-based actions on behalf of the command issuer. The issuer may be 12 | /// the overlay itself. The overlay manages network requests and responses and sends the result 13 | /// back to the issuer upon completion. 14 | #[derive(Debug)] 15 | pub enum OverlayCommand { 16 | /// Send a single portal request through the overlay. 17 | /// 18 | /// A `Request` corresponds to a single request message defined in the portal wire spec. 19 | Request(OverlayRequest), 20 | /// Perform a find content query through the overlay. 21 | /// 22 | /// A `FindContentQuery` issues multiple requests to find the content identified by `target`. 23 | /// The result is sent to the issuer over `callback`. 24 | FindContentQuery { 25 | /// The query target. 26 | target: TContentKey, 27 | /// A callback channel to transmit the result of the query. 28 | callback: oneshot::Sender, 29 | /// The configuration for the query. 30 | config: FindContentConfig, 31 | }, 32 | FindNodeQuery { 33 | /// The query target. 34 | target: NodeId, 35 | /// A callback channel to transmit the result of the query. 36 | callback: oneshot::Sender>, 37 | }, 38 | /// Sets up an event stream where the overlay server will return various events. 39 | RequestEventStream(oneshot::Sender>), 40 | /// Handle an event sent from another overlay. 41 | Event(EventEnvelope), 42 | } 43 | -------------------------------------------------------------------------------- /crates/portalnet/src/overlay/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod command; 2 | pub mod config; 3 | pub mod errors; 4 | pub mod ping_extensions; 5 | pub mod protocol; 6 | pub mod request; 7 | pub mod service; 8 | -------------------------------------------------------------------------------- /crates/portalnet/src/overlay/ping_extensions.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::types::ping_extensions::extension_types::PingExtensionType; 2 | 3 | pub trait PingExtensions { 4 | /// Returns true if the extension is supported by the clients subnetwork. 5 | fn is_supported(&self, extension: PingExtensionType) -> bool { 6 | self.supported_extensions().contains(&extension) 7 | } 8 | 9 | /// Returns the newest extension that is supported by both clients, used for extended ping 10 | /// responses. 11 | fn latest_mutually_supported_base_extension( 12 | &self, 13 | extensions: &[PingExtensionType], 14 | ) -> Option; 15 | 16 | /// Returns the extensions that are supported by the clients subnetwork. 17 | fn supported_extensions(&self) -> &[PingExtensionType]; 18 | } 19 | 20 | pub struct MockPingExtension; 21 | 22 | impl MockPingExtension { 23 | pub const SUPPORTED_EXTENSIONS: &[PingExtensionType] = &[ 24 | PingExtensionType::Capabilities, 25 | PingExtensionType::BasicRadius, 26 | ]; 27 | 28 | /// Base extensions that are required for the core subnetwork to function. 29 | /// These must be sorted by latest to oldest 30 | pub const BASE_EXTENSIONS: &[PingExtensionType] = &[PingExtensionType::BasicRadius]; 31 | } 32 | 33 | impl PingExtensions for MockPingExtension { 34 | fn latest_mutually_supported_base_extension( 35 | &self, 36 | extensions: &[PingExtensionType], 37 | ) -> Option { 38 | for base_extension in Self::BASE_EXTENSIONS { 39 | if extensions.contains(base_extension) { 40 | return Some(*base_extension); 41 | } 42 | } 43 | None 44 | } 45 | 46 | fn supported_extensions(&self) -> &[PingExtensionType] { 47 | Self::SUPPORTED_EXTENSIONS 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/portalnet/src/overlay/service/ping/handlers.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::types::ping_extensions::extensions::{ 2 | type_0::ClientInfoRadiusCapabilities, type_1::BasicRadius, type_2::HistoryRadius, 3 | }; 4 | 5 | use crate::types::node::Node; 6 | 7 | pub fn handle_capabilities( 8 | radius_capabilities: ClientInfoRadiusCapabilities, 9 | mut node: Node, 10 | ) -> Option { 11 | let capabilities = radius_capabilities.capabilities; 12 | if node.data_radius != radius_capabilities.data_radius 13 | || node.is_capabilities_different(&capabilities) 14 | { 15 | node.set_data_radius(radius_capabilities.data_radius); 16 | node.set_capabilities(capabilities.to_vec()); 17 | return Some(node); 18 | } 19 | None 20 | } 21 | 22 | pub fn handle_basic_radius(basic_radius: BasicRadius, mut node: Node) -> Option { 23 | let data_radius = basic_radius.data_radius; 24 | if node.data_radius != data_radius { 25 | node.set_data_radius(data_radius); 26 | return Some(node); 27 | } 28 | None 29 | } 30 | 31 | pub fn handle_history_radius(history_radius: HistoryRadius, mut node: Node) -> Option { 32 | let data_radius = history_radius.data_radius; 33 | let ephemeral_header_count = history_radius.ephemeral_header_count; 34 | if node.data_radius != data_radius 35 | || node.ephemeral_header_count != Some(ephemeral_header_count) 36 | { 37 | node.set_data_radius(data_radius); 38 | node.set_ephemeral_header_count(ephemeral_header_count); 39 | return Some(node); 40 | } 41 | None 42 | } 43 | -------------------------------------------------------------------------------- /crates/portalnet/src/overlay/service/utils.rs: -------------------------------------------------------------------------------- 1 | use ssz::Encode; 2 | 3 | /// Limits the elements count to a maximum packet size, including the discv5 header overhead. 4 | pub fn pop_while_ssz_bytes_len_gt( 5 | elements: &mut Vec, 6 | max_size: usize, 7 | ) { 8 | while elements.ssz_bytes_len() > max_size { 9 | elements.pop(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /crates/portalnet/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod kbucket; 2 | pub mod node; 3 | -------------------------------------------------------------------------------- /crates/portalnet/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod db; 2 | pub mod portal_wire; 3 | -------------------------------------------------------------------------------- /crates/portalnet/src/utp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod controller; 2 | pub mod timed_semaphore; 3 | -------------------------------------------------------------------------------- /crates/portalnet/src/utp/timed_semaphore.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::OwnedSemaphorePermit; 2 | use trin_metrics::timer::DiscardOnDropHistogramTimer; 3 | 4 | /// A owned semaphore which records the time it has been alive from initialization to when drop() is 5 | /// called. 6 | #[derive(Debug)] 7 | pub struct OwnedTimedSemaphorePermit { 8 | pub permit: OwnedSemaphorePermit, 9 | pub histogram_timer: DiscardOnDropHistogramTimer, 10 | } 11 | 12 | impl OwnedTimedSemaphorePermit { 13 | pub fn drop(self) { 14 | self.histogram_timer.stop_and_record(); 15 | drop(self.permit); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/rpc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rpc" 3 | description = "Implementations of jsonrpsee server API traits for Trin and server interface" 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy = { workspace = true, features = ["rpc-types-eth", "provider-ipc", "provider-ws", "pubsub", "reqwest"] } 16 | discv5.workspace = true 17 | eth_trie.workspace = true 18 | ethportal-api.workspace = true 19 | http = "1.1.0" 20 | portalnet.workspace = true 21 | reth-ipc.workspace = true 22 | revm.workspace = true 23 | serde.workspace = true 24 | serde_json.workspace = true 25 | strum.workspace = true 26 | thiserror.workspace = true 27 | tokio.workspace = true 28 | tower = { version = "0.4", features = ["full"] } 29 | tower-http = { version = "0.6", features = ["full"] } 30 | tracing.workspace = true 31 | trin-evm.workspace = true 32 | trin-utils.workspace = true 33 | trin-validation.workspace = true 34 | -------------------------------------------------------------------------------- /crates/rpc/README.md: -------------------------------------------------------------------------------- 1 | # rpc 2 | 3 | This crate contains implementations of `ethportal-api` jsonrpsee server API traits in Trin and interface for running the JSON-RPC server. 4 | -------------------------------------------------------------------------------- /crates/rpc/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use alloy::transports::http::reqwest::Url; 4 | use trin_utils::cli::Web3TransportType; 5 | 6 | /// Configuration for the RPC server. 7 | #[derive(Clone)] 8 | pub struct RpcConfig { 9 | pub web3_transport: Web3TransportType, 10 | pub web3_ipc_path: PathBuf, 11 | pub web3_http_address: Url, 12 | pub ws: bool, 13 | pub ws_port: u16, 14 | } 15 | -------------------------------------------------------------------------------- /crates/rpc/src/cors.rs: -------------------------------------------------------------------------------- 1 | use http::{HeaderValue, Method}; 2 | use tower_http::cors::{AllowOrigin, Any, CorsLayer}; 3 | 4 | /// Error thrown when parsing cors domains went wrong 5 | #[derive(Debug, thiserror::Error)] 6 | pub(crate) enum CorsDomainError { 7 | #[error("{domain} is an invalid header value")] 8 | InvalidHeader { domain: String }, 9 | #[error("Wildcard origin (`*`) cannot be passed as part of a list: {input}")] 10 | WildCardNotAllowed { input: String }, 11 | } 12 | 13 | /// Creates a [CorsLayer] from the given domains 14 | pub(crate) fn create_cors_layer(http_cors_domains: &str) -> Result { 15 | let cors = match http_cors_domains.trim() { 16 | "*" => CorsLayer::new() 17 | .allow_methods([Method::GET, Method::POST]) 18 | .allow_origin(Any) 19 | .allow_headers(Any), 20 | _ => { 21 | let iter = http_cors_domains.split(','); 22 | if iter.clone().any(|o| o == "*") { 23 | return Err(CorsDomainError::WildCardNotAllowed { 24 | input: http_cors_domains.to_string(), 25 | }); 26 | } 27 | 28 | let origins = iter 29 | .map(|domain| { 30 | domain 31 | .parse::() 32 | .map_err(|_| CorsDomainError::InvalidHeader { 33 | domain: domain.to_string(), 34 | }) 35 | }) 36 | .collect::, _>>()?; 37 | 38 | let origin = AllowOrigin::list(origins); 39 | CorsLayer::new() 40 | .allow_methods([Method::GET, Method::POST]) 41 | .allow_origin(origin) 42 | .allow_headers(Any) 43 | } 44 | }; 45 | Ok(cors) 46 | } 47 | -------------------------------------------------------------------------------- /crates/rpc/src/fetch.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::types::jsonrpc::{endpoints::SubnetworkEndpoint, request::JsonRpcRequest}; 2 | use serde_json::Value; 3 | use tokio::sync::mpsc; 4 | 5 | use crate::{ 6 | errors::{ContentNotFoundJsonError, RpcServeError}, 7 | serde::from_value, 8 | }; 9 | 10 | /// Fetch and deserialize data from Portal subnetwork. 11 | pub async fn proxy_to_subnet( 12 | network: &mpsc::UnboundedSender>, 13 | endpoint: TEndpoint, 14 | ) -> Result 15 | where 16 | TEndpoint: SubnetworkEndpoint + Clone, 17 | TOutput: serde::de::DeserializeOwned, 18 | { 19 | let (resp_tx, mut resp_rx) = mpsc::unbounded_channel::>(); 20 | let message = JsonRpcRequest { 21 | endpoint, 22 | resp: resp_tx, 23 | }; 24 | let _ = network.send(message); 25 | 26 | let Some(response) = resp_rx.recv().await else { 27 | return Err(RpcServeError::Message(format!( 28 | "Internal error: No response from {} subnetwork", 29 | TEndpoint::subnetwork() 30 | ))); 31 | }; 32 | 33 | match response { 34 | Ok(result) => from_value(result), 35 | Err(msg) => { 36 | if msg.contains("Unable to locate content on the network") { 37 | if let Ok(err) = serde_json::from_str::(&msg) { 38 | return Err(err.into()); 39 | } 40 | } 41 | 42 | Err(RpcServeError::Message(msg)) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/rpc/src/serde.rs: -------------------------------------------------------------------------------- 1 | use serde_json::{from_value as serde_json_from_value, Value}; 2 | 3 | use crate::errors::RpcServeError; 4 | 5 | // Required for the ContentNotFound error type 6 | #[allow(clippy::result_large_err)] 7 | pub fn from_value(value: Value) -> Result { 8 | serde_json_from_value(value).map_err(|e| RpcServeError::Message(e.to_string())) 9 | } 10 | -------------------------------------------------------------------------------- /crates/rpc/src/web3_rpc.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::{version::get_trin_version, Web3ApiServer}; 2 | 3 | use crate::jsonrpsee::core::{async_trait, RpcResult}; 4 | 5 | pub struct Web3Api; 6 | 7 | impl Web3Api { 8 | pub fn new() -> Self { 9 | Self 10 | } 11 | } 12 | 13 | impl Default for Web3Api { 14 | fn default() -> Self { 15 | Self::new() 16 | } 17 | } 18 | 19 | #[async_trait] 20 | impl Web3ApiServer for Web3Api { 21 | async fn client_version(&self) -> RpcResult { 22 | let trin_version = get_trin_version(); 23 | Ok(format!("trin v{trin_version}")) 24 | } 25 | } 26 | 27 | impl std::fmt::Debug for Web3Api { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | f.debug_struct("Web3Api").finish_non_exhaustive() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/storage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-storage" 3 | description = "Storage library for Trin." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | discv5.workspace = true 17 | ethportal-api.workspace = true 18 | r2d2.workspace = true 19 | r2d2_sqlite.workspace = true 20 | rand.workspace = true 21 | rusqlite.workspace = true 22 | strum.workspace = true 23 | tempfile.workspace = true 24 | thiserror.workspace = true 25 | tracing.workspace = true 26 | trin-metrics.workspace = true 27 | 28 | [dev-dependencies] 29 | anyhow.workspace = true 30 | quickcheck.workspace = true 31 | rstest.workspace = true 32 | -------------------------------------------------------------------------------- /crates/storage/src/error.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::{ 2 | types::{content_key::error::ContentKeyError, distance::Distance}, 3 | utils::bytes::ByteUtilsError, 4 | ContentValueError, 5 | }; 6 | use thiserror::Error; 7 | 8 | use crate::versioned::{ContentType, StoreVersion}; 9 | 10 | /// An error from an operation on a `ContentStore`. 11 | #[derive(Debug, Error)] 12 | pub enum ContentStoreError { 13 | #[error("An error from the underlying database: {0:?}")] 14 | Database(String), 15 | 16 | #[error("IO error: {0:?}")] 17 | Io(#[from] std::io::Error), 18 | 19 | /// Unable to store content because it does not fall within the store's radius. 20 | #[error("radius {radius} insufficient to store content at distance {distance}")] 21 | InsufficientRadius { 22 | radius: Distance, 23 | distance: Distance, 24 | }, 25 | 26 | /// Unable to store or retrieve data because it is invalid. 27 | #[error("data invalid {message}")] 28 | InvalidData { message: String }, 29 | 30 | #[error("rusqlite error {0}")] 31 | Rusqlite(#[from] rusqlite::Error), 32 | 33 | #[error("r2d2 error {0}")] 34 | R2D2(#[from] r2d2::Error), 35 | 36 | #[error("unable to use byte utils {0}")] 37 | ByteUtilsError(#[from] ByteUtilsError), 38 | 39 | #[error("unable to use content key {0}")] 40 | ContentKey(#[from] ContentKeyError), 41 | 42 | #[error("unable to use content value {0}")] 43 | ContentValue(#[from] ContentValueError), 44 | 45 | #[error("Invalid store version '{version}' for table '{content_type}'")] 46 | InvalidStoreVersion { 47 | content_type: ContentType, 48 | version: StoreVersion, 49 | }, 50 | 51 | #[error("Store migration from {old_version} to {new_version} is not supported")] 52 | UnsupportedStoreMigration { 53 | old_version: StoreVersion, 54 | new_version: StoreVersion, 55 | }, 56 | } 57 | -------------------------------------------------------------------------------- /crates/storage/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | use discv5::enr::NodeId; 2 | use ethportal_api::types::{distance::Distance, network::Subnetwork}; 3 | use tempfile::TempDir; 4 | 5 | use crate::{ 6 | config::StorageCapacityConfig, error::ContentStoreError, PortalStorageConfig, 7 | PortalStorageConfigFactory, 8 | }; 9 | 10 | /// Creates temporary directory and PortalStorageConfig. 11 | pub fn create_test_portal_storage_config_with_capacity( 12 | capacity_mb: u32, 13 | ) -> Result<(TempDir, PortalStorageConfig), ContentStoreError> { 14 | let temp_dir = TempDir::new()?; 15 | let config = PortalStorageConfigFactory::new( 16 | StorageCapacityConfig::Combined { 17 | total_mb: capacity_mb, 18 | subnetworks: vec![Subnetwork::History], 19 | }, 20 | NodeId::random(), 21 | temp_dir.path().to_path_buf(), 22 | ) 23 | .unwrap() 24 | .create(&Subnetwork::History, Distance::MAX) 25 | .unwrap(); 26 | Ok((temp_dir, config)) 27 | } 28 | 29 | pub fn generate_random_bytes(length: usize) -> Vec { 30 | (0..length).map(|_| rand::random::()).collect() 31 | } 32 | -------------------------------------------------------------------------------- /crates/storage/src/versioned/ephemeral_v1/config.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use ethportal_api::types::network::Subnetwork; 4 | use r2d2::Pool; 5 | use r2d2_sqlite::SqliteConnectionManager; 6 | 7 | use crate::{versioned::ContentType, PortalStorageConfig}; 8 | 9 | /// The config for the EphemeralV1Store 10 | #[derive(Clone, Debug)] 11 | pub struct EphemeralV1StoreConfig { 12 | pub content_type: ContentType, 13 | pub subnetwork: Subnetwork, 14 | pub node_data_dir: PathBuf, 15 | pub sql_connection_pool: Pool, 16 | } 17 | 18 | #[allow(unused)] 19 | impl EphemeralV1StoreConfig { 20 | pub fn new( 21 | content_type: ContentType, 22 | subnetwork: Subnetwork, 23 | config: PortalStorageConfig, 24 | ) -> Self { 25 | Self { 26 | content_type, 27 | subnetwork, 28 | node_data_dir: config.node_data_dir, 29 | sql_connection_pool: config.sql_connection_pool, 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/storage/src/versioned/ephemeral_v1/mod.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | pub(super) mod sql; 3 | mod store; 4 | 5 | pub use config::EphemeralV1StoreConfig; 6 | #[allow(unused)] 7 | pub use store::EphemeralV1Store; 8 | -------------------------------------------------------------------------------- /crates/storage/src/versioned/id_indexed_v1/config.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use discv5::enr::NodeId; 4 | use ethportal_api::types::{distance::Distance, network::Subnetwork}; 5 | use r2d2::Pool; 6 | use r2d2_sqlite::SqliteConnectionManager; 7 | 8 | use super::pruning_strategy::PruningConfig; 9 | use crate::{versioned::ContentType, PortalStorageConfig}; 10 | 11 | /// The config for the IdIndexedV1Store 12 | #[derive(Clone, Debug)] 13 | pub struct IdIndexedV1StoreConfig { 14 | pub content_type: ContentType, 15 | pub subnetwork: Subnetwork, 16 | pub node_id: NodeId, 17 | pub node_data_dir: PathBuf, 18 | pub storage_capacity_bytes: u64, 19 | pub sql_connection_pool: Pool, 20 | pub pruning_config: PruningConfig, 21 | pub max_radius: Distance, 22 | } 23 | 24 | impl IdIndexedV1StoreConfig { 25 | pub fn new( 26 | content_type: ContentType, 27 | subnetwork: Subnetwork, 28 | config: PortalStorageConfig, 29 | ) -> Self { 30 | Self { 31 | content_type, 32 | subnetwork, 33 | node_id: config.node_id, 34 | node_data_dir: config.node_data_dir, 35 | storage_capacity_bytes: config.storage_capacity_bytes, 36 | sql_connection_pool: config.sql_connection_pool, 37 | // consider making this a parameter if we start using non-default value 38 | pruning_config: PruningConfig::default(), 39 | max_radius: config.max_radius, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/storage/src/versioned/id_indexed_v1/mod.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | mod pruning_strategy; 3 | pub(super) mod sql; 4 | mod store; 5 | 6 | pub use config::IdIndexedV1StoreConfig; 7 | pub use store::IdIndexedV1Store; 8 | -------------------------------------------------------------------------------- /crates/storage/src/versioned/sql.rs: -------------------------------------------------------------------------------- 1 | // The store_info queries 2 | 3 | pub const STORE_INFO_CREATE_TABLE: &str = " 4 | CREATE TABLE IF NOT EXISTS store_info ( 5 | content_type TEXT PRIMARY KEY, 6 | version TEXT NOT NULL 7 | )"; 8 | 9 | pub const STORE_INFO_UPDATE: &str = " 10 | INSERT OR REPLACE INTO store_info (content_type, version) 11 | VALUES (:content_type, :version)"; 12 | 13 | pub const STORE_INFO_LOOKUP: &str = " 14 | SELECT version 15 | FROM store_info 16 | WHERE content_type = :content_type 17 | LIMIT 1"; 18 | 19 | // The table management queries 20 | 21 | pub const TABLE_EXISTS: &str = " 22 | SELECT name 23 | FROM sqlite_master 24 | WHERE type='table' AND name=:table_name"; 25 | -------------------------------------------------------------------------------- /crates/storage/src/versioned/store.rs: -------------------------------------------------------------------------------- 1 | use super::{ContentType, StoreVersion}; 2 | use crate::error::ContentStoreError; 3 | 4 | /// A trait for the versioned content store. Instance of it should be created using 5 | /// `create_store` function. 6 | pub trait VersionedContentStore: Sized { 7 | type Config; 8 | 9 | /// Returns the version of the store. 10 | fn version() -> StoreVersion; 11 | 12 | /// Migrates content from previous version to the new version. 13 | fn migrate_from( 14 | content_type: &ContentType, 15 | old_version: StoreVersion, 16 | config: &Self::Config, 17 | ) -> Result<(), ContentStoreError>; 18 | 19 | /// Creates the instance of the store. This shouldn't be used directly. Store should be 20 | /// created using `create_store` function. 21 | fn create(content_type: ContentType, config: Self::Config) -> Result; 22 | } 23 | -------------------------------------------------------------------------------- /crates/subnetworks/beacon/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-beacon" 3 | description = "Beacon network subprotocol for Trin." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | anyhow.workspace = true 17 | chrono.workspace = true 18 | discv5.workspace = true 19 | ethereum_ssz.workspace = true 20 | ethportal-api.workspace = true 21 | light-client.workspace = true 22 | parking_lot.workspace = true 23 | portalnet.workspace = true 24 | r2d2.workspace = true 25 | r2d2_sqlite.workspace = true 26 | rusqlite.workspace = true 27 | serde_json.workspace = true 28 | ssz_types.workspace = true 29 | tokio.workspace = true 30 | tracing.workspace = true 31 | tree_hash.workspace = true 32 | trin-metrics.workspace = true 33 | trin-storage.workspace = true 34 | trin-validation.workspace = true 35 | utp-rs.workspace = true 36 | 37 | [dev-dependencies] 38 | serde.workspace = true 39 | serde_yaml.workspace = true 40 | snap.workspace = true 41 | tempfile.workspace = true 42 | trin-utils.workspace = true 43 | -------------------------------------------------------------------------------- /crates/subnetworks/beacon/README.md: -------------------------------------------------------------------------------- 1 | # Beacon network subprotocol 2 | 3 | Please refer to the docs for more information. 4 | -------------------------------------------------------------------------------- /crates/subnetworks/beacon/src/assets/trusted_block_root.txt: -------------------------------------------------------------------------------- 1 | 0x4ffc29ffdf9979f6c81111c8b3a4a1a0535d623536ff47fdf5fa3bd15e5f4437 2 | -------------------------------------------------------------------------------- /crates/subnetworks/beacon/src/ping_extensions.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::types::ping_extensions::{ 2 | consts::BEACON_SUPPORTED_EXTENSIONS, extension_types::PingExtensionType, 3 | }; 4 | use portalnet::overlay::ping_extensions::PingExtensions; 5 | 6 | pub struct BeaconPingExtensions {} 7 | 8 | impl BeaconPingExtensions { 9 | pub const SUPPORTED_EXTENSIONS: &[PingExtensionType] = BEACON_SUPPORTED_EXTENSIONS; 10 | 11 | /// Base extensions that are required for the core subnetwork to function. 12 | /// These must be sorted by latest to oldest 13 | pub const BASE_EXTENSIONS: &[PingExtensionType] = &[PingExtensionType::BasicRadius]; 14 | } 15 | 16 | impl PingExtensions for BeaconPingExtensions { 17 | fn latest_mutually_supported_base_extension( 18 | &self, 19 | extensions: &[PingExtensionType], 20 | ) -> Option { 21 | for base_extension in Self::BASE_EXTENSIONS { 22 | if extensions.contains(base_extension) { 23 | return Some(*base_extension); 24 | } 25 | } 26 | None 27 | } 28 | 29 | fn supported_extensions(&self) -> &[PingExtensionType] { 30 | Self::SUPPORTED_EXTENSIONS 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/subnetworks/history/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-history" 3 | description = "History network subprotocol for Trin." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | anyhow.workspace = true 17 | discv5.workspace = true 18 | ethereum_ssz.workspace = true 19 | ethportal-api.workspace = true 20 | parking_lot.workspace = true 21 | portalnet.workspace = true 22 | rusqlite.workspace = true 23 | serde_json.workspace = true 24 | tokio.workspace = true 25 | tracing.workspace = true 26 | tree_hash.workspace = true 27 | trin-metrics.workspace = true 28 | trin-storage.workspace = true 29 | trin-validation.workspace = true 30 | utp-rs.workspace = true 31 | 32 | [dev-dependencies] 33 | env_logger.workspace = true 34 | quickcheck.workspace = true 35 | rand.workspace = true 36 | rstest.workspace = true 37 | serde.workspace = true 38 | serde_yaml.workspace = true 39 | serial_test.workspace = true 40 | ssz_types.workspace = true 41 | test-log.workspace = true 42 | tokio-test.workspace = true 43 | tracing-subscriber.workspace = true 44 | trin-utils.workspace = true 45 | -------------------------------------------------------------------------------- /crates/subnetworks/history/README.md: -------------------------------------------------------------------------------- 1 | # History network subprotocol 2 | 3 | Please refer to the docs for more information. 4 | -------------------------------------------------------------------------------- /crates/subnetworks/history/src/ping_extensions.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::types::ping_extensions::{ 2 | consts::HISTORY_SUPPORTED_EXTENSIONS, extension_types::PingExtensionType, 3 | }; 4 | use portalnet::overlay::ping_extensions::PingExtensions; 5 | 6 | pub struct HistoryPingExtensions {} 7 | 8 | impl HistoryPingExtensions { 9 | pub const SUPPORTED_EXTENSIONS: &[PingExtensionType] = HISTORY_SUPPORTED_EXTENSIONS; 10 | 11 | /// Base extensions that are required for the core subnetwork to function. 12 | /// These must be sorted by latest to oldest 13 | pub const BASE_EXTENSIONS: &[PingExtensionType] = &[PingExtensionType::HistoryRadius]; 14 | } 15 | 16 | impl PingExtensions for HistoryPingExtensions { 17 | fn latest_mutually_supported_base_extension( 18 | &self, 19 | extensions: &[PingExtensionType], 20 | ) -> Option { 21 | for base_extension in Self::BASE_EXTENSIONS { 22 | if extensions.contains(base_extension) { 23 | return Some(*base_extension); 24 | } 25 | } 26 | None 27 | } 28 | 29 | fn supported_extensions(&self) -> &[PingExtensionType] { 30 | Self::SUPPORTED_EXTENSIONS 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/subnetworks/state/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-state" 3 | description = "State network subprotocol for Trin." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy = { workspace = true, features = ["getrandom"] } 16 | anyhow.workspace = true 17 | cpu-time = "1.0.0" 18 | discv5.workspace = true 19 | eth_trie.workspace = true 20 | ethportal-api.workspace = true 21 | keccak-hash.workspace = true 22 | parking_lot.workspace = true 23 | portalnet.workspace = true 24 | serde_json.workspace = true 25 | thiserror.workspace = true 26 | tokio.workspace = true 27 | tracing.workspace = true 28 | trin-storage.workspace = true 29 | trin-validation.workspace = true 30 | utp-rs.workspace = true 31 | 32 | [dev-dependencies] 33 | env_logger.workspace = true 34 | rstest.workspace = true 35 | serde.workspace = true 36 | serde_yaml.workspace = true 37 | serial_test.workspace = true 38 | test-log.workspace = true 39 | tracing-subscriber.workspace = true 40 | trin-utils.workspace = true 41 | -------------------------------------------------------------------------------- /crates/subnetworks/state/README.md: -------------------------------------------------------------------------------- 1 | # State network subprotocol 2 | 3 | Please refer to the docs for more information. 4 | -------------------------------------------------------------------------------- /crates/subnetworks/state/src/ping_extensions.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::types::ping_extensions::{ 2 | consts::STATE_SUPPORTED_EXTENSIONS, extension_types::PingExtensionType, 3 | }; 4 | use portalnet::overlay::ping_extensions::PingExtensions; 5 | 6 | pub struct StatePingExtensions {} 7 | 8 | impl StatePingExtensions { 9 | pub const SUPPORTED_EXTENSIONS: &[PingExtensionType] = STATE_SUPPORTED_EXTENSIONS; 10 | 11 | /// Base extensions that are required for the core subnetwork to function. 12 | /// These must be sorted by latest to oldest 13 | pub const BASE_EXTENSIONS: &[PingExtensionType] = &[PingExtensionType::BasicRadius]; 14 | } 15 | 16 | impl PingExtensions for StatePingExtensions { 17 | fn latest_mutually_supported_base_extension( 18 | &self, 19 | extensions: &[PingExtensionType], 20 | ) -> Option { 21 | for base_extension in Self::BASE_EXTENSIONS { 22 | if extensions.contains(base_extension) { 23 | return Some(*base_extension); 24 | } 25 | } 26 | None 27 | } 28 | 29 | fn supported_extensions(&self) -> &[PingExtensionType] { 30 | Self::SUPPORTED_EXTENSIONS 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/subnetworks/state/src/validation/mod.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod trie; 3 | mod validator; 4 | 5 | pub use validator::StateValidator; 6 | -------------------------------------------------------------------------------- /crates/utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-utils" 3 | description = "Utils library for Trin." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | anyhow.workspace = true 17 | ethereum_ssz.workspace = true 18 | ethportal-api.workspace = true 19 | directories.workspace = true 20 | serde.workspace = true 21 | serde_json.workspace = true 22 | serde_yaml.workspace = true 23 | tempfile.workspace = true 24 | tracing.workspace = true 25 | tracing-subscriber = { workspace = true, features = ["env-filter"] } 26 | 27 | [target.'cfg(windows)'.dependencies] 28 | # The crates for detecting whether the terminal supports colors are OS-specific. 29 | ansi_term = "0.12" 30 | 31 | [target.'cfg(not(windows))'.dependencies] 32 | # The crates for detecting whether the terminal supports colors are OS-specific. 33 | atty = "0.2.14" 34 | -------------------------------------------------------------------------------- /crates/utils/README.md: -------------------------------------------------------------------------------- 1 | # Trin-utils 2 | Library for shared utils across trin workspace members. Utils only used by a single workspace member belong in a root-level `utils` module. 3 | -------------------------------------------------------------------------------- /crates/utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | #![warn(clippy::uninlined_format_args)] 3 | 4 | pub mod cli; 5 | pub mod dir; 6 | pub mod log; 7 | pub mod submodules; 8 | pub mod testing; 9 | -------------------------------------------------------------------------------- /crates/utils/src/log.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use tracing_subscriber::EnvFilter; 4 | 5 | pub fn init_tracing_logger() { 6 | let rust_log = env::var(EnvFilter::DEFAULT_ENV).unwrap_or_default(); 7 | let env_filter = match rust_log.is_empty() { 8 | true => EnvFilter::builder().parse_lossy("info,discv5=error,utp_rs=error"), 9 | false => EnvFilter::builder().parse_lossy(rust_log), 10 | }; 11 | 12 | tracing_subscriber::fmt() 13 | .with_env_filter(env_filter) 14 | .with_ansi(detect_ansi_support()) 15 | .init(); 16 | } 17 | 18 | pub fn detect_ansi_support() -> bool { 19 | #[cfg(windows)] 20 | { 21 | use ansi_term::enable_ansi_support; 22 | enable_ansi_support().is_ok() 23 | } 24 | #[cfg(not(windows))] 25 | { 26 | // Detect whether our log output (which goes to stdout) is going to a terminal. 27 | // For example, instead of the terminal, it might be getting piped into another file, which 28 | // probably ought to be plain text. 29 | let is_terminal = atty::is(atty::Stream::Stdout); 30 | if !is_terminal { 31 | return false; 32 | } 33 | 34 | // Return whether terminal defined in TERM supports ANSI 35 | std::env::var("TERM") 36 | .map(|term| term != "dumb") 37 | .unwrap_or(false) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /crates/utils/src/testing.rs: -------------------------------------------------------------------------------- 1 | use ethportal_api::{ContentValue, ContentValueError, OverlayContentKey, RawContentValue}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// The common test vectors type. 5 | #[derive(Debug, Clone, Serialize, Deserialize)] 6 | pub struct ContentItem { 7 | pub content_key: K, 8 | #[serde(rename = "content_value")] 9 | pub raw_content_value: RawContentValue, 10 | } 11 | 12 | impl ContentItem { 13 | pub fn content_value>(&self) -> Result { 14 | V::decode(&self.content_key, &self.raw_content_value) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crates/validation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trin-validation" 3 | description = "Validation layer for the Portal Network data." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy.workspace = true 16 | alloy-hardforks.workspace = true 17 | anyhow.workspace = true 18 | enr = "0.13.0" 19 | ethereum_hashing = "0.7.0" 20 | ethereum_ssz.workspace = true 21 | ethereum_ssz_derive.workspace = true 22 | ethportal-api.workspace = true 23 | futures.workspace = true 24 | lazy_static.workspace = true 25 | parking_lot.workspace = true 26 | rust-embed.workspace = true 27 | serde.workspace = true 28 | serde_yaml.workspace = true 29 | serde_json.workspace = true 30 | ssz_types.workspace = true 31 | tokio.workspace = true 32 | tracing.workspace = true 33 | tree_hash.workspace = true 34 | tree_hash_derive.workspace = true 35 | trin-metrics.workspace = true 36 | 37 | [dev-dependencies] 38 | quickcheck.workspace = true 39 | quickcheck_macros = "1.0.0" 40 | rstest.workspace = true 41 | trin-utils.workspace = true 42 | -------------------------------------------------------------------------------- /crates/validation/README.md: -------------------------------------------------------------------------------- 1 | # Trin-Validation 2 | Contains datatypes required for validating the canonical-ness of portal network data. 3 | - `HeaderOracle` 4 | - `MasterAccumulator` 5 | -------------------------------------------------------------------------------- /crates/validation/src/assets/chain_head/README.md: -------------------------------------------------------------------------------- 1 | Contains Default data for initializing `ChainHead` (crates/validation/src/chain_head) structures. 2 | 3 | Currently set to values at Pectra fork. 4 | -------------------------------------------------------------------------------- /crates/validation/src/assets/chain_head/pectra_historical_summaries.ssz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/validation/src/assets/chain_head/pectra_historical_summaries.ssz -------------------------------------------------------------------------------- /crates/validation/src/assets/epoch_accs/0x5ec1ffb8c3b146f42606c74ced973dc16ec5a107c0345858c343fc94780b4218.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/validation/src/assets/epoch_accs/0x5ec1ffb8c3b146f42606c74ced973dc16ec5a107c0345858c343fc94780b4218.bin -------------------------------------------------------------------------------- /crates/validation/src/assets/epoch_accs/0xcddbda3fd6f764602c06803ff083dbfc73f2bb396df17a31e5457329b9a0f38d.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/validation/src/assets/epoch_accs/0xcddbda3fd6f764602c06803ff083dbfc73f2bb396df17a31e5457329b9a0f38d.bin -------------------------------------------------------------------------------- /crates/validation/src/assets/epoch_accs/0xe6ebe562c89bc8ecb94dc9b2889a27a816ec05d3d6bd1625acad72227071e721.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/validation/src/assets/epoch_accs/0xe6ebe562c89bc8ecb94dc9b2889a27a816ec05d3d6bd1625acad72227071e721.bin -------------------------------------------------------------------------------- /crates/validation/src/assets/epoch_accs/0xf216a28afb2212269b634b9b44ff327a4a79f261640ff967f7e3283e3a184c70.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/validation/src/assets/epoch_accs/0xf216a28afb2212269b634b9b44ff327a4a79f261640ff967f7e3283e3a184c70.bin -------------------------------------------------------------------------------- /crates/validation/src/assets/historical_roots.ssz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/validation/src/assets/historical_roots.ssz -------------------------------------------------------------------------------- /crates/validation/src/assets/merge_macc.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/crates/validation/src/assets/merge_macc.bin -------------------------------------------------------------------------------- /crates/validation/src/constants.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{b256, B256}; 2 | 3 | /// The default hash of the pre-merge accumulator at the time of the merge block. 4 | pub const DEFAULT_PRE_MERGE_ACC_HASH: B256 = 5 | b256!("0x8eac399e24480dce3cfe06f4bdecba51c6e5d0c46200e3e8611a0b44a3a69ff9"); 6 | 7 | // EIP-155 chain ID for Ethereum mainnet 8 | pub const CHAIN_ID: usize = 1; 9 | -------------------------------------------------------------------------------- /crates/validation/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::unwrap_used)] 2 | #![warn(clippy::uninlined_format_args)] 3 | 4 | pub mod accumulator; 5 | pub mod chain_head; 6 | pub mod constants; 7 | pub mod header_validator; 8 | pub mod historical_roots_acc; 9 | pub mod historical_summaries_provider; 10 | pub mod merkle; 11 | pub mod oracle; 12 | pub mod validator; 13 | 14 | use rust_embed::RustEmbed; 15 | 16 | #[derive(RustEmbed)] 17 | #[folder = "src/assets/"] 18 | #[prefix = "validation_assets/"] 19 | struct TrinValidationAssets; 20 | -------------------------------------------------------------------------------- /crates/validation/src/merkle/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod proof; 2 | pub mod safe_arith; 3 | -------------------------------------------------------------------------------- /docker/Dockerfile.bridge: -------------------------------------------------------------------------------- 1 | FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef 2 | WORKDIR /app 3 | 4 | RUN apt-get update && apt-get install clang -y 5 | 6 | FROM chef AS planner 7 | COPY . . 8 | RUN cargo chef prepare --recipe-path recipe.json 9 | 10 | FROM chef AS builder 11 | COPY --from=planner /app/recipe.json recipe.json 12 | 13 | # Build dependencies - this is the caching Docker layer! 14 | RUN cargo chef cook --release --recipe-path recipe.json 15 | 16 | # Build application 17 | # Copy over all project folders specified in the .dockerignore 18 | COPY . . 19 | RUN cargo build --release --locked -p portal-bridge 20 | 21 | # We do not need the Rust toolchain to run the binary! 22 | FROM ubuntu 23 | WORKDIR /app 24 | 25 | # copy build artifacts from build stage 26 | COPY --from=builder /app/target/release/portal-bridge /usr/bin/ 27 | 28 | RUN apt-get update && apt-get install libcurl4 -y 29 | 30 | ENV RUST_LOG=error,portal_bridge=debug,portalnet=info 31 | 32 | ENTRYPOINT ["/usr/bin/portal-bridge"] 33 | -------------------------------------------------------------------------------- /docker/Dockerfile.e2hs-writer: -------------------------------------------------------------------------------- 1 | FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef 2 | WORKDIR /app 3 | 4 | RUN apt-get update && apt-get install clang -y 5 | 6 | FROM chef AS planner 7 | COPY . . 8 | RUN cargo chef prepare --recipe-path recipe.json 9 | 10 | FROM chef AS builder 11 | COPY --from=planner /app/recipe.json recipe.json 12 | 13 | # Build dependencies - this is the caching Docker layer! 14 | RUN cargo chef cook --release --recipe-path recipe.json 15 | 16 | # Build application 17 | # Copy over all project folders specified in the .dockerignore 18 | COPY . . 19 | RUN cargo build --release --locked -p e2hs-writer 20 | 21 | # We do not need the Rust toolchain to run the binary! 22 | FROM ubuntu AS runtime 23 | WORKDIR /app 24 | 25 | RUN apt-get update && apt-get install -y ca-certificates 26 | 27 | # copy build artifacts from build stage 28 | COPY --from=builder /app/target/release/e2hs-writer /usr/bin/ 29 | 30 | ENTRYPOINT ["/usr/bin/e2hs-writer"] 31 | -------------------------------------------------------------------------------- /docker/Dockerfile.trin: -------------------------------------------------------------------------------- 1 | FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef 2 | WORKDIR /app 3 | 4 | RUN apt-get update && apt-get install clang -y 5 | 6 | FROM chef AS planner 7 | COPY . . 8 | RUN cargo chef prepare --recipe-path recipe.json 9 | 10 | FROM chef AS builder 11 | COPY --from=planner /app/recipe.json recipe.json 12 | 13 | # Build dependencies - this is the caching Docker layer! 14 | RUN cargo chef cook --release --recipe-path recipe.json 15 | 16 | # Build application 17 | # Copy over all project folders specified in the .dockerignore 18 | COPY . . 19 | RUN cargo build --release --locked --bin trin 20 | 21 | # We do not need the Rust toolchain to run the binary! 22 | FROM ubuntu AS runtime 23 | WORKDIR /app 24 | 25 | # copy build artifacts from build stage 26 | COPY --from=builder /app/target/release/trin /usr/bin/ 27 | 28 | ENV RUST_LOG=debug 29 | 30 | ENTRYPOINT ["/usr/bin/trin"] 31 | -------------------------------------------------------------------------------- /docker/Dockerfile.trin-execution: -------------------------------------------------------------------------------- 1 | FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef 2 | WORKDIR /app 3 | 4 | RUN apt-get update && apt-get install clang -y 5 | 6 | FROM chef AS planner 7 | COPY . . 8 | RUN cargo chef prepare --recipe-path recipe.json 9 | 10 | FROM chef AS builder 11 | COPY --from=planner /app/recipe.json recipe.json 12 | 13 | # Build dependencies - this is the caching Docker layer! 14 | RUN cargo chef cook --release --recipe-path recipe.json 15 | 16 | # Build application 17 | # Copy over all project folders specified in the .dockerignore 18 | COPY . . 19 | RUN cargo build --release --locked -p trin-execution 20 | 21 | # We do not need the Rust toolchain to run the binary! 22 | FROM ubuntu AS runtime 23 | WORKDIR /app 24 | 25 | # copy build artifacts from build stage 26 | COPY --from=builder /app/target/release/trin-execution /usr/bin/ 27 | 28 | ENV RUST_LOG=debug 29 | 30 | ENTRYPOINT ["/usr/bin/trin-execution"] 31 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | trin: 4 | container_name: trin 5 | hostname: trin 6 | image: portalnetwork/trin:latest 7 | environment: 8 | - RUST_LOG=info 9 | command: "--web3-transport http --web3-http-address http://0.0.0.0:8545/" 10 | network_mode: "host" 11 | volumes: 12 | - ${HOME}/.local/share:${HOME}/.local/share 13 | restart: always 14 | -------------------------------------------------------------------------------- /metrics/README.md: -------------------------------------------------------------------------------- 1 | # Trin Metrics 2 | 3 | You can run this docker compose file it will run 4 | - prometheus 5 | - grafana 6 | - setup the metrics dashboard 7 | 8 | Metrics are useful for telling how Trin is performing. 9 | 10 | The default username and password is `admin`. 11 | 12 | ### How to run 13 | ```sh 14 | docker compose up 15 | ``` 16 | 17 | ### View the dashboard at 18 | http://localhost:3000/d/trin-metrics/trin-metrics 19 | 20 | ***WARNING***: don't forget to run `Trin` with metric exporting on! 21 | 22 | **Example** 23 | ```bash 24 | cargo run -p trin -- --enable-metrics-with-url 0.0.0.0:9100 --web3-http-address http://0.0.0.0:8545 --web3-transport http 25 | ``` 26 | -------------------------------------------------------------------------------- /metrics/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | prometheus: 3 | image: prom/prometheus 4 | container_name: prometheus 5 | command: 6 | - '--config.file=/etc/prometheus/prometheus.yml' 7 | network_mode: "host" 8 | restart: unless-stopped 9 | volumes: 10 | - ./prometheus:/etc/prometheus 11 | - prom_data_trin:/prometheus 12 | grafana: 13 | image: grafana/grafana 14 | container_name: grafana 15 | network_mode: "host" 16 | restart: unless-stopped 17 | volumes: 18 | - ./grafana/datasource.yaml:/etc/grafana/provisioning/datasources/datasource.yaml 19 | - ./grafana/dashboard.yaml:/etc/grafana/provisioning/dashboards/main.yaml 20 | - ./grafana/dashboards:/var/lib/grafana/dashboards 21 | volumes: 22 | prom_data_trin: 23 | -------------------------------------------------------------------------------- /metrics/grafana/dashboard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'Trin' 5 | orgId: 1 6 | type: file 7 | options: 8 | path: /var/lib/grafana/dashboards 9 | -------------------------------------------------------------------------------- /metrics/grafana/datasource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: Prometheus 5 | type: prometheus 6 | uid: trinprom 7 | url: http://localhost:9090 8 | orgId: 1 9 | access: proxy 10 | isDefault: true 11 | 12 | -------------------------------------------------------------------------------- /metrics/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | scrape_timeout: 10s 4 | evaluation_interval: 15s 5 | alerting: 6 | alertmanagers: 7 | - follow_redirects: true 8 | enable_http2: true 9 | scheme: http 10 | timeout: 10s 11 | api_version: v2 12 | static_configs: 13 | - targets: [] 14 | scrape_configs: 15 | - job_name: trin 16 | honor_timestamps: true 17 | scrape_interval: 15s 18 | scrape_timeout: 10s 19 | metrics_path: /metrics 20 | scheme: http 21 | follow_redirects: true 22 | enable_http2: true 23 | fallback_scrape_protocol: PrometheusText0.0.4 24 | static_configs: 25 | - targets: 26 | - localhost:9100 27 | - host.docker.internal:9100 28 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # The default profile includes rustc, rust-std, cargo, rust-docs, rustfmt and clippy. 3 | # https://rust-lang.github.io/rustup/concepts/profiles.html 4 | profile = "default" 5 | channel = "1.87.0" 6 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | imports_granularity = "Crate" 2 | group_imports = "StdExternalCrate" 3 | comment_width = 100 4 | wrap_comments = true 5 | format_code_in_doc_comments = true 6 | doc_comment_code_block_width = 100 7 | -------------------------------------------------------------------------------- /test_assets/beacon/bellatrix/ValidSignedBeaconBlock/signed_beacon_block_15537397.ssz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/beacon/bellatrix/ValidSignedBeaconBlock/signed_beacon_block_15537397.ssz -------------------------------------------------------------------------------- /test_assets/beacon/deneb/LightClientBootstrap/ssz_random/case_0/serialized.ssz_snappy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/beacon/deneb/LightClientBootstrap/ssz_random/case_0/serialized.ssz_snappy -------------------------------------------------------------------------------- /test_assets/beacon/deneb/LightClientFinalityUpdate/ssz_random/case_0/serialized.ssz_snappy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/beacon/deneb/LightClientFinalityUpdate/ssz_random/case_0/serialized.ssz_snappy -------------------------------------------------------------------------------- /test_assets/beacon/deneb/LightClientOptimisticUpdate/ssz_random/case_0/serialized.ssz_snappy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/beacon/deneb/LightClientOptimisticUpdate/ssz_random/case_0/serialized.ssz_snappy -------------------------------------------------------------------------------- /test_assets/beacon/deneb/LightClientUpdate/ssz_random/case_0/serialized.ssz_snappy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/beacon/deneb/LightClientUpdate/ssz_random/case_0/serialized.ssz_snappy -------------------------------------------------------------------------------- /test_assets/beacon/deneb/LightClientUpdate/ssz_random/case_1/serialized.ssz_snappy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/beacon/deneb/LightClientUpdate/ssz_random/case_1/serialized.ssz_snappy -------------------------------------------------------------------------------- /test_assets/e2hs/mainnet-00000-a6860fef.e2hs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/e2hs/mainnet-00000-a6860fef.e2hs -------------------------------------------------------------------------------- /test_assets/era1/mainnet-00000-5ec1ffb8.era1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/era1/mainnet-00000-5ec1ffb8.era1 -------------------------------------------------------------------------------- /test_assets/era1/mainnet-00001-a5364e9a.era1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/era1/mainnet-00001-a5364e9a.era1 -------------------------------------------------------------------------------- /test_assets/era1/mainnet-00010-5f5d4516.era1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/era1/mainnet-00010-5f5d4516.era1 -------------------------------------------------------------------------------- /test_assets/era1/test-mainnet-01600-xxxxxxxx.era1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/era1/test-mainnet-01600-xxxxxxxx.era1 -------------------------------------------------------------------------------- /test_assets/era1/test-mainnet-01896-xxxxxx.era1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/era1/test-mainnet-01896-xxxxxx.era1 -------------------------------------------------------------------------------- /test_assets/mainnet/block_body_14764013.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/block_body_14764013.bin -------------------------------------------------------------------------------- /test_assets/mainnet/block_body_17139055.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/block_body_17139055.bin -------------------------------------------------------------------------------- /test_assets/mainnet/large_content/15040641/body.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/large_content/15040641/body.bin -------------------------------------------------------------------------------- /test_assets/mainnet/large_content/15040641/header.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/large_content/15040641/header.bin -------------------------------------------------------------------------------- /test_assets/mainnet/large_content/15040641/receipts.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/large_content/15040641/receipts.bin -------------------------------------------------------------------------------- /test_assets/mainnet/large_content/15040708/body.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/large_content/15040708/body.bin -------------------------------------------------------------------------------- /test_assets/mainnet/large_content/15040708/header.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/large_content/15040708/header.bin -------------------------------------------------------------------------------- /test_assets/mainnet/large_content/15040708/receipts.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/large_content/15040708/receipts.bin -------------------------------------------------------------------------------- /test_assets/mainnet/large_content/README.txt: -------------------------------------------------------------------------------- 1 | # Large test content.. 2 | This is a folder of large content values to be used in specific testing scenarios... 3 | 4 | # block 15040641 - 0x2be48ebbbcd9a91a3bc4354b4c864789d6144d605f629df8ed11c0602f599bbf 5 | - 447_939 bytes total 6 | - header_with_proof: 1_030 bytes 7 | - content_key: 0x002be48ebbbcd9a91a3bc4354b4c864789d6144d605f629df8ed11c0602f599bbf 8 | - content_id: 0x76df3e911f784cb9288382157e43ea3223a8358c3e0e4d06243ff191a55fa3d9 9 | - body: 126_134 bytes 10 | - content_key: 0x012be48ebbbcd9a91a3bc4354b4c864789d6144d605f629df8ed11c0602f599bbf 11 | - content_id: 0x41b6cf15cbcb92d2f695b32d1c80ec3b5845ad723b2d6fef4677b527c2c1a6e7 12 | - receipts: 320_775 bytes 13 | - content_key: 0x022be48ebbbcd9a91a3bc4354b4c864789d6144d605f629df8ed11c0602f599bbf 14 | - content_id: 0xb829a6f3f21a0afce1f45f79da776101462790d3fc3c1beed6a2cb2c91ddb122 15 | 16 | # block 15040708 - 0x944ade7c054495265fa190494368e510fa960c1b498347f0d3584130d2a3a0d9 17 | - 987_653 bytes total 18 | - header_with_proof: 1_024 bytes 19 | - content_key: 0x00944ade7c054495265fa190494368e510fa960c1b498347f0d3584130d2a3a0d9 20 | - content_id: 0x0f356e24e9152a537d26bd62908e00dcb6db0cfdd1e38b74e347fba268461e51 21 | - body: 763_493 bytes 22 | - content_key: 0x01944ade7c054495265fa190494368e510fa960c1b498347f0d3584130d2a3a0d9 23 | - content_id: 0xb45d64fca22a045e1fa52ef79797e9dd88295777e0effc221ede6d664aff03a7 24 | - receipts: 223_136 bytes 25 | - content_key: 0x02944ade7c054495265fa190494368e510fa960c1b498347f0d3584130d2a3a0d9 26 | - content_id: 0x9f5bfdc22418ff3fe8c86dca88100812af9827d9f8157fe0b39249be4061bf57 27 | -------------------------------------------------------------------------------- /test_assets/mainnet/receipts_14764013.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/trin/ebc206702592f08d8174af9ea5d2f12bb6e1ae0c/test_assets/mainnet/receipts_14764013.bin -------------------------------------------------------------------------------- /testing/ef-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ef-tests" 3 | authors.workspace = true 4 | edition.workspace = true 5 | keywords.workspace = true 6 | license.workspace = true 7 | readme.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | alloy.workspace = true 14 | anyhow.workspace = true 15 | ethereum_hashing.workspace = true 16 | ethereum_serde_utils.workspace = true 17 | ethereum_ssz.workspace = true 18 | ethereum_ssz_derive.workspace = true 19 | paste = "1.0.15" 20 | rstest.workspace = true 21 | serde.workspace = true 22 | serde_yaml.workspace = true 23 | snap.workspace = true 24 | ssz_types.workspace = true 25 | tree_hash.workspace = true 26 | tree_hash_derive.workspace = true 27 | 28 | # trin dependencies 29 | ethportal-api.workspace = true 30 | 31 | [features] 32 | ef-tests = [] 33 | -------------------------------------------------------------------------------- /testing/ef-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod macros; 2 | -------------------------------------------------------------------------------- /testing/ethportal-peertest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethportal-peertest" 3 | description = "Testing utilities for trin" 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | alloy = { workspace = true, features = ["getrandom"] } 16 | alloy-hardforks.workspace = true 17 | anyhow.workspace = true 18 | discv5.workspace = true 19 | e2store.workspace = true 20 | ethereum_ssz.workspace = true 21 | ethportal-api.workspace = true 22 | futures.workspace = true 23 | hex.workspace = true 24 | itertools.workspace = true 25 | jsonrpsee = { workspace = true, features = ["async-client", "client", "macros", "server"] } 26 | portalnet.workspace = true 27 | rand.workspace = true 28 | reth-ipc.workspace = true 29 | rpc.workspace = true 30 | serde.workspace = true 31 | serde_json.workspace = true 32 | serde_yaml.workspace = true 33 | tempfile.workspace = true 34 | tokio.workspace = true 35 | tracing.workspace = true 36 | tracing-subscriber.workspace = true 37 | tree_hash.workspace = true 38 | trin.workspace = true 39 | trin-history.workspace = true 40 | trin-state.workspace = true 41 | trin-utils.workspace = true 42 | trin-validation.workspace = true 43 | url.workspace = true 44 | 45 | [target.'cfg(windows)'.dependencies] 46 | uds_windows.workspace = true 47 | 48 | [dev-dependencies] 49 | serial_test.workspace = true 50 | -------------------------------------------------------------------------------- /testing/ethportal-peertest/README.md: -------------------------------------------------------------------------------- 1 | # ethportal-peertest 2 | 3 | Home for an integration testing tool for trin. 4 | -------------------------------------------------------------------------------- /testing/ethportal-peertest/src/scenarios/eth_rpc.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::U256; 2 | use ethportal_api::EthApiClient; 3 | use tracing::info; 4 | use trin_validation::constants::CHAIN_ID; 5 | 6 | use crate::Peertest; 7 | 8 | pub async fn test_eth_chain_id(peertest: &Peertest) { 9 | info!("Testing eth_chainId"); 10 | let target = &peertest.bootnode.ipc_client; 11 | let chain_id = target.chain_id().await.unwrap(); 12 | // For now, the chain ID is always 1 -- portal only supports mainnet Ethereum 13 | assert_eq!(chain_id, U256::from(CHAIN_ID)); 14 | } 15 | -------------------------------------------------------------------------------- /testing/ethportal-peertest/src/scenarios/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic; 2 | pub mod eth_rpc; 3 | pub mod find; 4 | pub mod offer_accept; 5 | pub mod paginate; 6 | pub mod ping; 7 | pub mod put_content; 8 | pub mod state; 9 | pub mod utp; 10 | pub mod validation; 11 | -------------------------------------------------------------------------------- /testing/ethportal-peertest/tests/utils/mod.rs: -------------------------------------------------------------------------------- 1 | // sets the global tracing subscriber, to be used by all other tests 2 | pub fn init_tracing() { 3 | let subscriber = tracing_subscriber::fmt() 4 | .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) 5 | .with_ansi(trin_utils::log::detect_ansi_support()) 6 | .finish(); 7 | // returns err if already set, which is fine and we just ignore the err 8 | let _ = tracing::subscriber::set_global_default(subscriber); 9 | } 10 | -------------------------------------------------------------------------------- /testing/utp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "utp-testing" 3 | description = "A testing suite for the UTP protocol." 4 | authors.workspace = true 5 | categories.workspace = true 6 | edition.workspace = true 7 | keywords.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | version.workspace = true 13 | 14 | [dependencies] 15 | anyhow.workspace = true 16 | clap.workspace = true 17 | discv5.workspace = true 18 | ethportal-api.workspace = true 19 | jsonrpsee = { workspace = true, features = ["full"]} 20 | portalnet.workspace = true 21 | rand.workspace = true 22 | tokio.workspace = true 23 | tracing.workspace = true 24 | tracing-subscriber.workspace = true 25 | trin-utils.workspace = true 26 | trin-validation.workspace = true 27 | utp-rs.workspace = true 28 | 29 | [[bin]] 30 | name = "utp-test-app" 31 | path = "src/bin/test_app.rs" 32 | 33 | [[bin]] 34 | name = "utp-test-suite" 35 | path = "src/bin/test_suite.rs" 36 | -------------------------------------------------------------------------------- /testing/utp/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.87.0-bullseye AS builder 2 | 3 | RUN apt-get update \ 4 | && apt-get install clang -y \ 5 | && apt-get clean \ 6 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 7 | 8 | ARG BRANCH_NAME=master 9 | ARG REPO_URL="https://github.com/ethereum/trin.git" 10 | 11 | RUN git clone $REPO_URL \ 12 | && cd trin \ 13 | && git checkout ${BRANCH_NAME} -- 14 | 15 | WORKDIR /trin 16 | 17 | RUN cargo build -p utp-testing --release 18 | 19 | RUN mv target/release/utp-test-app /bin/ 20 | 21 | FROM debian:bullseye-slim AS deploy 22 | 23 | RUN apt-get update \ 24 | && apt-get install -y ethtool net-tools \ 25 | && apt-get clean \ 26 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 27 | 28 | COPY --from=builder /bin/utp-test-app /bin/utp-test-app 29 | 30 | ENV RUST_LOG=TRACE 31 | 32 | COPY setup.sh . 33 | RUN chmod +x setup.sh 34 | 35 | COPY run_endpoint.sh . 36 | RUN chmod +x run_endpoint.sh 37 | 38 | ENTRYPOINT [ "./run_endpoint.sh" ] 39 | -------------------------------------------------------------------------------- /testing/utp/docker/circleci/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y ethtool net-tools \ 5 | && apt-get clean \ 6 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 7 | 8 | COPY ./utp-test-app /bin/utp-test-app 9 | 10 | ENV RUST_LOG=debug 11 | 12 | COPY setup.sh . 13 | RUN chmod +x setup.sh 14 | 15 | COPY run_endpoint.sh . 16 | RUN chmod +x run_endpoint.sh 17 | 18 | ENTRYPOINT [ "./run_endpoint.sh" ] 19 | -------------------------------------------------------------------------------- /testing/utp/docker/run_endpoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Set up the routing needed for the simulation. 5 | /setup.sh 6 | 7 | echo "Client params: $CLIENT_PARAMS" 8 | 9 | ./bin/utp-test-app $CLIENT_PARAMS 10 | -------------------------------------------------------------------------------- /testing/utp/docker/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Setting up routes..." 4 | # By default, docker containers don't compute UDP / TCP checksums. 5 | # When packets run through ns3 however, the receiving endpoint requires valid checksums. 6 | # This command makes sure that the endpoints set the checksum on outgoing packets. 7 | ethtool -K eth0 tx off 8 | 9 | # this relies on the IPv4 address being first in the "hostname -I" output 10 | IP=$(hostname -I | cut -f1 -d" ") 11 | GATEWAY="${IP%.*}.2" 12 | echo "Gateway is $GATEWAY" 13 | UNNEEDED_ROUTE="${IP%.*}.0" 14 | echo "Unneeded route is $UNNEEDED_ROUTE" 15 | 16 | echo "Endpoint's IPv4 address is $IP" 17 | 18 | route add -net 193.167.0.0 netmask 255.255.0.0 gw "$GATEWAY" 19 | echo "ROUTE ADDED" 20 | 21 | # delete unused route 22 | route del -net "$UNNEEDED_ROUTE" netmask 255.255.255.0 23 | echo "ROUTE DELETED" 24 | 25 | # TODO figure out if we need ipv6 configs 26 | # # this relies on the IPv6 address being second in the "hostname -I" output 27 | # IP=$(hostname -I | cut -f2 -d" ") 28 | # GATEWAY="${IP%:*}:2" 29 | # UNNEEDED_ROUTE="${IP%:*}:" 30 | # echo "Endpoint's IPv6 address is $IP" 31 | 32 | # ip -d route add fd00:cafe:cafe::/48 via $GATEWAY 33 | # # delete unused route 34 | # ip -d route del $UNNEEDED_ROUTE/64 35 | 36 | # create the /logs and the /logs/qlog directory 37 | mkdir -p /logs/qlog 38 | -------------------------------------------------------------------------------- /testing/utp/src/bin/test_app.rs: -------------------------------------------------------------------------------- 1 | use std::{net::SocketAddr, str::FromStr}; 2 | 3 | use clap::Parser; 4 | use tracing::info; 5 | use trin_utils::log::init_tracing_logger; 6 | use utp_testing::{cli::TestAppConfig, run_test_app}; 7 | 8 | /// uTP test app, used for creation of a `test-app` docker image 9 | #[tokio::main] 10 | async fn main() { 11 | init_tracing_logger(); 12 | 13 | let config = TestAppConfig::parse(); 14 | 15 | let udp_listen_address = format!("{}:{}", config.udp_listen_address, config.udp_port); 16 | let udp_listen_address = SocketAddr::from_str(&udp_listen_address).unwrap(); 17 | let (rpc_addr, enr, _handle) = run_test_app( 18 | config.udp_port, 19 | udp_listen_address, 20 | config.rpc_listen_address, 21 | config.rpc_port, 22 | ) 23 | .await 24 | .unwrap(); 25 | 26 | info!("uTP test app started. RPC address: {rpc_addr}, Enr: {enr}"); 27 | 28 | tokio::signal::ctrl_c() 29 | .await 30 | .expect("failed to pause until ctrl-c"); 31 | } 32 | -------------------------------------------------------------------------------- /testing/utp/src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | #[derive(Debug, Parser)] 4 | #[command(name = "utP test app CLI")] 5 | pub struct TestAppConfig { 6 | #[arg(long, required = true)] 7 | pub udp_listen_address: String, 8 | 9 | #[arg(long, required = true)] 10 | pub rpc_listen_address: String, 11 | 12 | #[arg(long, required = true)] 13 | pub udp_port: u16, 14 | 15 | #[arg(long, required = true)] 16 | pub rpc_port: u16, 17 | } 18 | -------------------------------------------------------------------------------- /testing/utp/src/rpc.rs: -------------------------------------------------------------------------------- 1 | use crate::{rpc, RpcResult}; 2 | 3 | /// JSON-RPC endpoint for client and server 4 | #[rpc(server, client)] 5 | pub trait Rpc { 6 | #[method(name = "get_utp_payload")] 7 | async fn get_utp_payload(&self) -> RpcResult; 8 | 9 | #[method(name = "local_enr")] 10 | fn local_enr(&self) -> RpcResult; 11 | 12 | #[method(name = "prepare_to_recv")] 13 | async fn prepare_to_recv(&self, enr: String, cid_send: u16, cid_recv: u16) 14 | -> RpcResult; 15 | 16 | #[method(name = "send_utp_payload")] 17 | async fn send_utp_payload( 18 | &self, 19 | enr: String, 20 | cid_send: u16, 21 | cid_recv: u16, 22 | payload: Vec, 23 | ) -> RpcResult; 24 | } 25 | --------------------------------------------------------------------------------