mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-08-24 22:29:22 +00:00
Merge branch 'main' into marvin/nonce
This commit is contained in:
@@ -4,6 +4,9 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build.workspace = true
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nssa_core.workspace = true
|
||||
token_core.workspace = true
|
||||
|
||||
@@ -7,18 +7,17 @@ use nssa_core::{
|
||||
|
||||
/// Initializes a default account under the ownership of this program.
|
||||
fn initialize_account(pre_state: AccountWithMetadata) -> AccountPostState {
|
||||
let account_to_claim = AccountPostState::new_claimed(pre_state.account.clone());
|
||||
let account_to_claim = AccountPostState::new_claimed(pre_state.account);
|
||||
let is_authorized = pre_state.is_authorized;
|
||||
|
||||
// Continue only if the account to claim has default values
|
||||
if account_to_claim.account() != &Account::default() {
|
||||
panic!("Account must be uninitialized");
|
||||
}
|
||||
assert!(
|
||||
account_to_claim.account() == &Account::default(),
|
||||
"Account must be uninitialized"
|
||||
);
|
||||
|
||||
// Continue only if the owner authorized this operation
|
||||
if !is_authorized {
|
||||
panic!("Account must be authorized");
|
||||
}
|
||||
assert!(is_authorized, "Account must be authorized");
|
||||
|
||||
account_to_claim
|
||||
}
|
||||
@@ -30,27 +29,26 @@ fn transfer(
|
||||
balance_to_move: u128,
|
||||
) -> Vec<AccountPostState> {
|
||||
// Continue only if the sender has authorized this operation
|
||||
if !sender.is_authorized {
|
||||
panic!("Sender must be authorized");
|
||||
}
|
||||
|
||||
// Continue only if the sender has enough balance
|
||||
if sender.account.balance < balance_to_move {
|
||||
panic!("Sender has insufficient balance");
|
||||
}
|
||||
assert!(sender.is_authorized, "Sender must be authorized");
|
||||
|
||||
// Create accounts post states, with updated balances
|
||||
let sender_post = {
|
||||
// Modify sender's balance
|
||||
let mut sender_post_account = sender.account.clone();
|
||||
sender_post_account.balance -= balance_to_move;
|
||||
let mut sender_post_account = sender.account;
|
||||
sender_post_account.balance = sender_post_account
|
||||
.balance
|
||||
.checked_sub(balance_to_move)
|
||||
.expect("Sender has insufficient balance");
|
||||
AccountPostState::new(sender_post_account)
|
||||
};
|
||||
|
||||
let recipient_post = {
|
||||
// Modify recipient's balance
|
||||
let mut recipient_post_account = recipient.account.clone();
|
||||
recipient_post_account.balance += balance_to_move;
|
||||
let mut recipient_post_account = recipient.account;
|
||||
recipient_post_account.balance = recipient_post_account
|
||||
.balance
|
||||
.checked_add(balance_to_move)
|
||||
.expect("Recipient balance overflow");
|
||||
|
||||
// Claim recipient account if it has default program owner
|
||||
if recipient_post_account.program_owner == DEFAULT_PROGRAM_ID {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use nssa_core::program::{AccountPostState, ProgramInput, read_nssa_inputs, write_nssa_outputs};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
const PRIZE: u128 = 150;
|
||||
|
||||
@@ -28,7 +28,7 @@ impl Challenge {
|
||||
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;
|
||||
let difficulty = usize::from(self.difficulty);
|
||||
digest[..difficulty].iter().all(|&b| b == 0)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ impl Challenge {
|
||||
}
|
||||
}
|
||||
|
||||
/// A pinata program
|
||||
/// A pinata program.
|
||||
fn main() {
|
||||
// Read input accounts.
|
||||
// It is expected to receive only two accounts: [pinata_account, winner_account]
|
||||
@@ -52,9 +52,8 @@ fn main() {
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
|
||||
let [pinata, winner] = match pre_states.try_into() {
|
||||
Ok(array) => array,
|
||||
Err(_) => return,
|
||||
let Ok([pinata, winner]) = <[_; 2]>::try_from(pre_states) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let data = Challenge::new(&pinata.account.data);
|
||||
@@ -65,13 +64,19 @@ fn main() {
|
||||
|
||||
let mut pinata_post = pinata.account.clone();
|
||||
let mut winner_post = winner.account.clone();
|
||||
pinata_post.balance -= PRIZE;
|
||||
pinata_post.balance = pinata_post
|
||||
.balance
|
||||
.checked_sub(PRIZE)
|
||||
.expect("Not enough balance in the pinata");
|
||||
pinata_post.data = data
|
||||
.next_data()
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.expect("33 bytes should fit into Data");
|
||||
winner_post.balance += PRIZE;
|
||||
winner_post.balance = winner_post
|
||||
.balance
|
||||
.checked_add(PRIZE)
|
||||
.expect("Overflow when adding prize to winner");
|
||||
|
||||
write_nssa_outputs(
|
||||
instruction_words,
|
||||
|
||||
@@ -5,7 +5,7 @@ use nssa_core::{
|
||||
write_nssa_outputs_with_chained_call,
|
||||
},
|
||||
};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
const PRIZE: u128 = 150;
|
||||
|
||||
@@ -34,7 +34,7 @@ impl Challenge {
|
||||
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;
|
||||
let difficulty = usize::from(self.difficulty);
|
||||
digest[..difficulty].iter().all(|&b| b == 0)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl Challenge {
|
||||
}
|
||||
}
|
||||
|
||||
/// A pinata program
|
||||
/// A pinata program.
|
||||
fn main() {
|
||||
// Read input accounts.
|
||||
// It is expected to receive three accounts: [pinata_definition, pinata_token_holding,
|
||||
@@ -59,13 +59,15 @@ fn main() {
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
|
||||
let [
|
||||
pinata_definition,
|
||||
pinata_token_holding,
|
||||
winner_token_holding,
|
||||
] = match pre_states.try_into() {
|
||||
Ok(array) => array,
|
||||
Err(_) => return,
|
||||
let Ok(
|
||||
[
|
||||
pinata_definition,
|
||||
pinata_token_holding,
|
||||
winner_token_holding,
|
||||
],
|
||||
) = <[_; 3]>::try_from(pre_states)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let data = Challenge::new(&pinata_definition.account.data);
|
||||
|
||||
@@ -16,29 +16,6 @@ use nssa_core::{
|
||||
};
|
||||
use risc0_zkvm::{guest::env, serde::to_vec};
|
||||
|
||||
fn main() {
|
||||
let PrivacyPreservingCircuitInput {
|
||||
program_outputs,
|
||||
visibility_mask,
|
||||
private_account_keys,
|
||||
private_account_nsks,
|
||||
private_account_membership_proofs,
|
||||
program_id,
|
||||
} = env::read();
|
||||
|
||||
let execution_state = ExecutionState::derive_from_outputs(program_id, program_outputs);
|
||||
|
||||
let output = compute_circuit_output(
|
||||
execution_state,
|
||||
&visibility_mask,
|
||||
&private_account_keys,
|
||||
&private_account_nsks,
|
||||
&private_account_membership_proofs,
|
||||
);
|
||||
|
||||
env::commit(&output);
|
||||
}
|
||||
|
||||
/// State of the involved accounts before and after program execution.
|
||||
struct ExecutionState {
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
@@ -60,7 +37,7 @@ impl ExecutionState {
|
||||
};
|
||||
let mut chained_calls = VecDeque::from_iter([(initial_call, None)]);
|
||||
|
||||
let mut execution_state = ExecutionState {
|
||||
let mut execution_state = Self {
|
||||
pre_states: Vec::new(),
|
||||
post_states: HashMap::new(),
|
||||
};
|
||||
@@ -111,11 +88,13 @@ impl ExecutionState {
|
||||
);
|
||||
execution_state.validate_and_sync_states(
|
||||
chained_call.program_id,
|
||||
authorized_pdas,
|
||||
&authorized_pdas,
|
||||
program_output.pre_states,
|
||||
program_output.post_states,
|
||||
);
|
||||
chain_calls_counter += 1;
|
||||
chain_calls_counter = chain_calls_counter.checked_add(1).expect(
|
||||
"Chain calls counter should not overflow as it checked before incrementing",
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
@@ -151,7 +130,7 @@ impl ExecutionState {
|
||||
fn validate_and_sync_states(
|
||||
&mut self,
|
||||
program_id: ProgramId,
|
||||
authorized_pdas: HashSet<AccountId>,
|
||||
authorized_pdas: &HashSet<AccountId>,
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
post_states: Vec<AccountPostState>,
|
||||
) {
|
||||
@@ -171,12 +150,12 @@ impl ExecutionState {
|
||||
.pre_states
|
||||
.iter()
|
||||
.find(|acc| acc.account_id == pre_account_id)
|
||||
.map(|acc| acc.is_authorized)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
.map_or_else(
|
||||
|| panic!(
|
||||
"Pre state must exist in execution state for account {pre_account_id:?}",
|
||||
)
|
||||
});
|
||||
),
|
||||
|acc| acc.is_authorized
|
||||
);
|
||||
|
||||
let is_authorized =
|
||||
previous_is_authorized || authorized_pdas.contains(&pre_account_id);
|
||||
@@ -245,10 +224,10 @@ fn compute_circuit_output(
|
||||
let mut private_membership_proofs_iter = private_account_membership_proofs.iter();
|
||||
|
||||
let mut output_index = 0;
|
||||
for (visibility_mask, (pre_state, post_state)) in
|
||||
for (account_visibility_mask, (pre_state, post_state)) in
|
||||
visibility_mask.iter().copied().zip(states_iter)
|
||||
{
|
||||
match visibility_mask {
|
||||
match account_visibility_mask {
|
||||
0 => {
|
||||
// Public account
|
||||
output.public_pre_states.push(pre_state);
|
||||
@@ -265,7 +244,7 @@ fn compute_circuit_output(
|
||||
"AccountId mismatch"
|
||||
);
|
||||
|
||||
let (new_nullifier, new_nonce) = if visibility_mask == 1 {
|
||||
let new_nullifier = if account_visibility_mask == 1 {
|
||||
// Private account with authentication
|
||||
|
||||
let Some(nsk) = private_nsks_iter.next() else {
|
||||
@@ -351,7 +330,9 @@ fn compute_circuit_output(
|
||||
|
||||
output.new_commitments.push(commitment_post);
|
||||
output.ciphertexts.push(encrypted_account);
|
||||
output_index += 1;
|
||||
output_index = output_index
|
||||
.checked_add(1)
|
||||
.unwrap_or_else(|| panic!("Too many private accounts, output index overflow"));
|
||||
}
|
||||
_ => panic!("Invalid visibility mask value"),
|
||||
}
|
||||
@@ -381,18 +362,8 @@ fn compute_nullifier_and_set_digest(
|
||||
npk: &NullifierPublicKey,
|
||||
nsk: &NullifierSecretKey,
|
||||
) -> (Nullifier, CommitmentSetDigest) {
|
||||
membership_proof_opt
|
||||
.as_ref()
|
||||
.map(|membership_proof| {
|
||||
// Compute commitment set digest associated with provided auth path
|
||||
let commitment_pre = Commitment::new(npk, pre_account);
|
||||
let set_digest = compute_digest_for_path(&commitment_pre, membership_proof);
|
||||
|
||||
// Compute update nullifier
|
||||
let nullifier = Nullifier::for_account_update(&commitment_pre, nsk);
|
||||
(nullifier, set_digest)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
membership_proof_opt.as_ref().map_or_else(
|
||||
|| {
|
||||
assert_eq!(
|
||||
*pre_account,
|
||||
Account::default(),
|
||||
@@ -402,5 +373,40 @@ fn compute_nullifier_and_set_digest(
|
||||
// Compute initialization nullifier
|
||||
let nullifier = Nullifier::for_account_initialization(npk);
|
||||
(nullifier, DUMMY_COMMITMENT_HASH)
|
||||
})
|
||||
},
|
||||
|membership_proof| {
|
||||
// Compute commitment set digest associated with provided auth path
|
||||
let commitment_pre = Commitment::new(npk, pre_account);
|
||||
let set_digest = compute_digest_for_path(&commitment_pre, membership_proof);
|
||||
|
||||
// Compute update nullifier
|
||||
let nullifier = Nullifier::for_account_update(&commitment_pre, nsk);
|
||||
(nullifier, set_digest)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let PrivacyPreservingCircuitInput {
|
||||
program_outputs,
|
||||
visibility_mask,
|
||||
private_account_nonces,
|
||||
private_account_keys,
|
||||
private_account_nsks,
|
||||
private_account_membership_proofs,
|
||||
program_id,
|
||||
} = env::read();
|
||||
|
||||
let execution_state = ExecutionState::derive_from_outputs(program_id, program_outputs);
|
||||
|
||||
let output = compute_circuit_output(
|
||||
execution_state,
|
||||
&visibility_mask,
|
||||
&private_account_nonces,
|
||||
&private_account_keys,
|
||||
&private_account_nsks,
|
||||
&private_account_membership_proofs,
|
||||
);
|
||||
|
||||
env::commit(&output);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user