Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion ethexe/blob-loader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ rust-version.workspace = true
ethexe-common = { workspace = true, features = ["std"] }
gprimitives = { workspace = true, features = ["std"] }

alloy = { workspace = true, features = ["rpc", "rpc-types", "rpc-types-beacon"] }
alloy = { workspace = true, features = [
"rpc",
"rpc-types",
"rpc-types-beacon",
] }
thiserror.workspace = true
reqwest = { workspace = true, features = ["default-tls", "json"] }
serde_json.workspace = true
Expand All @@ -22,6 +26,7 @@ tokio = { workspace = true, features = ["rt-multi-thread", "fs"] }
gear-workspace-hack.workspace = true

[dev-dependencies]
ethexe-db = { workspace = true, features = ["mock"] }
gprimitives = { workspace = true, features = ["ethexe", "std"] }
gsigner.workspace = true
ethexe-ethereum.workspace = true
Expand Down
135 changes: 15 additions & 120 deletions ethexe/blob-loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ pub struct ConsensusLayerConfig {

#[derive(Clone)]
struct ConsensusLayerBlobReader {
pub provider: RootProvider,
pub http_client: Client,
pub config: ConsensusLayerConfig,
provider: RootProvider,
http_client: Client,
config: ConsensusLayerConfig,
}

impl ConsensusLayerBlobReader {
Expand Down Expand Up @@ -298,6 +298,16 @@ impl<DB: Database> BlobLoader<DB> {
db,
})
}

#[cfg(test)]
fn new_with_consensus_reader(db: DB, blobs_reader: ConsensusLayerBlobReader) -> Self {
Self {
futures: FuturesUnordered::new(),
codes_loading: HashSet::new(),
blobs_reader,
db,
}
}
}

impl<DB: Database> BlobLoaderService for BlobLoader<DB> {
Expand Down Expand Up @@ -332,8 +342,8 @@ impl<DB: Database> BlobLoaderService for BlobLoader<DB> {
async move {
blobs_reader
.read_blob(code_id, tx_hash)
.map(|res| res.map(|code| CodeAndIdUnchecked { code_id, code }))
.await
.map(|code| CodeAndIdUnchecked { code_id, code })
}
.boxed(),
);
Expand Down Expand Up @@ -381,119 +391,4 @@ fn handle_blob(
}

#[cfg(test)]
mod tests {
use super::*;
use alloy::{node_bindings::Anvil, providers::ext::AnvilApi};
use ethexe_common::gear_core::ids::prelude::CodeIdExt;
use ethexe_ethereum::deploy::EthereumDeployer;
use gsigner::secp256k1::{PrivateKey, Signer};

#[ignore = "until blob will be available on beacon node"]
#[tokio::test]
async fn test_read_code_from_tx_hash() {
let signer = Signer::memory();
let private_key: PrivateKey =
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
.parse()
.unwrap();
let public_key = signer.import(private_key).unwrap();
let alice_address = signer.address(public_key);

let beacon_block_time = Duration::from_secs(1);
let anvil = Anvil::new().block_time(beacon_block_time.as_secs()).spawn();

let ethereum = EthereumDeployer::new(&anvil.ws_endpoint(), signer.clone(), alice_address)
.await
.unwrap()
.with_validators(vec![alice_address].try_into().unwrap())
.deploy()
.await
.unwrap();

let consensus_cfg = ConsensusLayerConfig {
ethereum_rpc: anvil.endpoint(),
ethereum_beacon_rpc: anvil.endpoint(),
beacon_block_time,
attempts: const { NonZero::new(3).unwrap() },
};

let blobs_reader = ConsensusLayerBlobReader {
provider: ProviderBuilder::default()
.connect(&consensus_cfg.ethereum_rpc)
.await
.unwrap(),
http_client: Client::new(),
config: consensus_cfg,
};

let code = &[];
let (tx_hash, expected_code_id) = ethereum
.router()
.request_code_validation(code)
.await
.unwrap();

// set chain id to 1 to avoid anvil special case
blobs_reader.provider.anvil_set_chain_id(1).await.unwrap();

let code = blobs_reader
.read_blob(expected_code_id, tx_hash)
.await
.unwrap();
assert_eq!(expected_code_id, CodeId::generate(&code));
}

#[test]
fn test_handle_blob() {
let code_id = CodeId::generate(&[1, 2, 3, 4]);

// correct blob
let blob = vec![1, 2, 3, 4];
let mut previously_received_code_id = None;
let result =
handle_blob(blob.clone(), code_id, &mut previously_received_code_id, 1).unwrap();
assert_eq!(result, blob);

// blob with incorrect code id
let blob = vec![4, 3, 2, 1];
let blob_code_id = CodeId::generate(&blob);
let mut previously_received_code_id = None;
let result = handle_blob(blob.clone(), code_id, &mut previously_received_code_id, 1);
assert!(matches!(
result,
Err(ReaderError::CodeIdMismatch {
expected,
found,
}) if expected == code_id && found == blob_code_id
),);
assert_eq!(previously_received_code_id, Some(blob_code_id));

// same incorrect blob again - should be considered as loaded
let result =
handle_blob(blob.clone(), code_id, &mut previously_received_code_id, 2).unwrap();
assert_eq!(result, blob);

// same incorrect blob again, but another code id
let previously_received_code_id = CodeId::from([1; 32]);
let result = handle_blob(
blob.clone(),
code_id,
&mut Some(previously_received_code_id),
2,
);
assert!(matches!(
result,
Err(ReaderError::CodeIdMismatch {
expected,
found,
}) if expected == code_id && found == blob_code_id
));

// empty blob
let blob = vec![];
let mut previously_received_code_id = None;
let result = handle_blob(blob.clone(), code_id, &mut previously_received_code_id, 1);
assert!(result.is_err());
assert!(previously_received_code_id.is_none());
}
}
mod tests;
Loading
Loading