mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
Merge branch 'main' into Pravdyvy/wallet-privacy-preserving-transactions
This commit is contained in:
commit
2ce773705d
@ -22,6 +22,7 @@ workspace = true
|
||||
|
||||
[dependencies.sequencer_core]
|
||||
path = "../sequencer_core"
|
||||
features = ["testnet"]
|
||||
|
||||
[dependencies.sequencer_runner]
|
||||
path = "../sequencer_runner"
|
||||
|
||||
@ -279,21 +279,10 @@ pub async fn test_success_two_transactions() {
|
||||
info!("Second TX Success!");
|
||||
}
|
||||
|
||||
pub async fn test_get_account_wallet_command() {
|
||||
let command = Command::GetAccount {
|
||||
addr: ACC_SENDER.to_string(),
|
||||
};
|
||||
|
||||
pub async fn test_get_account() {
|
||||
let wallet_config = fetch_config().unwrap();
|
||||
|
||||
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
|
||||
|
||||
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 account");
|
||||
let account = seq_client
|
||||
.get_account(ACC_SENDER.to_string())
|
||||
.await
|
||||
@ -594,6 +583,50 @@ pub async fn test_success_shielded_transfer_to_another_foreign_account() {
|
||||
info!("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();
|
||||
@ -627,7 +660,7 @@ pub async fn main_tests_runner() -> Result<()> {
|
||||
test_cleanup_wrap!(home_dir, test_failure);
|
||||
}
|
||||
"test_get_account_wallet_command" => {
|
||||
test_cleanup_wrap!(home_dir, test_get_account_wallet_command);
|
||||
test_cleanup_wrap!(home_dir, test_get_account);
|
||||
}
|
||||
"test_success_two_transactions" => {
|
||||
test_cleanup_wrap!(home_dir, test_success_two_transactions);
|
||||
@ -662,12 +695,14 @@ pub async fn main_tests_runner() -> Result<()> {
|
||||
test_success_shielded_transfer_to_another_foreign_account
|
||||
);
|
||||
}
|
||||
"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_get_account_wallet_command);
|
||||
test_cleanup_wrap!(
|
||||
home_dir,
|
||||
test_success_private_transfer_to_another_owned_account
|
||||
@ -688,6 +723,7 @@ pub async fn main_tests_runner() -> Result<()> {
|
||||
home_dir,
|
||||
test_success_shielded_transfer_to_another_foreign_account
|
||||
);
|
||||
test_cleanup_wrap!(home_dir, test_pinata);
|
||||
}
|
||||
_ => {
|
||||
anyhow::bail!("Unknown test name");
|
||||
|
||||
@ -19,3 +19,6 @@ k256 = "0.13.3"
|
||||
[dev-dependencies]
|
||||
test-program-methods = { path = "test_program_methods" }
|
||||
hex-literal = "1.0.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@ -34,3 +34,4 @@ fn main() {
|
||||
|
||||
write_nssa_outputs(vec![sender, receiver], vec![sender_post, receiver_post]);
|
||||
}
|
||||
|
||||
|
||||
70
nssa/program_methods/guest/src/bin/pinata.rs
Normal file
70
nssa/program_methods/guest/src/bin/pinata.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use nssa_core::program::{ProgramInput, read_nssa_inputs, write_nssa_outputs};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
|
||||
const PRIZE: u128 = 150;
|
||||
|
||||
type Instruction = u128;
|
||||
|
||||
struct Challenge {
|
||||
difficulty: u8,
|
||||
seed: [u8; 32],
|
||||
}
|
||||
|
||||
impl Challenge {
|
||||
fn new(bytes: &[u8]) -> Self {
|
||||
assert_eq!(bytes.len(), 33);
|
||||
let difficulty = bytes[0];
|
||||
assert!(difficulty <= 32);
|
||||
|
||||
let mut seed = [0; 32];
|
||||
seed.copy_from_slice(&bytes[1..]);
|
||||
Self { difficulty, seed }
|
||||
}
|
||||
|
||||
// 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[..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)
|
||||
}
|
||||
|
||||
fn next_data(self) -> [u8; 33] {
|
||||
let mut result = [0; 33];
|
||||
result[0] = self.difficulty;
|
||||
result[1..].copy_from_slice(Impl::hash_bytes(&self.seed).as_bytes());
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// A pinata program
|
||||
fn main() {
|
||||
// Read input accounts.
|
||||
// It is expected to receive only two accounts: [pinata_account, winner_account]
|
||||
let ProgramInput {
|
||||
pre_states,
|
||||
instruction: solution,
|
||||
} = read_nssa_inputs::<Instruction>();
|
||||
|
||||
let [pinata, winner] = match pre_states.try_into() {
|
||||
Ok(array) => array,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let data = Challenge::new(&pinata.account.data);
|
||||
|
||||
if !data.validate_solution(solution) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut pinata_post = pinata.account.clone();
|
||||
let mut winner_post = winner.account.clone();
|
||||
pinata_post.balance -= PRIZE;
|
||||
pinata_post.data = data.next_data().to_vec();
|
||||
winner_post.balance += PRIZE;
|
||||
|
||||
write_nssa_outputs(vec![pinata, winner], vec![pinata_post, winner_post]);
|
||||
}
|
||||
@ -2,7 +2,9 @@ use nssa_core::{
|
||||
account::{Account, AccountWithMetadata},
|
||||
program::{InstructionData, ProgramId, ProgramOutput},
|
||||
};
|
||||
use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_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;
|
||||
|
||||
@ -75,6 +77,16 @@ impl Program {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
|
||||
impl Program {
|
||||
pub fn pinata() -> Self {
|
||||
Self {
|
||||
id: PINATA_ID,
|
||||
elf: PINATA_ELF,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nssa_core::account::{Account, AccountWithMetadata};
|
||||
|
||||
@ -211,6 +211,24 @@ impl V01State {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Testnet only. Refactor to prevent compilation on 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 {
|
||||
|
||||
|
||||
@ -24,3 +24,7 @@ path = "../common"
|
||||
|
||||
[dependencies.nssa]
|
||||
path = "../nssa"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
testnet = []
|
||||
|
||||
@ -28,8 +28,16 @@ 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, initial_commitments);
|
||||
|
||||
#[cfg(feature = "testnet")]
|
||||
let state = {
|
||||
let mut this = nssa::V01State::new_with_genesis_accounts(&init_accs, initial_commitments);
|
||||
this.add_pinata_program("cafe".repeat(16).parse().unwrap());
|
||||
this
|
||||
};
|
||||
|
||||
let mut data = [0; 32];
|
||||
let mut prev_block_hash = [0; 32];
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@ use std::{fs::File, io::Write, path::PathBuf, str::FromStr, sync::Arc};
|
||||
|
||||
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
||||
use common::{
|
||||
sequencer_client::SequencerClient,
|
||||
transaction::{EncodedTransaction, NSSATransaction},
|
||||
sequencer_client::{json::SendTxResponse, SequencerClient},
|
||||
transaction::{EncodedTransaction, NSSATransaction}, ExecutionFailureKind,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
@ -85,6 +85,33 @@ impl WalletCore {
|
||||
.generate_new_privacy_preserving_transaction_key_chain()
|
||||
}
|
||||
|
||||
// pub fn search_for_initial_account(&self, acc_addr: Address) -> Option<Account> {
|
||||
// for initial_acc in &self.storage.wallet_config.initial_accounts {
|
||||
// if initial_acc.address() == acc_addr {
|
||||
// return Some(initial_acc.account().clone());
|
||||
// }
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
|
||||
pub async fn claim_pinata(
|
||||
&self,
|
||||
pinata_addr: Address,
|
||||
winner_addr: Address,
|
||||
solution: u128,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
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_public(tx).await?)
|
||||
}
|
||||
|
||||
///Get account balance
|
||||
pub async fn get_account_balance(&self, acc: Address) -> Result<u128> {
|
||||
Ok(self
|
||||
@ -252,6 +279,19 @@ pub enum Command {
|
||||
#[arg(short, long)]
|
||||
addr: String,
|
||||
},
|
||||
// TODO: Testnet only. Refactor to prevent compilation on 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
|
||||
@ -596,6 +636,20 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
|
||||
let account: HumanReadableAccount = wallet_core.get_account(addr).await?.into();
|
||||
println!("{}", serde_json::to_string(&account).unwrap());
|
||||
}
|
||||
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:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user