mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
DAO::propose(): goodbye nullifiers, hello SMT
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
k = 13;
|
||||
k = 14;
|
||||
field = "pallas";
|
||||
|
||||
constant "ProposeInput" {
|
||||
@@ -19,7 +19,10 @@ witness "ProposeInput" {
|
||||
Base coin_token_blind,
|
||||
|
||||
Uint32 leaf_pos,
|
||||
MerklePath path,
|
||||
MerklePath coin_path,
|
||||
|
||||
Base null_tree_root,
|
||||
SparseMerklePath null_path,
|
||||
|
||||
Base signature_secret,
|
||||
}
|
||||
@@ -40,10 +43,18 @@ circuit "ProposeInput" {
|
||||
);
|
||||
|
||||
# We need this to detect whether the above coin was already spent.
|
||||
# To avoid leaking timing & other info, we can just make a
|
||||
# money::transfer() call within the same tx.
|
||||
# Use a SMT, and show that at this position, the leaf is ZERO
|
||||
ZERO = witness_base(0);
|
||||
ONE = witness_base(1);
|
||||
nullifier = poseidon_hash(coin_secret, coin);
|
||||
constrain_instance(nullifier);
|
||||
is_member = sparse_tree_is_member(
|
||||
null_tree_root, # Expected root
|
||||
null_path, # Path to root
|
||||
ZERO, # Leaf value
|
||||
nullifier # Position
|
||||
);
|
||||
constrain_equal_base(is_member, ONE);
|
||||
constrain_instance(null_tree_root);
|
||||
|
||||
# Pedersen commitment for coin's coin_value
|
||||
vcv = ec_mul_short(coin_value, VALUE_COMMIT_VALUE);
|
||||
@@ -57,8 +68,8 @@ circuit "ProposeInput" {
|
||||
constrain_instance(coin_token_commit);
|
||||
|
||||
# Merkle root
|
||||
root = merkle_root(leaf_pos, path, coin);
|
||||
constrain_instance(root);
|
||||
merkle_coin_root = merkle_root(leaf_pos, coin_path, coin);
|
||||
constrain_instance(merkle_coin_root);
|
||||
|
||||
# Finally we derive a public key for the signature and constrain
|
||||
# its coordinates:
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use darkfi_money_contract::model::{CoinAttributes, Nullifier};
|
||||
use darkfi_money_contract::model::CoinAttributes;
|
||||
use darkfi_sdk::{
|
||||
bridgetree,
|
||||
bridgetree::Hashable,
|
||||
crypto::{
|
||||
note::AeadEncryptedNote, pasta_prelude::*, pedersen::pedersen_commitment_u64,
|
||||
poseidon_hash, Blind, FuncId, MerkleNode, PublicKey, ScalarBlind, SecretKey,
|
||||
poseidon_hash, smt::SmtMemoryFp, Blind, FuncId, MerkleNode, PublicKey, ScalarBlind,
|
||||
SecretKey,
|
||||
},
|
||||
pasta::pallas,
|
||||
};
|
||||
@@ -36,16 +37,17 @@ use darkfi::{
|
||||
|
||||
use crate::model::{Dao, DaoProposal, DaoProposeParams, DaoProposeParamsInput, VecAuthCallCommit};
|
||||
|
||||
pub struct DaoProposeStakeInput {
|
||||
pub struct DaoProposeStakeInput<'a> {
|
||||
pub secret: SecretKey,
|
||||
pub note: darkfi_money_contract::client::MoneyNote,
|
||||
pub leaf_position: bridgetree::Position,
|
||||
pub merkle_path: Vec<MerkleNode>,
|
||||
pub money_null_smt: &'a SmtMemoryFp,
|
||||
pub signature_secret: SecretKey,
|
||||
}
|
||||
|
||||
pub struct DaoProposeCall {
|
||||
pub inputs: Vec<DaoProposeStakeInput>,
|
||||
pub struct DaoProposeCall<'a> {
|
||||
pub inputs: Vec<DaoProposeStakeInput<'a>>,
|
||||
pub proposal: DaoProposal,
|
||||
pub dao: Dao,
|
||||
pub dao_leaf_position: bridgetree::Position,
|
||||
@@ -53,7 +55,7 @@ pub struct DaoProposeCall {
|
||||
pub dao_merkle_root: MerkleNode,
|
||||
}
|
||||
|
||||
impl DaoProposeCall {
|
||||
impl<'a> DaoProposeCall<'a> {
|
||||
pub fn make(
|
||||
self,
|
||||
burn_zkbin: &ZkBinary,
|
||||
@@ -80,6 +82,22 @@ impl DaoProposeCall {
|
||||
let note = input.note;
|
||||
let leaf_pos: u64 = input.leaf_position.into();
|
||||
|
||||
let public_key = PublicKey::from_secret(input.secret);
|
||||
let coin = CoinAttributes {
|
||||
public_key,
|
||||
value: note.value,
|
||||
token_id: note.token_id,
|
||||
spend_hook: FuncId::none(),
|
||||
user_data: pallas::Base::ZERO,
|
||||
blind: note.coin_blind,
|
||||
}
|
||||
.to_coin();
|
||||
let nullifier = poseidon_hash([input.secret.inner(), coin.inner()]);
|
||||
|
||||
let smt_null_root = input.money_null_smt.root();
|
||||
let smt_null_path = input.money_null_smt.prove_membership(&nullifier);
|
||||
assert!(smt_null_path.verify(&smt_null_root, &pallas::Base::ZERO, &nullifier));
|
||||
|
||||
let prover_witnesses = vec![
|
||||
Witness::Base(Value::known(input.secret.inner())),
|
||||
Witness::Base(Value::known(pallas::Base::from(note.value))),
|
||||
@@ -91,23 +109,14 @@ impl DaoProposeCall {
|
||||
Witness::Base(Value::known(gov_token_blind.inner())),
|
||||
Witness::Uint32(Value::known(leaf_pos.try_into().unwrap())),
|
||||
Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
|
||||
Witness::Base(Value::known(smt_null_root)),
|
||||
Witness::SparseMerklePath(Value::known(smt_null_path.path)),
|
||||
Witness::Base(Value::known(input.signature_secret.inner())),
|
||||
];
|
||||
|
||||
let public_key = PublicKey::from_secret(input.secret);
|
||||
let coin = CoinAttributes {
|
||||
public_key,
|
||||
value: note.value,
|
||||
token_id: note.token_id,
|
||||
spend_hook: FuncId::none(),
|
||||
user_data: pallas::Base::ZERO,
|
||||
blind: note.coin_blind,
|
||||
}
|
||||
.to_coin();
|
||||
|
||||
// TODO: We need a generic ZkSet widget to avoid doing this all the time
|
||||
|
||||
let merkle_root = {
|
||||
let merkle_coin_root = {
|
||||
let position: u64 = input.leaf_position.into();
|
||||
let mut current = MerkleNode::from(coin.inner());
|
||||
for (level, sibling) in input.merkle_path.iter().enumerate() {
|
||||
@@ -121,8 +130,6 @@ impl DaoProposeCall {
|
||||
current
|
||||
};
|
||||
|
||||
let nullifier: Nullifier = poseidon_hash([input.secret.inner(), coin.inner()]).into();
|
||||
|
||||
let token_commit = poseidon_hash([note.token_id.inner(), gov_token_blind.inner()]);
|
||||
assert_eq!(self.dao.gov_token_id, note.token_id);
|
||||
|
||||
@@ -132,11 +139,11 @@ impl DaoProposeCall {
|
||||
let (sig_x, sig_y) = signature_public.xy();
|
||||
|
||||
let public_inputs = vec![
|
||||
nullifier.inner(),
|
||||
smt_null_root,
|
||||
*value_coords.x(),
|
||||
*value_coords.y(),
|
||||
token_commit,
|
||||
merkle_root.inner(),
|
||||
merkle_coin_root.inner(),
|
||||
sig_x,
|
||||
sig_y,
|
||||
];
|
||||
@@ -147,8 +154,12 @@ impl DaoProposeCall {
|
||||
let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)?;
|
||||
proofs.push(input_proof);
|
||||
|
||||
let input =
|
||||
DaoProposeParamsInput { nullifier, value_commit, merkle_root, signature_public };
|
||||
let input = DaoProposeParamsInput {
|
||||
value_commit,
|
||||
merkle_coin_root,
|
||||
smt_null_root,
|
||||
signature_public,
|
||||
};
|
||||
inputs.push(input);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ use darkfi_sdk::{
|
||||
dark_tree::DarkLeaf,
|
||||
db::{db_contains_key, db_lookup, db_set},
|
||||
error::{ContractError, ContractResult},
|
||||
merkle_add, msg,
|
||||
merkle::merkle_add,
|
||||
msg,
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
|
||||
use darkfi_money_contract::{
|
||||
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
|
||||
MONEY_CONTRACT_NULLIFIER_ROOTS_TREE,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, pasta_prelude::*, ContractId, MerkleNode, PublicKey},
|
||||
@@ -39,6 +38,7 @@ use crate::{
|
||||
model::{DaoBlindAggregateVote, DaoProposalMetadata, DaoProposeParams, DaoProposeUpdate},
|
||||
DaoFunction, DAO_CONTRACT_DB_DAO_MERKLE_ROOTS, DAO_CONTRACT_DB_PROPOSAL_BULLAS,
|
||||
DAO_CONTRACT_ZKAS_DAO_PROPOSE_INPUT_NS, DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS,
|
||||
PROPOSAL_SNAPSHOT_CUTOFF_LIMIT,
|
||||
};
|
||||
|
||||
/// `get_metdata` function for `Dao::Propose`
|
||||
@@ -74,11 +74,11 @@ pub(crate) fn dao_propose_get_metadata(
|
||||
zk_public_inputs.push((
|
||||
DAO_CONTRACT_ZKAS_DAO_PROPOSE_INPUT_NS.to_string(),
|
||||
vec![
|
||||
input.nullifier.inner(),
|
||||
input.smt_null_root,
|
||||
*value_coords.x(),
|
||||
*value_coords.y(),
|
||||
params.token_commit,
|
||||
input.merkle_root.inner(),
|
||||
input.merkle_coin_root.inner(),
|
||||
sig_x,
|
||||
sig_y,
|
||||
],
|
||||
@@ -120,26 +120,42 @@ pub(crate) fn dao_propose_process_instruction(
|
||||
let params: DaoProposeParams = deserialize(&self_.data[1..])?;
|
||||
|
||||
let coin_roots_db = db_lookup(*MONEY_CONTRACT_ID, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
let money_nullifier_db = db_lookup(*MONEY_CONTRACT_ID, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
||||
let mut propose_nullifiers = Vec::with_capacity(params.inputs.len());
|
||||
let null_roots_db = db_lookup(*MONEY_CONTRACT_ID, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
|
||||
for input in ¶ms.inputs {
|
||||
// Check the Merkle roots for the input coins are valid
|
||||
if !db_contains_key(coin_roots_db, &serialize(&input.merkle_root))? {
|
||||
msg!("[Dao::Propose] Error: Invalid input Merkle root: {}", input.merkle_root);
|
||||
let Some(coin_root_data) = db_get(coin_roots_db, &serialize(&input.merkle_coin_root))?
|
||||
else {
|
||||
msg!(
|
||||
"[Dao::Propose] Error: Invalid input Merkle root: {:?}",
|
||||
input.merkle_coin_root.inner()
|
||||
);
|
||||
return Err(DaoError::InvalidInputMerkleRoot.into())
|
||||
};
|
||||
|
||||
// Check the SMT roots for the input nullifiers are valid
|
||||
let Some(null_root_data) = db_get(null_roots_db, &serialize(&input.smt_null_root))? else {
|
||||
msg!("[Dao::Propose] Error: Invalid input SMT root: {:?}", input.smt_null_root);
|
||||
return Err(DaoError::InvalidInputMerkleRoot.into())
|
||||
};
|
||||
|
||||
// Both roots must snapshot the exact same state
|
||||
if coin_root_data != null_root_data {
|
||||
msg!("[Dao::Propose] Error: coin roots snapshot for {:?} does not match nulls root snapshot {:?}",
|
||||
input.merkle_coin_root.inner(), input.smt_null_root);
|
||||
return Err(DaoError::NonMatchingSnapshotRoots.into())
|
||||
}
|
||||
|
||||
// Check the coins weren't already spent
|
||||
// The nullifiers should not already exist. It is the double-spend protection.
|
||||
if propose_nullifiers.contains(&input.nullifier) ||
|
||||
db_contains_key(money_nullifier_db, &serialize(&input.nullifier))?
|
||||
{
|
||||
msg!("[Dao::Vote] Error: Coin is already spent");
|
||||
return Err(DaoError::CoinAlreadySpent.into())
|
||||
assert_eq!(coin_root_data.len(), 32 + 2);
|
||||
let tx_hash = &coin_root_data[0..32];
|
||||
// Get block_height where tx_hash was confirmed
|
||||
let tx_height = get_verifying_block_height() as u32;
|
||||
let current_height = get_verifying_block_height() as u32;
|
||||
if current_height - tx_height > PROPOSAL_SNAPSHOT_CUTOFF_LIMIT {
|
||||
msg!("[Dao::Propose] Error: Snapshot is too old. Current height: {}, snapshot height: {}",
|
||||
current_height, tx_height);
|
||||
return Err(DaoError::SnapshotTooOld.into())
|
||||
}
|
||||
|
||||
propose_nullifiers.push(input.nullifier);
|
||||
}
|
||||
|
||||
// Is the DAO bulla generated in the ZK proof valid
|
||||
|
||||
@@ -32,6 +32,12 @@ pub enum DaoError {
|
||||
#[error("Invalid input Merkle root")]
|
||||
InvalidInputMerkleRoot,
|
||||
|
||||
#[error("Snapshoot roots do not match")]
|
||||
NonMatchingSnapshotRoots,
|
||||
|
||||
#[error("Snapshoot is past the cutoff limit")]
|
||||
SnapshotTooOld,
|
||||
|
||||
#[error("Invalid DAO Merkle root")]
|
||||
InvalidDaoMerkleRoot,
|
||||
|
||||
@@ -94,24 +100,26 @@ impl From<DaoError> for ContractError {
|
||||
DaoError::DaoAlreadyExists => Self::Custom(2),
|
||||
DaoError::ProposalInputsEmpty => Self::Custom(3),
|
||||
DaoError::InvalidInputMerkleRoot => Self::Custom(4),
|
||||
DaoError::InvalidDaoMerkleRoot => Self::Custom(5),
|
||||
DaoError::ProposalAlreadyExists => Self::Custom(6),
|
||||
DaoError::VoteInputsEmpty => Self::Custom(7),
|
||||
DaoError::ProposalNonexistent => Self::Custom(8),
|
||||
DaoError::ProposalEnded => Self::Custom(9),
|
||||
DaoError::CoinAlreadySpent => Self::Custom(10),
|
||||
DaoError::DoubleVote => Self::Custom(11),
|
||||
DaoError::ExecCallWrongChildCallsLen => Self::Custom(12),
|
||||
DaoError::ExecCallWrongChildCall => Self::Custom(13),
|
||||
DaoError::ExecCallInvalidFormat => Self::Custom(14),
|
||||
DaoError::ExecCallValueMismatch => Self::Custom(15),
|
||||
DaoError::VoteCommitMismatch => Self::Custom(16),
|
||||
DaoError::AuthXferSiblingWrongContractId => Self::Custom(17),
|
||||
DaoError::AuthXferSiblingWrongFunctionCode => Self::Custom(18),
|
||||
DaoError::AuthXferNonMatchingEncInputUserData => Self::Custom(19),
|
||||
DaoError::AuthXferCallNotFoundInParent => Self::Custom(20),
|
||||
DaoError::AuthXferWrongNumberOutputs => Self::Custom(21),
|
||||
DaoError::AuthXferWrongOutputCoin => Self::Custom(22),
|
||||
DaoError::NonMatchingSnapshotRoots => Self::Custom(5),
|
||||
DaoError::SnapshotTooOld => Self::Custom(6),
|
||||
DaoError::InvalidDaoMerkleRoot => Self::Custom(7),
|
||||
DaoError::ProposalAlreadyExists => Self::Custom(8),
|
||||
DaoError::VoteInputsEmpty => Self::Custom(9),
|
||||
DaoError::ProposalNonexistent => Self::Custom(10),
|
||||
DaoError::ProposalEnded => Self::Custom(11),
|
||||
DaoError::CoinAlreadySpent => Self::Custom(12),
|
||||
DaoError::DoubleVote => Self::Custom(13),
|
||||
DaoError::ExecCallWrongChildCallsLen => Self::Custom(14),
|
||||
DaoError::ExecCallWrongChildCall => Self::Custom(15),
|
||||
DaoError::ExecCallInvalidFormat => Self::Custom(16),
|
||||
DaoError::ExecCallValueMismatch => Self::Custom(17),
|
||||
DaoError::VoteCommitMismatch => Self::Custom(18),
|
||||
DaoError::AuthXferSiblingWrongContractId => Self::Custom(19),
|
||||
DaoError::AuthXferSiblingWrongFunctionCode => Self::Custom(20),
|
||||
DaoError::AuthXferNonMatchingEncInputUserData => Self::Custom(21),
|
||||
DaoError::AuthXferCallNotFoundInParent => Self::Custom(22),
|
||||
DaoError::AuthXferWrongNumberOutputs => Self::Custom(23),
|
||||
DaoError::AuthXferWrongOutputCoin => Self::Custom(24),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ pub const DAO_CONTRACT_ZKAS_DAO_AUTH_MONEY_TRANSFER_NS: &str = "AuthMoneyTransfe
|
||||
/// zkas dao auth money_transfer encrypted coin circuit namespace
|
||||
pub const DAO_CONTRACT_ZKAS_DAO_AUTH_MONEY_TRANSFER_ENC_COIN_NS: &str = "AuthMoneyTransferEncCoin";
|
||||
|
||||
/// Not allowed to make proposals using snapshots with block heights older than this depth
|
||||
pub const PROPOSAL_SNAPSHOT_CUTOFF_LIMIT: u32 = 100;
|
||||
|
||||
// ANCHOR: dao-blockwindow
|
||||
const BLOCK_TIME: u64 = 90;
|
||||
const SECS_IN_HOUR: u64 = 60 * 60;
|
||||
|
||||
@@ -242,11 +242,12 @@ pub struct DaoProposeParams {
|
||||
// ANCHOR: dao-propose-params-input
|
||||
/// Input for a DAO proposal
|
||||
pub struct DaoProposeParamsInput {
|
||||
pub nullifier: Nullifier,
|
||||
/// Value commitment for the input
|
||||
pub value_commit: pallas::Point,
|
||||
/// Merkle root for the input's inclusion proof
|
||||
pub merkle_root: MerkleNode,
|
||||
/// Merkle root for the input's coin inclusion proof
|
||||
pub merkle_coin_root: MerkleNode,
|
||||
/// SMT root for the input's nullifier exclusion proof
|
||||
pub smt_null_root: pallas::Base,
|
||||
/// Public key used for signing
|
||||
pub signature_public: PublicKey,
|
||||
}
|
||||
|
||||
@@ -125,16 +125,29 @@ fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
|
||||
zkas_db_set(&token_mint_v1_bincode[..])?;
|
||||
zkas_db_set(&token_frz_v1_bincode[..])?;
|
||||
|
||||
// FIXME: Get tx hash from env
|
||||
let tx_hash = [0u8; 32];
|
||||
// No way to access call_idx here
|
||||
//assert!(ix.len() > 4);
|
||||
//let call_idx: u32 = deserialize(&ix[0..4])?;
|
||||
let call_idx = 110u16;
|
||||
let mut roots_value_data = Vec::with_capacity(32 + 2);
|
||||
tx_hash.encode(&mut roots_value_data)?;
|
||||
call_idx.encode(&mut roots_value_data)?;
|
||||
assert_eq!(roots_value_data.len(), 32 + 2);
|
||||
|
||||
// Set up a database tree to hold Merkle roots of all coin trees
|
||||
// k=root_hash:32, v=(block_height:3, tx_idx:2, call_idx: 2)
|
||||
// k=root_hash:32, v=(tx_hash:32, call_idx: 2)
|
||||
if db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE).is_err() {
|
||||
db_init(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
let db_coin_roots = db_init(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
db_set(db_coin_roots, &serialize(&EMPTY_COINS_TREE_ROOT), &roots_value_data)?;
|
||||
}
|
||||
|
||||
// Set up a database tree to hold Merkle roots of all nullifier trees
|
||||
// k=root_hash:32, v=(block_height:3, tx_idx:2, call_idx: 2)
|
||||
// k=root_hash:32, v=(tx_hash:32, call_idx: 2)
|
||||
if db_lookup(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE).is_err() {
|
||||
db_init(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
let db_null_roots = db_init(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
db_set(db_null_roots, &serialize(&EMPTY_NODES_FP[0]), &roots_value_data)?;
|
||||
}
|
||||
|
||||
// Set up a database tree to hold all coins ever seen
|
||||
|
||||
@@ -21,7 +21,8 @@ use darkfi_sdk::{
|
||||
dark_tree::DarkLeaf,
|
||||
db::{db_contains_key, db_lookup, db_set},
|
||||
error::{ContractError, ContractResult},
|
||||
merkle_add, msg,
|
||||
merkle::{merkle_add, sparse_merkle_insert_batch},
|
||||
msg,
|
||||
pasta::pallas,
|
||||
util::get_verifying_block_height,
|
||||
ContractCall,
|
||||
@@ -33,7 +34,8 @@ use crate::{
|
||||
model::{MoneyGenesisMintParamsV1, MoneyGenesisMintUpdateV1, DARK_TOKEN_ID},
|
||||
MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
|
||||
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
|
||||
MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
|
||||
MONEY_CONTRACT_NULLIFIER_ROOTS_TREE, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
|
||||
/// `get_metadata` function for `Money::GenesisMintV1`
|
||||
@@ -140,7 +142,19 @@ pub(crate) fn money_genesis_mint_process_update_v1(
|
||||
// Grab all db handles we want to work on
|
||||
let info_db = db_lookup(cid, MONEY_CONTRACT_INFO_TREE)?;
|
||||
let coins_db = db_lookup(cid, MONEY_CONTRACT_COINS_TREE)?;
|
||||
let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
||||
let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
let nullifier_roots_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
|
||||
// This will just make a snapshot to match the coins one
|
||||
msg!("[GenesisMintV1] Updating nullifiers snapshot");
|
||||
sparse_merkle_insert_batch(
|
||||
info_db,
|
||||
nullifiers_db,
|
||||
nullifier_roots_db,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT,
|
||||
&vec![],
|
||||
)?;
|
||||
|
||||
msg!("[GenesisMintV1] Adding new coin to the set");
|
||||
db_set(coins_db, &serialize(&update.coin), &[])?;
|
||||
|
||||
@@ -22,7 +22,8 @@ use darkfi_sdk::{
|
||||
dark_tree::DarkLeaf,
|
||||
db::{db_contains_key, db_lookup, db_set},
|
||||
error::{ContractError, ContractResult},
|
||||
merkle_add, msg,
|
||||
merkle::{merkle_add, sparse_merkle_insert_batch},
|
||||
msg,
|
||||
pasta::pallas,
|
||||
util::{get_last_block_height, get_verifying_block_height},
|
||||
ContractCall,
|
||||
@@ -34,7 +35,8 @@ use crate::{
|
||||
model::{MoneyPoWRewardParamsV1, MoneyPoWRewardUpdateV1, DARK_TOKEN_ID},
|
||||
MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
|
||||
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
|
||||
MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
|
||||
MONEY_CONTRACT_NULLIFIER_ROOTS_TREE, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
|
||||
/// `get_metadata` function for `Money::PoWRewardV1`
|
||||
@@ -165,7 +167,19 @@ pub(crate) fn money_pow_reward_process_update_v1(
|
||||
// Grab all db handles we want to work on
|
||||
let info_db = db_lookup(cid, MONEY_CONTRACT_INFO_TREE)?;
|
||||
let coins_db = db_lookup(cid, MONEY_CONTRACT_COINS_TREE)?;
|
||||
let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
||||
let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
let nullifier_roots_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
|
||||
// This will just make a snapshot to match the coins one
|
||||
msg!("[PowRewardV1] Updating nullifiers snapshot");
|
||||
sparse_merkle_insert_batch(
|
||||
info_db,
|
||||
nullifiers_db,
|
||||
nullifier_roots_db,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT,
|
||||
&vec![],
|
||||
)?;
|
||||
|
||||
msg!("[PoWRewardV1] Adding new coin to the set");
|
||||
db_set(coins_db, &serialize(&update.coin), &[])?;
|
||||
|
||||
@@ -21,7 +21,8 @@ use darkfi_sdk::{
|
||||
dark_tree::DarkLeaf,
|
||||
db::{db_contains_key, db_lookup, db_set},
|
||||
error::{ContractError, ContractResult},
|
||||
merkle_add, msg,
|
||||
merkle::{merkle_add, sparse_merkle_insert_batch},
|
||||
msg,
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
@@ -32,7 +33,8 @@ use crate::{
|
||||
model::{MoneyTokenMintParamsV1, MoneyTokenMintUpdateV1},
|
||||
MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
|
||||
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
|
||||
MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
|
||||
MONEY_CONTRACT_NULLIFIER_ROOTS_TREE, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
|
||||
};
|
||||
|
||||
/// `get_metadata` function for `Money::TokenMintV1`
|
||||
@@ -106,7 +108,19 @@ pub(crate) fn money_token_mint_process_update_v1(
|
||||
// Grab all db handles we want to work on
|
||||
let info_db = db_lookup(cid, MONEY_CONTRACT_INFO_TREE)?;
|
||||
let coins_db = db_lookup(cid, MONEY_CONTRACT_COINS_TREE)?;
|
||||
let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
||||
let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
let nullifier_roots_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
|
||||
// This will just make a snapshot to match the coins one
|
||||
msg!("[MintV1] Updating nullifiers snapshot");
|
||||
sparse_merkle_insert_batch(
|
||||
info_db,
|
||||
nullifiers_db,
|
||||
nullifier_roots_db,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT,
|
||||
&vec![],
|
||||
)?;
|
||||
|
||||
msg!("[MintV1] Adding new coin to the set");
|
||||
db_set(coins_db, &serialize(&update.coin), &[])?;
|
||||
|
||||
@@ -83,7 +83,8 @@ pub const MONEY_CONTRACT_LATEST_COIN_ROOT: &[u8] = b"last_coins_root";
|
||||
pub const MONEY_CONTRACT_LATEST_NULLIFIER_ROOT: &[u8] = b"last_nullifiers_root";
|
||||
pub const MONEY_CONTRACT_TOTAL_FEES_PAID: &[u8] = b"total_fees_paid";
|
||||
|
||||
/// Precalculated root hash for a tree containing Fp::ZERO to save gas
|
||||
/// Precalculated root hash for a tree containing only a single Fp::ZERO coin.
|
||||
/// Used to save gas.
|
||||
pub const EMPTY_COINS_TREE_ROOT: [u8; 32] = [
|
||||
0xb8, 0xc1, 0x07, 0x5a, 0x80, 0xa8, 0x09, 0x65, 0xc2, 0x39, 0x8f, 0x71, 0x1f, 0xe7, 0x3e, 0x05,
|
||||
0xb4, 0xed, 0xae, 0xde, 0xf1, 0x62, 0xf2, 0x61, 0xd4, 0xee, 0xd7, 0xcd, 0x72, 0x74, 0x8d, 0x17,
|
||||
|
||||
@@ -110,6 +110,12 @@ impl TestHarness {
|
||||
}
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
let nullifier = fee_params.input.nullifier.inner();
|
||||
wallet
|
||||
.money_null_smt
|
||||
.insert_batch(vec![(nullifier, nullifier)])
|
||||
.expect("smt.insert_batch()");
|
||||
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
|
||||
@@ -274,6 +274,9 @@ impl TestHarness {
|
||||
outputs.push(fee_params.output.clone());
|
||||
}
|
||||
|
||||
let nullifiers = inputs.iter().map(|i| i.nullifier.inner()).map(|l| (l, l)).collect();
|
||||
wallet.money_null_smt.insert_batch(nullifiers).expect("smt.insert_batch()");
|
||||
|
||||
for input in inputs {
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
|
||||
@@ -118,6 +118,12 @@ impl TestHarness {
|
||||
wallet.dao_leafs.insert(params.dao_bulla, leaf_pos);
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
let nullifier = fee_params.input.nullifier.inner();
|
||||
wallet
|
||||
.money_null_smt
|
||||
.insert_batch(vec![(nullifier, nullifier)])
|
||||
.expect("smt.insert_batch()");
|
||||
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
|
||||
@@ -73,6 +73,32 @@ impl TestHarness {
|
||||
|
||||
let signature_secret = SecretKey::random(&mut OsRng);
|
||||
|
||||
debug!("ABOUT TO SHOW DB INTERNALZZZ!!!!!");
|
||||
{
|
||||
let blockchain = &wallet.validator.blockchain;
|
||||
let contracts = &blockchain.contracts;
|
||||
let tree = contracts
|
||||
.lookup(&blockchain.sled_db, &MONEY_CONTRACT_ID, "nullifier_roots")
|
||||
.unwrap();
|
||||
for kv in tree.iter() {
|
||||
let (key, value) = kv.unwrap();
|
||||
debug!("STATE {:?}", key);
|
||||
debug!(" => {:?}", value);
|
||||
}
|
||||
}
|
||||
debug!("[REDUX] ABOUT TO SHOW DB INTERNALZZZ!!!!!");
|
||||
{
|
||||
let blockchain = &wallet.validator.blockchain;
|
||||
let contracts = &blockchain.contracts;
|
||||
let tree =
|
||||
contracts.lookup(&blockchain.sled_db, &MONEY_CONTRACT_ID, "coin_roots").unwrap();
|
||||
for kv in tree.iter() {
|
||||
let (key, value) = kv.unwrap();
|
||||
debug!("STATE {:?}", key);
|
||||
debug!(" => {:?}", value);
|
||||
}
|
||||
}
|
||||
|
||||
let input = DaoProposeStakeInput {
|
||||
secret: wallet.keypair.secret,
|
||||
note: propose_owncoin.note.clone(),
|
||||
@@ -81,6 +107,7 @@ impl TestHarness {
|
||||
.money_merkle_tree
|
||||
.witness(propose_owncoin.leaf_position, 0)
|
||||
.unwrap(),
|
||||
money_null_smt: &wallet.money_null_smt,
|
||||
signature_secret,
|
||||
};
|
||||
|
||||
@@ -197,6 +224,12 @@ impl TestHarness {
|
||||
wallet.dao_prop_leafs.insert(params.proposal_bulla, (prop_leaf_pos, prop_money_snapshot));
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
let nullifier = fee_params.input.nullifier.inner();
|
||||
wallet
|
||||
.money_null_smt
|
||||
.insert_batch(vec![(nullifier, nullifier)])
|
||||
.expect("smt.insert_batch()");
|
||||
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
|
||||
@@ -155,6 +155,12 @@ impl TestHarness {
|
||||
}
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
let nullifier = fee_params.input.nullifier.inner();
|
||||
wallet
|
||||
.money_null_smt
|
||||
.insert_batch(vec![(nullifier, nullifier)])
|
||||
.expect("smt.insert_batch()");
|
||||
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
|
||||
@@ -35,7 +35,10 @@ use darkfi_dao_contract::model::{DaoBulla, DaoProposalBulla};
|
||||
use darkfi_money_contract::client::OwnCoin;
|
||||
use darkfi_sdk::{
|
||||
bridgetree,
|
||||
crypto::{Keypair, MerkleNode, MerkleTree},
|
||||
crypto::{
|
||||
smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
|
||||
Keypair, MerkleNode, MerkleTree,
|
||||
},
|
||||
pasta::pallas,
|
||||
};
|
||||
use darkfi_serial::{Encodable, WriteExt};
|
||||
@@ -87,8 +90,8 @@ pub fn init_logger() {
|
||||
// We check this error so we can execute same file tests in parallel,
|
||||
// otherwise second one fails to init logger here.
|
||||
if simplelog::TermLogger::init(
|
||||
simplelog::LevelFilter::Info,
|
||||
//simplelog::LevelFilter::Debug,
|
||||
//simplelog::LevelFilter::Info,
|
||||
simplelog::LevelFilter::Debug,
|
||||
//simplelog::LevelFilter::Trace,
|
||||
cfg.build(),
|
||||
simplelog::TerminalMode::Mixed,
|
||||
@@ -122,6 +125,8 @@ pub struct Wallet {
|
||||
pub validator: ValidatorPtr,
|
||||
/// Holder's instance of the Merkle tree for the `Money` contract
|
||||
pub money_merkle_tree: MerkleTree,
|
||||
/// Holder's instance of the Merkle tree for the `Money` contract
|
||||
pub money_null_smt: SmtMemoryFp,
|
||||
/// Holder's instance of the Merkle tree for the `DAO` contract (holding DAO bullas)
|
||||
pub dao_merkle_tree: MerkleTree,
|
||||
/// Holder's instance of the Merkle tree for the `DAO` contract (holding DAO proposals)
|
||||
@@ -170,12 +175,17 @@ impl Wallet {
|
||||
money_merkle_tree.append(MerkleNode::from(pallas::Base::ZERO));
|
||||
money_merkle_tree.mark().unwrap();
|
||||
|
||||
let hasher = PoseidonFp::new();
|
||||
let store = MemoryStorageFp::new();
|
||||
let money_null_smt = SmtMemoryFp::new(store, hasher.clone(), &EMPTY_NODES_FP);
|
||||
|
||||
Ok(Self {
|
||||
keypair,
|
||||
token_mint_authority,
|
||||
contract_deploy_authority,
|
||||
validator,
|
||||
money_merkle_tree,
|
||||
money_null_smt,
|
||||
dao_merkle_tree: MerkleTree::new(100),
|
||||
dao_proposals_tree: MerkleTree::new(100),
|
||||
unspent_money_coins: vec![],
|
||||
|
||||
@@ -163,6 +163,12 @@ impl TestHarness {
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
|
||||
let nullifier = params.input.nullifier.inner();
|
||||
wallet
|
||||
.money_null_smt
|
||||
.insert_batch(vec![(nullifier, nullifier)])
|
||||
.expect("smt.insert_batch()");
|
||||
|
||||
wallet.add_transaction("money::fee", tx, block_height, self.verify_fees).await?;
|
||||
wallet.money_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
|
||||
|
||||
|
||||
@@ -201,6 +201,9 @@ impl TestHarness {
|
||||
outputs.push(fee_params.output.clone());
|
||||
}
|
||||
|
||||
let nullifiers = inputs.iter().map(|i| i.nullifier.inner()).map(|l| (l, l)).collect();
|
||||
wallet.money_null_smt.insert_batch(nullifiers).expect("smt.insert_batch()");
|
||||
|
||||
for input in inputs {
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
|
||||
@@ -340,6 +340,12 @@ impl TestHarness {
|
||||
let mut found_owncoins = vec![];
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
if append {
|
||||
let nullifier = fee_params.input.nullifier.inner();
|
||||
wallet
|
||||
.money_null_smt
|
||||
.insert_batch(vec![(nullifier, nullifier)])
|
||||
.expect("smt.insert_batch()");
|
||||
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
|
||||
@@ -134,8 +134,11 @@ impl TestHarness {
|
||||
inputs.push(fee_params.input.clone());
|
||||
}
|
||||
|
||||
let nullifiers = inputs.iter().map(|i| i.nullifier.inner()).map(|l| (l, l)).collect();
|
||||
wallet.money_null_smt.insert_batch(nullifiers).expect("smt.insert_batch()");
|
||||
|
||||
if append {
|
||||
for input in inputs.iter() {
|
||||
for input in &inputs {
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
@@ -156,7 +159,7 @@ impl TestHarness {
|
||||
outputs.push(fee_params.output.clone());
|
||||
}
|
||||
|
||||
for output in outputs.iter() {
|
||||
for output in &outputs {
|
||||
if !append {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u
|
||||
}
|
||||
|
||||
/// Lookup a database handle from its name.
|
||||
/// If it does not exist, push it to the Vector of db_handles.
|
||||
/// If it exists, push it to the Vector of db_handles.
|
||||
///
|
||||
/// Returns the index of the DbHandle in the db_handles Vector on success.
|
||||
/// Otherwise, returns an error value.
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::io::Cursor;
|
||||
|
||||
use darkfi_sdk::crypto::{MerkleNode, MerkleTree};
|
||||
use darkfi_serial::{serialize, Decodable, Encodable, WriteExt};
|
||||
use log::{debug, error, warn};
|
||||
use log::{debug, error};
|
||||
use wasmer::{FunctionEnvMut, WasmPtr};
|
||||
|
||||
use super::acl::acl_allow;
|
||||
@@ -156,15 +156,6 @@ pub(crate) fn merkle_add(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u3
|
||||
}
|
||||
};
|
||||
|
||||
// Nothing to do so just return here
|
||||
if coins.is_empty() {
|
||||
warn!(
|
||||
target: "runtime::merkle::merkle_add",
|
||||
"[WASM] [{}] merkle_add(): Nothing to add! Returning.", cid,
|
||||
);
|
||||
return darkfi_sdk::entrypoint::SUCCESS
|
||||
}
|
||||
|
||||
// Make sure we've read the entire buffer
|
||||
if buf_reader.position() != (len as u64) {
|
||||
error!(
|
||||
|
||||
@@ -24,7 +24,7 @@ use darkfi_sdk::crypto::{
|
||||
};
|
||||
use darkfi_serial::{serialize, Decodable, Encodable};
|
||||
use halo2_proofs::pasta::pallas;
|
||||
use log::{debug, error, warn};
|
||||
use log::{debug, error};
|
||||
use num_bigint::BigUint;
|
||||
use wasmer::{FunctionEnvMut, WasmPtr};
|
||||
|
||||
@@ -201,15 +201,6 @@ pub(crate) fn sparse_merkle_insert_batch(
|
||||
}
|
||||
};
|
||||
|
||||
// Nothing to do so just return here
|
||||
if nullifiers.is_empty() {
|
||||
warn!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): Nothing to add! Returning.", cid
|
||||
);
|
||||
return darkfi_sdk::entrypoint::SUCCESS
|
||||
}
|
||||
|
||||
// Make sure we've read the entire buffer
|
||||
if buf_reader.position() != (len as u64) {
|
||||
error!(
|
||||
|
||||
@@ -167,6 +167,10 @@ impl<
|
||||
/// Takes a batch of field elements, inserts these hashes into the tree,
|
||||
/// and updates the Merkle root.
|
||||
pub fn insert_batch(&mut self, leaves: Vec<(F, F)>) -> ContractResult {
|
||||
if leaves.is_empty() {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// Nodes that need recalculating
|
||||
let mut dirty_idxs = Vec::new();
|
||||
for (pos, leaf) in leaves {
|
||||
|
||||
@@ -44,7 +44,6 @@ pub mod crypto;
|
||||
|
||||
/// Merkle
|
||||
pub mod merkle;
|
||||
pub use merkle::merkle_add;
|
||||
|
||||
/// Transaction structure
|
||||
pub mod tx;
|
||||
|
||||
@@ -119,7 +119,7 @@ macro_rules! impl_try_from {
|
||||
match value {
|
||||
HeapVar::$variant(v) => Ok(v),
|
||||
x => {
|
||||
error!("Invalid TryFrom conversion {:?}", x);
|
||||
error!("Expected {}, but instead got: {:?}", stringify!($variant), x);
|
||||
Err(plonk::Error::Synthesis)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user