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

Commit

Permalink
feat: send token sequencer test
Browse files Browse the repository at this point in the history
  • Loading branch information
Marko Atanasievski committed May 16, 2023
1 parent 789b803 commit 080db61
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 12 deletions.
1 change: 0 additions & 1 deletion crates/topos-sequencer-subnet-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ impl SubnetClientListener {
.await
.map_err(Error::EthersProviderError)?
.ok_or(Error::InvalidBlockNumber(next_block_number))?;

let block_number = block
.number
.ok_or(Error::InvalidBlockNumber(next_block_number))?;
Expand Down
2 changes: 2 additions & 0 deletions crates/topos-sequencer-subnet-client/src/subnet_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use ethers::{
signers::Signer,
};
use std::sync::Arc;
use tracing::{info};

abigen!(IToposCore, "abi/IToposCore.json");

Expand All @@ -33,6 +34,7 @@ pub(crate) async fn get_block_events(

for event in topos_core_events {
if let IToposCoreEvents::CrossSubnetMessageSentFilter(f) = event {
info!("Received CrossSubnetMessageSentFilter event: {f:?}");
result.push(SubnetEvent::CrossSubnetMessageSent {
target_subnet_id: f.target_subnet_id.into(),
})
Expand Down
209 changes: 198 additions & 11 deletions crates/topos-sequencer-subnet-runtime/tests/subnet_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use dockertest::{
Composition, DockerTest, Image, LogAction, LogOptions, LogPolicy, LogSource, PullPolicy, Source,
};
use ethers::{
abi::{ethabi::ethereum_types::U256, Token},
abi::{ethabi::ethereum_types::U256, Address, Token},
contract::abigen,
core::k256::ecdsa::SigningKey,
core::types::Filter,
Expand All @@ -18,7 +18,7 @@ use std::collections::HashSet;
use std::sync::Arc;
use test_log::test;
use tokio::sync::oneshot;
use topos_sequencer_subnet_runtime::proxy::SubnetRuntimeProxyCommand;
use topos_sequencer_subnet_runtime::proxy::{SubnetRuntimeProxyCommand, SubnetRuntimeProxyEvent};
use tracing::{error, info, Span};
use tracing_opentelemetry::OpenTelemetrySpanExt;

Expand Down Expand Up @@ -50,9 +50,26 @@ abigen!(ToposCoreProxyContract, "npm:@topos-network/topos-smart-contracts@1.0.1/
abigen!(ToposMessagingContract, "npm:@topos-network/topos-smart-contracts@1.0.1/artifacts/contracts/topos-core/ToposMessaging.sol/ToposMessaging.json");
abigen!(IToposCore, "npm:@topos-network/topos-smart-contracts@1.0.1/artifacts/contracts/interfaces/IToposCore.sol/IToposCore.json");
abigen!(IToposMessaging, "npm:@topos-network/topos-smart-contracts@1.0.1/artifacts/contracts/interfaces/IToposMessaging.sol/IToposMessaging.json");
abigen!(
IERC20,
r"[
function totalSupply() external view returns (uint)
function balanceOf(address account) external view returns (uint)
function transfer(address recipient, uint amount) external returns (bool)
function allowance(address owner, address spender) external view returns (uint)
function approve(address spender, uint amount) external returns (bool)
function transferFrom(address sender, address recipient, uint amount) external returns (bool)
]"
);

type IToposCoreClient = IToposCore<SignerMiddleware<Provider<Http>, Wallet<SigningKey>>>;
type IToposMessagingClient = IToposMessaging<SignerMiddleware<Provider<Http>, Wallet<SigningKey>>>;
type IERC20Client = IERC20<SignerMiddleware<Provider<Http>, Wallet<SigningKey>>>;

fn spawn_subnet_node(
stop_subnet_receiver: tokio::sync::oneshot::Receiver<()>,
Expand Down Expand Up @@ -108,8 +125,8 @@ fn spawn_subnet_node(

#[allow(dead_code)]
struct Context {
pub topos_core: IToposCoreClient,
pub topos_messaging: IToposMessagingClient,
pub i_topos_core: IToposCoreClient,
pub i_topos_messaging: IToposMessagingClient,
pub subnet_node_handle: Option<tokio::task::JoinHandle<()>>,
pub subnet_stop_sender: Option<tokio::sync::oneshot::Sender<()>>,
pub port: u32,
Expand Down Expand Up @@ -261,6 +278,70 @@ async fn deploy_contracts(
Ok((i_topos_core, i_topos_messaging))
}

async fn deploy_test_token(
deploy_key: &str,
endpoint: &str,
topos_messaging_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_topos_messaging = IToposMessaging::new(topos_messaging_address, client.clone());

// Deploy token
let token_name: Token = Token::String("Test Token".to_string());
let token_symbol: Token = Token::String("TKX".to_string());
let token_mint_cap: Token = Token::Uint(U256::from(100_000_000));
let token_address_zero: Token =
Token::Address("0000000000000000000000000000000000000000".parse()?);
let token_daily_mint_limit: Token = Token::Uint(U256::from(100));
let token_initial_supply: Token = Token::Uint(U256::from(10_000_000));
let token_encoded_params: ethers::types::Bytes = ethers::abi::encode(&[
token_name.clone(),
token_symbol.clone(),
token_mint_cap,
token_address_zero,
token_daily_mint_limit,
token_initial_supply,
])
.into();
info!(
"Deploying new token {} with symbol {}",
token_name, token_symbol
);
if let Err(e) = i_topos_messaging
.deploy_token(token_encoded_params)
.legacy()
.gas(5000000)
.send()
.await
.map_err(|e| {
error!("Unable deploy token: {e}");
e
})?
.await
{
panic!("Error deploying token: {e}");
};

let events = i_topos_messaging
.event::<i_topos_messaging::TokenDeployedFilter>()
.from_block(0);
let events = events.query().await?;
let token_address = events[0].token_address;
info!("Token contract deploye 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 {
let (subnet_stop_sender, subnet_stop_receiver) = oneshot::channel::<()>();
Expand All @@ -286,8 +367,8 @@ async fn context_running_subnet_node(#[default(8545)] port: u32) -> Context {
info!("Contracts successfully deployed");
// Context with subnet container working in the background and ready deployed contracts
Context {
topos_core: i_topos_core,
topos_messaging: i_topos_messaging,
i_topos_core: i_topos_core,
i_topos_messaging: i_topos_messaging,
subnet_node_handle: Some(subnet_node_handle),
subnet_stop_sender: Some(subnet_stop_sender),
port,
Expand Down Expand Up @@ -328,7 +409,7 @@ async fn test_subnet_node_get_block_info(
let context = context_running_subnet_node.await;
match topos_sequencer_subnet_client::SubnetClientListener::new(
&context.jsonrpc_ws(),
&("0x".to_string() + &hex::encode(context.topos_core.address())),
&("0x".to_string() + &hex::encode(context.i_topos_core.address())),
)
.await
{
Expand Down Expand Up @@ -390,7 +471,7 @@ async fn test_subnet_certificate_push_call(
let context = context_running_subnet_node.await;
let test_private_key = generate_test_private_key();
let subnet_smart_contract_address =
"0x".to_string() + &hex::encode(context.topos_core.address());
"0x".to_string() + &hex::encode(context.i_topos_core.address());
let runtime_proxy_worker = SubnetRuntimeProxyWorker::new(
SubnetRuntimeProxyConfig {
subnet_id: SOURCE_SUBNET_ID_1,
Expand Down Expand Up @@ -437,7 +518,7 @@ async fn test_subnet_certificate_push_call(
let provider = Provider::<Http>::try_from(format!("http://127.0.0.1:{}", context.port))?;
let client = Arc::new(provider);
let filter = Filter::new()
.address(context.topos_core.address())
.address(context.i_topos_core.address())
.event("CertStored(bytes32,bytes32)")
.from_block(0);
let logs = client.get_logs(&filter).await?;
Expand Down Expand Up @@ -473,7 +554,7 @@ async fn test_subnet_certificate_get_checkpoints_call(
use topos_core::api::checkpoints;
let context = context_running_subnet_node.await;
let subnet_smart_contract_address =
"0x".to_string() + &hex::encode(context.topos_core.address());
"0x".to_string() + &hex::encode(context.i_topos_core.address());
let subnet_jsonrpc_endpoint = "http://".to_string() + &context.jsonrpc();

// Get checkpoints when contract is empty
Expand Down Expand Up @@ -591,7 +672,7 @@ async fn test_subnet_id_call(
) -> Result<(), Box<dyn std::error::Error>> {
let context = context_running_subnet_node.await;
let subnet_smart_contract_address =
"0x".to_string() + &hex::encode(context.topos_core.address());
"0x".to_string() + &hex::encode(context.i_topos_core.address());
let subnet_jsonrpc_endpoint = "http://".to_string() + &context.jsonrpc();

// Create subnet client
Expand Down Expand Up @@ -625,3 +706,109 @@ async fn test_subnet_id_call(
context.shutdown().await?;
Ok(())
}

/// Test perform send token and check for transaction
/// in the certificate (by observing target subnets)
#[rstest]
#[test(tokio::test)]
#[serial]
async fn test_subnet_send_token_processing(
#[with(8546)]
#[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());

// 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))
.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?;

// Approve token spending
if let Err(e) = i_erc20
.approve(context.i_topos_messaging.address(), U256::from(10))
.legacy()
.gas(400000)
.send()
.await?
.await
{
panic!("Unable to perform token approval {e}");
}

// Perform send token
info!("Sending token");
if let Err(e) = context
.i_topos_messaging
.send_token(
TARGET_SUBNET_ID_2.into(),
"00000000000000000000000000000000000000AA".parse()?,
i_erc20.address(),
U256::from(2),
)
.legacy()
.gas(5_000_000)
.send()
.await?
.await
{
panic!("Unable to send token: {e}");
};
info!("Waiting for certificate with send token transaction...");
let assertion = async move {
while let Ok(event) = runtime_proxy_worker.next_event().await {
match event {
SubnetRuntimeProxyEvent::NewCertificate{cert, ctx: _} => {
info!("New certificate event received, cert id: {} target subnets: {:?}", cert.id, cert.target_subnets);
if cert.target_subnets.len() == 1 && cert.target_subnets == vec![TARGET_SUBNET_ID_2] {
info!("Received certificate with requested target subnet {}", cert.target_subnets[0]);
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(10), assertion)
.await
.is_err()
{
panic!("Timeout waiting for command");
}

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

0 comments on commit 080db61

Please sign in to comment.