mohab metwally 3 лет назад
Родитель
Сommit
ed04dec32a

+ 0 - 1
src/consensus/state.rs

@@ -294,7 +294,6 @@ impl ConsensusState {
             0,
             epoch_secrets.merkle_paths[0],
             seeds[0],
-            epoch_secrets.secret_keys[0],
             &mut self.coins_tree,
         );
         coins.push(coin);

+ 1 - 2
src/contract/money/proof/lead_mint.zk

@@ -39,6 +39,5 @@ circuit "LeadMint" {
         c1_cm_x = ec_get_x(c1_cm);
         c1_cm_y = ec_get_y(c1_cm);
         c1_cm_hash = poseidon_hash(c1_cm_x, c1_cm_y);
-        constrain_instance(c1_cm_x);
-        constrain_instance(c1_cm_y);
+        constrain_instance(c1_cm_hash);
 }

+ 315 - 0
src/contract/money/src/client.rs

@@ -371,6 +371,7 @@ impl TransferMintRevealed {
     }
 }
 
+
 #[allow(clippy::too_many_arguments)]
 fn create_transfer_mint_proof(
     zkbin: &ZkBinary,
@@ -476,6 +477,165 @@ fn create_transfer_burn_proof(
     Ok((proof, revealed))
 }
 
+struct StakeLeadMintRevealed {
+    pub value_commit: ValueCommit,
+    pub pk: pallas::Base,
+    pub commitment_x: pallas::Base,
+    pub commitment_y: pallas::Base,
+}
+
+impl StakeLeadMintRevealed {
+    pub fn compute(value: pallas::Base,
+                   pk: pallas::Base,
+                   value_blind: pallas::Scalar,
+                   commitment: pallas::Point
+
+    ) -> Self {
+        let value_commit = pedersen_commitment_base(value, value_blind);
+        let coord = commitment.to_affine().coordinates().unwrap();
+        Self {
+            value_commit,
+            pk,
+            *coord.x(),
+            *coord.y(),
+        }
+
+    }
+    pub fn to_vec(&self) -> Vec<pallas::Base> {
+        vec![
+            self.value_commit,
+            self.pk,
+            self.commitment_x,
+            self.commitment_y,
+        ]
+    }
+}
+
+struct UnstakeLeadBurnRevealed {
+    pub value_commit: ValueCommit,
+    pub pk: pallas::Base,
+    pub commitment_x: pallas::Base,
+    pub commitment_y: pallas::Base,
+    pub commitment_root: pallas::Base,
+    pub sk_root: pallas::Base,
+    pub nullifier: pallas::Base,
+}
+
+impl UnstakeLeadBurnRevealed {
+    pub fn compute(
+        value: pallas::Base,
+        pk: pallas::Base,
+        commitment: pallas::Point,
+        commitment_root: pallas::Base,
+        sk_root: pallas::Base,
+        nullifier: pallas::Base,
+    ) -> Self {
+        let value_commit = pedersen_commitment_base(value, value_blind);
+        let coord = commitment.to_affine().coordinates().unwrap();
+        Self {
+            value_commit,
+            pk,
+            commitment_x,
+            commitment_y,
+            commitment_root,
+            sk_root,
+            nullifier,
+        }
+    }
+
+    pub fn to_vec(&self) -> Vec<pallas::Base> {
+        vec![
+            self.value_commit,
+            self.pk,
+            self.commitment_x,
+            self.commitment_y,
+            self.commitment_root,
+            self.sk_root,
+            self.nullifier,
+        ]
+    }
+}
+
+fn create_stake_mint_proof(
+    zkbin: &ZkBinary, // LeadMint contract binary
+    pk: &ProvingKey,
+    public_key: pallas::Base,
+    coin_commitment: pallas::Point,
+    value: pallas::Base,
+    value_blind: ValueBlind,
+    coin_blind: pallas::Base,
+    sk: pallas::Base,
+    sk_root: pallas::Base,
+    tau: pallas::Base,
+    nonce: pallas::Base, // rho
+) > Result<(Proof, StakeLeadMintRevealed)> {
+    let revealed = StakeLeadMintRevealed::compute(
+        value,
+        public_key,
+        coin_commitment,
+    );
+
+    let prover_witnesses = vec![
+        Witness::Base(Value::known(sk)),
+        Witness::Base(Value::known(sk_root)),
+        Witness::Base(Value::known(tau)),
+        Witness::Base(Value::known(nonce)),
+        Witness::Scalar(Value::known(coin_blind)),
+        Witness::Base(Value::known(value)),
+        Witness::Scalar(Value::known(value_blind)),
+    ];
+    let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
+    let proof = Proof::create(pk, &[circuit], &revealed.to_vec(), &mut OsRng)?;
+
+    Ok((proof, revealed))
+}
+
+fn create_unstake_burn_proof(
+    zkbin: &ZkBinary,
+    pk: &ProvingKey,
+    value: pallas::Base,
+    value_blind: ValueBlind,
+    coin_blind: pallas::Base,
+    public_key: pallas::Base,
+    sk_root: pallas::Base,
+    sk_pos: incrementalmerkletree::Position,
+    sk_path: Vec<MerkleNode>,
+    commitment_merkle_path: Vec<MerkleNode>,
+    commitment: pallas::Point,
+    commitment_root: pallas::Base,
+    commitment_pos: incrementalmerkletree::Position,
+    tau: pallas::Base,
+    nonce: pallas::Base,
+    nullifier: pallas::Base,
+) -> Result<(Proof, UnstakeLeadBurnRevealed)> {
+    let revealed = UnstakeLeadMintRevealed::compute(
+        value,
+        public_key,
+        commitment,
+        commitment_root,
+        sk_root,
+        nullifier,
+    );
+
+    let prover_witnesses = vec![
+        Witness::MerklePath(Value::known(commitment_merkle_path.try_into().unwrap())),
+        Witness::Uint32(Value::known(u64::from(commitment_pos).try_into().unwrap())), // u32
+        Witness::Uint32(Value::known(u64::from(sk_pos).try_into().unwrap())), // u32
+        Witness::Base(Value::known(sk)),
+        Witness::Base(Value::known(sk_root)),
+        Witness::MerklePath(Value::known(sk_path.try_into().unwrap())),
+        Witness::Base(Value::known(tau)),
+        Witness::Base(Value::known(nonce)),
+        Witness::Scalar(Value::known(coin_blind)),
+        Witness::Base(Value::known(value)),
+        Witness::Scalar(Value::known(value_blind)),
+    ];
+    let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
+    let proof = Proof::create(pk, &[circuit], &revealed.to_vec(), &mut OsRng)?;
+
+    Ok((proof, revealed))
+}
+
 /// Build half of the money contract OTC swap transaction parameters with the given data:
 /// * `value_send` - Amount to send
 /// * `token_id_send` - Token ID to send
@@ -898,6 +1058,161 @@ pub fn build_transfer_tx(
     Ok((params, zk_proofs, signature_secrets, spent_coins))
 }
 
+pub fn build_stake_tx(
+    pubkey: &PublicKey,
+    value_send: u64,
+    value_recv: u64,
+    value_blinds: &[ValueBlind],
+    coins: &[OwnCoin],
+    tx_tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    cm_tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    sk_tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    mint_zkbin: &ZkBinary,
+    mint_pk: &ProvingKey,
+    burn_zkbin: &ZkBinary,
+    burn_pk: &ProvingKey,
+    slot_index: pallas::Base,
+    eta: pallas::Base,
+) -> Result<(
+    MoneyStakeParams,
+    Vec<Proof>,
+    Vec<LeadCoin>,
+    Vec<ValueBlind>,
+    Vec<ValueBlind>,
+)> {
+    // convert owncoins to leadcoins.
+    let token_blind = ValueBlind::random(&mut OsRng);
+    let leadcoins : Vec<LeadCoin>= vec![];
+    let mut params = MoneyStakeParams {
+        inputs: vec![],
+        outputs: vec![],
+    };
+    let mut proofs = vec![];
+    let mut own_blinds = vec![];
+    let mut lead_blinds = vec![];
+    for coin in coins.iter() {
+        // burn the coin
+        let value_blind = ValueBlind::random(&mut OsRng);
+        own_blinds.push(value_blind);
+        let spend_hook = pallas::Base::zero();
+        let user_data = pallas::Base::zero();
+        let user_data_blind = pallas::Base::random(&mut OsRng);
+        let tx_leaf_position = coin.leaf_position;
+        let tx_root = tx_tree.root(0).unwrap();
+        let tx_merkle_path = tx_tree.authentication_path(tx_leaf_position, &tx_root).unwrap();
+        let signature_secret = SecretKey::random(&mut OsRng);
+        //signature_secrets.push(signature_secret);
+        let (own_proof, own_revealed) = create_transfer_burn_proof(
+            burn_zkbin,
+            burn_pk,
+            coin.note.value,
+            coin.note.token_id,
+            coin.note.value_blind,
+            coin.note.token_blind,
+            coin.note.serial,
+            spend_hook,
+            user_data,
+            user_data_blind,
+            coin.secret,
+            coin.leaf_position,
+            tx_merkle_path.clone(),
+            signature_secret,
+        )?;
+        params.inputs.push(Input {
+            value_commit: own_revealed.value_commit,
+            token_commit: own_revealed.token_commit,
+            nullifier: own_revealed.nullifier,
+            merkle_root: own_revealed.merkle_root,
+            spend_hook: own_revealed.spend_hook,
+            user_data_enc: own_revealed.user_data_enc,
+            signature_public: own_revealed.signature_public,
+        });
+        proofs.push(own_proof);
+        let lead_value_blind = ValueBlind::random(&mut OsRng);
+        lead_blinds.push(lead_value_blind);
+        sk_tree.append(&MerkleNode::from(coin.secret));
+        let sk_pos = sk_tree.witness().unwrap();
+        let sk_root = sk_tree.root(0).unwrap();
+        let sk_merkle_path = sk_tree.authentication_path(sk_pos, &sk_root).unwrap();
+        let leadcoin = LeadCoin::new(
+            eta, // randomness from last finalized block.
+            coin.note.value,
+            slot_index, // tau
+            coin.secret, // coin secret key
+            sk_root,
+            sk_pos,
+            sk_merkle_path,
+            cm_tree,
+        );
+        leadcoins.push(leadcoin);
+        let lead_coin_blind = ValueBlind::random(&mut OsRng);
+        let public_key = leadcoin.pk();
+        let (lead_proof, lead_revealed) = create_stake_mint_proof(
+            mint_zkbin,
+            mint_pk,
+            public_key,
+            leadcoin.coin1_commitment,
+            coin.note.value,
+            lead_value_blind,
+            lead_coin_blind,
+            coin.secret,
+            sk_root,
+            slot_index, // tau
+            coin.note.serial, // nonce
+        )?;
+        let coin_commit_coords = [
+            lead_revealed.commitment_x,
+            lead_revealed.commitment_y,
+        ];
+        let coin_commit_hash = poseidon_hash(coords);
+        params.outputs.push(StakedOutput{
+            lead_revealed.value_commit,
+            coin_commit_hash,
+            public_key,
+        });
+        proofs.push(lead_proof);
+    }
+    Ok((params, proofs, leadcoins, own_blinds, lead_blinds))
+}
+
+pub fn build_unstake_tx(
+    pubkey: &PublicKey,
+    value_send: u64,
+    value_recv: u64,
+    value_blinds: &[ValueBlind],
+    coins: &[LeadCoin],
+    tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    mint_zkbin: &ZkBinary,
+    mint_pk: &ProvingKey,
+    burn_zkbin: &ZkBinary,
+    burn_pk: &ProvingKey,
+) -> Result<(
+    MoneyUnStakeParams,
+    Vec<Proof>,
+    Vec<SecretKey>,
+    Vec<OwnCoin>,
+    Vec<ValueBlind>,
+    Vec<ValueBlind>,
+)> {
+    // convert leadcoin to owncoin
+    let token_blind = ValueBlind::random(&mut OsRng);
+    let lowncoins : Vec<LeadCoin>= vec![];
+    let mut params = MoneyStakeParams {
+        inputs: vec![],
+        outputs: vec![],
+    };
+    let mut proofs = vec![];
+    let mut own_blinds = vec![];
+    let mut lead_blinds = vec![];
+    for coin in coins.iter() {
+        // burn lead coin
+        // mint own coin
+    }
+    // return proofs created
+    // return secret keys
+    // blind values.
+}
+
 fn compute_remainder_blind(
     clear_inputs: &[ClearInput],
     input_blinds: &[ValueBlind],

+ 162 - 3
src/contract/money/src/lib.rs

@@ -100,6 +100,11 @@ pub const MONEY_CONTRACT_ZKAS_BURN_NS_V1: &str = "Burn_V1";
 /// zkas token mint contract namespace
 pub const MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1: &str = "TokenMint_V1";
 
+/// zkas lead  mint contract namespace
+pub const MONEY_CONTRACT_ZKAS_LEAD_MINT_NS: &str = "Lead_Mint";
+/// zkas lead burn contract namespace
+pub const MONEY_CONTRACT_ZKAS_LEAD_BURN_NS: &str = "Lead_Burn";
+
 /// This function runs when the contract is (re)deployed and initialized.
 #[cfg(not(feature = "no-entrypoint"))]
 fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
@@ -116,8 +121,13 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
     };
     let mint_v1_bincode = include_bytes!("../proof/mint_v1.zk.bin");
     let burn_v1_bincode = include_bytes!("../proof/burn_v1.zk.bin");
+
     let token_mint_v1_bincode = include_bytes!("../proof/token_mint_v1.zk.bin");
 
+
+    let mint_lead_bincode = include_bytes!("../proof/lead_mint.zk.bin");
+    let burn_lead_bincode = include_bytes!("../proof/lead_burn.zk.bin");
+
     /* TODO: Do I really want to make zkas a dependency? Yeah, in the future.
        For now we take anything.
     let zkbin = ZkBinary::decode(mint_bincode)?;
@@ -133,6 +143,9 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
     db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_BURN_NS_V1), &burn_v1_bincode[..])?;
     db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1), &token_mint_v1_bincode[..])?;
 
+    db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_LEAD_MINT_NS), &mint_lead_bincode[..])?;
+    db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_LEAD_BURN_NS), &burn_lead_bincode[..])?;
+
     // Set up a database tree to hold Merkle roots
     let _ = match db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE) {
         Ok(v) => v,
@@ -145,10 +158,23 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
         Err(_) => db_init(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?,
     };
 
+
     // Set up a database tree to hold the set of fixed-supply tokens
     let _ = match db_lookup(cid, MONEY_CONTRACT_FIXED_SUPPLY_TREE) {
         Ok(v) => v,
         Err(_) => db_init(cid, MONEY_CONTRACT_FIXED_SUPPLY_TREE)?,
+
+    // Set up a database tree to hold lead Merkle roots
+    let _ = match db_lookup(cid, MONEY_CONTRACT_LEAD_COIN_ROOTS_TREE) {
+        Ok(v) => v,
+        Err(_) => db_init(cid, MONEY_CONTRACT_LEAD_COIN_ROOTS_TREE)?,
+    };
+
+    // Set up a database tree to hold nullifiers
+    let _ = match db_lookup(cid, MONEY_CONTRACT_LEAD_NULLIFIERS_TREE) {
+        Ok(v) => v,
+        Err(_) => db_init(cid, MONEY_CONTRACT_LEAD_NULLIFIERS_TREE)?,
+
     };
 
     // Set up a database tree for arbitrary data
@@ -242,7 +268,57 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
             set_return_data(&metadata)?;
         }
 
-        MoneyFunction::Stake => unimplemented!(),
+        MoneyFunction::Stake => {
+            let params: MoneyStakeParams = deserialize(&self_.data[1..])?;
+
+            let mut zk_public_values: Vec<(String, Vec<pallas::Base>)> = vec![];
+            let mut signature_pubkeys: Vec<PublicKey> = vec![];
+
+            for input in &params.inputs {
+                let value_coords = input.value_commit.to_affine().coordinates().unwrap();
+                let token_coords = input.token_commit.to_affine().coordinates().unwrap();
+                let (sig_x, sig_y) = input.signature_public.xy();
+
+                zk_public_values.push((
+                    MONEY_CONTRACT_ZKAS_BURN_NS_V1.to_string(),
+                    vec![
+                        input.nullifier.inner(),
+                        *value_coords.x(),
+                        *value_coords.y(),
+                        *token_coords.x(),
+                        *token_coords.y(),
+                        input.merkle_root.inner(),
+                        input.user_data_enc,
+                        sig_x,
+                        sig_y,
+                    ],
+                ));
+
+                signature_pubkeys.push(input.signature_public);
+            }
+
+            for output in &params.outputs {
+                let value_coords = output.value_commit.to_affine().coordinates().unwrap();
+
+                zk_public_values.push((
+                    MONEY_CONTRACT_ZKAS_LEAD_MINT_NS.to_string(),
+                    vec![
+                        *value_coords.x(),
+                        *value_coords.y(),
+                        output.coin_pk_hash,
+                        output.coin,
+                    ],
+                ));
+            }
+
+            let mut metadata = vec![];
+            zk_public_values.encode(&mut metadata)?;
+            signature_pubkeys.encode(&mut metadata)?;
+
+            // Using this, we pass the above data to the host.
+            set_return_data(&metadata)?;
+        }
+
         MoneyFunction::Unstake => unimplemented!(),
         MoneyFunction::Mint => unimplemented!(),
     };
@@ -451,7 +527,69 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
 
         MoneyFunction::Stake => {
             msg!("[Stake] Entered match arm");
-            unimplemented!();
+            let params: MoneyStakeParams = deserialize(&self_.data[1..])?;
+
+            assert!(params.inputs.len() == params.outputs.len());
+
+            let info_db = db_lookup(cid, MONEY_CONTRACT_INFO_TREE)?;
+            let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_LEAD_NULLIFIERS_TREE)?;
+            let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_LEAD_COIN_ROOTS_TREE)?;
+
+
+            // Accumulator for the value commitments
+            let mut valcom_total = pallas::Point::identity();
+
+            // State transition for payments
+            let mut new_nullifiers = Vec::with_capacity(params.inputs.len());
+
+            msg!("[Stake] Iterating over anonymous inputs");
+            for (i, input) in params.inputs.iter().enumerate() {
+                // The Merkle root is used to know whether this is a coin that existed
+                // in a previous state.
+                if !db_contains_key(coin_roots_db, &serialize(&input.merkle_root))? {
+                    msg!("[Stake] Error: Merkle root not found in previous state (input {})", i);
+                    return Err(ContractError::Custom(21))
+                }
+
+                // The nullifiers should not already exist. It is the double-spend protection.
+                if new_nullifiers.contains(&input.nullifier) ||
+                    db_contains_key(nullifiers_db, &serialize(&input.nullifier))?
+                {
+                    msg!("[Stake] Error: Duplicate nullifier found in input {}", i);
+                    return Err(ContractError::Custom(22))
+                }
+
+                new_nullifiers.push(input.nullifier);
+                valcom_total += input.value_commit;
+            }
+
+            // Newly created coins for this transaction are in the outputs.
+            let mut new_coins = Vec::with_capacity(params.outputs.len());
+            for (i, output) in params.outputs.iter().enumerate() {
+                // TODO: Should we have coins in a sled tree too to check dupes?
+                if new_coins.contains(&Coin::from(output.coin_commit_hash)) {
+                    msg!("[Stake] Error: Duplicate coin found in output {}", i);
+                    return Err(ContractError::Custom(23))
+                }
+                new_coins.push(Coin::from(output.coin));
+                valcom_total -= output.value_commit;
+            }
+
+            // If the accumulator is not back in its initial state, there's a value mismatch.
+            if valcom_total != pallas::Point::identity() {
+                msg!("[Stake] Error: Value commitments do not result in identity");
+                return Err(ContractError::Custom(24))
+            }
+
+            // Create a state update
+            let update = MoneyStakeUpdate { nullifiers: new_nullifiers, coins: new_coins };
+            let mut update_data = vec![];
+            update_data.write_u8(MoneyFunction::Stake as u8)?;
+            update.encode(&mut update_data)?;
+            set_return_data(&update_data)?;
+            msg!("[Stake] State update set!");
+
+            Ok(())
         }
 
         MoneyFunction::Unstake => {
@@ -492,7 +630,28 @@ fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
             Ok(())
         }
 
-        MoneyFunction::Stake => unimplemented!(),
+        MoneyFunction::Stake => {
+            let update: MoneyStakeUpdate = deserialize(&update_data[1..])?;
+
+            let info_db = db_lookup(cid, MONEY_CONTRACT_LEAD_INFO_TREE)?;
+            let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_LEAD_NULLIFIERS_TREE)?;
+            let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_LEAD_COIN_ROOTS_TREE)?;
+
+            for nullifier in update.nullifiers {
+                db_set(nullifiers_db, &serialize(&nullifier), &[])?;
+            }
+
+            msg!("Adding coins {:?} to Merkle tree", update.coins);
+            let coins: Vec<_> = update.coins.iter().map(|x| MerkleNode::from(x.inner())).collect();
+            merkle_add(
+                info_db,
+                coin_roots_db,
+                &serialize(&MONEY_CONTRACT_LEAD_COIN_MERKLE_TREE),
+                &coins,
+            )?;
+
+            Ok(())
+        }
         MoneyFunction::Unstake => unimplemented!(),
         MoneyFunction::Mint => unimplemented!(),
     }

+ 16 - 3
src/contract/money/src/state.rs

@@ -57,12 +57,16 @@ pub struct StakedInput {
 /// Staked anonymous output
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 pub struct StakedOutput {
+    /// Pedersen commitment for the output's value
+    pub value_commit: ValueCommit,
     /// Minted coin
-    pub coin: pallas::Base,
+    pub coin_commit_hash: pallas::Base,
+    /// coin pk hash
+    pub coin_pk_hash: pallas::Base,
     /// The encrypted note ciphertext
-    pub ciphertext: Vec<u8>,
+    //pub ciphertext: Vec<u8>,
     /// The ephemeral public key
-    pub ephem_public: PublicKey,
+    //pub ephem_public: PublicKey,
 }
 
 /// Inputs and outputs for a payment
@@ -85,6 +89,15 @@ pub struct MoneyTransferUpdate {
     pub coins: Vec<Coin>,
 }
 
+/// State update produced by a staking
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+pub struct MoneyStakeUpdate {
+    /// Revealed nullifiers
+    pub nullifiers: Vec<Nullifier>,
+    /// Minted coins
+    pub coins: Vec<Coin>,
+}
+
 /// A transaction's clear input
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 pub struct ClearInput {