mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-07 15:53:14 +00:00
public transactions wip
This commit is contained in:
parent
a06af6da0a
commit
aba8f3549f
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -2987,6 +2987,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
"log",
|
"log",
|
||||||
|
"nssa",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"reqwest 0.11.27",
|
"reqwest 0.11.27",
|
||||||
"risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)",
|
"risc0-zkvm 2.3.1 (git+https://github.com/risc0/risc0.git?branch=release-2.3)",
|
||||||
@ -3069,10 +3070,19 @@ dependencies = [
|
|||||||
name = "nssa"
|
name = "nssa"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"nssa-core",
|
||||||
"program-methods",
|
"program-methods",
|
||||||
"risc0-zkvm 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"risc0-zkvm 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nssa-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"risc0-zkvm 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|||||||
@ -5,4 +5,5 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
risc0-zkvm = "2.2"
|
risc0-zkvm = "2.2"
|
||||||
|
nssa-core = {path="core"}
|
||||||
program-methods = { path = "program_methods" }
|
program-methods = { path = "program_methods" }
|
||||||
|
|||||||
8
nssa/core/Cargo.toml
Normal file
8
nssa/core/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "nssa-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
risc0-zkvm = "2.2"
|
||||||
|
serde = { version = "1.0", default-features = false }
|
||||||
3
nssa/core/src/account/commitment.rs
Normal file
3
nssa/core/src/account/commitment.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub(crate) struct Commitment {
|
||||||
|
value: [u8; 32],
|
||||||
|
}
|
||||||
37
nssa/core/src/account/mod.rs
Normal file
37
nssa/core/src/account/mod.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
mod commitment;
|
||||||
|
mod nullifier;
|
||||||
|
|
||||||
|
pub(crate) use commitment::Commitment;
|
||||||
|
pub(crate) use nullifier::Nullifier;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::program::ProgramId;
|
||||||
|
|
||||||
|
pub type Nonce = u128;
|
||||||
|
type Data = Vec<u8>;
|
||||||
|
|
||||||
|
/// Account to be used both in public and private contexts
|
||||||
|
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct Account {
|
||||||
|
pub program_owner: ProgramId,
|
||||||
|
pub balance: u128,
|
||||||
|
pub data: Data,
|
||||||
|
pub nonce: Nonce,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AccountWithMetadata {
|
||||||
|
pub account: Account,
|
||||||
|
pub is_authorized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Account {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
program_owner: [0; 8],
|
||||||
|
balance: 0,
|
||||||
|
data: vec![],
|
||||||
|
nonce: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
nssa/core/src/account/nullifier.rs
Normal file
3
nssa/core/src/account/nullifier.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub(crate) struct Nullifier {
|
||||||
|
value: [u8; 32]
|
||||||
|
}
|
||||||
2
nssa/core/src/lib.rs
Normal file
2
nssa/core/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod account;
|
||||||
|
pub mod program;
|
||||||
63
nssa/core/src/program.rs
Normal file
63
nssa/core/src/program.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::account::{Account, AccountWithMetadata};
|
||||||
|
|
||||||
|
pub type ProgramId = [u32; 8];
|
||||||
|
|
||||||
|
/// A trait to be implemented by inner programs.
|
||||||
|
pub trait Program {
|
||||||
|
const PROGRAM_ID: ProgramId;
|
||||||
|
const PROGRAM_ELF: &[u8];
|
||||||
|
type InstructionData: Serialize + for<'de> Deserialize<'de>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates well-behaved program execution
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `pre_states`: The list of input accounts, each annotated with authorization metadata.
|
||||||
|
/// - `post_states`: The list of resulting accounts after executing the program logic.
|
||||||
|
/// - `executing_program_id`: The identifier of the program that was executed.
|
||||||
|
pub fn validate_constraints(
|
||||||
|
pre_states: &[AccountWithMetadata],
|
||||||
|
post_states: &[Account],
|
||||||
|
executing_program_id: ProgramId,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
// 1. Lengths must match
|
||||||
|
if pre_states.len() != post_states.len() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pre, post) in pre_states.iter().zip(post_states) {
|
||||||
|
// 2. Nonce must remain unchanged
|
||||||
|
if pre.account.nonce != post.nonce {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Ownership change only allowed from default accounts
|
||||||
|
if pre.account.program_owner != post.program_owner && pre.account != Account::default() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Decreasing balance only allowed if owned by executing program
|
||||||
|
if post.balance < pre.account.balance && pre.account.program_owner != executing_program_id {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Data changes only allowed if owned by executing program
|
||||||
|
if pre.account.data != post.data
|
||||||
|
&& (executing_program_id != pre.account.program_owner
|
||||||
|
|| executing_program_id != post.program_owner)
|
||||||
|
{
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Total balance is preserved
|
||||||
|
let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum();
|
||||||
|
let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum();
|
||||||
|
if total_balance_pre_states != total_balance_post_states {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
1898
nssa/program_methods/guest/Cargo.lock
generated
1898
nssa/program_methods/guest/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,4 +7,5 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
risc0-zkvm = { version = "2.2.0", default-features = false, features = ['std'] }
|
risc0-zkvm = { version = "2.2.0", default-features = false, features = ['std'] }
|
||||||
|
nssa-core = {path = "../../core"}
|
||||||
|
|
||||||
|
|||||||
31
nssa/program_methods/guest/src/bin/authenticated_transfer.rs
Normal file
31
nssa/program_methods/guest/src/bin/authenticated_transfer.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use nssa_core::account::{Account, AccountWithMetadata};
|
||||||
|
use risc0_zkvm::guest::env;
|
||||||
|
|
||||||
|
/// A transfer of balance program.
|
||||||
|
/// To be used both in public and private contexts.
|
||||||
|
fn main() {
|
||||||
|
// Read input accounts.
|
||||||
|
// It is expected to receive only two accounts: [sender_account, receiver_account]
|
||||||
|
let input_accounts: Vec<AccountWithMetadata> = env::read();
|
||||||
|
let balance_to_move: u128 = env::read();
|
||||||
|
|
||||||
|
// Unpack sender and receiver
|
||||||
|
assert_eq!(input_accounts.len(), 2);
|
||||||
|
let [sender, receiver] = input_accounts
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or_else(|_| panic!("Bad input"));
|
||||||
|
|
||||||
|
// Check sender has authorized this operation
|
||||||
|
assert!(sender.is_authorized);
|
||||||
|
|
||||||
|
// Check sender has enough balance
|
||||||
|
assert!(sender.account.balance >= balance_to_move);
|
||||||
|
|
||||||
|
// Create accounts post states, with updated balances
|
||||||
|
let mut sender_post = sender.account.clone();
|
||||||
|
let mut receiver_post = receiver.account.clone();
|
||||||
|
sender_post.balance -= balance_to_move;
|
||||||
|
receiver_post.balance += balance_to_move;
|
||||||
|
|
||||||
|
env::commit(&vec![sender_post, receiver_post]);
|
||||||
|
}
|
||||||
@ -1,6 +0,0 @@
|
|||||||
use risc0_zkvm::guest::env;
|
|
||||||
fn main() {
|
|
||||||
let a: u32 = env::read();
|
|
||||||
env::commit(&a);
|
|
||||||
}
|
|
||||||
|
|
||||||
17
nssa/src/address.rs
Normal file
17
nssa/src/address.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use crate::signature::PublicKey;
|
||||||
|
|
||||||
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub(crate) struct Address {
|
||||||
|
pub(crate) value: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
pub(crate) fn new(value: [u8; 32]) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_public_key(public_key: &PublicKey) -> Self {
|
||||||
|
// TODO: implement
|
||||||
|
Address::new([public_key.0; 32])
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,14 +1,47 @@
|
|||||||
pub fn add(left: u64, right: u64) -> u64 {
|
use nssa_core::{
|
||||||
left + right
|
account::{Account, AccountWithMetadata},
|
||||||
|
program::{Program, ProgramId},
|
||||||
|
};
|
||||||
|
use program_methods::{AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID};
|
||||||
|
use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor};
|
||||||
|
|
||||||
|
mod address;
|
||||||
|
mod public_transaction;
|
||||||
|
mod signature;
|
||||||
|
pub mod state;
|
||||||
|
|
||||||
|
struct AuthenticatedTransferProgram;
|
||||||
|
impl Program for AuthenticatedTransferProgram {
|
||||||
|
const PROGRAM_ID: ProgramId = AUTHENTICATED_TRANSFER_ID;
|
||||||
|
const PROGRAM_ELF: &[u8] = AUTHENTICATED_TRANSFER_ELF;
|
||||||
|
type InstructionData = u128;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
/// Writes inputs to `env_builder` in the order expected by the programs
|
||||||
mod tests {
|
fn write_inputs<P: Program>(
|
||||||
use super::*;
|
pre_states: &[AccountWithMetadata],
|
||||||
|
instruction_data: P::InstructionData,
|
||||||
#[test]
|
env_builder: &mut ExecutorEnvBuilder,
|
||||||
fn it_works() {
|
) -> Result<(), ()> {
|
||||||
let result = add(2, 2);
|
let pre_states = pre_states.to_vec();
|
||||||
assert_eq!(result, 4);
|
env_builder.write(&pre_states).map_err(|_| ())?;
|
||||||
}
|
env_builder.write(&instruction_data).map_err(|_| ())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_public<P: Program>(
|
||||||
|
pre_states: &[AccountWithMetadata],
|
||||||
|
instruction_data: P::InstructionData,
|
||||||
|
) -> Result<Vec<Account>, ()> {
|
||||||
|
// Write inputs to the program
|
||||||
|
let mut env_builder = ExecutorEnv::builder();
|
||||||
|
write_inputs::<P>(pre_states, instruction_data, &mut env_builder)?;
|
||||||
|
let env = env_builder.build().unwrap();
|
||||||
|
|
||||||
|
// Execute the program (without proving)
|
||||||
|
let executor = default_executor();
|
||||||
|
let session_info = executor.execute(env, P::PROGRAM_ELF).map_err(|_| ())?;
|
||||||
|
|
||||||
|
// Get (inputs and) outputs
|
||||||
|
session_info.journal.decode().map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|||||||
85
nssa/src/public_transaction.rs
Normal file
85
nssa/src/public_transaction.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use nssa_core::{
|
||||||
|
account::{Account, Nonce},
|
||||||
|
program::ProgramId,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
address::Address,
|
||||||
|
signature::{PrivateKey, PublicKey, Signature},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct Message {
|
||||||
|
pub(crate) program_id: ProgramId,
|
||||||
|
pub(crate) addresses: Vec<Address>,
|
||||||
|
pub(crate) nonces: Vec<Nonce>,
|
||||||
|
// TODO: change to Vec<u8> for general programs
|
||||||
|
pub(crate) instruction_data: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
pub(crate) fn new(
|
||||||
|
program_id: ProgramId,
|
||||||
|
addresses: Vec<Address>,
|
||||||
|
nonces: Vec<Nonce>,
|
||||||
|
instruction_data: u128,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
program_id,
|
||||||
|
addresses,
|
||||||
|
nonces,
|
||||||
|
instruction_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
//TODO: implement
|
||||||
|
vec![0, 0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct WitnessSet {
|
||||||
|
pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WitnessSet {
|
||||||
|
pub(crate) fn for_message(message: &Message, private_keys: &[PrivateKey]) -> Self {
|
||||||
|
let message_bytes = message.to_bytes();
|
||||||
|
let signatures_and_public_keys = private_keys
|
||||||
|
.iter()
|
||||||
|
.map(|key| (Signature::new(key, &message_bytes), PublicKey::new(key)))
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
signatures_and_public_keys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct PublicTransaction {
|
||||||
|
message: Message,
|
||||||
|
witness_set: WitnessSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PublicTransaction {
|
||||||
|
pub(crate) fn message(&self) -> &Message {
|
||||||
|
&self.message
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn witness_set(&self) -> &WitnessSet {
|
||||||
|
&self.witness_set
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn signer_addresses(&self) -> Vec<Address> {
|
||||||
|
self.witness_set
|
||||||
|
.signatures_and_public_keys
|
||||||
|
.iter()
|
||||||
|
.map(|(_, public_key)| Address::from_public_key(public_key))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new(message: Message, witness_set: WitnessSet) -> Self {
|
||||||
|
Self {
|
||||||
|
message,
|
||||||
|
witness_set,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
nssa/src/signature.rs
Normal file
26
nssa/src/signature.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use crate::{address::Address, public_transaction::Message};
|
||||||
|
|
||||||
|
pub(crate) struct Signature;
|
||||||
|
|
||||||
|
// TODO: Dummy impl. Replace by actual private key.
|
||||||
|
pub(crate) struct PrivateKey(pub(crate) u8);
|
||||||
|
// TODO: Dummy impl. Replace by actual public key.
|
||||||
|
pub(crate) struct PublicKey(pub(crate) u8);
|
||||||
|
|
||||||
|
impl PublicKey {
|
||||||
|
pub(crate) fn new(key: &PrivateKey) -> Self {
|
||||||
|
// TODO: implement
|
||||||
|
Self(key.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_valid_for(&self, message: &Message, public_key: &PublicKey) -> bool {
|
||||||
|
// TODO: implement
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
169
nssa/src/state.rs
Normal file
169
nssa/src/state.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
use crate::{
|
||||||
|
AuthenticatedTransferProgram, address::Address, execute_public,
|
||||||
|
public_transaction::PublicTransaction,
|
||||||
|
};
|
||||||
|
use nssa_core::{
|
||||||
|
account::{Account, AccountWithMetadata},
|
||||||
|
program::{Program, validate_constraints},
|
||||||
|
};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
struct V01State {
|
||||||
|
public_state: HashMap<Address, Account>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl V01State {
|
||||||
|
fn transition_from_public_transaction(&mut self, tx: PublicTransaction) -> Result<(), ()> {
|
||||||
|
let state_diff = self
|
||||||
|
.execute_and_verify_public_transaction(&tx)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
|
||||||
|
for (address, post) in state_diff.into_iter() {
|
||||||
|
let current_account = self.get_account_by_address_mut(address);
|
||||||
|
*current_account = post;
|
||||||
|
}
|
||||||
|
|
||||||
|
for address in tx.signer_addresses() {
|
||||||
|
let current_account = self.get_account_by_address_mut(address);
|
||||||
|
current_account.nonce += 1;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_account_by_address_mut(&mut self, address: Address) -> &mut Account {
|
||||||
|
self.public_state
|
||||||
|
.entry(address)
|
||||||
|
.or_insert_with(Account::default)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_account_by_address(&self, address: &Address) -> Account {
|
||||||
|
self.public_state
|
||||||
|
.get(address)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(Account::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_and_verify_public_transaction(
|
||||||
|
&mut self,
|
||||||
|
tx: &PublicTransaction,
|
||||||
|
) -> Result<HashMap<Address, Account>, ()> {
|
||||||
|
let message = tx.message();
|
||||||
|
let witness_set = tx.witness_set();
|
||||||
|
|
||||||
|
// All addresses must be different
|
||||||
|
if message.addresses.iter().collect::<HashSet<_>>().len() != message.addresses.len() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.nonces.len() != witness_set.signatures_and_public_keys.len() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut authorized_addresses = Vec::new();
|
||||||
|
for ((signature, public_key), nonce) in witness_set
|
||||||
|
.signatures_and_public_keys
|
||||||
|
.iter()
|
||||||
|
.zip(message.nonces.iter())
|
||||||
|
{
|
||||||
|
// Check the signature is valid
|
||||||
|
if !signature.is_valid_for(message, public_key) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the nonce corresponds to the current nonce on the public state.
|
||||||
|
let address = Address::from_public_key(public_key);
|
||||||
|
let current_nonce = self.get_account_by_address(&address).nonce;
|
||||||
|
if current_nonce != *nonce {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
authorized_addresses.push(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build pre_states for execution
|
||||||
|
let pre_states: Vec<_> = message
|
||||||
|
.addresses
|
||||||
|
.iter()
|
||||||
|
.map(|address| AccountWithMetadata {
|
||||||
|
account: self.get_account_by_address(address),
|
||||||
|
is_authorized: authorized_addresses.contains(address),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Check the `program_id` corresponds to a built-in program
|
||||||
|
// Only allowed program so far is the authenticated transfer program
|
||||||
|
if message.program_id != AuthenticatedTransferProgram::PROGRAM_ID {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Execute program
|
||||||
|
let post_states =
|
||||||
|
execute_public::<AuthenticatedTransferProgram>(&pre_states, message.instruction_data)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
|
||||||
|
// Verify execution corresponds to a well-behaved program.
|
||||||
|
// See the # Programs section for the definition of the `validate_constraints` method.
|
||||||
|
validate_constraints(&pre_states, &post_states, message.program_id).map_err(|_| ())?;
|
||||||
|
|
||||||
|
if (post_states.len() != message.addresses.len()) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(message
|
||||||
|
.addresses
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.zip(post_states.into_iter())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
public_transaction::{self, WitnessSet},
|
||||||
|
signature::PrivateKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn genesis_state_for_tests() -> (V01State, Address) {
|
||||||
|
let account_1 = {
|
||||||
|
let mut this = Account::default();
|
||||||
|
this.program_owner = AuthenticatedTransferProgram::PROGRAM_ID;
|
||||||
|
this.balance = 100;
|
||||||
|
this
|
||||||
|
};
|
||||||
|
let address_1 = Address::new([1; 32]);
|
||||||
|
let public_state = [(address_1.clone(), account_1)].into_iter().collect();
|
||||||
|
(V01State { public_state }, address_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transfer_transaction_for_tests(
|
||||||
|
from: Address,
|
||||||
|
from_key: PrivateKey,
|
||||||
|
to: Address,
|
||||||
|
balance: u128,
|
||||||
|
) -> PublicTransaction {
|
||||||
|
let addresses = vec![from, to];
|
||||||
|
let nonces = vec![0];
|
||||||
|
let program_id = AuthenticatedTransferProgram::PROGRAM_ID;
|
||||||
|
let message = public_transaction::Message::new(program_id, addresses, nonces, balance);
|
||||||
|
let witness_set = WitnessSet::for_message(&message, &[from_key]);
|
||||||
|
PublicTransaction::new(message, witness_set)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_1() {
|
||||||
|
let (mut genesis_state, address) = genesis_state_for_tests();
|
||||||
|
let from = address;
|
||||||
|
let from_key = PrivateKey(1);
|
||||||
|
let to = Address::new([2; 32]);
|
||||||
|
let balance_to_move = 5;
|
||||||
|
let tx = transfer_transaction_for_tests(from, from_key, to.clone(), 5);
|
||||||
|
let _ = genesis_state.transition_from_public_transaction(tx);
|
||||||
|
assert_eq!(
|
||||||
|
genesis_state.get_account_by_address(&to).balance,
|
||||||
|
balance_to_move
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user