diff --git a/crates/contracts/hyllar/src/lib.rs b/crates/contracts/hyllar/src/lib.rs index a9871e9a..ceb0faac 100644 --- a/crates/contracts/hyllar/src/lib.rs +++ b/crates/contracts/hyllar/src/lib.rs @@ -20,7 +20,7 @@ pub mod indexer; #[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Debug, Clone)] pub struct Hyllar { total_supply: u128, - balances: BTreeMap, // Balances for each account + pub balances: BTreeMap, // Balances for each account #[serde_as(as = "Vec<(_, _)>")] allowances: BTreeMap<(String, String), u128>, // Allowances (owner, spender) } diff --git a/tests/consensus_tests.rs b/tests/consensus_tests.rs index a1b2f966..c5ec0f3c 100644 --- a/tests/consensus_tests.rs +++ b/tests/consensus_tests.rs @@ -6,8 +6,10 @@ mod fixtures; mod e2e_consensus { + use std::time::Duration; + use client_sdk::helpers::risc0::Risc0Prover; - use client_sdk::transaction_builder::{ProvableBlobTx, TxExecutorBuilder}; + use client_sdk::transaction_builder::{ProvableBlobTx, TxExecutor, TxExecutorBuilder}; use fixtures::test_helpers::send_transaction; use hydentity::client::{register_identity, verify_identity}; use hydentity::Hydentity; @@ -20,7 +22,7 @@ mod e2e_consensus { use hyllar::Hyllar; use staking::client::{delegate, stake}; use staking::state::Staking; - use tracing::info; + use tracing::{info, warn}; use super::*; @@ -160,6 +162,50 @@ mod e2e_consensus { Ok(()) } + async fn gen_txs( + ctx: &mut E2ECtx, + tx_ctx: &mut TxExecutor, + id: String, + amount: u128, + ) -> Result<()> { + let identity = Identity(format!("{}.hydentity", id)); + { + let mut transaction = ProvableBlobTx::new(identity.clone()); + + register_identity(&mut transaction, "hydentity".into(), "password".to_owned())?; + + let tx_hash = send_transaction(ctx.client(), transaction, tx_ctx).await; + tracing::warn!("Register TX Hash: {:?}", tx_hash); + } + + // tokio::time::sleep(Duration::from_millis(500)).await; + + { + let mut transaction = ProvableBlobTx::new("faucet.hydentity".into()); + + verify_identity( + &mut transaction, + "hydentity".into(), + &tx_ctx.hydentity, + "password".to_string(), + )?; + + transfer( + &mut transaction, + "hyllar".into(), + identity.0.clone(), + amount, + )?; + + let tx_hash = send_transaction(ctx.client(), transaction, tx_ctx).await; + tracing::warn!("Transfer TX Hash: {:?}", tx_hash); + } + + // tokio::time::sleep(Duration::from_millis(500)).await; + + Ok(()) + } + #[test_log::test(tokio::test)] async fn can_rejoin_blocking_consensus() -> Result<()> { let mut ctx = E2ECtx::new_multi_with_indexer(2, 500).await?; @@ -205,4 +251,83 @@ mod e2e_consensus { Ok(()) } + + async fn init_states(ctx: &mut E2ECtx) -> TxExecutor { + let hyllar: Hyllar = ctx + .indexer_client() + .fetch_current_state(&"hyllar".into()) + .await + .unwrap(); + let hydentity: Hydentity = ctx + .indexer_client() + .fetch_current_state(&"hydentity".into()) + .await + .unwrap(); + + let states = States { + hyllar, + hydentity, + staking: Staking::default(), + }; + + TxExecutorBuilder::new(states) + // Replace prover binaries for non-reproducible mode. + .with_prover("hydentity".into(), Risc0Prover::new(HYDENTITY_ELF)) + .with_prover("hyllar".into(), Risc0Prover::new(HYLLAR_ELF)) + .build() + } + + #[test_log::test(tokio::test)] + async fn can_restart_single_node_after_txs() -> Result<()> { + let mut ctx = E2ECtx::new_single_with_indexer(500).await?; + + _ = ctx.wait_height(1).await; + + let consensus = ctx.client().get_consensus_info().await?; + assert_eq!(consensus.validators.len(), 0, "expected 0 validators"); + + // Gen a few txs + let mut tx_ctx = init_states(&mut ctx).await; + + warn!("Starting generating txs"); + + for i in 0..6 { + _ = gen_txs(&mut ctx, &mut tx_ctx, format!("alex{}", i), 100 + i).await; + } + + ctx.wait_height(1).await?; + + ctx.stop_node(0).await?; + ctx.restart_node(0)?; + + ctx.wait_height(3).await?; + + // Resync last state + // let mut tx_ctx = init_states(&mut ctx).await; + + let consensus = ctx.client().get_consensus_info().await?; + assert_eq!(consensus.validators.len(), 0, "expected 0 validators"); + + for i in 6..10 { + _ = gen_txs(&mut ctx, &mut tx_ctx, format!("alex{}", i), 100 + i).await; + tokio::time::sleep(Duration::from_millis(100)).await; + } + + ctx.wait_height(1).await?; + + let state: Hyllar = ctx + .indexer_client() + .fetch_current_state(&ContractName::new("hyllar")) + .await?; + + dbg!(&state.balances); + + for i in 0..10 { + let balance = state.balances.get(&*format!("alex{}.hydentity", i)); + info!("Checking alex{}.hydentity balance: {:?}", i, balance); + assert_eq!(balance.unwrap(), &((100 + i) as u128)); + } + + Ok(()) + } } diff --git a/tests/fixtures/ctx.rs b/tests/fixtures/ctx.rs index a995e9ca..1190451e 100644 --- a/tests/fixtures/ctx.rs +++ b/tests/fixtures/ctx.rs @@ -121,6 +121,53 @@ impl E2ECtx { }) } + pub async fn new_single_with_indexer(slot_duration: u64) -> Result { + std::env::set_var("RISC0_DEV_MODE", "1"); + + let pg = Self::init().await; + + let mut conf_maker = ConfMaker::default(); + conf_maker.default.consensus.slot_duration = slot_duration; + conf_maker.default.single_node = Some(true); + conf_maker.default.consensus.genesis_stakers = + vec![("single-node".to_string(), 100)].into_iter().collect(); + conf_maker.default.database_url = format!( + "postgres://postgres:postgres@localhost:{}/postgres", + pg.get_host_port_ipv4(5432).await.unwrap() + ); + + let node_conf = conf_maker.build("single-node"); + let node = test_helpers::TestProcess::new("hyle", node_conf.clone()) + //.log("hyle=info,tower_http=error") + .start(); + + // Request something on node1 to be sure it's alive and working + let client = NodeApiHttpClient { + url: Url::parse(&format!("http://{}", &node.conf.rest)).unwrap(), + reqwest_client: Client::new(), + }; + + // Start indexer + let mut indexer_conf = conf_maker.build("indexer"); + indexer_conf.da_address = node_conf.da_address.clone(); + let indexer = test_helpers::TestProcess::new("indexer", indexer_conf.clone()).start(); + + let url = format!("http://{}", &indexer_conf.rest); + let indexer_client = Some(IndexerApiHttpClient::new(url).unwrap()); + + // Wait for node2 to properly spin up + wait_height(&client, 1).await?; + + info!("🚀 E2E test environment is ready!"); + Ok(E2ECtx { + pg: Some(pg), + nodes: vec![node, indexer], + clients: vec![client], + client_index: 0, + indexer_client, + slot_duration, + }) + } pub async fn new_multi(count: usize, slot_duration: u64) -> Result { std::env::set_var("RISC0_DEV_MODE", "1");