contract/consensus: Introduce separate Merkle tree for unstaked coins

This commit is contained in:
parazyd
2023-06-11 19:48:46 +02:00
parent 2ec4d0fc59
commit b243e2f5f6
7 changed files with 61 additions and 37 deletions

View File

@@ -36,7 +36,7 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
use crate::{
error::ConsensusError,
model::{calculate_grace_period, ConsensusProposalUpdateV1},
model::{ConsensusProposalUpdateV1, GRACE_PERIOD},
ConsensusFunction,
};
@@ -120,7 +120,7 @@ pub(crate) fn consensus_unstake_request_process_instruction_v1(
msg!("[ConsensusUnstakeRequestV1] Validating anonymous input");
// The coin has passed through the grace period and is allowed to request unstake.
if input.epoch != 0 && get_verifying_slot_epoch() - input.epoch <= calculate_grace_period() {
if input.epoch != 0 && get_verifying_slot_epoch() - input.epoch <= GRACE_PERIOD {
msg!("[ConsensusUnstakeRequestV1] Error: Coin is not allowed to request unstake yet");
return Err(ConsensusError::CoinStillInGracePeriod.into())
}
@@ -138,14 +138,6 @@ pub(crate) fn consensus_unstake_request_process_instruction_v1(
return Err(MoneyError::DuplicateNullifier.into())
}
/*
// Check that the coin hasn't existed before in unstake set.
if db_contains_key(unstaked_coins_db, &serialize(&input.coin))? {
msg!("[ConsensusUnstakeRequestV1] Error: Unstaked coin found in input");
return Err(MoneyError::DuplicateCoin.into())
}
*/
msg!("[ConsensusUnstakeRequestV1] Validating anonymous output");
// Verify value commits match
@@ -166,8 +158,6 @@ pub(crate) fn consensus_unstake_request_process_instruction_v1(
let mut update_data = vec![];
update_data.write_u8(ConsensusFunction::UnstakeRequestV1 as u8)?;
update.encode(&mut update_data)?;
// and return it
Ok(update_data)
}
@@ -185,10 +175,10 @@ pub(crate) fn consensus_unstake_request_process_update_v1(
msg!("[ConsensusUnstakeRequestV1] Adding new nullifier to the set");
db_set(nullifiers_db, &serialize(&update.nullifier), &[])?;
msg!("[ConsensusUnstakeRequestV1] Adding new coin to the set");
msg!("[ConsensusUnstakeRequestV1] Adding new coin to the unstaked coins set");
db_set(unstaked_coins_db, &serialize(&update.coin), &[])?;
msg!("[ConsensusUnstakeRequestV1] Adding new coin to the Merkle tree");
msg!("[ConsensusUnstakeRequestV1] Adding new coin to the unstaked coins Merkle tree");
let coins: Vec<_> = vec![MerkleNode::from(update.coin.inner())];
merkle_add(
info_db,

View File

@@ -72,12 +72,14 @@ pub struct ConsensusProposalUpdateV1 {
/// Parameters for `Consensus::UnstakeRequest`
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
// ANCHOR: ConsensusUnstakeRequestParams
pub struct ConsensusUnstakeRequestParamsV1 {
/// Burnt token revealed info
pub input: ConsensusInput,
/// Anonymous output
pub output: Output,
}
// ANCHOR_END: ConsensusUnstakeRequestParams
// ======================================================================
// Consensus parameters configuration

View File

@@ -106,7 +106,7 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> {
// Gather new staked owncoin
let alice_staked_oc =
th.gather_consensus_owncoin(Holder::Alice, genesis_stake_params.output, None)?;
th.gather_consensus_staked_owncoin(Holder::Alice, genesis_stake_params.output, None)?;
// Verify values match
assert!(ALICE_INITIAL == alice_staked_oc.note.value);
@@ -139,7 +139,7 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> {
th.assert_trees();
// Gather new staked owncoin which includes the reward
let alice_rewarded_staked_oc = th.gather_consensus_owncoin(
let alice_rewarded_staked_oc = th.gather_consensus_staked_owncoin(
Holder::Alice,
proposal_params.output,
Some(proposal_output_secret_key),
@@ -184,7 +184,7 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> {
th.assert_trees();
// Gather new unstake request owncoin
let alice_unstake_request_oc = th.gather_consensus_owncoin(
let alice_unstake_request_oc = th.gather_consensus_unstaked_owncoin(
Holder::Alice,
unstake_request_params.output,
Some(unstake_request_secret_key),

View File

@@ -154,7 +154,8 @@ pub struct Wallet {
pub keypair: Keypair,
pub state: ValidatorStatePtr,
pub money_merkle_tree: MerkleTree,
pub consensus_merkle_tree: MerkleTree,
pub consensus_staked_merkle_tree: MerkleTree,
pub consensus_unstaked_merkle_tree: MerkleTree,
pub wallet: WalletPtr,
pub coins: Vec<OwnCoin>,
pub spent_coins: Vec<OwnCoin>,
@@ -179,7 +180,8 @@ impl Wallet {
.await?;
let money_merkle_tree = MerkleTree::new(100);
let consensus_merkle_tree = MerkleTree::new(100);
let consensus_staked_merkle_tree = MerkleTree::new(100);
let consensus_unstaked_merkle_tree = MerkleTree::new(100);
let coins = vec![];
let spent_coins = vec![];
@@ -188,7 +190,8 @@ impl Wallet {
keypair,
state,
money_merkle_tree,
consensus_merkle_tree,
consensus_staked_merkle_tree,
consensus_unstaked_merkle_tree,
wallet,
coins,
spent_coins,
@@ -395,7 +398,7 @@ impl ConsensusTestHarness {
let erroneous_txs =
wallet.state.read().await.verify_transactions(&[tx], slot, true).await?;
assert!(erroneous_txs.is_empty());
wallet.consensus_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
wallet.consensus_staked_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
tx_action_benchmark.verify_times.push(timer.elapsed());
Ok(())
@@ -515,7 +518,7 @@ impl ConsensusTestHarness {
let erroneous_txs =
wallet.state.read().await.verify_transactions(&[tx], slot, true).await?;
assert!(erroneous_txs.is_empty());
wallet.consensus_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
wallet.consensus_staked_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
tx_action_benchmark.verify_times.push(timer.elapsed());
Ok(())
@@ -542,7 +545,7 @@ impl ConsensusTestHarness {
slot_checkpoint,
fork_hash,
fork_previous_hash: fork_hash,
merkle_tree: wallet.consensus_merkle_tree.clone(),
merkle_tree: wallet.consensus_staked_merkle_tree.clone(),
proposal_zkbin: proposal_zkbin.clone(),
proposal_pk: proposal_pk.clone(),
}
@@ -591,7 +594,7 @@ impl ConsensusTestHarness {
let erroneous_txs =
wallet.state.read().await.verify_transactions(&[tx], slot, true).await?;
assert!(erroneous_txs.is_empty());
wallet.consensus_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
wallet.consensus_staked_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
tx_action_benchmark.verify_times.push(timer.elapsed());
Ok(())
@@ -636,7 +639,7 @@ impl ConsensusTestHarness {
let unstake_request_call_debris = ConsensusUnstakeRequestCallBuilder {
coin: staked_oc.clone(),
epoch,
tree: wallet.consensus_merkle_tree.clone(),
tree: wallet.consensus_staked_merkle_tree.clone(),
burn_zkbin: burn_zkbin.clone(),
burn_pk: burn_pk.clone(),
mint_zkbin: mint_zkbin.clone(),
@@ -686,7 +689,7 @@ impl ConsensusTestHarness {
let erroneous_txs =
wallet.state.read().await.verify_transactions(&[tx], slot, true).await?;
assert!(erroneous_txs.is_empty());
wallet.consensus_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
wallet.consensus_unstaked_merkle_tree.append(&MerkleNode::from(params.output.coin.inner()));
tx_action_benchmark.verify_times.push(timer.elapsed());
Ok(())
@@ -727,7 +730,7 @@ impl ConsensusTestHarness {
// Building Consensus::Unstake params
let consensus_unstake_call_debris = ConsensusUnstakeCallBuilder {
coin: staked_oc.clone(),
tree: wallet.consensus_merkle_tree.clone(),
tree: wallet.consensus_unstaked_merkle_tree.clone(),
burn_zkbin: burn_zkbin.clone(),
burn_pk: burn_pk.clone(),
}
@@ -830,14 +833,38 @@ impl ConsensusTestHarness {
Ok(oc)
}
pub fn gather_consensus_owncoin(
pub fn gather_consensus_staked_owncoin(
&mut self,
holder: Holder,
output: ConsensusOutput,
secret_key: Option<SecretKey>,
) -> Result<ConsensusOwnCoin> {
let wallet = self.holders.get_mut(&holder).unwrap();
let leaf_position = wallet.consensus_merkle_tree.witness().unwrap();
let leaf_position = wallet.consensus_staked_merkle_tree.witness().unwrap();
let secret_key = match secret_key {
Some(key) => key,
None => wallet.keypair.secret,
};
let note: ConsensusNote = output.note.decrypt(&secret_key)?;
let oc = ConsensusOwnCoin {
coin: Coin::from(output.coin),
note: note.clone(),
secret: secret_key,
nullifier: Nullifier::from(poseidon_hash([wallet.keypair.secret.inner(), note.serial])),
leaf_position,
};
Ok(oc)
}
pub fn gather_consensus_unstaked_owncoin(
&mut self,
holder: Holder,
output: ConsensusOutput,
secret_key: Option<SecretKey>,
) -> Result<ConsensusOwnCoin> {
let wallet = self.holders.get_mut(&holder).unwrap();
let leaf_position = wallet.consensus_unstaked_merkle_tree.witness().unwrap();
let secret_key = match secret_key {
Some(key) => key,
None => wallet.keypair.secret,

View File

@@ -87,8 +87,11 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
th.assert_trees();
// Gather new staked owncoin
let alice_staked_oc =
th.gather_consensus_owncoin(Holder::Alice, stake_params.output, Some(stake_secret_key))?;
let alice_staked_oc = th.gather_consensus_staked_owncoin(
Holder::Alice,
stake_params.output,
Some(stake_secret_key),
)?;
// Verify values match
assert!(alice_oc.note.value == alice_staked_oc.note.value);
@@ -136,7 +139,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
th.assert_trees();
// Gather new staked owncoin which includes the reward
let alice_rewarded_staked_oc = th.gather_consensus_owncoin(
let alice_rewarded_staked_oc = th.gather_consensus_staked_owncoin(
Holder::Alice,
proposal_params.output,
Some(proposal_decryption_secret_key),
@@ -181,7 +184,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
th.assert_trees();
// Gather new unstake request owncoin
let alice_unstake_request_oc = th.gather_consensus_owncoin(
let alice_unstake_request_oc = th.gather_consensus_unstaked_owncoin(
Holder::Alice,
unstake_request_params.output,
Some(unstake_request_secret_key),

View File

@@ -32,7 +32,7 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
use crate::{
error::MoneyError,
model::{ConsensusUnstakeParamsV1, MoneyUnstakeParamsV1, MoneyUnstakeUpdateV1},
MoneyFunction, CONSENSUS_CONTRACT_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_NULLIFIERS_TREE,
MoneyFunction, CONSENSUS_CONTRACT_NULLIFIERS_TREE, CONSENSUS_CONTRACT_UNSTAKED_COIN_ROOTS_TREE,
MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE, MONEY_CONTRACT_COIN_ROOTS_TREE,
MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
};
@@ -89,8 +89,8 @@ pub(crate) fn money_unstake_process_instruction_v1(
let money_coins_db = db_lookup(cid, MONEY_CONTRACT_COINS_TREE)?;
let consensus_nullifiers_db =
db_lookup(*CONSENSUS_CONTRACT_ID, CONSENSUS_CONTRACT_NULLIFIERS_TREE)?;
let consensus_coin_roots_db =
db_lookup(*CONSENSUS_CONTRACT_ID, CONSENSUS_CONTRACT_COIN_ROOTS_TREE)?;
let consensus_unstaked_coin_roots_db =
db_lookup(*CONSENSUS_CONTRACT_ID, CONSENSUS_CONTRACT_UNSTAKED_COIN_ROOTS_TREE)?;
// ===================================
// Perform the actual state transition
@@ -118,7 +118,7 @@ pub(crate) fn money_unstake_process_instruction_v1(
// The Merkle root is used to know whether this is a coin that
// existed in a previous state.
if !db_contains_key(consensus_coin_roots_db, &serialize(&input.merkle_root))? {
if !db_contains_key(consensus_unstaked_coin_roots_db, &serialize(&input.merkle_root))? {
msg!("[MoneyUnstakeV1] Error: Merkle root not found in previous state");
return Err(MoneyError::TransferMerkleRootNotFound.into())
}

View File

@@ -253,10 +253,12 @@ pub struct ConsensusStakeUpdateV1 {
/// Parameters for `Consensus::UnstakeRequest`
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
// ANCHOR: ConsensusUnstakeReqParams
pub struct ConsensusUnstakeReqParamsV1 {
pub input: ConsensusInput,
pub output: ConsensusOutput,
}
// ANCHOR_END: ConsensusUnstakeReqParams
/// Parameters for `Consensus::Unstake`
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]