diff --git a/Cargo.lock b/Cargo.lock index 2d01f8c3b..cf1f0cbef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8280,7 +8280,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "utp-rs" version = "0.1.0-alpha.8" -source = "git+https://github.com/ethereum/utp?tag=v0.1.0-alpha.12#1dde17849dda4d07494d089517deea0c2954fae7" +source = "git+https://github.com/ethereum/utp?tag=v0.1.0-alpha.13#00e1b0abc088d14ce470c6649e9fc058a052a73a" dependencies = [ "async-trait", "delay_map", diff --git a/Cargo.toml b/Cargo.toml index 3aa833b42..2ba637338 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ trin-storage = { path = "trin-storage" } trin-utils = { path = "trin-utils" } trin-validation = { path = "trin-validation" } url = "2.3.1" -utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.12" } +utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.13" } [dev-dependencies] ethers-core = { version = "2.0", default-features = false} diff --git a/ethportal-peertest/src/scenarios/offer_accept.rs b/ethportal-peertest/src/scenarios/offer_accept.rs index adf2562f2..30780c3d3 100644 --- a/ethportal-peertest/src/scenarios/offer_accept.rs +++ b/ethportal-peertest/src/scenarios/offer_accept.rs @@ -1,20 +1,17 @@ -use std::{ - net::{IpAddr, Ipv4Addr}, - str::FromStr, -}; +use std::str::FromStr; -use tokio::time::{sleep, Duration}; use tracing::info; use crate::{ - utils::{fixture_header_with_proof, wait_for_history_content}, + utils::{ + fixture_block_body, fixture_epoch_acc_1, fixture_epoch_acc_2, fixture_header_with_proof, + fixture_receipts, wait_for_history_content, + }, Peertest, }; use ethportal_api::{ - jsonrpsee::async_client::Client, - types::{cli::TrinConfig, enr::Enr}, - utils::bytes::hex_encode, - Discv5ApiClient, HistoryNetworkApiClient, + jsonrpsee::async_client::Client, types::enr::Enr, utils::bytes::hex_encode, Discv5ApiClient, + HistoryNetworkApiClient, }; pub async fn test_unpopulated_offer(peertest: &Peertest, target: &Client) { @@ -42,11 +39,9 @@ pub async fn test_unpopulated_offer(peertest: &Peertest, target: &Client) { assert_eq!(hex_encode(result.content_keys.into_bytes()), "0x03"); // Check if the stored content value in bootnode's DB matches the offered - let received_content_value = - wait_for_history_content(&peertest.bootnode.ipc_client, content_key).await; assert_eq!( - content_value, received_content_value, - "The received content {received_content_value:?}, must match the expected {content_value:?}", + content_value, + wait_for_history_content(&peertest.bootnode.ipc_client, content_key).await, ); } @@ -92,91 +87,235 @@ pub async fn test_populated_offer(peertest: &Peertest, target: &Client) { assert_eq!(hex_encode(result.content_keys.into_bytes()), "0x03"); // Check if the stored content value in bootnode's DB matches the offered - let received_content_value = - wait_for_history_content(&peertest.bootnode.ipc_client, content_key).await; assert_eq!( - content_value, received_content_value, - "The received content {received_content_value:?}, must match the expected {content_value:?}", + content_value, + wait_for_history_content(&peertest.bootnode.ipc_client, content_key).await, ); } pub async fn test_offer_propagates_gossip(peertest: &Peertest, target: &Client) { info!("Testing populated offer propagates gossip"); - // connect target to network - let _ = target.ping(peertest.bootnode.enr.clone()).await.unwrap(); - - // Spin up a fresh client, not connected to existing peertest - let test_ip_addr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); - // Use an uncommon port for the peertest to avoid clashes. - let test_discovery_port = 8889; - let external_addr = format!("{test_ip_addr}:{test_discovery_port}"); - let fresh_ipc_path = format!("/tmp/trin-jsonrpc-{test_discovery_port}.ipc"); - let trin_config = TrinConfig::new_from( - [ - "trin", - "--portal-subnetworks", - "history", - "--external-address", - external_addr.as_str(), - "--mb", - "10", - "--web3-ipc-path", - fresh_ipc_path.as_str(), - "--ephemeral", - "--discovery-port", - test_discovery_port.to_string().as_ref(), - "--bootnodes", - "none", - ] - .iter(), - ) - .unwrap(); - let _test_client_rpc_handle = trin::run_trin(trin_config).await.unwrap(); - let fresh_target = reth_ipc::client::IpcClientBuilder::default() - .build(fresh_ipc_path) - .await - .unwrap(); - let fresh_enr = fresh_target.node_info().await.unwrap().enr; - - // connect target to network - let _ = target.ping(fresh_enr.clone()).await.unwrap(); - + // get content values to gossip let (content_key, content_value) = fixture_header_with_proof(); // use populated offer which means content will *not* be stored in the target's local db target .offer( - fresh_enr.clone(), + peertest.bootnode.enr.clone(), content_key.clone(), content_value.clone(), ) .await .unwrap(); - // sleep to let gossip propagate - sleep(Duration::from_secs(1)).await; - // validate that every node in the network now has a local copy of the header - assert!( - HistoryNetworkApiClient::local_content(target, content_key.clone()) - .await - .is_ok() - ); - assert!( - HistoryNetworkApiClient::local_content(&fresh_target, content_key.clone()) - .await - .is_ok() - ); - assert!(HistoryNetworkApiClient::local_content( - &peertest.nodes[0].ipc_client, - content_key.clone() - ) - .await - .is_ok()); - assert!(HistoryNetworkApiClient::local_content( - &peertest.bootnode.ipc_client, - content_key.clone() - ) - .await - .is_ok()); + assert_eq!( + content_value, + wait_for_history_content(target, content_key.clone()).await, + ); + assert_eq!( + content_value, + wait_for_history_content(&peertest.nodes[0].ipc_client, content_key.clone()).await, + ); + assert_eq!( + content_value, + wait_for_history_content(&peertest.bootnode.ipc_client, content_key).await, + ); +} + +pub async fn test_offer_propagates_gossip_with_large_content(peertest: &Peertest, target: &Client) { + info!("Testing populated offer propagates gossips single large content"); + // 512kb epoch accumulator + let (content_key, content_value) = fixture_epoch_acc_1(); + + // Store content to offer in the testnode db + let store_result = target + .store(content_key.clone(), content_value.clone()) + .await + .unwrap(); + assert!(store_result); + target + .wire_offer( + peertest.bootnode.ipc_client.node_info().await.unwrap().enr, + vec![content_key.clone()], + ) + .await + .unwrap(); + + // validate that every node in the network now has a local copy of the accumulator + assert_eq!( + content_value, + wait_for_history_content(target, content_key.clone()).await, + ); + assert_eq!( + content_value, + wait_for_history_content(&peertest.nodes[0].ipc_client, content_key.clone()).await, + ); + assert_eq!( + content_value, + wait_for_history_content(&peertest.bootnode.ipc_client, content_key).await, + ); +} + +// multiple content values, < 1mb payload +pub async fn test_offer_propagates_gossip_multiple_content_values( + peertest: &Peertest, + target: &Client, +) { + info!("Testing populated offer propagates gossips multiple content values simultaneously"); + // get content values to gossip + let (header_key, header_value) = fixture_header_with_proof(); + let (body_key, body_value) = fixture_block_body(); + let (receipts_key, receipts_value) = fixture_receipts(); + let (acc_key_1, acc_value_1) = fixture_epoch_acc_1(); + + // offer header content for validation later + target + .offer( + peertest.bootnode.enr.clone(), + header_key.clone(), + header_value.clone(), + ) + .await + .unwrap(); + + // check that header content is available + assert_eq!( + header_value, + wait_for_history_content(target, header_key.clone()).await, + ); + assert_eq!( + header_value, + wait_for_history_content(&peertest.bootnode.ipc_client, header_key.clone()).await, + ); + assert_eq!( + header_value, + wait_for_history_content(&peertest.nodes[0].ipc_client, header_key.clone()).await, + ); + + // Store content to offer in the testnode db + let store_result = target + .store(body_key.clone(), body_value.clone()) + .await + .unwrap(); + assert!(store_result); + let store_result = target + .store(receipts_key.clone(), receipts_value.clone()) + .await + .unwrap(); + assert!(store_result); + let store_result = target + .store(acc_key_1.clone(), acc_value_1.clone()) + .await + .unwrap(); + assert!(store_result); + + // here everythings stored in target + target + .wire_offer( + peertest.bootnode.ipc_client.node_info().await.unwrap().enr, + vec![body_key.clone(), receipts_key.clone(), acc_key_1.clone()], + ) + .await + .unwrap(); + + // check that body content is available + assert_eq!( + body_value, + wait_for_history_content(target, body_key.clone()).await, + ); + assert_eq!( + body_value, + wait_for_history_content(&peertest.bootnode.ipc_client, body_key.clone()).await, + ); + assert_eq!( + body_value, + wait_for_history_content(&peertest.nodes[0].ipc_client, body_key.clone()).await, + ); + // check that receipts content is available + assert_eq!( + receipts_value, + wait_for_history_content(target, receipts_key.clone()).await, + ); + assert_eq!( + receipts_value, + wait_for_history_content(&peertest.bootnode.ipc_client, receipts_key.clone()).await, + ); + assert_eq!( + receipts_value, + wait_for_history_content(&peertest.nodes[0].ipc_client, receipts_key.clone()).await, + ); + + // check that acc_content_1 is available + assert_eq!( + acc_value_1, + wait_for_history_content(target, acc_key_1.clone()).await, + ); + assert_eq!( + acc_value_1, + wait_for_history_content(&peertest.bootnode.ipc_client, acc_key_1.clone()).await, + ); + assert_eq!( + acc_value_1, + wait_for_history_content(&peertest.nodes[0].ipc_client, acc_key_1).await, + ); +} + +// multiple content values, > 1mb payload +pub async fn test_offer_propagates_gossip_multiple_large_content_values( + peertest: &Peertest, + target: &Client, +) { + info!("Testing populated offer propagates gossips multiple large content simultaneously"); + + // get content values to gossip + let (acc_key_1, acc_value_1) = fixture_epoch_acc_1(); + let (acc_key_2, acc_value_2) = fixture_epoch_acc_2(); + + // Store content to offer in the testnode db + let store_result = target + .store(acc_key_1.clone(), acc_value_1.clone()) + .await + .unwrap(); + assert!(store_result); + let store_result = target + .store(acc_key_2.clone(), acc_value_2.clone()) + .await + .unwrap(); + assert!(store_result); + target + .wire_offer( + peertest.bootnode.ipc_client.node_info().await.unwrap().enr, + vec![acc_key_1.clone(), acc_key_2.clone()], + ) + .await + .unwrap(); + + // check that acc_content_1 is available + assert_eq!( + acc_value_1, + wait_for_history_content(target, acc_key_1.clone()).await, + ); + assert_eq!( + acc_value_1, + wait_for_history_content(&peertest.bootnode.ipc_client, acc_key_1.clone()).await, + ); + assert_eq!( + acc_value_1, + wait_for_history_content(&peertest.nodes[0].ipc_client, acc_key_1).await, + ); + + // check that acc_content_2 is available + assert_eq!( + acc_value_2, + wait_for_history_content(target, acc_key_2.clone()).await, + ); + assert_eq!( + acc_value_2, + wait_for_history_content(&peertest.bootnode.ipc_client, acc_key_2.clone()).await, + ); + assert_eq!( + acc_value_2, + wait_for_history_content(&peertest.nodes[0].ipc_client, acc_key_2).await, + ); } diff --git a/ethportal-peertest/src/utils.rs b/ethportal-peertest/src/utils.rs index 40814e743..95c5e6ea2 100644 --- a/ethportal-peertest/src/utils.rs +++ b/ethportal-peertest/src/utils.rs @@ -9,7 +9,7 @@ use serde_yaml::Value; use ureq::serde::Deserialize; use ethportal_api::{ - BeaconContentKey, BeaconContentValue, BeaconNetworkApiClient, HistoryContentKey, + BeaconContentKey, BeaconContentValue, BeaconNetworkApiClient, ContentValue, HistoryContentKey, HistoryContentValue, HistoryNetworkApiClient, StateContentKey, StateContentValue, StateNetworkApiClient, }; @@ -119,6 +119,31 @@ pub fn fixture_receipts() -> (HistoryContentKey, HistoryContentValue) { read_fixture("portal-spec-tests/tests/mainnet/history/receipts/14764013.yaml") } +/// Epoch Accumulator #1659 +/// Content Key: 0x030013c08b64bf7e3afab80ad4f8ea9423f1a7d8b31a149fc3b832d7980719c60c +/// Content ID: 0x61f6fd26ed4fb88cfae02ad691e4f41e0053e0305cb62f7cdfde5a7967ffbe65 +pub fn fixture_epoch_acc_1() -> (HistoryContentKey, HistoryContentValue) { + read_epoch_acc("0013c08b64bf7e3afab80ad4f8ea9423f1a7d8b31a149fc3b832d7980719c60c") +} + +/// Epoch Accumulator #434 +/// Content Key: 0x03ed8823c84177d8ffabf104566f313a2b2a43d05304ba6c74c2f5555bae0ef329 +/// Content ID: 0x29e3c0966a85ee262ef4afeccd89721fda0962ae563a3818e81798fe28bdb37e +pub fn fixture_epoch_acc_2() -> (HistoryContentKey, HistoryContentValue) { + read_epoch_acc("ed8823c84177d8ffabf104566f313a2b2a43d05304ba6c74c2f5555bae0ef329") +} + +fn read_epoch_acc(hash: &str) -> (HistoryContentKey, HistoryContentValue) { + let epoch_acc = std::fs::read(format!("test_assets/mainnet/0x03{hash}.portalcontent")).unwrap(); + let epoch_acc_hash = ethportal_api::utils::bytes::hex_decode(&format!("0x{hash}")).unwrap(); + let content_key = + ethportal_api::HistoryContentKey::EpochAccumulator(ethportal_api::EpochAccumulatorKey { + epoch_hash: alloy_primitives::B256::from_slice(&epoch_acc_hash), + }); + let content_value = ethportal_api::HistoryContentValue::decode(&epoch_acc).unwrap(); + (content_key, content_value) +} + #[derive(Debug, Clone, Deserialize)] pub struct StateContentData { #[serde(rename = "content_key")] diff --git a/portalnet/Cargo.toml b/portalnet/Cargo.toml index a8e8260fa..3aa8ce820 100644 --- a/portalnet/Cargo.toml +++ b/portalnet/Cargo.toml @@ -44,7 +44,7 @@ trin-metrics = { path = "../trin-metrics" } trin-storage = { path = "../trin-storage" } trin-utils = { path = "../trin-utils" } trin-validation = { path = "../trin-validation" } -utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.12" } +utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.13" } [target.'cfg(windows)'.dependencies] uds_windows = "1.0.1" diff --git a/test_assets/mainnet/0x030013c08b64bf7e3afab80ad4f8ea9423f1a7d8b31a149fc3b832d7980719c60c.portalcontent b/test_assets/mainnet/0x030013c08b64bf7e3afab80ad4f8ea9423f1a7d8b31a149fc3b832d7980719c60c.portalcontent new file mode 100644 index 000000000..929507e27 Binary files /dev/null and b/test_assets/mainnet/0x030013c08b64bf7e3afab80ad4f8ea9423f1a7d8b31a149fc3b832d7980719c60c.portalcontent differ diff --git a/test_assets/mainnet/0x03ed8823c84177d8ffabf104566f313a2b2a43d05304ba6c74c2f5555bae0ef329.portalcontent b/test_assets/mainnet/0x03ed8823c84177d8ffabf104566f313a2b2a43d05304ba6c74c2f5555bae0ef329.portalcontent new file mode 100644 index 000000000..e72014c53 Binary files /dev/null and b/test_assets/mainnet/0x03ed8823c84177d8ffabf104566f313a2b2a43d05304ba6c74c2f5555bae0ef329.portalcontent differ diff --git a/tests/self_peertest.rs b/tests/self_peertest.rs index 2081b201d..11c0e5b95 100644 --- a/tests/self_peertest.rs +++ b/tests/self_peertest.rs @@ -233,6 +233,42 @@ async fn peertest_history_offer_propagates_gossip() { handle.stop().unwrap(); } +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn peertest_history_offer_propagates_gossip_with_large_content() { + let (peertest, target, handle) = setup_peertest("mainnet").await; + peertest::scenarios::offer_accept::test_offer_propagates_gossip_with_large_content( + &peertest, &target, + ) + .await; + peertest.exit_all_nodes(); + handle.stop().unwrap(); +} + +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn peertest_history_offer_propagates_gossip_multiple_content_values() { + let (peertest, target, handle) = setup_peertest("mainnet").await; + peertest::scenarios::offer_accept::test_offer_propagates_gossip_multiple_content_values( + &peertest, &target, + ) + .await; + peertest.exit_all_nodes(); + handle.stop().unwrap(); +} + +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn peertest_history_offer_propagates_gossip_multiple_large_content_values() { + let (peertest, target, handle) = setup_peertest("mainnet").await; + peertest::scenarios::offer_accept::test_offer_propagates_gossip_multiple_large_content_values( + &peertest, &target, + ) + .await; + peertest.exit_all_nodes(); + handle.stop().unwrap(); +} + #[tokio::test(flavor = "multi_thread")] #[serial] async fn peertest_ping_cross_discv5_protocol_id() { diff --git a/trin-beacon/Cargo.toml b/trin-beacon/Cargo.toml index 491518639..0234a0975 100644 --- a/trin-beacon/Cargo.toml +++ b/trin-beacon/Cargo.toml @@ -29,7 +29,7 @@ tracing = "0.1.36" trin-metrics = { path = "../trin-metrics" } trin-storage = { path = "../trin-storage" } trin-validation = { path = "../trin-validation" } -utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.12" } +utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.13" } [dev-dependencies] serde_yaml = "0.9.33" diff --git a/trin-history/Cargo.toml b/trin-history/Cargo.toml index 46a10802b..3c51f69b2 100644 --- a/trin-history/Cargo.toml +++ b/trin-history/Cargo.toml @@ -24,7 +24,7 @@ tracing = "0.1.36" tree_hash = { git = "https://github.com/KolbyML/tree_hash.git", rev = "8aaf8bb4184148768d48e2cfbbdd0b95d1da8730" } trin-storage = { path = "../trin-storage" } trin-validation = { path = "../trin-validation" } -utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.12" } +utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.13" } [dev-dependencies] env_logger = "0.9.0" diff --git a/trin-state/Cargo.toml b/trin-state/Cargo.toml index 46b0889e6..59189f3b7 100644 --- a/trin-state/Cargo.toml +++ b/trin-state/Cargo.toml @@ -26,7 +26,7 @@ tokio = {version = "1.14.0", features = ["full"]} tracing = "0.1.36" trin-storage = { path = "../trin-storage" } trin-validation = { path = "../trin-validation" } -utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.12" } +utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.13" } [dev-dependencies] env_logger = "0.9.0" diff --git a/utp-testing/Cargo.toml b/utp-testing/Cargo.toml index bda6d61c1..5e7f7fce1 100644 --- a/utp-testing/Cargo.toml +++ b/utp-testing/Cargo.toml @@ -23,7 +23,7 @@ tracing-subscriber = "0.3.15" trin-utils = { path = "../trin-utils" } trin-validation = { path="../trin-validation" } tokio = {version = "1.14.0", features = ["full"]} -utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.12" } +utp-rs = { git = "https://github.com/ethereum/utp", tag = "v0.1.0-alpha.13" } [[bin]] name = "utp-test-app"