mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
Merge branch 'main' into schouhy/add-program-deployment-example-pda
This commit is contained in:
commit
44f6f44fa2
16
.github/pull_request_template.md
vendored
16
.github/pull_request_template.md
vendored
@ -10,10 +10,10 @@ TO COMPLETE
|
|||||||
|
|
||||||
TO COMPLETE
|
TO COMPLETE
|
||||||
|
|
||||||
[ ] Change ...
|
- [ ] Change ...
|
||||||
[ ] Add ...
|
- [ ] Add ...
|
||||||
[ ] Fix ...
|
- [ ] Fix ...
|
||||||
[ ] ...
|
- [ ] ...
|
||||||
|
|
||||||
## 🧪 How to Test
|
## 🧪 How to Test
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ TO COMPLETE IF APPLICABLE
|
|||||||
|
|
||||||
*Mark only completed items. A complete PR should have all boxes ticked.*
|
*Mark only completed items. A complete PR should have all boxes ticked.*
|
||||||
|
|
||||||
[ ] Complete PR description
|
- [ ] Complete PR description
|
||||||
[ ] Implement the core functionality
|
- [ ] Implement the core functionality
|
||||||
[ ] Add/update tests
|
- [ ] Add/update tests
|
||||||
[ ] Add/update documentation and inline comments
|
- [ ] Add/update documentation and inline comments
|
||||||
|
|||||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -22,6 +22,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y build-essential clang libclang-dev libssl-dev pkg-config
|
||||||
|
|
||||||
- name: Install active toolchain
|
- name: Install active toolchain
|
||||||
run: rustup install
|
run: rustup install
|
||||||
|
|
||||||
|
|||||||
23
.github/workflows/deploy.yml
vendored
Normal file
23
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: Deploy Sequencer
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Deploy to server
|
||||||
|
uses: appleboy/ssh-action@v1.2.4
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.DEPLOY_SSH_HOST }}
|
||||||
|
username: ${{ secrets.DEPLOY_SSH_USERNAME }}
|
||||||
|
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
|
envs: GITHUB_ACTOR
|
||||||
|
script_path: ci_scripts/deploy.sh
|
||||||
@ -50,8 +50,9 @@ borsh = "1.5.7"
|
|||||||
base58 = "0.2.0"
|
base58 = "0.2.0"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
|
|
||||||
rocksdb = { version = "0.21.0", default-features = false, features = [
|
rocksdb = { version = "0.24.0", default-features = false, features = [
|
||||||
"snappy",
|
"snappy",
|
||||||
|
"bindgen-runtime",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[workspace.dependencies.rand]
|
[workspace.dependencies.rand]
|
||||||
|
|||||||
18
README.md
18
README.md
@ -69,16 +69,14 @@ Install build dependencies
|
|||||||
- On Linux
|
- On Linux
|
||||||
Ubuntu / Debian
|
Ubuntu / Debian
|
||||||
```sh
|
```sh
|
||||||
apt install build-essential clang libssl-dev pkg-config
|
apt install build-essential clang libclang-dev libssl-dev pkg-config
|
||||||
```
|
```
|
||||||
|
|
||||||
Fedora
|
Fedora
|
||||||
```sh
|
```sh
|
||||||
sudo dnf install clang openssl-devel pkgconf llvm
|
sudo dnf install clang clang-devel openssl-devel pkgconf
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note for Fedora 41+ users:** GCC 14+ has stricter C++ standard library headers that cause build failures with the bundled RocksDB. You must set `CXXFLAGS="-include cstdint"` when running cargo commands. See the [Run tests](#run-tests) section for examples.
|
|
||||||
|
|
||||||
- On Mac
|
- On Mac
|
||||||
```sh
|
```sh
|
||||||
xcode-select --install
|
xcode-select --install
|
||||||
@ -110,9 +108,6 @@ The NSSA repository includes both unit and integration test suites.
|
|||||||
```bash
|
```bash
|
||||||
# RISC0_DEV_MODE=1 is used to skip proof generation and reduce test runtime overhead
|
# RISC0_DEV_MODE=1 is used to skip proof generation and reduce test runtime overhead
|
||||||
RISC0_DEV_MODE=1 cargo test --release
|
RISC0_DEV_MODE=1 cargo test --release
|
||||||
|
|
||||||
# On Fedora 41+ (GCC 14+), prefix with CXXFLAGS to fix RocksDB build:
|
|
||||||
CXXFLAGS="-include cstdint" RISC0_DEV_MODE=1 cargo test --release
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Integration tests
|
### Integration tests
|
||||||
@ -122,9 +117,6 @@ export NSSA_WALLET_HOME_DIR=$(pwd)/integration_tests/configs/debug/wallet/
|
|||||||
cd integration_tests
|
cd integration_tests
|
||||||
# RISC0_DEV_MODE=1 skips proof generation; RUST_LOG=info enables runtime logs
|
# RISC0_DEV_MODE=1 skips proof generation; RUST_LOG=info enables runtime logs
|
||||||
RUST_LOG=info RISC0_DEV_MODE=1 cargo run $(pwd)/configs/debug all
|
RUST_LOG=info RISC0_DEV_MODE=1 cargo run $(pwd)/configs/debug all
|
||||||
|
|
||||||
# On Fedora 41+ (GCC 14+), prefix with CXXFLAGS to fix RocksDB build:
|
|
||||||
CXXFLAGS="-include cstdint" RUST_LOG=info RISC0_DEV_MODE=1 cargo run $(pwd)/configs/debug all
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Run the sequencer
|
# Run the sequencer
|
||||||
@ -134,9 +126,6 @@ The sequencer can be run locally:
|
|||||||
```bash
|
```bash
|
||||||
cd sequencer_runner
|
cd sequencer_runner
|
||||||
RUST_LOG=info cargo run --release configs/debug
|
RUST_LOG=info cargo run --release configs/debug
|
||||||
|
|
||||||
# On Fedora 41+ (GCC 14+), prefix with CXXFLAGS to fix RocksDB build:
|
|
||||||
CXXFLAGS="-include cstdint" RUST_LOG=info cargo run --release configs/debug
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If everything went well you should see an output similar to this:
|
If everything went well you should see an output similar to this:
|
||||||
@ -162,9 +151,6 @@ This repository includes a CLI for interacting with the Nescience sequencer. To
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install --path wallet --force
|
cargo install --path wallet --force
|
||||||
|
|
||||||
# On Fedora 41+ (GCC 14+), prefix with CXXFLAGS to fix RocksDB build:
|
|
||||||
CXXFLAGS="-include cstdint" cargo install --path wallet --force
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `wallet help` to check everything went well.
|
Run `wallet help` to check everything went well.
|
||||||
|
|||||||
84
ci_scripts/deploy.sh
Normal file
84
ci_scripts/deploy.sh
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Base directory for deployment
|
||||||
|
LSSA_DIR="/home/arjentix/test_deploy/lssa"
|
||||||
|
|
||||||
|
# Expect GITHUB_ACTOR to be passed as first argument or environment variable
|
||||||
|
GITHUB_ACTOR="${1:-${GITHUB_ACTOR:-unknown}}"
|
||||||
|
|
||||||
|
# Function to log messages with timestamp
|
||||||
|
log_deploy() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S %Z')] $1" >> "${LSSA_DIR}/deploy.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Error handler
|
||||||
|
handle_error() {
|
||||||
|
echo "✗ Deployment failed by: ${GITHUB_ACTOR}"
|
||||||
|
log_deploy "Deployment failed by: ${GITHUB_ACTOR}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
find_sequencer_runner_pids() {
|
||||||
|
pgrep -f "sequencer_runner" | grep -v $$
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set trap to catch any errors
|
||||||
|
trap 'handle_error' ERR
|
||||||
|
|
||||||
|
# Log deployment info
|
||||||
|
log_deploy "Deployment initiated by: ${GITHUB_ACTOR}"
|
||||||
|
|
||||||
|
# Navigate to code directory
|
||||||
|
if [ ! -d "${LSSA_DIR}/code" ]; then
|
||||||
|
mkdir -p "${LSSA_DIR}/code"
|
||||||
|
fi
|
||||||
|
cd "${LSSA_DIR}/code"
|
||||||
|
|
||||||
|
# Stop current sequencer if running
|
||||||
|
if find_sequencer_runner_pids > /dev/null; then
|
||||||
|
echo "Stopping current sequencer..."
|
||||||
|
find_sequencer_runner_pids | xargs -r kill -SIGINT || true
|
||||||
|
sleep 2
|
||||||
|
# Force kill if still running
|
||||||
|
find_sequencer_runner_pids | grep -v $$ | xargs -r kill -9 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clone or update repository
|
||||||
|
if [ -d ".git" ]; then
|
||||||
|
echo "Updating existing repository..."
|
||||||
|
git fetch origin
|
||||||
|
git checkout main
|
||||||
|
git reset --hard origin/main
|
||||||
|
else
|
||||||
|
echo "Cloning repository..."
|
||||||
|
git clone https://github.com/vacp2p/nescience-testnet.git .
|
||||||
|
git checkout main
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build sequencer_runner and wallet in release mode
|
||||||
|
echo "Building sequencer_runner"
|
||||||
|
# That could be just `cargo build --release --bin sequencer_runner --bin wallet`
|
||||||
|
# but we have `no_docker` feature bug, see issue #179
|
||||||
|
cd sequencer_runner
|
||||||
|
cargo build --release
|
||||||
|
cd ../wallet
|
||||||
|
cargo build --release
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Run sequencer_runner with config
|
||||||
|
echo "Starting sequencer_runner..."
|
||||||
|
export RUST_LOG=info
|
||||||
|
nohup ./target/release/sequencer_runner "${LSSA_DIR}/configs/sequencer" > "${LSSA_DIR}/sequencer.log" 2>&1 &
|
||||||
|
|
||||||
|
# Wait 5 seconds and check health using wallet
|
||||||
|
sleep 5
|
||||||
|
if ./target/release/wallet check-health; then
|
||||||
|
echo "✓ Sequencer started successfully and is healthy"
|
||||||
|
log_deploy "Deployment completed successfully by: ${GITHUB_ACTOR}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "✗ Sequencer failed health check"
|
||||||
|
tail -n 50 "${LSSA_DIR}/sequencer.log"
|
||||||
|
handle_error
|
||||||
|
fi
|
||||||
@ -1681,6 +1681,49 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
|
|||||||
info!("Success!");
|
info!("Success!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[nssa_integration_test]
|
||||||
|
pub async fn test_authenticated_transfer_initialize_function_private() {
|
||||||
|
info!("########## test initialize private account for authenticated transfer ##########");
|
||||||
|
let command =
|
||||||
|
Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None }));
|
||||||
|
let SubcommandReturnValue::RegisterAccount { account_id } =
|
||||||
|
wallet::cli::execute_subcommand(command).await.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Error creating account");
|
||||||
|
};
|
||||||
|
|
||||||
|
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
||||||
|
account_id: make_private_account_input_from_str(&account_id.to_string()),
|
||||||
|
});
|
||||||
|
wallet::cli::execute_subcommand(command).await.unwrap();
|
||||||
|
|
||||||
|
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||||
|
|
||||||
|
info!("Checking correct execution");
|
||||||
|
let command = Command::Account(AccountSubcommand::SyncPrivate {});
|
||||||
|
wallet::cli::execute_subcommand(command).await.unwrap();
|
||||||
|
|
||||||
|
let wallet_config = fetch_config().await.unwrap();
|
||||||
|
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
|
||||||
|
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let new_commitment1 = wallet_storage
|
||||||
|
.get_private_account_commitment(&account_id)
|
||||||
|
.unwrap();
|
||||||
|
assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await);
|
||||||
|
|
||||||
|
let account = wallet_storage.get_account_private(&account_id).unwrap();
|
||||||
|
|
||||||
|
let expected_program_owner = Program::authenticated_transfer_program().id();
|
||||||
|
let expected_balance = 0;
|
||||||
|
|
||||||
|
assert_eq!(account.program_owner, expected_program_owner);
|
||||||
|
assert_eq!(account.balance, expected_balance);
|
||||||
|
assert!(account.data.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[nssa_integration_test]
|
#[nssa_integration_test]
|
||||||
pub async fn test_pinata_private_receiver() {
|
pub async fn test_pinata_private_receiver() {
|
||||||
info!("########## test_pinata_private_receiver ##########");
|
info!("########## test_pinata_private_receiver ##########");
|
||||||
|
|||||||
@ -167,7 +167,8 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
|||||||
(sender_npk.clone(), sender_ss),
|
(sender_npk.clone(), sender_ss),
|
||||||
(recipient_npk.clone(), recipient_ss),
|
(recipient_npk.clone(), recipient_ss),
|
||||||
],
|
],
|
||||||
&[(sender_nsk, proof)],
|
&[sender_nsk],
|
||||||
|
&[Some(proof)],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@ -10,11 +10,23 @@ use crate::{
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct PrivacyPreservingCircuitInput {
|
pub struct PrivacyPreservingCircuitInput {
|
||||||
|
/// Outputs of the program execution.
|
||||||
pub program_outputs: Vec<ProgramOutput>,
|
pub program_outputs: Vec<ProgramOutput>,
|
||||||
|
/// Visibility mask for accounts.
|
||||||
|
///
|
||||||
|
/// - `0` - public account
|
||||||
|
/// - `1` - private account with authentication
|
||||||
|
/// - `2` - private account without authentication
|
||||||
pub visibility_mask: Vec<u8>,
|
pub visibility_mask: Vec<u8>,
|
||||||
|
/// Nonces of private accounts.
|
||||||
pub private_account_nonces: Vec<Nonce>,
|
pub private_account_nonces: Vec<Nonce>,
|
||||||
|
/// Public keys of private accounts.
|
||||||
pub private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>,
|
pub private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>,
|
||||||
pub private_account_auth: Vec<(NullifierSecretKey, MembershipProof)>,
|
/// Nullifier secret keys for authorized private accounts.
|
||||||
|
pub private_account_nsks: Vec<NullifierSecretKey>,
|
||||||
|
/// Membership proofs for private accounts. Can be [`None`] for uninitialized accounts.
|
||||||
|
pub private_account_membership_proofs: Vec<Option<MembershipProof>>,
|
||||||
|
/// Program ID.
|
||||||
pub program_id: ProgramId,
|
pub program_id: ProgramId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ fn initialize_account(pre_state: AccountWithMetadata) -> AccountPostState {
|
|||||||
|
|
||||||
// Continue only if the owner authorized this operation
|
// Continue only if the owner authorized this operation
|
||||||
if !is_authorized {
|
if !is_authorized {
|
||||||
panic!("Invalid input");
|
panic!("Account must be authorized");
|
||||||
}
|
}
|
||||||
|
|
||||||
account_to_claim
|
account_to_claim
|
||||||
@ -31,12 +31,12 @@ fn transfer(
|
|||||||
) -> Vec<AccountPostState> {
|
) -> Vec<AccountPostState> {
|
||||||
// Continue only if the sender has authorized this operation
|
// Continue only if the sender has authorized this operation
|
||||||
if !sender.is_authorized {
|
if !sender.is_authorized {
|
||||||
panic!("Invalid input");
|
panic!("Sender must be authorized");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue only if the sender has enough balance
|
// Continue only if the sender has enough balance
|
||||||
if sender.account.balance < balance_to_move {
|
if sender.account.balance < balance_to_move {
|
||||||
panic!("Invalid input");
|
panic!("Sender has insufficient balance");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create accounts post states, with updated balances
|
// Create accounts post states, with updated balances
|
||||||
|
|||||||
@ -16,7 +16,8 @@ fn main() {
|
|||||||
visibility_mask,
|
visibility_mask,
|
||||||
private_account_nonces,
|
private_account_nonces,
|
||||||
private_account_keys,
|
private_account_keys,
|
||||||
private_account_auth,
|
private_account_nsks,
|
||||||
|
private_account_membership_proofs,
|
||||||
mut program_id,
|
mut program_id,
|
||||||
} = env::read();
|
} = env::read();
|
||||||
|
|
||||||
@ -63,7 +64,8 @@ fn main() {
|
|||||||
for (i, program_output) in program_outputs.iter().enumerate() {
|
for (i, program_output) in program_outputs.iter().enumerate() {
|
||||||
let mut program_output = program_output.clone();
|
let mut program_output = program_output.clone();
|
||||||
|
|
||||||
// Check that `program_output` is consistent with the execution of the corresponding program.
|
// Check that `program_output` is consistent with the execution of the corresponding
|
||||||
|
// program.
|
||||||
let program_output_words =
|
let program_output_words =
|
||||||
&to_vec(&program_output).expect("program_output must be serializable");
|
&to_vec(&program_output).expect("program_output must be serializable");
|
||||||
env::verify(program_id, program_output_words)
|
env::verify(program_id, program_output_words)
|
||||||
@ -131,7 +133,8 @@ fn main() {
|
|||||||
|
|
||||||
let mut private_nonces_iter = private_account_nonces.iter();
|
let mut private_nonces_iter = private_account_nonces.iter();
|
||||||
let mut private_keys_iter = private_account_keys.iter();
|
let mut private_keys_iter = private_account_keys.iter();
|
||||||
let mut private_auth_iter = private_account_auth.iter();
|
let mut private_nsks_iter = private_account_nsks.iter();
|
||||||
|
let mut private_membership_proofs_iter = private_account_membership_proofs.iter();
|
||||||
|
|
||||||
let mut output_index = 0;
|
let mut output_index = 0;
|
||||||
for i in 0..n_accounts {
|
for i in 0..n_accounts {
|
||||||
@ -141,9 +144,7 @@ fn main() {
|
|||||||
public_pre_states.push(pre_states[i].clone());
|
public_pre_states.push(pre_states[i].clone());
|
||||||
|
|
||||||
let mut post = state_diff.get(&pre_states[i].account_id).unwrap().clone();
|
let mut post = state_diff.get(&pre_states[i].account_id).unwrap().clone();
|
||||||
if pre_states[i].is_authorized {
|
|
||||||
post.nonce += 1;
|
|
||||||
}
|
|
||||||
if post.program_owner == DEFAULT_PROGRAM_ID {
|
if post.program_owner == DEFAULT_PROGRAM_ID {
|
||||||
// Claim account
|
// Claim account
|
||||||
post.program_owner = program_id;
|
post.program_owner = program_id;
|
||||||
@ -160,8 +161,7 @@ fn main() {
|
|||||||
|
|
||||||
if visibility_mask[i] == 1 {
|
if visibility_mask[i] == 1 {
|
||||||
// Private account with authentication
|
// Private account with authentication
|
||||||
let (nsk, membership_proof) =
|
let nsk = private_nsks_iter.next().expect("Missing nsk");
|
||||||
private_auth_iter.next().expect("Missing private auth");
|
|
||||||
|
|
||||||
// Verify the nullifier public key
|
// Verify the nullifier public key
|
||||||
let expected_npk = NullifierPublicKey::from(nsk);
|
let expected_npk = NullifierPublicKey::from(nsk);
|
||||||
@ -169,19 +169,38 @@ fn main() {
|
|||||||
panic!("Nullifier public key mismatch");
|
panic!("Nullifier public key mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute commitment set digest associated with provided auth path
|
|
||||||
let commitment_pre = Commitment::new(npk, &pre_states[i].account);
|
|
||||||
let set_digest = compute_digest_for_path(&commitment_pre, membership_proof);
|
|
||||||
|
|
||||||
// Check pre_state authorization
|
// Check pre_state authorization
|
||||||
if !pre_states[i].is_authorized {
|
if !pre_states[i].is_authorized {
|
||||||
panic!("Pre-state not authorized");
|
panic!("Pre-state not authorized");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute update nullifier
|
let membership_proof_opt = private_membership_proofs_iter
|
||||||
let nullifier = Nullifier::for_account_update(&commitment_pre, nsk);
|
.next()
|
||||||
|
.expect("Missing membership proof");
|
||||||
|
let (nullifier, set_digest) = membership_proof_opt
|
||||||
|
.as_ref()
|
||||||
|
.map(|membership_proof| {
|
||||||
|
// Compute commitment set digest associated with provided auth path
|
||||||
|
let commitment_pre = Commitment::new(npk, &pre_states[i].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(|| {
|
||||||
|
if pre_states[i].account != Account::default() {
|
||||||
|
panic!("Found new private account with non default values.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute initialization nullifier
|
||||||
|
let nullifier = Nullifier::for_account_initialization(npk);
|
||||||
|
(nullifier, DUMMY_COMMITMENT_HASH)
|
||||||
|
});
|
||||||
new_nullifiers.push((nullifier, set_digest));
|
new_nullifiers.push((nullifier, set_digest));
|
||||||
} else {
|
} else {
|
||||||
|
// Private account without authentication
|
||||||
if pre_states[i].account != Account::default() {
|
if pre_states[i].account != Account::default() {
|
||||||
panic!("Found new private account with non default values.");
|
panic!("Found new private account with non default values.");
|
||||||
}
|
}
|
||||||
@ -190,7 +209,13 @@ fn main() {
|
|||||||
panic!("Found new private account marked as authorized.");
|
panic!("Found new private account marked as authorized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute initialization nullifier
|
let membership_proof_opt = private_membership_proofs_iter
|
||||||
|
.next()
|
||||||
|
.expect("Missing membership proof");
|
||||||
|
assert!(
|
||||||
|
membership_proof_opt.is_none(),
|
||||||
|
"Membership proof must be None for unauthorized accounts"
|
||||||
|
);
|
||||||
let nullifier = Nullifier::for_account_initialization(npk);
|
let nullifier = Nullifier::for_account_initialization(npk);
|
||||||
new_nullifiers.push((nullifier, DUMMY_COMMITMENT_HASH));
|
new_nullifiers.push((nullifier, DUMMY_COMMITMENT_HASH));
|
||||||
}
|
}
|
||||||
@ -225,15 +250,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if private_nonces_iter.next().is_some() {
|
if private_nonces_iter.next().is_some() {
|
||||||
panic!("Too many nonces.");
|
panic!("Too many nonces");
|
||||||
}
|
}
|
||||||
|
|
||||||
if private_keys_iter.next().is_some() {
|
if private_keys_iter.next().is_some() {
|
||||||
panic!("Too many private account keys.");
|
panic!("Too many private account keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
if private_auth_iter.next().is_some() {
|
if private_nsks_iter.next().is_some() {
|
||||||
panic!("Too many private account authentication keys.");
|
panic!("Too many private account authentication keys");
|
||||||
|
}
|
||||||
|
|
||||||
|
if private_membership_proofs_iter.next().is_some() {
|
||||||
|
panic!("Too many private account membership proofs");
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = PrivacyPreservingCircuitOutput {
|
let output = PrivacyPreservingCircuitOutput {
|
||||||
|
|||||||
@ -1,35 +1,48 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountId, AccountWithMetadata, Data, data::DATA_MAX_LENGTH_IN_BYTES},
|
account::{Account, AccountId, AccountWithMetadata, Data},
|
||||||
program::{
|
program::{
|
||||||
AccountPostState, DEFAULT_PROGRAM_ID, ProgramInput, read_nssa_inputs, write_nssa_outputs,
|
AccountPostState, DEFAULT_PROGRAM_ID, ProgramInput, read_nssa_inputs, write_nssa_outputs,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// The token program has three functions:
|
// The token program has three functions:
|
||||||
// 1. New token definition. Arguments to this function are:
|
// 1. New token definition.
|
||||||
// * Two **default** accounts: [definition_account, holding_account]. The first default account
|
// Arguments to this function are:
|
||||||
// will be initialized with the token definition account values. The second account will be
|
// * Two **default** accounts: [definition_account, holding_account].
|
||||||
// initialized to a token holding account for the new token, holding the entire total supply.
|
// The first default account will be initialized with the token definition account values. The second account will
|
||||||
// * An instruction data of 23-bytes, indicating the total supply and the token name, with the
|
// be initialized to a token holding account for the new token, holding the entire total supply.
|
||||||
// following layout: [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)] The
|
// * An instruction data of 23-bytes, indicating the total supply and the token name, with
|
||||||
// name cannot be equal to [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
// the following layout:
|
||||||
// 2. Token transfer Arguments to this function are:
|
// [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)]
|
||||||
|
// The name cannot be equal to [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||||
|
// 2. Token transfer
|
||||||
|
// Arguments to this function are:
|
||||||
// * Two accounts: [sender_account, recipient_account].
|
// * Two accounts: [sender_account, recipient_account].
|
||||||
// * An instruction data byte string of length 23, indicating the total supply with the
|
// * An instruction data byte string of length 23, indicating the total supply with the following layout
|
||||||
// following layout [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00
|
// [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||||
// || 0x00 || 0x00].
|
// 3. Initialize account with zero balance
|
||||||
// 3. Initialize account with zero balance Arguments to this function are:
|
// Arguments to this function are:
|
||||||
// * Two accounts: [definition_account, account_to_initialize].
|
// * Two accounts: [definition_account, account_to_initialize].
|
||||||
// * An dummy byte string of length 23, with the following layout [0x02 || 0x00 || 0x00 || 0x00
|
// * An dummy byte string of length 23, with the following layout
|
||||||
// || ... || 0x00 || 0x00].
|
// [0x02 || 0x00 || 0x00 || 0x00 || ... || 0x00 || 0x00].
|
||||||
|
// 4. Burn tokens from a Token Holding account (thus lowering total supply)
|
||||||
|
// Arguments to this function are:
|
||||||
|
// * Two accounts: [definition_account, holding_account].
|
||||||
|
// * Authorization required: holding_account
|
||||||
|
// * An instruction data byte string of length 23, indicating the balance to burn with the folloiwng layout
|
||||||
|
// [0x03 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||||
|
// 5. Mint additional supply of tokens tokens to a Token Holding account (thus increasing total supply)
|
||||||
|
// Arguments to this function are:
|
||||||
|
// * Two accounts: [definition_account, holding_account].
|
||||||
|
// * Authorization required: definition_account
|
||||||
|
// * An instruction data byte string of length 23, indicating the balance to mint with the folloiwng layout
|
||||||
|
// [0x04 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||||
|
|
||||||
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
||||||
const TOKEN_DEFINITION_DATA_SIZE: usize = 23;
|
const TOKEN_DEFINITION_DATA_SIZE: usize = 23;
|
||||||
const _: () = assert!(TOKEN_DEFINITION_DATA_SIZE <= DATA_MAX_LENGTH_IN_BYTES);
|
|
||||||
|
|
||||||
const TOKEN_HOLDING_TYPE: u8 = 1;
|
const TOKEN_HOLDING_TYPE: u8 = 1;
|
||||||
const TOKEN_HOLDING_DATA_SIZE: usize = 49;
|
const TOKEN_HOLDING_DATA_SIZE: usize = 49;
|
||||||
const _: () = assert!(TOKEN_HOLDING_DATA_SIZE <= DATA_MAX_LENGTH_IN_BYTES);
|
|
||||||
|
|
||||||
struct TokenDefinition {
|
struct TokenDefinition {
|
||||||
account_type: u8,
|
account_type: u8,
|
||||||
@ -150,7 +163,7 @@ fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<Ac
|
|||||||
recipient_holding.balance = recipient_holding
|
recipient_holding.balance = recipient_holding
|
||||||
.balance
|
.balance
|
||||||
.checked_add(balance_to_move)
|
.checked_add(balance_to_move)
|
||||||
.expect("Recipient balance overflow.");
|
.expect("Recipient balance overflow");
|
||||||
|
|
||||||
let sender_post = {
|
let sender_post = {
|
||||||
let mut this = sender.account.clone();
|
let mut this = sender.account.clone();
|
||||||
@ -225,7 +238,7 @@ fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec<AccountPostStat
|
|||||||
let account_to_initialize = &pre_states[1];
|
let account_to_initialize = &pre_states[1];
|
||||||
|
|
||||||
if account_to_initialize.account != Account::default() {
|
if account_to_initialize.account != Account::default() {
|
||||||
panic!("Only uninitialized accounts can be initialized");
|
panic!("Only Uninitialized accounts can be initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: #212 We should check that this is an account owned by the token program.
|
// TODO: #212 We should check that this is an account owned by the token program.
|
||||||
@ -246,6 +259,126 @@ fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec<AccountPostStat
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec<AccountPostState> {
|
||||||
|
if pre_states.len() != 2 {
|
||||||
|
panic!("Invalid number of accounts");
|
||||||
|
}
|
||||||
|
|
||||||
|
let definition = &pre_states[0];
|
||||||
|
let user_holding = &pre_states[1];
|
||||||
|
|
||||||
|
let definition_values =
|
||||||
|
TokenDefinition::parse(&definition.account.data).expect("Definition account must be valid");
|
||||||
|
let user_values = TokenHolding::parse(&user_holding.account.data)
|
||||||
|
.expect("Token Holding account must be valid");
|
||||||
|
|
||||||
|
if definition.account_id != user_values.definition_id {
|
||||||
|
panic!("Mismatch token definition and token holding");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user_holding.is_authorized {
|
||||||
|
panic!("Authorization is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if user_values.balance < balance_to_burn {
|
||||||
|
panic!("Insufficient balance to burn");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut post_user_holding = user_holding.account.clone();
|
||||||
|
let mut post_definition = definition.account.clone();
|
||||||
|
|
||||||
|
post_user_holding.data = TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: user_values.account_type,
|
||||||
|
definition_id: user_values.definition_id,
|
||||||
|
balance: user_values
|
||||||
|
.balance
|
||||||
|
.checked_sub(balance_to_burn)
|
||||||
|
.expect("Checked above"),
|
||||||
|
});
|
||||||
|
|
||||||
|
post_definition.data = TokenDefinition::into_data(TokenDefinition {
|
||||||
|
account_type: definition_values.account_type,
|
||||||
|
name: definition_values.name,
|
||||||
|
total_supply: definition_values
|
||||||
|
.total_supply
|
||||||
|
.checked_sub(balance_to_burn)
|
||||||
|
.expect("Total supply underflow"),
|
||||||
|
});
|
||||||
|
|
||||||
|
vec![
|
||||||
|
AccountPostState::new(post_definition),
|
||||||
|
AccountPostState::new(post_user_holding),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mint_additional_supply(
|
||||||
|
pre_states: &[AccountWithMetadata],
|
||||||
|
amount_to_mint: u128,
|
||||||
|
) -> Vec<AccountPostState> {
|
||||||
|
if pre_states.len() != 2 {
|
||||||
|
panic!("Invalid number of accounts");
|
||||||
|
}
|
||||||
|
|
||||||
|
let definition = &pre_states[0];
|
||||||
|
let token_holding = &pre_states[1];
|
||||||
|
|
||||||
|
if !definition.is_authorized {
|
||||||
|
panic!("Definition authorization is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
let definition_values =
|
||||||
|
TokenDefinition::parse(&definition.account.data).expect("Definition account must be valid");
|
||||||
|
|
||||||
|
let token_holding_values: TokenHolding = if token_holding.account == Account::default() {
|
||||||
|
TokenHolding::new(&definition.account_id)
|
||||||
|
} else {
|
||||||
|
TokenHolding::parse(&token_holding.account.data).expect("Holding account must be valid")
|
||||||
|
};
|
||||||
|
|
||||||
|
if definition.account_id != token_holding_values.definition_id {
|
||||||
|
panic!("Mismatch token definition and token holding");
|
||||||
|
}
|
||||||
|
|
||||||
|
let token_holding_post_data = TokenHolding {
|
||||||
|
account_type: token_holding_values.account_type,
|
||||||
|
definition_id: token_holding_values.definition_id,
|
||||||
|
balance: token_holding_values
|
||||||
|
.balance
|
||||||
|
.checked_add(amount_to_mint)
|
||||||
|
.expect("New balance overflow"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let post_total_supply = definition_values
|
||||||
|
.total_supply
|
||||||
|
.checked_add(amount_to_mint)
|
||||||
|
.expect("Total supply overflow");
|
||||||
|
|
||||||
|
let post_definition_data = TokenDefinition {
|
||||||
|
account_type: definition_values.account_type,
|
||||||
|
name: definition_values.name,
|
||||||
|
total_supply: post_total_supply,
|
||||||
|
};
|
||||||
|
|
||||||
|
let post_definition = {
|
||||||
|
let mut this = definition.account.clone();
|
||||||
|
this.data = post_definition_data.into_data();
|
||||||
|
AccountPostState::new(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
let token_holding_post = {
|
||||||
|
let mut this = token_holding.account.clone();
|
||||||
|
this.data = token_holding_post_data.into_data();
|
||||||
|
|
||||||
|
// Claim the recipient account if it has default program owner
|
||||||
|
if this.program_owner == DEFAULT_PROGRAM_ID {
|
||||||
|
AccountPostState::new_claimed(this)
|
||||||
|
} else {
|
||||||
|
AccountPostState::new(this)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
vec![post_definition, token_holding_post]
|
||||||
|
}
|
||||||
|
|
||||||
type Instruction = [u8; 23];
|
type Instruction = [u8; 23];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -295,6 +428,34 @@ fn main() {
|
|||||||
}
|
}
|
||||||
initialize_account(&pre_states)
|
initialize_account(&pre_states)
|
||||||
}
|
}
|
||||||
|
3 => {
|
||||||
|
let balance_to_burn = u128::from_le_bytes(
|
||||||
|
instruction[1..17]
|
||||||
|
.try_into()
|
||||||
|
.expect("Balance to burn must be 16 bytes little-endian"),
|
||||||
|
);
|
||||||
|
let name: [u8; 6] = instruction[17..]
|
||||||
|
.try_into()
|
||||||
|
.expect("Name must be 6 bytes long");
|
||||||
|
assert_eq!(name, [0; 6]);
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
burn(&pre_states, balance_to_burn)
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
let balance_to_mint = u128::from_le_bytes(
|
||||||
|
instruction[1..17]
|
||||||
|
.try_into()
|
||||||
|
.expect("Balance to burn must be 16 bytes little-endian"),
|
||||||
|
);
|
||||||
|
let name: [u8; 6] = instruction[17..]
|
||||||
|
.try_into()
|
||||||
|
.expect("Name must be 6 bytes long");
|
||||||
|
assert_eq!(name, [0; 6]);
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
mint_additional_supply(&pre_states, balance_to_mint)
|
||||||
|
}
|
||||||
_ => panic!("Invalid instruction"),
|
_ => panic!("Invalid instruction"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -306,8 +467,9 @@ mod tests {
|
|||||||
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
|
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
TOKEN_DEFINITION_DATA_SIZE, TOKEN_HOLDING_DATA_SIZE, TOKEN_HOLDING_TYPE,
|
TOKEN_DEFINITION_DATA_SIZE, TOKEN_DEFINITION_TYPE, TOKEN_HOLDING_DATA_SIZE,
|
||||||
initialize_account, new_definition, transfer,
|
TOKEN_HOLDING_TYPE, TokenDefinition, TokenHolding, burn, initialize_account,
|
||||||
|
mint_additional_supply, new_definition, transfer,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[should_panic(expected = "Invalid number of input accounts")]
|
#[should_panic(expected = "Invalid number of input accounts")]
|
||||||
@ -705,4 +867,455 @@ mod tests {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum BalanceEnum {
|
||||||
|
InitSupply,
|
||||||
|
HoldingBalance,
|
||||||
|
InitSupplyBurned,
|
||||||
|
HoldingBalanceBurned,
|
||||||
|
BurnSuccess,
|
||||||
|
BurnInsufficient,
|
||||||
|
MintSuccess,
|
||||||
|
InitSupplyMint,
|
||||||
|
HoldingBalanceMint,
|
||||||
|
MintOverflow,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AccountsEnum {
|
||||||
|
DefinitionAccountAuth,
|
||||||
|
DefinitionAccountNotAuth,
|
||||||
|
HoldingDiffDef,
|
||||||
|
HoldingSameDefAuth,
|
||||||
|
HoldingSameDefNotAuth,
|
||||||
|
HoldingSameDefNotAuthOverflow,
|
||||||
|
DefinitionAccountPostBurn,
|
||||||
|
HoldingAccountPostBurn,
|
||||||
|
Uninit,
|
||||||
|
InitMint,
|
||||||
|
DefinitionAccountMint,
|
||||||
|
HoldingSameDefMint,
|
||||||
|
HoldingSameDefAuthLargeBalance,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum IdEnum {
|
||||||
|
PoolDefinitionId,
|
||||||
|
PoolDefinitionIdDiff,
|
||||||
|
HoldingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn helper_account_constructor(selection: AccountsEnum) -> AccountWithMetadata {
|
||||||
|
match selection {
|
||||||
|
AccountsEnum::DefinitionAccountAuth => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenDefinition::into_data(TokenDefinition {
|
||||||
|
account_type: TOKEN_DEFINITION_TYPE,
|
||||||
|
name: [2; 6],
|
||||||
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupply),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
},
|
||||||
|
AccountsEnum::DefinitionAccountNotAuth => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenDefinition::into_data(TokenDefinition {
|
||||||
|
account_type: TOKEN_DEFINITION_TYPE,
|
||||||
|
name: [2; 6],
|
||||||
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupply),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
},
|
||||||
|
AccountsEnum::HoldingDiffDef => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: helper_id_constructor(IdEnum::PoolDefinitionIdDiff),
|
||||||
|
balance: helper_balance_constructor(BalanceEnum::HoldingBalance),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: helper_id_constructor(IdEnum::HoldingId),
|
||||||
|
},
|
||||||
|
AccountsEnum::HoldingSameDefAuth => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
balance: helper_balance_constructor(BalanceEnum::HoldingBalance),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: helper_id_constructor(IdEnum::HoldingId),
|
||||||
|
},
|
||||||
|
AccountsEnum::HoldingSameDefNotAuth => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
balance: helper_balance_constructor(BalanceEnum::HoldingBalance),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: helper_id_constructor(IdEnum::HoldingId),
|
||||||
|
},
|
||||||
|
AccountsEnum::HoldingSameDefNotAuthOverflow => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
balance: helper_balance_constructor(BalanceEnum::InitSupply),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: helper_id_constructor(IdEnum::HoldingId),
|
||||||
|
},
|
||||||
|
AccountsEnum::DefinitionAccountPostBurn => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenDefinition::into_data(TokenDefinition {
|
||||||
|
account_type: TOKEN_DEFINITION_TYPE,
|
||||||
|
name: [2; 6],
|
||||||
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupplyBurned),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
},
|
||||||
|
AccountsEnum::HoldingAccountPostBurn => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
balance: helper_balance_constructor(BalanceEnum::HoldingBalanceBurned),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: helper_id_constructor(IdEnum::HoldingId),
|
||||||
|
},
|
||||||
|
AccountsEnum::Uninit => AccountWithMetadata {
|
||||||
|
account: Account::default(),
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: helper_id_constructor(IdEnum::HoldingId),
|
||||||
|
},
|
||||||
|
AccountsEnum::InitMint => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [0u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
balance: helper_balance_constructor(BalanceEnum::MintSuccess),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: helper_id_constructor(IdEnum::HoldingId),
|
||||||
|
},
|
||||||
|
AccountsEnum::HoldingSameDefMint => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
balance: helper_balance_constructor(BalanceEnum::HoldingBalanceMint),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
},
|
||||||
|
AccountsEnum::DefinitionAccountMint => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenDefinition::into_data(TokenDefinition {
|
||||||
|
account_type: TOKEN_DEFINITION_TYPE,
|
||||||
|
name: [2; 6],
|
||||||
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
},
|
||||||
|
AccountsEnum::HoldingSameDefAuthLargeBalance => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenHolding::into_data(TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
balance: helper_balance_constructor(BalanceEnum::MintOverflow),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
},
|
||||||
|
_ => panic!("Invalid selection"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn helper_balance_constructor(selection: BalanceEnum) -> u128 {
|
||||||
|
match selection {
|
||||||
|
BalanceEnum::InitSupply => 100_000,
|
||||||
|
BalanceEnum::HoldingBalance => 1_000,
|
||||||
|
BalanceEnum::InitSupplyBurned => 99_500,
|
||||||
|
BalanceEnum::HoldingBalanceBurned => 500,
|
||||||
|
BalanceEnum::BurnSuccess => 500,
|
||||||
|
BalanceEnum::BurnInsufficient => 1_500,
|
||||||
|
BalanceEnum::MintSuccess => 50_000,
|
||||||
|
BalanceEnum::InitSupplyMint => 150_000,
|
||||||
|
BalanceEnum::HoldingBalanceMint => 51_000,
|
||||||
|
BalanceEnum::MintOverflow => (2 as u128).pow(128) - 40_000,
|
||||||
|
_ => panic!("Invalid selection"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn helper_id_constructor(selection: IdEnum) -> AccountId {
|
||||||
|
match selection {
|
||||||
|
IdEnum::PoolDefinitionId => AccountId::new([15; 32]),
|
||||||
|
IdEnum::PoolDefinitionIdDiff => AccountId::new([16; 32]),
|
||||||
|
IdEnum::HoldingId => AccountId::new([17; 32]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Invalid number of accounts")]
|
||||||
|
fn test_burn_invalid_number_of_accounts() {
|
||||||
|
let pre_states = vec![helper_account_constructor(
|
||||||
|
AccountsEnum::DefinitionAccountAuth,
|
||||||
|
)];
|
||||||
|
let _post_states = burn(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::BurnSuccess),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Mismatch token definition and token holding")]
|
||||||
|
fn test_burn_mismatch_def() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingDiffDef),
|
||||||
|
];
|
||||||
|
let _post_states = burn(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::BurnSuccess),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Authorization is missing")]
|
||||||
|
fn test_burn_missing_authorization() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth),
|
||||||
|
];
|
||||||
|
let _post_states = burn(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::BurnSuccess),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Insufficient balance to burn")]
|
||||||
|
fn test_burn_insufficient_balance() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefAuth),
|
||||||
|
];
|
||||||
|
let _post_states = burn(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::BurnInsufficient),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Total supply underflow")]
|
||||||
|
fn test_burn_total_supply_underflow() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefAuthLargeBalance),
|
||||||
|
];
|
||||||
|
let _post_states = burn(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintOverflow),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_burn_success() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefAuth),
|
||||||
|
];
|
||||||
|
let post_states = burn(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::BurnSuccess),
|
||||||
|
);
|
||||||
|
|
||||||
|
let def_post = post_states[0].clone();
|
||||||
|
let holding_post = post_states[1].clone();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
*def_post.account()
|
||||||
|
== helper_account_constructor(AccountsEnum::DefinitionAccountPostBurn).account
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
*holding_post.account()
|
||||||
|
== helper_account_constructor(AccountsEnum::HoldingAccountPostBurn).account
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Invalid number of accounts")]
|
||||||
|
fn test_mint_invalid_number_of_accounts() {
|
||||||
|
let pre_states = vec![helper_account_constructor(
|
||||||
|
AccountsEnum::DefinitionAccountAuth,
|
||||||
|
)];
|
||||||
|
let _post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintSuccess),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Holding account must be valid")]
|
||||||
|
fn test_mint_not_valid_holding_account() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountNotAuth),
|
||||||
|
];
|
||||||
|
let _post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintSuccess),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Definition authorization is missing")]
|
||||||
|
fn test_mint_missing_authorization() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountNotAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth),
|
||||||
|
];
|
||||||
|
let _post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintSuccess),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Mismatch token definition and token holding")]
|
||||||
|
fn test_mint_mismatched_token_definition() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingDiffDef),
|
||||||
|
];
|
||||||
|
let _post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintSuccess),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mint_success() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth),
|
||||||
|
];
|
||||||
|
let post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintSuccess),
|
||||||
|
);
|
||||||
|
|
||||||
|
let def_post = post_states[0].clone();
|
||||||
|
let holding_post = post_states[1].clone();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
*def_post.account()
|
||||||
|
== helper_account_constructor(AccountsEnum::DefinitionAccountMint).account
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
*holding_post.account()
|
||||||
|
== helper_account_constructor(AccountsEnum::HoldingSameDefMint).account
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mint_uninit_holding_success() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::Uninit),
|
||||||
|
];
|
||||||
|
let post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintSuccess),
|
||||||
|
);
|
||||||
|
|
||||||
|
let def_post = post_states[0].clone();
|
||||||
|
let holding_post = post_states[1].clone();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
*def_post.account()
|
||||||
|
== helper_account_constructor(AccountsEnum::DefinitionAccountMint).account
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
*holding_post.account() == helper_account_constructor(AccountsEnum::InitMint).account
|
||||||
|
);
|
||||||
|
assert!(holding_post.requires_claim() == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Total supply overflow")]
|
||||||
|
fn test_mint_total_supply_overflow() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth),
|
||||||
|
];
|
||||||
|
let _post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintOverflow),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "New balance overflow")]
|
||||||
|
fn test_mint_holding_account_overflow() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefNotAuthOverflow),
|
||||||
|
];
|
||||||
|
let _post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintOverflow),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,13 +44,15 @@ impl From<Program> for ProgramWithDependencies {
|
|||||||
|
|
||||||
/// Generates a proof of the execution of a NSSA program inside the privacy preserving execution
|
/// Generates a proof of the execution of a NSSA program inside the privacy preserving execution
|
||||||
/// circuit
|
/// circuit
|
||||||
|
#[expect(clippy::too_many_arguments, reason = "TODO: fix later")]
|
||||||
pub fn execute_and_prove(
|
pub fn execute_and_prove(
|
||||||
pre_states: &[AccountWithMetadata],
|
pre_states: &[AccountWithMetadata],
|
||||||
instruction_data: &InstructionData,
|
instruction_data: &InstructionData,
|
||||||
visibility_mask: &[u8],
|
visibility_mask: &[u8],
|
||||||
private_account_nonces: &[u128],
|
private_account_nonces: &[u128],
|
||||||
private_account_keys: &[(NullifierPublicKey, SharedSecretKey)],
|
private_account_keys: &[(NullifierPublicKey, SharedSecretKey)],
|
||||||
private_account_auth: &[(NullifierSecretKey, MembershipProof)],
|
private_account_nsks: &[NullifierSecretKey],
|
||||||
|
private_account_membership_proofs: &[Option<MembershipProof>],
|
||||||
program_with_dependencies: &ProgramWithDependencies,
|
program_with_dependencies: &ProgramWithDependencies,
|
||||||
) -> Result<(PrivacyPreservingCircuitOutput, Proof), NssaError> {
|
) -> Result<(PrivacyPreservingCircuitOutput, Proof), NssaError> {
|
||||||
let mut program = &program_with_dependencies.program;
|
let mut program = &program_with_dependencies.program;
|
||||||
@ -105,7 +107,8 @@ pub fn execute_and_prove(
|
|||||||
visibility_mask: visibility_mask.to_vec(),
|
visibility_mask: visibility_mask.to_vec(),
|
||||||
private_account_nonces: private_account_nonces.to_vec(),
|
private_account_nonces: private_account_nonces.to_vec(),
|
||||||
private_account_keys: private_account_keys.to_vec(),
|
private_account_keys: private_account_keys.to_vec(),
|
||||||
private_account_auth: private_account_auth.to_vec(),
|
private_account_nsks: private_account_nsks.to_vec(),
|
||||||
|
private_account_membership_proofs: private_account_membership_proofs.to_vec(),
|
||||||
program_id: program_with_dependencies.program.id(),
|
program_id: program_with_dependencies.program.id(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -195,7 +198,7 @@ mod tests {
|
|||||||
let expected_sender_post = Account {
|
let expected_sender_post = Account {
|
||||||
program_owner: program.id(),
|
program_owner: program.id(),
|
||||||
balance: 100 - balance_to_move,
|
balance: 100 - balance_to_move,
|
||||||
nonce: 1,
|
nonce: 0,
|
||||||
data: Data::default(),
|
data: Data::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -218,6 +221,7 @@ mod tests {
|
|||||||
&[0xdeadbeef],
|
&[0xdeadbeef],
|
||||||
&[(recipient_keys.npk(), shared_secret.clone())],
|
&[(recipient_keys.npk(), shared_secret.clone())],
|
||||||
&[],
|
&[],
|
||||||
|
&[None],
|
||||||
&Program::authenticated_transfer_program().into(),
|
&Program::authenticated_transfer_program().into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -315,10 +319,8 @@ mod tests {
|
|||||||
(sender_keys.npk(), shared_secret_1.clone()),
|
(sender_keys.npk(), shared_secret_1.clone()),
|
||||||
(recipient_keys.npk(), shared_secret_2.clone()),
|
(recipient_keys.npk(), shared_secret_2.clone()),
|
||||||
],
|
],
|
||||||
&[(
|
&[sender_keys.nsk],
|
||||||
sender_keys.nsk,
|
&[commitment_set.get_proof_for(&commitment_sender), None],
|
||||||
commitment_set.get_proof_for(&commitment_sender).unwrap(),
|
|
||||||
)],
|
|
||||||
&program.into(),
|
&program.into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@ -222,6 +222,15 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn noop() -> Self {
|
||||||
|
use test_program_methods::{NOOP_ELF, NOOP_ID};
|
||||||
|
|
||||||
|
Program {
|
||||||
|
id: NOOP_ID,
|
||||||
|
elf: NOOP_ELF.to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn modified_transfer_program() -> Self {
|
pub fn modified_transfer_program() -> Self {
|
||||||
use test_program_methods::MODIFIED_TRANSFER_ELF;
|
use test_program_methods::MODIFIED_TRANSFER_ELF;
|
||||||
// This unwrap won't panic since the `MODIFIED_TRANSFER_ELF` comes from risc0 build of
|
// This unwrap won't panic since the `MODIFIED_TRANSFER_ELF` comes from risc0 build of
|
||||||
|
|||||||
@ -154,6 +154,12 @@ impl V02State {
|
|||||||
*current_account = post;
|
*current_account = post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5. Increment nonces for public signers
|
||||||
|
for account_id in tx.signer_account_ids() {
|
||||||
|
let current_account = self.get_account_by_id_mut(account_id);
|
||||||
|
current_account.nonce += 1;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,6 +868,7 @@ pub mod tests {
|
|||||||
&[0xdeadbeef],
|
&[0xdeadbeef],
|
||||||
&[(recipient_keys.npk(), shared_secret)],
|
&[(recipient_keys.npk(), shared_secret)],
|
||||||
&[],
|
&[],
|
||||||
|
&[None],
|
||||||
&Program::authenticated_transfer_program().into(),
|
&Program::authenticated_transfer_program().into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -910,10 +917,8 @@ pub mod tests {
|
|||||||
(sender_keys.npk(), shared_secret_1),
|
(sender_keys.npk(), shared_secret_1),
|
||||||
(recipient_keys.npk(), shared_secret_2),
|
(recipient_keys.npk(), shared_secret_2),
|
||||||
],
|
],
|
||||||
&[(
|
&[sender_keys.nsk],
|
||||||
sender_keys.nsk,
|
&[state.get_proof_for_commitment(&sender_commitment), None],
|
||||||
state.get_proof_for_commitment(&sender_commitment).unwrap(),
|
|
||||||
)],
|
|
||||||
&program.into(),
|
&program.into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -962,10 +967,8 @@ pub mod tests {
|
|||||||
&[1, 0],
|
&[1, 0],
|
||||||
&[new_nonce],
|
&[new_nonce],
|
||||||
&[(sender_keys.npk(), shared_secret)],
|
&[(sender_keys.npk(), shared_secret)],
|
||||||
&[(
|
&[sender_keys.nsk],
|
||||||
sender_keys.nsk,
|
&[state.get_proof_for_commitment(&sender_commitment)],
|
||||||
state.get_proof_for_commitment(&sender_commitment).unwrap(),
|
|
||||||
)],
|
|
||||||
&program.into(),
|
&program.into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1179,6 +1182,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1205,6 +1209,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1231,6 +1236,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1257,6 +1263,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1285,6 +1292,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.to_owned().into(),
|
&program.to_owned().into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1311,6 +1319,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1346,6 +1355,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1372,6 +1382,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1407,6 +1418,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1444,6 +1456,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1484,7 +1497,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1518,7 +1532,50 @@ pub mod tests {
|
|||||||
&[1, 2],
|
&[1, 2],
|
||||||
&[0xdeadbeef1, 0xdeadbeef2],
|
&[0xdeadbeef1, 0xdeadbeef2],
|
||||||
&private_account_keys,
|
&private_account_keys,
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
|
&program.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_circuit_fails_if_insufficient_commitment_proofs_are_provided() {
|
||||||
|
let program = Program::simple_balance_transfer();
|
||||||
|
let sender_keys = test_private_account_keys_1();
|
||||||
|
let recipient_keys = test_private_account_keys_2();
|
||||||
|
let private_account_1 = AccountWithMetadata::new(
|
||||||
|
Account {
|
||||||
|
program_owner: program.id(),
|
||||||
|
balance: 100,
|
||||||
|
..Account::default()
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
&sender_keys.npk(),
|
||||||
|
);
|
||||||
|
let private_account_2 =
|
||||||
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
||||||
|
|
||||||
|
// Setting no second commitment proof.
|
||||||
|
let private_account_membership_proofs = [Some((0, vec![]))];
|
||||||
|
let result = execute_and_prove(
|
||||||
|
&[private_account_1, private_account_2],
|
||||||
|
&Program::serialize_instruction(10u128).unwrap(),
|
||||||
|
&[1, 2],
|
||||||
|
&[0xdeadbeef1, 0xdeadbeef2],
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
sender_keys.npk(),
|
||||||
|
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
recipient_keys.npk(),
|
||||||
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
&[sender_keys.nsk],
|
||||||
|
&private_account_membership_proofs,
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1543,7 +1600,7 @@ pub mod tests {
|
|||||||
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
||||||
|
|
||||||
// Setting no auth key for an execution with one non default private accounts.
|
// Setting no auth key for an execution with one non default private accounts.
|
||||||
let private_account_auth = [];
|
let private_account_nsks = [];
|
||||||
let result = execute_and_prove(
|
let result = execute_and_prove(
|
||||||
&[private_account_1, private_account_2],
|
&[private_account_1, private_account_2],
|
||||||
&Program::serialize_instruction(10u128).unwrap(),
|
&Program::serialize_instruction(10u128).unwrap(),
|
||||||
@ -1559,7 +1616,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&private_account_auth,
|
&private_account_nsks,
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1595,19 +1653,20 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
let private_account_auth = [
|
|
||||||
// Setting the recipient key to authorize the sender.
|
// Setting the recipient key to authorize the sender.
|
||||||
// This should be set to the sender private account in
|
// This should be set to the sender private account in
|
||||||
// a normal circumstance. The recipient can't authorize this.
|
// a normal circumstance. The recipient can't authorize this.
|
||||||
(recipient_keys.nsk, (0, vec![])),
|
let private_account_nsks = [recipient_keys.nsk];
|
||||||
];
|
let private_account_membership_proofs = [Some((0, vec![]))];
|
||||||
let result = execute_and_prove(
|
let result = execute_and_prove(
|
||||||
&[private_account_1, private_account_2],
|
&[private_account_1, private_account_2],
|
||||||
&Program::serialize_instruction(10u128).unwrap(),
|
&Program::serialize_instruction(10u128).unwrap(),
|
||||||
&[1, 2],
|
&[1, 2],
|
||||||
&[0xdeadbeef1, 0xdeadbeef2],
|
&[0xdeadbeef1, 0xdeadbeef2],
|
||||||
&private_account_keys,
|
&private_account_keys,
|
||||||
&private_account_auth,
|
&private_account_nsks,
|
||||||
|
&private_account_membership_proofs,
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1653,7 +1712,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1700,7 +1760,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1746,7 +1807,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1792,7 +1854,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1836,7 +1899,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1866,6 +1930,7 @@ pub mod tests {
|
|||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1907,7 +1972,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1953,7 +2019,8 @@ pub mod tests {
|
|||||||
&[1, 2],
|
&[1, 2],
|
||||||
&[0xdeadbeef1, 0xdeadbeef2],
|
&[0xdeadbeef1, 0xdeadbeef2],
|
||||||
&private_account_keys,
|
&private_account_keys,
|
||||||
&[(sender_keys.nsk, (0, vec![]))],
|
&[sender_keys.nsk],
|
||||||
|
&[Some((0, vec![]))],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1980,10 +2047,8 @@ pub mod tests {
|
|||||||
// Setting two private account keys for a circuit execution with only one non default
|
// Setting two private account keys for a circuit execution with only one non default
|
||||||
// private account (visibility mask equal to 1 means that auth keys are expected).
|
// private account (visibility mask equal to 1 means that auth keys are expected).
|
||||||
let visibility_mask = [1, 2];
|
let visibility_mask = [1, 2];
|
||||||
let private_account_auth = [
|
let private_account_nsks = [sender_keys.nsk, recipient_keys.nsk];
|
||||||
(sender_keys.nsk, (0, vec![])),
|
let private_account_membership_proofs = [Some((0, vec![])), Some((1, vec![]))];
|
||||||
(recipient_keys.nsk, (1, vec![])),
|
|
||||||
];
|
|
||||||
let result = execute_and_prove(
|
let result = execute_and_prove(
|
||||||
&[private_account_1, private_account_2],
|
&[private_account_1, private_account_2],
|
||||||
&Program::serialize_instruction(10u128).unwrap(),
|
&Program::serialize_instruction(10u128).unwrap(),
|
||||||
@ -1999,7 +2064,8 @@ pub mod tests {
|
|||||||
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
&private_account_auth,
|
&private_account_nsks,
|
||||||
|
&private_account_membership_proofs,
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2076,10 +2142,8 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let visibility_mask = [1, 1];
|
let visibility_mask = [1, 1];
|
||||||
let private_account_auth = [
|
let private_account_nsks = [sender_keys.nsk, sender_keys.nsk];
|
||||||
(sender_keys.nsk, (1, vec![])),
|
let private_account_membership_proofs = [Some((1, vec![])), Some((1, vec![]))];
|
||||||
(sender_keys.nsk, (1, vec![])),
|
|
||||||
];
|
|
||||||
let shared_secret = SharedSecretKey::new(&[55; 32], &sender_keys.ivk());
|
let shared_secret = SharedSecretKey::new(&[55; 32], &sender_keys.ivk());
|
||||||
let result = execute_and_prove(
|
let result = execute_and_prove(
|
||||||
&[private_account_1.clone(), private_account_1],
|
&[private_account_1.clone(), private_account_1],
|
||||||
@ -2090,7 +2154,8 @@ pub mod tests {
|
|||||||
(sender_keys.npk(), shared_secret.clone()),
|
(sender_keys.npk(), shared_secret.clone()),
|
||||||
(sender_keys.npk(), shared_secret),
|
(sender_keys.npk(), shared_secret),
|
||||||
],
|
],
|
||||||
&private_account_auth,
|
&private_account_nsks,
|
||||||
|
&private_account_membership_proofs,
|
||||||
&program.into(),
|
&program.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2391,15 +2456,10 @@ pub mod tests {
|
|||||||
&[1, 1],
|
&[1, 1],
|
||||||
&[from_new_nonce, to_new_nonce],
|
&[from_new_nonce, to_new_nonce],
|
||||||
&[(from_keys.npk(), to_ss), (to_keys.npk(), from_ss)],
|
&[(from_keys.npk(), to_ss), (to_keys.npk(), from_ss)],
|
||||||
|
&[from_keys.nsk, to_keys.nsk],
|
||||||
&[
|
&[
|
||||||
(
|
state.get_proof_for_commitment(&from_commitment),
|
||||||
from_keys.nsk,
|
state.get_proof_for_commitment(&to_commitment),
|
||||||
state.get_proof_for_commitment(&from_commitment).unwrap(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
to_keys.nsk,
|
|
||||||
state.get_proof_for_commitment(&to_commitment).unwrap(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
&program_with_deps,
|
&program_with_deps,
|
||||||
)
|
)
|
||||||
@ -2606,4 +2666,143 @@ pub mod tests {
|
|||||||
assert!(expected_sender_post == sender_post);
|
assert!(expected_sender_post == sender_post);
|
||||||
assert!(expected_recipient_post == recipient_post);
|
assert!(expected_recipient_post == recipient_post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_private_authorized_uninitialized_account() {
|
||||||
|
let mut state = V02State::new_with_genesis_accounts(&[], &[]);
|
||||||
|
|
||||||
|
// Set up keys for the authorized private account
|
||||||
|
let private_keys = test_private_account_keys_1();
|
||||||
|
|
||||||
|
// Create an authorized private account with default values (new account being initialized)
|
||||||
|
let authorized_account =
|
||||||
|
AccountWithMetadata::new(Account::default(), true, &private_keys.npk());
|
||||||
|
|
||||||
|
let program = Program::authenticated_transfer_program();
|
||||||
|
|
||||||
|
// Set up parameters for the new account
|
||||||
|
let esk = [3; 32];
|
||||||
|
let shared_secret = SharedSecretKey::new(&esk, &private_keys.ivk());
|
||||||
|
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||||
|
|
||||||
|
// Balance to initialize the account with (0 for a new account)
|
||||||
|
let balance: u128 = 0;
|
||||||
|
|
||||||
|
let nonce = 0xdeadbeef1;
|
||||||
|
|
||||||
|
// Execute and prove the circuit with the authorized account but no commitment proof
|
||||||
|
let (output, proof) = execute_and_prove(
|
||||||
|
std::slice::from_ref(&authorized_account),
|
||||||
|
&Program::serialize_instruction(balance).unwrap(),
|
||||||
|
&[1],
|
||||||
|
&[nonce],
|
||||||
|
&[(private_keys.npk(), shared_secret)],
|
||||||
|
&[private_keys.nsk],
|
||||||
|
&[None],
|
||||||
|
&program.into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Create message from circuit output
|
||||||
|
let message = Message::try_from_circuit_output(
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
vec![(private_keys.npk(), private_keys.ivk(), epk)],
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||||
|
|
||||||
|
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||||
|
let result = state.transition_from_privacy_preserving_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
let nullifier = Nullifier::for_account_initialization(&private_keys.npk());
|
||||||
|
assert!(state.private_state.1.contains(&nullifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_private_account_claimed_then_used_without_init_flag_should_fail() {
|
||||||
|
let mut state = V02State::new_with_genesis_accounts(&[], &[]).with_test_programs();
|
||||||
|
|
||||||
|
// Set up keys for the private account
|
||||||
|
let private_keys = test_private_account_keys_1();
|
||||||
|
|
||||||
|
// Step 1: Create a new private account with authorization
|
||||||
|
let authorized_account =
|
||||||
|
AccountWithMetadata::new(Account::default(), true, &private_keys.npk());
|
||||||
|
|
||||||
|
let claimer_program = Program::claimer();
|
||||||
|
|
||||||
|
// Set up parameters for claiming the new account
|
||||||
|
let esk = [3; 32];
|
||||||
|
let shared_secret = SharedSecretKey::new(&esk, &private_keys.ivk());
|
||||||
|
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||||
|
|
||||||
|
let balance: u128 = 0;
|
||||||
|
let nonce = 0xdeadbeef1;
|
||||||
|
|
||||||
|
// Step 2: Execute claimer program to claim the account with authentication
|
||||||
|
let (output, proof) = execute_and_prove(
|
||||||
|
std::slice::from_ref(&authorized_account),
|
||||||
|
&Program::serialize_instruction(balance).unwrap(),
|
||||||
|
&[1],
|
||||||
|
&[nonce],
|
||||||
|
&[(private_keys.npk(), shared_secret)],
|
||||||
|
&[private_keys.nsk],
|
||||||
|
&[None],
|
||||||
|
&claimer_program.into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let message = Message::try_from_circuit_output(
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
vec![(private_keys.npk(), private_keys.ivk(), epk)],
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||||
|
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
// Claim should succeed
|
||||||
|
assert!(
|
||||||
|
state
|
||||||
|
.transition_from_privacy_preserving_transaction(&tx)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify the account is now initialized (nullifier exists)
|
||||||
|
let nullifier = Nullifier::for_account_initialization(&private_keys.npk());
|
||||||
|
assert!(state.private_state.1.contains(&nullifier));
|
||||||
|
|
||||||
|
// Prepare new state of account
|
||||||
|
let account_metadata = {
|
||||||
|
let mut acc = authorized_account.clone();
|
||||||
|
acc.account.program_owner = Program::claimer().id();
|
||||||
|
acc
|
||||||
|
};
|
||||||
|
|
||||||
|
let noop_program = Program::noop();
|
||||||
|
let esk2 = [4; 32];
|
||||||
|
let shared_secret2 = SharedSecretKey::new(&esk2, &private_keys.ivk());
|
||||||
|
|
||||||
|
let nonce2 = 0xdeadbeef2;
|
||||||
|
|
||||||
|
// Step 3: Try to execute noop program with authentication but without initialization
|
||||||
|
let res = execute_and_prove(
|
||||||
|
std::slice::from_ref(&account_metadata),
|
||||||
|
&Program::serialize_instruction(()).unwrap(),
|
||||||
|
&[1],
|
||||||
|
&[nonce2],
|
||||||
|
&[(private_keys.npk(), shared_secret2)],
|
||||||
|
&[private_keys.nsk],
|
||||||
|
&[None],
|
||||||
|
&noop_program.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(matches!(res, Err(NssaError::CircuitProvingError(_))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
nssa/test_program_methods/guest/src/bin/noop.rs
Normal file
12
nssa/test_program_methods/guest/src/bin/noop.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use nssa_core::program::{read_nssa_inputs, write_nssa_outputs, ProgramInput, AccountPostState};
|
||||||
|
|
||||||
|
type Instruction = ();
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let (ProgramInput { pre_states, .. }, instruction_words) = read_nssa_inputs::<Instruction>();
|
||||||
|
|
||||||
|
let post_states = pre_states.iter().map(|account| {
|
||||||
|
AccountPostState::new(account.account.clone())
|
||||||
|
}).collect();
|
||||||
|
write_nssa_outputs(instruction_words, pre_states, post_states);
|
||||||
|
}
|
||||||
@ -285,7 +285,8 @@ impl WalletCore {
|
|||||||
.map(|keys| (keys.npk.clone(), keys.ssk.clone()))
|
.map(|keys| (keys.npk.clone(), keys.ssk.clone()))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
&acc_manager.private_account_auth(),
|
&acc_manager.private_account_auth(),
|
||||||
program,
|
&acc_manager.private_account_membership_proofs(),
|
||||||
|
&program.to_owned(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -305,7 +306,7 @@ impl WalletCore {
|
|||||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
|
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
|
||||||
&message,
|
&message,
|
||||||
proof,
|
proof,
|
||||||
&acc_manager.witness_signing_keys(),
|
&acc_manager.public_account_auth(),
|
||||||
);
|
);
|
||||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
|||||||
@ -58,7 +58,8 @@ impl WalletCore {
|
|||||||
&[0, 1],
|
&[0, 1],
|
||||||
&produce_random_nonces(1),
|
&produce_random_nonces(1),
|
||||||
&[(winner_npk.clone(), shared_secret_winner.clone())],
|
&[(winner_npk.clone(), shared_secret_winner.clone())],
|
||||||
&[(winner_nsk.unwrap(), winner_proof)],
|
&[(winner_nsk.unwrap())],
|
||||||
|
&[winner_proof],
|
||||||
&program.into(),
|
&program.into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -125,6 +126,7 @@ impl WalletCore {
|
|||||||
&produce_random_nonces(1),
|
&produce_random_nonces(1),
|
||||||
&[(winner_npk.clone(), shared_secret_winner.clone())],
|
&[(winner_npk.clone(), shared_secret_winner.clone())],
|
||||||
&[],
|
&[],
|
||||||
|
&[]
|
||||||
&program.into(),
|
&program.into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@ -133,11 +133,21 @@ impl AccountManager {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn private_account_auth(&self) -> Vec<(NullifierSecretKey, MembershipProof)> {
|
pub fn private_account_auth(&self) -> Vec<NullifierSecretKey> {
|
||||||
self.states
|
self.states
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|state| match state {
|
.filter_map(|state| match state {
|
||||||
State::Private(pre) => Some((pre.nsk?, pre.proof.clone()?)),
|
State::Private(pre) => pre.nsk,
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn private_account_membership_proofs(&self) -> Vec<Option<MembershipProof>> {
|
||||||
|
self.states
|
||||||
|
.iter()
|
||||||
|
.filter_map(|state| match state {
|
||||||
|
State::Private(pre) => Some(pre.proof.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
@ -153,7 +163,7 @@ impl AccountManager {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn witness_signing_keys(&self) -> Vec<&PrivateKey> {
|
pub fn public_account_auth(&self) -> Vec<&PrivateKey> {
|
||||||
self.states
|
self.states
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|state| match state {
|
.filter_map(|state| match state {
|
||||||
@ -185,7 +195,7 @@ async fn private_acc_preparation(
|
|||||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut nsk = Some(from_keys.private_key_holder.nullifier_secret_key);
|
let nsk = from_keys.private_key_holder.nullifier_secret_key;
|
||||||
|
|
||||||
let from_npk = from_keys.nullifer_public_key;
|
let from_npk = from_keys.nullifer_public_key;
|
||||||
let from_ipk = from_keys.incoming_viewing_public_key;
|
let from_ipk = from_keys.incoming_viewing_public_key;
|
||||||
@ -196,14 +206,12 @@ async fn private_acc_preparation(
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if proof.is_none() {
|
// TODO: Technically we could allow unauthorized owned accounts, but currently we don't have
|
||||||
nsk = None;
|
// support from that in the wallet.
|
||||||
}
|
let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &from_npk);
|
||||||
|
|
||||||
let sender_pre = AccountWithMetadata::new(from_acc.clone(), proof.is_some(), &from_npk);
|
|
||||||
|
|
||||||
Ok(AccountPreparedData {
|
Ok(AccountPreparedData {
|
||||||
nsk,
|
nsk: Some(nsk),
|
||||||
npk: from_npk,
|
npk: from_npk,
|
||||||
ipk: from_ipk,
|
ipk: from_ipk,
|
||||||
pre_state: sender_pre,
|
pre_state: sender_pre,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user