mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
contract/money: Use the ValidatorState API for tests.
This commit is contained in:
@@ -242,9 +242,10 @@ impl ValidatorState {
|
||||
|
||||
let mut vks = vec![];
|
||||
for i in zkas_db.iter() {
|
||||
debug!("Iterating over zkas db");
|
||||
let (zkas_ns, zkas_bincode) = i?;
|
||||
debug!("Deserializing namespace");
|
||||
let zkas_ns: String = deserialize(&zkas_ns)?;
|
||||
let zkas_bincode: Vec<u8> = deserialize(&zkas_bincode)?;
|
||||
info!("Creating VerifyingKey for zkas circuit with namespace {}", zkas_ns);
|
||||
let zkbin = ZkBinary::decode(&zkas_bincode)?;
|
||||
let circuit = ZkCircuit::new(empty_witnesses(&zkbin), zkbin);
|
||||
|
||||
@@ -23,7 +23,7 @@ rand = { version = "0.8.5", optional = true }
|
||||
# These are used just for the integration tests
|
||||
[dev-dependencies]
|
||||
async-std = {version = "1.12.0", features = ["attributes"]}
|
||||
darkfi = {path = "../../../", features = ["tx", "wasm-runtime"]}
|
||||
darkfi = {path = "../../../", features = ["tx", "blockchain"]}
|
||||
simplelog = "0.12.0"
|
||||
sled = "0.34.7"
|
||||
sqlx = {version = "0.6.2", features = ["runtime-async-std-native-tls", "sqlite"]}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use darkfi_sdk::{
|
||||
crypto::{Coin, ContractId, MerkleNode, MerkleTree, PublicKey},
|
||||
db::{db_contains_key, db_get, db_init, db_lookup, db_set},
|
||||
db::{db_contains_key, db_get, db_init, db_lookup, db_set, ZKAS_DB_NAME},
|
||||
error::{ContractError, ContractResult},
|
||||
merkle::merkle_add,
|
||||
msg,
|
||||
@@ -60,7 +60,6 @@ darkfi_sdk::define_contract!(
|
||||
);
|
||||
|
||||
// These are the different sled trees that will be created
|
||||
pub const ZKAS_TREE: &str = "zkas"; // <-- This should be a constant in darkfi-sdk
|
||||
pub const COIN_ROOTS_TREE: &str = "coin_roots";
|
||||
pub const NULLIFIERS_TREE: &str = "nullifiers";
|
||||
pub const INFO_TREE: &str = "info";
|
||||
@@ -82,10 +81,10 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
|
||||
|
||||
// The zkas circuits can simply be embedded in the wasm and set up by
|
||||
// the initialization. Note that the tree should then be called "zkas".
|
||||
// The lookups can then be done by `contract_id+zkas+namespace`.
|
||||
let zkas_db = match db_lookup(cid, ZKAS_TREE) {
|
||||
// The lookups can then be done by `contract_id+_zkas+namespace`.
|
||||
let zkas_db = match db_lookup(cid, ZKAS_DB_NAME) {
|
||||
Ok(v) => v,
|
||||
Err(_) => db_init(cid, ZKAS_TREE)?,
|
||||
Err(_) => db_init(cid, ZKAS_DB_NAME)?,
|
||||
};
|
||||
let mint_bincode = include_bytes!("../proof/mint.zk.bin");
|
||||
let burn_bincode = include_bytes!("../proof/burn.zk.bin");
|
||||
|
||||
@@ -20,24 +20,26 @@
|
||||
//! intended to. We initialize a state, deploy the contract, create a clear
|
||||
//! input, and then we try to spend it.
|
||||
//! Let's see if we manage.
|
||||
use std::{collections::HashMap, io::Cursor};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use darkfi::{
|
||||
blockchain::Blockchain,
|
||||
consensus::constants::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
|
||||
crypto::proof::{ProvingKey, VerifyingKey},
|
||||
runtime::vm_runtime::Runtime,
|
||||
consensus::{
|
||||
constants::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
|
||||
ValidatorState,
|
||||
},
|
||||
crypto::proof::ProvingKey,
|
||||
tx::Transaction,
|
||||
util::parse::decode_base10,
|
||||
wallet::WalletDb,
|
||||
zk::{vm::ZkCircuit, vm_stack::empty_witnesses},
|
||||
zkas::ZkBinary,
|
||||
Result,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{
|
||||
constants::MERKLE_DEPTH, poseidon_hash, ContractId, Keypair, MerkleNode, Nullifier,
|
||||
PublicKey, TokenId,
|
||||
constants::MERKLE_DEPTH, poseidon_hash, ContractId, Keypair, MerkleNode, Nullifier, TokenId,
|
||||
},
|
||||
db::ZKAS_DB_NAME,
|
||||
incrementalmerkletree::{bridgetree::BridgeTree, Tree},
|
||||
pasta::{
|
||||
group::ff::{Field, PrimeField},
|
||||
@@ -45,7 +47,7 @@ use darkfi_sdk::{
|
||||
},
|
||||
tx::ContractCall,
|
||||
};
|
||||
use darkfi_serial::{deserialize, serialize, Decodable, Encodable, WriteExt};
|
||||
use darkfi_serial::{deserialize, serialize, Encodable};
|
||||
use log::{debug, info};
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
@@ -68,44 +70,55 @@ async fn money_contract_execution() -> Result<()> {
|
||||
simplelog::ColorChoice::Auto,
|
||||
)?;
|
||||
|
||||
// Our main sled database references which live in memory during this test.
|
||||
info!("Initializing sled DBs");
|
||||
let faucet_sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let alice_sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let faucet_blockchain =
|
||||
Blockchain::new(&faucet_sled_db, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?;
|
||||
let alice_blockchain =
|
||||
Blockchain::new(&alice_sled_db, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?;
|
||||
|
||||
// A keypair we can use for the faucet whitelist
|
||||
let faucet_kp = Keypair::random(&mut OsRng);
|
||||
|
||||
// A keypair we'll use for Alice
|
||||
let alice_kp = Keypair::random(&mut OsRng);
|
||||
|
||||
// We deploy the contract natively and initialize its state.
|
||||
info!("Deploying WASM contract");
|
||||
let wasm_bincode = include_bytes!("../money_contract.wasm");
|
||||
let contract_id = ContractId::from(pallas::Base::from(u64::MAX - 420));
|
||||
let mut faucet_runtime =
|
||||
Runtime::new(&wasm_bincode[..], faucet_blockchain.clone(), contract_id)?;
|
||||
let mut alice_runtime = Runtime::new(&wasm_bincode[..], alice_blockchain.clone(), contract_id)?;
|
||||
|
||||
// The faucet's pubkey is allowed to make clear inputs
|
||||
let faucet_pubkeys = vec![faucet_kp.public];
|
||||
// Serialize the payload for the init/deploy function of the contract and run the deploy.
|
||||
let payload = serialize(&faucet_pubkeys);
|
||||
faucet_runtime.deploy(&payload)?;
|
||||
alice_runtime.deploy(&payload)?;
|
||||
|
||||
// At this point we've deployed the contract and we can begin executing it.
|
||||
// When the contract is deployed, we should be able to access everything from
|
||||
// the sled databases. We do it here just to confirm correct behaviour.
|
||||
// The wallets are just noops to get around the ValidatorState API
|
||||
let faucet_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
|
||||
let alice_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
|
||||
|
||||
// Our main sled database references which live in memory during this test.
|
||||
info!("Initializing ValidatorState");
|
||||
let faucet_sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let alice_sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let faucet_state = ValidatorState::new(
|
||||
&faucet_sled_db,
|
||||
*TESTNET_GENESIS_TIMESTAMP,
|
||||
*TESTNET_GENESIS_HASH_BYTES,
|
||||
faucet_wallet,
|
||||
faucet_pubkeys.clone(),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let alice_state = ValidatorState::new(
|
||||
&alice_sled_db,
|
||||
*TESTNET_GENESIS_TIMESTAMP,
|
||||
*TESTNET_GENESIS_HASH_BYTES,
|
||||
alice_wallet,
|
||||
faucet_pubkeys.clone(),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// In a hacky way, we just generate the proving keys for the circuits used.
|
||||
info!("Looking up zkas circuits from DB");
|
||||
let zkas_tree = String::from("zkas");
|
||||
let contract_id = ContractId::from(pallas::Base::from(u64::MAX - 420));
|
||||
|
||||
let zkas_mint_ns = String::from("Mint");
|
||||
let zkas_burn_ns = String::from("Burn");
|
||||
let db_handle =
|
||||
alice_blockchain.contracts.lookup(&alice_blockchain.sled_db, &contract_id, &zkas_tree)?;
|
||||
let alice_sled = &alice_state.read().await.blockchain.sled_db;
|
||||
let db_handle = alice_state.read().await.blockchain.contracts.lookup(
|
||||
alice_sled,
|
||||
&contract_id,
|
||||
ZKAS_DB_NAME,
|
||||
)?;
|
||||
let mint_zkbin = db_handle.get(&serialize(&zkas_mint_ns))?.unwrap();
|
||||
let burn_zkbin = db_handle.get(&serialize(&zkas_burn_ns))?.unwrap();
|
||||
info!("Decoding bincode");
|
||||
@@ -125,14 +138,6 @@ async fn money_contract_execution() -> Result<()> {
|
||||
vec![(zkas_mint_ns.clone(), mint_pk.clone()), (zkas_burn_ns.clone(), burn_pk.clone())];
|
||||
proving_keys.insert(contract_id.inner().to_repr(), pks);
|
||||
|
||||
info!("Creating zk verifying keys");
|
||||
let mut verifying_keys = HashMap::<[u8; 32], Vec<(String, VerifyingKey)>>::new();
|
||||
let mint_vk = VerifyingKey::build(k, &mint_circuit);
|
||||
let burn_vk = VerifyingKey::build(k, &burn_circuit);
|
||||
let vks =
|
||||
vec![(zkas_mint_ns.clone(), mint_vk.clone()), (zkas_burn_ns.clone(), burn_vk.clone())];
|
||||
verifying_keys.insert(contract_id.inner().to_repr(), vks);
|
||||
|
||||
// We also have to initialize the Merkle trees used for coins.
|
||||
info!("Initializing Merkle trees");
|
||||
let mut faucet_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
|
||||
@@ -169,18 +174,15 @@ async fn money_contract_execution() -> Result<()> {
|
||||
let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
|
||||
tx.signatures = vec![sigs];
|
||||
|
||||
// Get our ZK verifying keys in place for the tx verification
|
||||
let vks = verifying_keys.get(&contract_id.inner().to_repr()).unwrap();
|
||||
|
||||
// Let's first execute this transaction for the faucet to see if it passes.
|
||||
// Then Alice gets the tx and also executes it.
|
||||
info!("Executing transaction on the faucet's blockchain db");
|
||||
verify_transaction(&faucet_blockchain, vks, &tx)?;
|
||||
faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
|
||||
info!("Adding coin to faucet's Merkle tree");
|
||||
faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
|
||||
|
||||
info!("Executing transaction on Alice's blockchain db");
|
||||
verify_transaction(&alice_blockchain, vks, &tx)?;
|
||||
alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
|
||||
// TODO: FIXME: Actually have a look at the `merkle_add` calls
|
||||
alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
|
||||
let leaf_position = alice_merkle_tree.witness().unwrap();
|
||||
@@ -233,12 +235,12 @@ async fn money_contract_execution() -> Result<()> {
|
||||
tx.signatures = vec![sigs];
|
||||
|
||||
info!("Executing transaction on the faucet's blockchain db");
|
||||
verify_transaction(&faucet_blockchain, vks, &tx)?;
|
||||
faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
|
||||
info!("Adding coin to faucet's Merkle tree");
|
||||
faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
|
||||
|
||||
info!("Executing transaction on Alice's blockchain db");
|
||||
verify_transaction(&alice_blockchain, vks, &tx)?;
|
||||
alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
|
||||
// TODO: FIXME: Actually have a look at the `merkle_add` calls
|
||||
alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
|
||||
let leaf_position = alice_merkle_tree.witness().unwrap();
|
||||
@@ -289,77 +291,13 @@ async fn money_contract_execution() -> Result<()> {
|
||||
tx.signatures = vec![sigs];
|
||||
|
||||
info!("Executing transaction on the faucet's blockchain db");
|
||||
verify_transaction(&faucet_blockchain, vks, &tx)?;
|
||||
faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
|
||||
info!("Adding coin to faucet's Merkle tree");
|
||||
faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
|
||||
info!("Executing transaction on Alice's blockchain db");
|
||||
verify_transaction(&alice_blockchain, vks, &tx)?;
|
||||
alice_state.read().await.verify_transactions(&[tx], true).await?;
|
||||
// TODO: FIXME: Actually have a look at the `merkle_add` calls
|
||||
alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_transaction(
|
||||
blockchain: &Blockchain,
|
||||
verifying_keys: &[(String, VerifyingKey)],
|
||||
tx: &Transaction,
|
||||
) -> Result<()> {
|
||||
info!("Begin transcation verification");
|
||||
// Table of public inputs used for ZK proof verification
|
||||
let mut zkp_table = vec![];
|
||||
// Table of public keys used for signature verification
|
||||
let mut sig_table = vec![];
|
||||
// State updates produced by contract execution
|
||||
let mut updates = vec![];
|
||||
|
||||
// Iterate over all calls to get the metadata
|
||||
for (idx, call) in tx.calls.iter().enumerate() {
|
||||
info!("Verifying contract call {}", idx);
|
||||
let bincode = blockchain.wasm_bincode.get(call.contract_id)?;
|
||||
info!("Found wasm bincode for {}", call.contract_id);
|
||||
|
||||
// Write the actual payload data
|
||||
let mut payload = vec![];
|
||||
payload.write_u32(idx as u32)?; // Call index
|
||||
tx.calls.encode(&mut payload)?; // Actual call_data
|
||||
|
||||
// Instantiate the wasm runtime
|
||||
let mut runtime = Runtime::new(&bincode, blockchain.clone(), call.contract_id)?;
|
||||
info!("Executing \"metadata\" call");
|
||||
let metadata = runtime.metadata(&payload)?;
|
||||
let mut decoder = Cursor::new(&metadata);
|
||||
let zkp_pub: Vec<(String, Vec<pallas::Base>)> = Decodable::decode(&mut decoder)?;
|
||||
let sig_pub: Vec<PublicKey> = Decodable::decode(&mut decoder)?;
|
||||
zkp_table.push(zkp_pub);
|
||||
sig_table.push(sig_pub);
|
||||
info!("Successfully executed \"metadata\" call");
|
||||
|
||||
info!("Executing \"exec\" call");
|
||||
let update = runtime.exec(&payload)?;
|
||||
updates.push(update);
|
||||
info!("Successfully executed \"exec\" call");
|
||||
}
|
||||
|
||||
info!("Verifying transaction signatures");
|
||||
tx.verify_sigs(sig_table)?;
|
||||
info!("Signatures verified successfully");
|
||||
|
||||
info!("Verifying transaction ZK proofs");
|
||||
tx.verify_zkps(verifying_keys, zkp_table)?;
|
||||
info!("Transaction ZK proofs verified successfully");
|
||||
|
||||
// After the verification stage has passed, just apply all the changes.
|
||||
info!("Performing state updates");
|
||||
assert!(tx.calls.len() == updates.len());
|
||||
for (call, update) in tx.calls.iter().zip(updates.iter()) {
|
||||
let bincode = blockchain.wasm_bincode.get(call.contract_id)?;
|
||||
let mut runtime = Runtime::new(&bincode, blockchain.clone(), call.contract_id)?;
|
||||
info!("Executing \"apply\" call");
|
||||
runtime.apply(&update)?;
|
||||
info!("Successfully executed \"apply\" call");
|
||||
}
|
||||
|
||||
info!("Transaction verified successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user