From a432019b23392ac27bdc2adbca73380464ce1f5c Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 4 Sep 2025 17:05:12 -0300 Subject: [PATCH] wip --- integration_tests/Cargo.toml | 1 + integration_tests/src/lib.rs | 48 ++++++++++++++++++ nssa/Cargo.toml | 3 ++ nssa/program_methods/guest/src/bin/pinata.rs | 16 +++--- nssa/src/program.rs | 9 +++- nssa/src/state.rs | 53 +++++++------------- sequencer_core/Cargo.toml | 4 ++ sequencer_core/src/sequencer_store/mod.rs | 6 ++- sequencer_runner/src/lib.rs | 4 +- wallet/src/lib.rs | 52 +++++++++++++++++-- 10 files changed, 145 insertions(+), 51 deletions(-) diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 16e1b2e..644d2e3 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -20,6 +20,7 @@ workspace = true [dependencies.sequencer_core] path = "../sequencer_core" +features = ["testnet"] [dependencies.sequencer_runner] path = "../sequencer_runner" diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 9b7b970..cd2260f 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -272,6 +272,50 @@ pub async fn test_success_two_transactions() { info!("Second TX Success!"); } +pub async fn test_pinata() { + let pinata_addr = "cafe".repeat(16); + let pinata_prize = 150; + let solution = 989106; + let command = Command::ClaimPinata { + pinata_addr: pinata_addr.clone(), + winner_addr: ACC_SENDER.to_string(), + solution, + }; + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let pinata_balance_pre = seq_client + .get_account_balance(pinata_addr.clone()) + .await + .unwrap() + .balance; + + wallet::execute_subcommand(command).await.unwrap(); + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Checking correct balance move"); + let pinata_balance_post = seq_client + .get_account_balance(pinata_addr.clone()) + .await + .unwrap() + .balance; + + let winner_balance_post = seq_client + .get_account_balance(ACC_SENDER.to_string()) + .await + .unwrap() + .balance; + + assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize); + assert_eq!(winner_balance_post, 10000 + pinata_prize); + + info!("Success!"); +} + macro_rules! test_cleanup_wrap { ($home_dir:ident, $test_func:ident) => {{ let res = pre_test($home_dir.clone()).await.unwrap(); @@ -307,11 +351,15 @@ pub async fn main_tests_runner() -> Result<()> { "test_success_two_transactions" => { test_cleanup_wrap!(home_dir, test_success_two_transactions); } + "test_pinata" => { + test_cleanup_wrap!(home_dir, test_pinata); + } "all" => { test_cleanup_wrap!(home_dir, test_success_move_to_another_account); test_cleanup_wrap!(home_dir, test_success); test_cleanup_wrap!(home_dir, test_failure); test_cleanup_wrap!(home_dir, test_success_two_transactions); + test_cleanup_wrap!(home_dir, test_pinata); } _ => { anyhow::bail!("Unknown test name"); diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 3163902..25907a1 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -18,3 +18,6 @@ hex = "0.4.3" [dev-dependencies] test-program-methods = { path = "test_program_methods" } hex-literal = "1.0.0" + +[features] +default = [] diff --git a/nssa/program_methods/guest/src/bin/pinata.rs b/nssa/program_methods/guest/src/bin/pinata.rs index 43b0190..fbea167 100644 --- a/nssa/program_methods/guest/src/bin/pinata.rs +++ b/nssa/program_methods/guest/src/bin/pinata.rs @@ -1,4 +1,4 @@ -use nssa_core::program::{read_nssa_inputs, write_nssa_outputs, ProgramInput}; +use nssa_core::program::{ProgramInput, read_nssa_inputs, write_nssa_outputs}; use risc0_zkvm::sha::{Impl, Sha256}; const PRIZE: u128 = 150; @@ -21,10 +21,12 @@ impl Challenge { Self { difficulty, seed } } - fn is_nonce_valid(&self, nonce: Instruction) -> bool { + // Checks if the leftmost `self.difficulty` number of bytes of SHA256(self.data || solution) are + // zero. + fn validate_solution(&self, solution: Instruction) -> bool { let mut bytes = [0; 32 + 16]; - bytes.copy_from_slice(&self.seed); - bytes[32..].copy_from_slice(&nonce.to_le_bytes()); + bytes[..32].copy_from_slice(&self.seed); + bytes[32..].copy_from_slice(&solution.to_le_bytes()); let digest: [u8; 32] = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap(); let difficulty = self.difficulty as usize; digest[..difficulty].iter().all(|&b| b == 0) @@ -33,7 +35,7 @@ impl Challenge { fn next_data(self) -> [u8; 33] { let mut result = [0; 33]; result[0] = self.difficulty; - result.copy_from_slice(Impl::hash_bytes(&self.seed).as_bytes()); + result[1..].copy_from_slice(Impl::hash_bytes(&self.seed).as_bytes()); result } } @@ -44,7 +46,7 @@ fn main() { // It is expected to receive only two accounts: [pinata_account, winner_account] let ProgramInput { pre_states, - instruction: nonce, + instruction: solution, } = read_nssa_inputs::(); let [pinata, winner] = match pre_states.try_into() { @@ -54,7 +56,7 @@ fn main() { let data = Challenge::new(&pinata.account.data); - if !data.is_nonce_valid(nonce) { + if !data.validate_solution(solution) { return; } diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 63b7dc4..4828246 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -2,7 +2,9 @@ use nssa_core::{ account::{Account, AccountWithMetadata}, program::{InstructionData, ProgramId, ProgramOutput}, }; -use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID}; +use program_methods::{ + AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID, +}; use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec}; use serde::Serialize; @@ -73,11 +75,14 @@ impl Program { elf: AUTHENTICATED_TRANSFER_ELF, } } +} +// TODO: This is for testnet only, consider refactoring to have this not compiled for mainnet +impl Program { pub fn pinata() -> Self { Self { id: PINATA_ID, - elf: PINATA_ELF + elf: PINATA_ELF, } } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index ba6c49b..ef90f37 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -86,41 +86,6 @@ impl V01State { this } - pub fn add_pinata_accounts(&mut self) { - self.insert_program(Program::pinata()); - - let mut rng = OsRng; - let mut seed = [0; 32]; - - rng.fill_bytes(&mut seed); - self.public_state.insert( - "6a79aee868a1c641ea895582af7ddd6f2da339e3091a67eddcbfdaa1b9010001" - .parse() - .unwrap(), - Account { - program_owner: Program::pinata().id(), - balance: 1500, - // Difficulty: 3 - data: std::iter::once(3).chain(seed).collect(), - nonce: 0, - }, - ); - - rng.fill_bytes(&mut seed); - self.public_state.insert( - "6a79aee868a1c641ea895582af7ddd6f2da339e3091a67eddcbfdaa1b9010002" - .parse() - .unwrap(), - Account { - program_owner: Program::pinata().id(), - balance: 1500, - // Difficulty: 4 - data: std::iter::once(4).chain(seed).collect(), - nonce: 0, - }, - ); - } - pub(crate) fn insert_program(&mut self, program: Program) { self.builtin_programs.insert(program.id(), program); } @@ -242,6 +207,24 @@ impl V01State { } } +// TODO: This is for testnet only, consider refactoring to have this not compiled for mainnet +impl V01State { + pub fn add_pinata_program(&mut self, address: Address) { + self.insert_program(Program::pinata()); + + self.public_state.insert( + address, + Account { + program_owner: Program::pinata().id(), + balance: 1500, + // Difficulty: 3 + data: vec![3; 33], + nonce: 0, + }, + ); + } +} + #[cfg(test)] pub mod tests { diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index 1c56f11..aca92ff 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -22,3 +22,7 @@ path = "../common" [dependencies.nssa] path = "../nssa" + +[features] +default = [] +testnet = [] diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index f9c86cd..8d8ef23 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -26,9 +26,13 @@ impl SequecerChainStore { .map(|acc_data| (acc_data.addr.parse().unwrap(), acc_data.balance)) .collect(); + #[cfg(not(feature = "testnet"))] + let state = nssa::V01State::new_with_genesis_accounts(&init_accs); + + #[cfg(feature = "testnet")] let state = { let mut this = nssa::V01State::new_with_genesis_accounts(&init_accs); - this.add_pinata_accounts(); + this.add_pinata_program("cafe".repeat(16).parse().unwrap()); this }; diff --git a/sequencer_runner/src/lib.rs b/sequencer_runner/src/lib.rs index 068d3d7..b682a18 100644 --- a/sequencer_runner/src/lib.rs +++ b/sequencer_runner/src/lib.rs @@ -77,9 +77,7 @@ pub async fn main_runner() -> Result<()> { } //ToDo: Add restart on failures - let (_, main_loop_handle) = startup_sequencer(app_config).await?; - - main_loop_handle.await??; + let (_, _) = startup_sequencer(app_config).await?; Ok(()) } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 3814925..171dda9 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -84,6 +84,24 @@ impl WalletCore { None } + pub async fn claim_pinata( + &self, + pinata_addr: Address, + winner_addr: Address, + solution: u128, + ) -> Result { + let addresses = vec![pinata_addr, winner_addr]; + let program_id = nssa::program::Program::pinata().id(); + let message = + nssa::public_transaction::Message::try_new(program_id, addresses, vec![], solution) + .unwrap(); + + let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.sequencer_client.send_tx(tx).await?) + } + pub async fn send_public_native_token_transfer( &self, from: Address, @@ -191,6 +209,20 @@ pub enum Command { #[arg(short, long)] addr: String, }, + + // TODO: This is for testnet only, consider refactoring to have this not compiled for mainnet + // Claim piƱata prize + ClaimPinata { + ///pinata_addr - valid 32 byte hex string + #[arg(long)] + pinata_addr: String, + ///winner_addr - valid 32 byte hex string + #[arg(long)] + winner_addr: String, + ///solution - solution to pinata challenge + #[arg(long)] + solution: u128, + }, } ///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config @@ -228,7 +260,7 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let key = wallet_core.storage.user_data.get_account_signing_key(&addr); - info!("Generated new account with addr {addr:#?}"); + println!("Generated new account with addr {addr}"); info!("With key {key:#?}"); } Command::FetchTx { tx_hash } => { @@ -243,13 +275,27 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let addr = Address::from_str(&addr)?; let balance = wallet_core.get_account_balance(addr).await?; - info!("Accounts {addr:#?} balance is {balance}"); + println!("Accounts {addr} balance is {balance}"); } Command::GetAccountNonce { addr } => { let addr = Address::from_str(&addr)?; let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; - info!("Accounts {addr:#?} nonce is {nonce}"); + println!("Accounts {addr} nonce is {nonce}"); + } + Command::ClaimPinata { + pinata_addr, + winner_addr, + solution, + } => { + let res = wallet_core + .claim_pinata( + pinata_addr.parse().unwrap(), + winner_addr.parse().unwrap(), + solution, + ) + .await?; + info!("Results of tx send is {res:#?}"); } }