瀏覽代碼

unstake contract tx

mohab metwally 3 年之前
父節點
當前提交
95e2a0f426

+ 19 - 19
src/consensus/leadcoin.rs

@@ -58,26 +58,26 @@ pub const PREFIX_SN: u64 = 6;
 pub struct LeadCoin {
     /// Coin's stake value
     pub value: u64,
-    /// Commitment for coin1
-    pub coin1_commitment: pallas::Point,
-    /// Commitment for coin2 (rcpt coin)
-    pub coin2_commitment: pallas::Point,
-    /// Coin sk index
-    pub idx: u32,
     /// Coin timestamp as slot index.
     pub tau: pallas::Base,
     /// Coin nonce
     pub nonce: pallas::Base,
+    /// Commitment for coin1
+    pub coin1_commitment: pallas::Point,
+    /// Commitment for coin2 (rcpt coin)
+    pub coin2_commitment: pallas::Point,
     /// Merkle root of coin1 commitment
     pub coin1_commitment_root: MerkleNode,
+    /// Coin commitment position
+    pub coin1_commitment_pos: u32,
+    /// Merkle path to the coin1's commitment
+    pub coin1_commitment_merkle_path: [MerkleNode; MERKLE_DEPTH_LEADCOIN],
     /// coin1 sk
     pub coin1_sk: pallas::Base,
     /// Merkle root of the `coin1` secret key
     pub coin1_sk_root: MerkleNode,
     /// coin1 sk position in merkle tree
     pub coin1_sk_pos: u32,
-    /// Merkle path to the coin1's commitment
-    pub coin1_commitment_merkle_path: [MerkleNode; MERKLE_DEPTH_LEADCOIN],
     /// Merkle path to the secret key of `coin1`
     pub coin1_sk_merkle_path: [MerkleNode; MERKLE_DEPTH_LEADCOIN],
     /// coin1 commitment blinding factor
@@ -133,10 +133,10 @@ impl LeadCoin {
         let coin1_commitment_base = poseidon_hash(c1_base_msg);
         // Append the element to the Merkle tree
         coin_commitment_tree.append(&MerkleNode::from(coin1_commitment_base));
-        let leaf_pos = coin_commitment_tree.witness().unwrap();
+        let coin1_commitment_pos = coin_commitment_tree.witness().unwrap();
         let coin1_commitment_root = coin_commitment_tree.root(0).unwrap();
         let coin1_commitment_merkle_path =
-            coin_commitment_tree.authentication_path(leaf_pos, &coin1_commitment_root).unwrap();
+            coin_commitment_tree.authentication_path(coin1_commitment_pos, &coin1_commitment_root).unwrap();
         // Create commitment to coin2
         let coin2_commitment = Self::commitment(
             pk,
@@ -149,18 +149,18 @@ impl LeadCoin {
         // Return the object
         Self {
             value,
-            coin1_commitment,
-            coin2_commitment,
-            // TODO: Should be abs slot
-            idx: u32::try_from(usize::from(leaf_pos)).unwrap(),
             // Assume tau is sl for simplicity
             tau,
             nonce: pallas::Base::from(seed),
+            coin1_commitment,
+            coin2_commitment,
             coin1_commitment_root,
+            // TODO: Should be abs slot
+            coin1_commitment_pos: u32::try_from(usize::from(coin1_commitment_pos)).unwrap(),
+            coin1_commitment_merkle_path: coin1_commitment_merkle_path.try_into().unwrap(),
             coin1_sk,
             coin1_sk_root,
             coin1_sk_pos: u32::try_from(coin1_sk_pos).unwrap(),
-            coin1_commitment_merkle_path: coin1_commitment_merkle_path.try_into().unwrap(),
             coin1_sk_merkle_path,
             coin1_blind,
             coin2_blind,
@@ -319,16 +319,16 @@ impl LeadCoin {
             coin_commitment_tree.authentication_path(leaf_pos, &commitment_root).unwrap();
         LeadCoin {
             value: self.value + constants::REWARD,
-            coin1_commitment: self.coin2_commitment,
-            coin2_commitment: derived_c2_cm,
-            idx: u32::try_from(usize::from(leaf_pos)).unwrap(),
             tau: self.tau,
             nonce: derived_c1_rho,
+            coin1_commitment: self.coin2_commitment,
+            coin2_commitment: derived_c2_cm,
             coin1_commitment_root: commitment_root,
+            coin1_commitment_pos: u32::try_from(usize::from(leaf_pos)).unwrap(),
+            coin1_commitment_merkle_path: commitment_merkle_path.try_into().unwrap(),
             coin1_sk: self.coin1_sk,
             coin1_sk_root: self.coin1_sk_root,
             coin1_sk_pos: self.coin1_sk_pos,
-            coin1_commitment_merkle_path: commitment_merkle_path.try_into().unwrap(),
             coin1_sk_merkle_path: self.coin1_sk_merkle_path,
             coin1_blind: self.coin2_blind,
             coin2_blind: blind,

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

@@ -57,8 +57,7 @@ circuit "LeadBurn" {
                    c1_cm_x,
                    c1_cm_y
         );
-        constrain_instance(c1_cm_x);
-        constrain_instance(c1_cm_y);
+        constrain_instance(c1_cm_hash);
         
         # root of path to burnt coin commitment at given pos
         root = merkle_root(c1_cm_pos, c1_cm_path, c1_cm_hash);

+ 116 - 50
src/contract/money/src/client.rs

@@ -511,6 +511,40 @@ impl StakeLeadMintRevealed {
     }
 }
 
+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))
+}
+
 struct UnstakeLeadBurnRevealed {
     pub value_commit: ValueCommit,
     pub pk: pallas::Base,
@@ -556,40 +590,6 @@ impl UnstakeLeadBurnRevealed {
     }
 }
 
-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,
@@ -1060,9 +1060,6 @@ pub fn build_transfer_tx(
 
 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>,
@@ -1176,28 +1173,24 @@ pub fn build_stake_tx(
 }
 
 pub fn build_unstake_tx(
-    pubkey: &PublicKey,
-    value_send: u64,
-    value_recv: u64,
-    value_blinds: &[ValueBlind],
+    pubkey: &PublicKey, //recepient of owncoin public key
+    token_id_recv: TokenId,
     coins: &[LeadCoin],
-    tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
-    mint_zkbin: &ZkBinary,
+    mint_zkbin: &ZkBinary, // stake own mint binary
     mint_pk: &ProvingKey,
-    burn_zkbin: &ZkBinary,
+    burn_zkbin: &ZkBinary, // unstake lead burn binary
     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 {
+    let owncoins : Vec<OwnCoin>= vec![];
+    let mut params = MoneyUnstakeParams {
         inputs: vec![],
         outputs: vec![],
     };
@@ -1206,11 +1199,84 @@ pub fn build_unstake_tx(
     let mut lead_blinds = vec![];
     for coin in coins.iter() {
         // burn lead coin
+        let value_blind = ValueBlind::random(&mut OsRng);
+        lead_blinds.push(value_blind);
+        let (unstake_proof, unstake_revealed) = create_unstake_burn_proof(
+            burn_zkbin,
+            burn_pk,
+            coin.value,
+            value_blind,
+            coin.coin1_blind,
+            coin.pk(),
+            coin.coin1_sk_root,
+            coin.coin1_sk_pos,
+            coin.coin1_sk_merkle_path,
+            coin.coin1_commitment_merkle_path,
+            coin.coin1_commitment,
+            coin.coin1_commitment_root,
+            coin.coin1_commitment_pos,
+            coin.tau,
+            coin.nonce,
+            coin.sn(),
+        );
+        params.inputs.push(StakedInput{
+            coin.sn(),
+            unstake_revealed.value_commit,
+            poseidon_hash([unstake_revealed.commitment_x,
+                           unstake_revealed.commitment_y
+            ]),
+            unstake_revealed.pk,
+            unstake_revealed.commitment_root,
+            unstake_revealed.sk_root,
+        });
+        proofs.push(unstake_proof);
+        let own_value_blind = ValueBlind::random(&mut OsRng);
+        own_blinds.push(own_value_blind);
         // mint own coin
+        let serial = pallas::Base::random(&mut OsRng);
+        let coin_blind = pallas::Base::random(&mut OsRng);
+        let token_recv_blind = ValueBlind::random(&mut OsRng);
+        // Disable composability for this old obsolete API
+        let spend_hook = pallas::Base::zero();
+        let user_data = pallas::Base::zero();
+        let (proof, revealed) = create_transfer_mint_proof(
+            mint_zkbin,
+            mint_pk,
+            coin.value,
+            token_id_recv,
+            value_recv_blind,
+            token_recv_blind,
+            serial,
+            spend_hook,
+            user_data,
+            coin_blind,
+            pubkey, //receipient public_key
+        )?;
+        proofs.push(proof);
+        // Encrypted note
+        let note = Note {
+            serial,
+            value: coin.value,
+            token_id: token_id_recv,
+            coin_blind,
+            value_blind: value_blind,
+            token_blind: token_recv_blind,
+            // Here we store our secret key we use for signing
+            memo: vec![],
+        };
+
+        let encrypted_note = note.encrypt(&output.public_key)?;
+
+        params.outputs.push(Output {
+            value_commit: revealed.value_commit,
+            token_commit: revealed.token_commit,
+            coin: revealed.coin.inner(),
+            ciphertext: encrypted_note.ciphertext,
+            ephem_public: encrypted_note.ephem_public,
+        });
+
+        Ok((params, proofs, vec![], lead_blinds, own_blinds))
     }
-    // return proofs created
-    // return secret keys
-    // blind values.
 }
 
 fn compute_remainder_blind(

+ 118 - 6
src/contract/money/src/lib.rs

@@ -319,8 +319,51 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
             set_return_data(&metadata)?;
         }
 
-        MoneyFunction::Unstake => unimplemented!(),
-        MoneyFunction::Mint => unimplemented!(),
+
+        MoneyFunction::Unstake => {
+            let params: MoneyUnstakeParams = 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();
+                zk_public_values.push((
+                    MONEY_CONTRACT_ZKAS_LEAD_BURN_NS.to_string(),
+                    vec![
+                        *value_coords.x(),
+                        *value_coords.y(),
+                        input.coin_pk_hash,
+                        input.coin_commit_hash,
+                        input.coin_commit_root,
+                        input.sk_root,
+                        input.nullifier.inner(),
+                    ],
+                ));
+            }
+
+            for output in &params.outputs {
+                let value_coords = output.value_commit.to_affine().coordinates().unwrap();
+                let token_coords = output.token_commit.to_affine().coordinates().unwrap();
+
+                zk_public_values.push((
+                    MONEY_CONTRACT_ZKAS_MINT_NS_V1.to_string(),
+                    vec![
+                        output.coin,
+                        *value_coords.x(),
+                        *value_coords.y(),
+                        *token_coords.x(),
+                        *token_coords.y(),
+                    ],
+                ));
+            }
+            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)?;
+        }
     };
 
     Ok(())
@@ -594,7 +637,78 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
 
         MoneyFunction::Unstake => {
             msg!("[Unstake] Entered match arm");
-            unimplemented!();
+            let params: MoneyUnstakeParams = 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)?;
+            let sk_roots_db = db_lookup(cid, MONEY_CONTRACT_LEAD_SK_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.coin_commit_root))? {
+                    msg!("[Unstake] Error: Merkle root not found in previous state (input {})", i);
+                    return Err(ContractError::Custom(21))
+                }
+
+                //TODO adde sk root to db.
+                /*
+                if !db_contains_key(sk_roots_db, &serialize(&input.sk_root))? {
+                    msg!("[Unstake] Error: sk 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!("[Unstake] 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)) {
+                    msg!("[Unstake] 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!("[UnStake] Error: Value commitments do not result in identity");
+                return Err(ContractError::Custom(24))
+            }
+
+            // Create a state update
+            let update = MoneyUnstakeUpdate { nullifiers: new_nullifiers, coins: new_coins  };
+            let mut update_data = vec![];
+            update_data.write_u8(MoneyFunction::Unstake as u8)?;
+            update.encode(&mut update_data)?;
+            set_return_data(&update_data)?;
+            msg!("[Unstake] State update set!");
+
+            Ok(())
         }
 
         MoneyFunction::Mint => {
@@ -630,7 +744,7 @@ fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
             Ok(())
         }
 
-        MoneyFunction::Stake => {
+        MoneyFunction::Stake | MoneyFunction::Unstake => {
             let update: MoneyStakeUpdate = deserialize(&update_data[1..])?;
 
             let info_db = db_lookup(cid, MONEY_CONTRACT_LEAD_INFO_TREE)?;
@@ -652,7 +766,5 @@ fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
 
             Ok(())
         }
-        MoneyFunction::Unstake => unimplemented!(),
-        MoneyFunction::Mint => unimplemented!(),
     }
 }

+ 10 - 4
src/contract/money/src/state.rs

@@ -48,10 +48,16 @@ pub struct MoneyUnstakeParams {
 pub struct StakedInput {
     /// Revealed nullifier
     pub nullifier: Nullifier,
-    /// Revealed Merkle root
-    pub merkle_root: MerkleNode,
-    /// Public key for the signature
-    pub signature_public: PublicKey,
+    /// Pedersen commitment for the output's value
+    pub value_commit: ValueCommit,
+    /// Minted coin
+    pub coin_commit_hash: pallas::Base,
+    /// coin pk hash
+    pub coin_pk_hash: pallas::Base,
+    /// coin commitment root
+    pub coin_commit_root: MerkleNode,
+    /// sk root of merkle tree
+    pub sk_root: MerkleNode,
 }
 
 /// Staked anonymous output