Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

Commit

Permalink
feat: add multiple new certificate events test
Browse files Browse the repository at this point in the history
  • Loading branch information
atanmarko committed Sep 12, 2023
1 parent ccf4f1c commit e1cf7f7
Showing 1 changed file with 312 additions and 8 deletions.
320 changes: 312 additions & 8 deletions crates/topos-sequencer-subnet-runtime/tests/subnet_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use test_log::test;
use tokio::sync::oneshot;
use topos_core::uci::{Certificate, CertificateId, SubnetId, SUBNET_ID_LENGTH};
use topos_sequencer_subnet_runtime::proxy::{SubnetRuntimeProxyCommand, SubnetRuntimeProxyEvent};
use tracing::{error, info, Span};
use tracing::{error, info, warn, Span};
use tracing_opentelemetry::OpenTelemetrySpanExt;

mod common;
Expand All @@ -31,15 +31,29 @@ use topos_sequencer_subnet_runtime::{SubnetRuntimeProxyConfig, SubnetRuntimeProx

use topos_test_sdk::constants::*;

const STANDALONE_SUBNET: &str = "standalone-test";
const STANDALONE_SUBNET_WITH_LONG_BLOCKS: &str = "standalone-test-long-blocks";

const SUBNET_RPC_PORT: u32 = 8545;
const TEST_SECRET_ETHEREUM_KEY: &str =
"d7e2e00b43c12cf17239d4755ed744df6ca70a933fc7c8bbb7da1342a5ff2e38";
"d7e2e00b43c12cf17239d4755ed744df6ca70a933fc7c8bbb7da1342a5ff2e38"; // Account 0x4AAb25B4fAd0Beaac466050f3A7142A502f4Cf0a
const POLYGON_EDGE_CONTAINER: &str = "ghcr.io/topos-protocol/polygon-edge";
const POLYGON_EDGE_CONTAINER_TAG: &str = "develop";
const SUBNET_STARTUP_DELAY: u64 = 5; // seconds left for subnet startup
const TEST_SUBNET_ID: &str = "6464646464646464646464646464646464646464646464646464646464646464";
const ZERO_ADDRESS: &str = "0000000000000000000000000000000000000000";

// Accounts pre-filled in STANDALONE_SUBNET_WITH_LONG_BLOCKS
const TEST_ACCOUNT_ALITH_KEY: &str =
"5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133"; // Account 0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac
const TEST_ACCOUNT_ALITH_ACCOUNT: &str = "0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac";
const TEST_ACCOUNT_BALATHAR_KEY: &str =
"8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b"; // Account 0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07
const TEST_ACCOUNT_BALATHAR_ACCOUNT: &str = "0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07";
const TEST_ACCOUNT_CEZAR_KEY: &str =
"11eddfae7abe45531b3f18342c8062969323a7131d3043f1a33c40df74803cc7"; // Account 0x5283ac54A7B9669F6415168DC7a5FcEe05019E45
const TEST_ACCOUNT_CEZAR_ACCOUNT: &str = "0x5283ac54A7B9669F6415168DC7a5FcEe05019E45";

const PREV_CERTIFICATE_ID_1: CertificateId = CERTIFICATE_ID_4;
const PREV_CERTIFICATE_ID_2: CertificateId = CERTIFICATE_ID_5;
const CERTIFICATE_ID_1: CertificateId = CERTIFICATE_ID_6;
Expand Down Expand Up @@ -108,7 +122,14 @@ fn spawn_subnet_node(
stop_subnet_receiver: tokio::sync::oneshot::Receiver<()>,
subnet_ready_sender: tokio::sync::oneshot::Sender<()>,
port: u32,
subnet_type: &str,
) -> Result<tokio::task::JoinHandle<()>, Box<dyn std::error::Error>> {
let subnet_test_type = subnet_type.to_string();
let subnet_startup_delay: u64 = if subnet_type == STANDALONE_SUBNET_WITH_LONG_BLOCKS {
SUBNET_STARTUP_DELAY * 6
} else {
SUBNET_STARTUP_DELAY
};
let handle = tokio::task::spawn_blocking(move || {
let source = Source::DockerHub;
let img = Image::with_repository(POLYGON_EDGE_CONTAINER)
Expand All @@ -123,7 +144,7 @@ fn spawn_subnet_node(
let mut polygon_edge_node_docker = DockerTest::new().with_default_source(source);

// Setup command for polygon edge binary
let cmd: Vec<String> = vec!["standalone-test".to_string()];
let cmd: Vec<String> = vec![subnet_test_type];

polygon_edge_node_docker.add_composition(
polygon_edge_node
Expand All @@ -142,7 +163,7 @@ fn spawn_subnet_node(
container.name(),
);
// TODO: use polling of network block number or some other means to learn when subnet node has started
tokio::time::sleep(tokio::time::Duration::from_secs(SUBNET_STARTUP_DELAY)).await;
tokio::time::sleep(tokio::time::Duration::from_secs(subnet_startup_delay)).await;
subnet_ready_sender
.send(())
.expect("subnet ready channel available");
Expand Down Expand Up @@ -198,6 +219,40 @@ impl Drop for Context {
}
}

async fn create_new_erc20msg_client(
deploy_key: &str,
endpoint: &str,
erc20_messaging_contract_address: Address,
) -> Result<IERC20MessagingClient, Box<dyn std::error::Error>> {
let wallet: LocalWallet = deploy_key.parse()?;
let http_provider =
Provider::<Http>::try_from(endpoint)?.interval(std::time::Duration::from_millis(20u64));
let chain_id = http_provider.get_chainid().await?;
let client = Arc::new(SignerMiddleware::new(
http_provider,
wallet.clone().with_chain_id(chain_id.as_u64()),
));
let i_erc20_messaging = IERC20Messaging::new(erc20_messaging_contract_address, client);
Ok(i_erc20_messaging)
}

async fn create_new_erc20_client(
deploy_key: &str,
endpoint: &str,
erc20_contract_address: Address,
) -> Result<IERC20Client, Box<dyn std::error::Error>> {
let wallet: LocalWallet = deploy_key.parse()?;
let http_provider =
Provider::<Http>::try_from(endpoint)?.interval(std::time::Duration::from_millis(20u64));
let chain_id = http_provider.get_chainid().await?;
let client = Arc::new(SignerMiddleware::new(
http_provider,
wallet.clone().with_chain_id(chain_id.as_u64()),
));
let i_erc20 = IERC20::new(erc20_contract_address, client);
Ok(i_erc20)
}

async fn deploy_contracts(
deploy_key: &str,
endpoint: &str,
Expand Down Expand Up @@ -396,21 +451,27 @@ async fn deploy_test_token(
.from_block(0);
let events = events.query().await?;
let token_address = events[0].token_address;
info!("Token contract deploye to {}", token_address.to_string());
info!("Token contract deployed to {}", token_address.to_string());

let i_erc20 = IERC20Client::new(token_address, client);

Ok(i_erc20)
}

#[fixture]
async fn context_running_subnet_node(#[default(8545)] port: u32) -> Context {
async fn context_running_subnet_node(
#[default(8545)] port: u32,
#[default(STANDALONE_SUBNET)] subnet_type: &str,
) -> Context {
let (subnet_stop_sender, subnet_stop_receiver) = oneshot::channel::<()>();
let (subnet_ready_sender, subnet_ready_receiver) = oneshot::channel::<()>();
info!("Starting subnet node...");
info!(
"Starting subnet node on port {}, subnet_type: {}",
port, subnet_type
);

let subnet_node_handle =
match spawn_subnet_node(subnet_stop_receiver, subnet_ready_sender, port) {
match spawn_subnet_node(subnet_stop_receiver, subnet_ready_sender, port, subnet_type) {
Ok(subnet_node_handle) => subnet_node_handle,
Err(e) => {
panic!("Failed to start the polygon edge subnet node as part of test context: {e}");
Expand Down Expand Up @@ -918,3 +979,246 @@ async fn test_subnet_send_token_processing(
context.shutdown().await?;
Ok(())
}

/// Test multiple send token events in a block
/// Test is slow, block time is 12 seconds
#[rstest]
#[test(tokio::test)]
#[serial]
async fn test_subnet_multiple_send_token_in_a_block(
#[with(8546, STANDALONE_SUBNET_WITH_LONG_BLOCKS)]
#[future]
context_running_subnet_node: Context,
) -> Result<(), Box<dyn std::error::Error>> {
let context = context_running_subnet_node.await;
let test_private_key = hex::decode(TEST_SECRET_ETHEREUM_KEY).unwrap();
let subnet_jsonrpc_endpoint = "http://".to_string() + &context.jsonrpc();
let subnet_smart_contract_address =
"0x".to_string() + &hex::encode(context.i_topos_core.address());
let number_of_send_token_transactions: usize = 4;

warn!("Block time is intentionally long, this is slow test...");

// Create runtime proxy worker
info!("Creating subnet runtime proxy");
let mut runtime_proxy_worker = SubnetRuntimeProxyWorker::new(
SubnetRuntimeProxyConfig {
subnet_id: SOURCE_SUBNET_ID_1,
endpoint: context.jsonrpc(),
subnet_contract_address: subnet_smart_contract_address.clone(),
verifier: 0,
source_head_certificate_id: None,
},
test_private_key.clone(),
)
.await?;
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
info!("Set source head certificate to 0");
if let Err(e) = runtime_proxy_worker
.set_source_head_certificate_id(Some((CERTIFICATE_ID_1, 0)))
.await
{
panic!("Unable to set source head certificate id: {e}");
}

// Deploy token contract
let i_erc20 = deploy_test_token(
&hex::encode(&test_private_key),
&subnet_jsonrpc_endpoint,
context.i_topos_messaging.address(),
)
.await?;

// Send token to other addresses
info!("Transfer tokens");
let test_accounts: Vec<_> = vec![
TEST_ACCOUNT_ALITH_ACCOUNT.parse()?,
TEST_ACCOUNT_BALATHAR_ACCOUNT.parse()?,
TEST_ACCOUNT_CEZAR_ACCOUNT.parse()?,
];
for test_account in test_accounts {
if let Err(e) = i_erc20
.transfer(test_account, U256::from(10))
.legacy()
.gas(DEFAULT_GAS)
.send()
.await?
.await
{
panic!("Unable to perform token transfer {e}");
}
}
info!("Tokens transfered");

let mut erc20_clients = vec![
create_new_erc20_client(
TEST_SECRET_ETHEREUM_KEY,
&subnet_jsonrpc_endpoint,
context.i_erc20_messaging.address(),
)
.await
.expect("Valid erc20 client"),
create_new_erc20_client(
TEST_ACCOUNT_ALITH_KEY,
&subnet_jsonrpc_endpoint,
context.i_erc20_messaging.address(),
)
.await
.expect("Valid erc20 client"),
create_new_erc20_client(
TEST_ACCOUNT_BALATHAR_KEY,
&subnet_jsonrpc_endpoint,
context.i_erc20_messaging.address(),
)
.await
.expect("Valid erc20 client"),
create_new_erc20_client(
TEST_ACCOUNT_CEZAR_KEY,
&subnet_jsonrpc_endpoint,
context.i_erc20_messaging.address(),
)
.await
.expect("Valid erc20 client"),
];

// Approve token spending
info!("Approve token spending");
for erc20_client in &mut erc20_clients {
if let Err(e) = erc20_client
.approve(context.i_topos_messaging.address(), U256::from(10))
.legacy()
.gas(DEFAULT_GAS)
.send()
.await?
.await
{
panic!("Unable to perform token approval {e}");
}
}
info!("Token spending approved");

info!("Initializing multiple subnet clients");
let mut target_subnets = vec![
(
TARGET_SUBNET_ID_5,
create_new_erc20msg_client(
TEST_SECRET_ETHEREUM_KEY,
&subnet_jsonrpc_endpoint,
context.i_erc20_messaging.address(),
)
.await
.expect("Valid client"),
),
(
TARGET_SUBNET_ID_4,
create_new_erc20msg_client(
TEST_ACCOUNT_ALITH_KEY,
&subnet_jsonrpc_endpoint,
context.i_erc20_messaging.address(),
)
.await
.expect("Valid client"),
),
(
TARGET_SUBNET_ID_3,
create_new_erc20msg_client(
TEST_ACCOUNT_BALATHAR_KEY,
&subnet_jsonrpc_endpoint,
context.i_erc20_messaging.address(),
)
.await
.expect("Valid client"),
),
(
TARGET_SUBNET_ID_2,
create_new_erc20msg_client(
TEST_ACCOUNT_CEZAR_KEY,
&subnet_jsonrpc_endpoint,
context.i_erc20_messaging.address(),
)
.await
.expect("Valid client"),
),
];

// Perform multiple send token actions
info!("Sending multiple transactions in parallel");
let mut handles = Vec::new();
for i in 1..=number_of_send_token_transactions {
let i_erc20_address = i_erc20.address();
let (target_subnet, i_erc20_messaging) = target_subnets.pop().unwrap();
let handle = tokio::spawn(async move {
info!("Sending transaction {i}");
if let Err(e) = i_erc20_messaging
.send_token(
target_subnet.into(),
i_erc20_address,
"00000000000000000000000000000000000000AA".parse().unwrap(),
U256::from(i),
)
.legacy()
.gas(DEFAULT_GAS)
.send()
.await
.map_err(|e| {
error!("Unable to send token, contract error: {e}");
})
.unwrap()
.await
{
error!("Unable to send token {e}");
panic!("Unable to send token: {e}");
};
info!("Transaction {} sent", i);
});
handles.push(handle);
}
for handle in handles {
handle.await.expect("Send token task correctly finished");
}
info!("All token transactions sent!");

info!("Waiting for certificate with send token transaction...");
let mut received_certificates = Vec::new();
let assertion = async move {
while let Ok(event) = runtime_proxy_worker.next_event().await {
if let SubnetRuntimeProxyEvent::NewCertificate {
cert,
block_number,
ctx: _,
} = event
{
info!(
"New certificate event received, block number: {} cert id: {} target subnets: {:?}",
block_number, cert.id, cert.target_subnets
);
if !cert.target_subnets.is_empty() {
received_certificates.push(cert);
if received_certificates.len() == number_of_send_token_transactions {
info!(
"Received all expected certificates for subnets {:?}",
received_certificates
.iter()
.flat_map(|c| c.target_subnets.iter())
.collect::<Vec<_>>()
);
return Ok::<(), Box<dyn std::error::Error>>(());
}
}
}
}
panic!("Expected event not received");
};

// Set big timeout to prevent flaky fails. Instead fail/panic early in the test to indicate actual error
if tokio::time::timeout(std::time::Duration::from_secs(60), assertion)
.await
.is_err()
{
panic!("Timeout waiting for command");
}

info!("Shutting down context...");
context.shutdown().await?;
Ok(())
}

0 comments on commit e1cf7f7

Please sign in to comment.