Przeglądaj źródła

Merge branch 'asset-id'

Nym 5 lat temu
rodzic
commit
d47301ed0f

+ 20 - 3
proofs/mint.pism

@@ -9,7 +9,9 @@ constant CRH_IVK BlakePersonalization
 contract mint_contract
     # Value commitment
     param value U64
+    param asset_id U64
     param randomness_value Fr
+    param randomness_asset Fr
 
     param serial Fr
     param randomness_coin Fr
@@ -17,7 +19,9 @@ contract mint_contract
 start
     # Witness input values
     u64_as_binary_le value param:value
+    u64_as_binary_le asset_id param:asset_id
     fr_as_binary_le randomness_value param:randomness_value
+    fr_as_binary_le randomness_asset param:randomness_asset
     fr_as_binary_le serial param:serial
     fr_as_binary_le randomness_coin param:randomness_coin
 
@@ -33,8 +37,18 @@ start
     # emit cv
     emit_ec cv
 
+    # Make asset_id commitment
+    # A = a * G_VCV + r_a * G_VCR
+
+    ec_mul_const vca asset_id G_VCV
+    ec_mul_const rca randomness_asset G_VCR
+    ec_add ca vca rca
+    # emit ca
+    emit_ec ca
+
+
     # Make the coin
-    # C = Hash(public_key, value, serial, randomness_coin)
+    # C = Hash(public_key, value, asset_id, serial, randomness_coin)
 
     # Build the preimage to hash
     alloc_binary preimage
@@ -46,6 +60,9 @@ start
     # value
     binary_extend preimage value
 
+    # asset_id
+    binary_extend preimage asset_id
+
 # Fr values are 252 bits so we need to pad it with extra 0s
 # to match the Rust values which are 256 bits
 {% macro binary_put_fr(binary, var) -%}
@@ -66,8 +83,8 @@ start
     # Value:            u64             = 64 bits
     # Serial:           Fr              = 252 + 4 bits padding
     # Randomness coin   Fr              = 252 + 4 bits padding
-    # TOTAL: 832 bits for preimage
-    static_assert_binary_size preimage 832
+    # TOTAL: 896 bits for preimage
+    static_assert_binary_size preimage 896
     blake2s coin preimage CRH_IVK
     emit_binary coin
 end

+ 23 - 7
proofs/spend.pism

@@ -4,28 +4,32 @@ constant G_SPEND FixedGenerator
 constant PRF_NF BlakePersonalization
 constant CRH_IVK BlakePersonalization
 constant NOTE_COMMIT PedersenPersonalization
-{% for i in range(4) %}
+{% for i in range(32) %}
     constant MERKLE_{{ i }} PedersenPersonalization
 {% endfor %}
 
 contract spend_contract
     # Value commitment
     param value U64
+    param asset_id U64
     param randomness_value Fr
+    param randomness_asset Fr
 
     param serial Fr
     param randomness_coin Fr
     param secret Fr
     param signature_secret Fr
 
-{% for i in range(4) %}
+{% for i in range(32) %}
     param branch_{{ i }} Scalar
     param is_right_{{ i }} Bool
 {% endfor %}
 start
     # Witness input values
     u64_as_binary_le value param:value
+    u64_as_binary_le asset_id param:asset_id
     fr_as_binary_le randomness_value param:randomness_value
+    fr_as_binary_le randomness_asset param:randomness_asset
 
     # Make value commitment
     # V = v * G_VCV + r * G_VCR
@@ -36,6 +40,15 @@ start
     # emit cv
     emit_ec cv
 
+    # Make asset_id commitment
+    # A = a * G_VCV + r * G_VCR
+
+    ec_mul_const vca asset_id G_VCV
+    ec_mul_const rca randomness_asset G_VCR
+    ec_add ca vca rca
+    # emit ca
+    emit_ec ca
+
     # Make the nullifier
     # N = Hash(secret, serial)
     fr_as_binary_le serial param:serial
@@ -73,7 +86,7 @@ start
     ec_mul_const public secret G_SPEND
 
     # Make the coin (same as mint contract)
-    # C = Hash(public_key, value, serial, randomness_coin)
+    # C = Hash(public_key, value, asset_id, serial, randomness_coin)
     fr_as_binary_le randomness_coin param:randomness_coin
 
     # Build the preimage to hash
@@ -86,6 +99,9 @@ start
     # value
     binary_extend preimage value
 
+    # asset_id
+    binary_extend preimage asset_id
+
     # serial
     {{ binary_put_fr("preimage", "serial") }}
 
@@ -96,8 +112,8 @@ start
     # Value:            u64             = 64 bits
     # Serial:           Fr              = 252 + 4 bits padding
     # Randomness coin   Fr              = 252 + 4 bits padding
-    # TOTAL: 832 bits for preimage
-    static_assert_binary_size preimage 832
+    # TOTAL: 896 bits for preimage
+    static_assert_binary_size preimage 896
     blake2s coin preimage CRH_IVK
     # Debug stuff. Normally we don't reveal the coin in the spend proof.
     #binary_clone coin2 coin
@@ -108,8 +124,8 @@ start
     # left = coin_commit.u
     ec_get_u current cm
 
-    # Our merkle tree has a height of 4
-{% for i in range(4) %}
+    # Our merkle tree has a height of 32
+{% for i in range(32) %}
     # left = current
     # right = branch[{{ i }}]
     alloc_scalar branch param:branch_{{ i }}

+ 1 - 0
res/schema.sql

@@ -10,6 +10,7 @@ CREATE TABLE IF NOT EXISTS coins(
     coin BLOB NOT NULL,
     serial BLOB NOT NULL,
     value INT NOT NULL,
+    asset_id INT NOT NULL,
     coin_blind BLOB NOT NULL,
     valcom_blind BLOB NOT NULL,
     tree BLOB NOT NULL,

+ 4 - 0
src/bin/mint-classic.rs

@@ -11,7 +11,9 @@ fn main() {
     let public = jubjub::SubgroupPoint::random(&mut OsRng);
 
     let value = 110;
+    let asset_id = 1;
     let randomness_value: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
+    let randomness_asset: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
 
     let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let randomness_coin: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
@@ -25,7 +27,9 @@ fn main() {
     let (proof, revealed) = create_mint_proof(
         &params,
         value,
+        asset_id,
         randomness_value,
+        randomness_asset,
         serial,
         randomness_coin,
         public,

+ 6 - 0
src/bin/spend-classic.rs

@@ -57,6 +57,7 @@ struct SpendRevealedValues {
 impl SpendRevealedValues {
     fn compute(
         value: u64,
+        asset_id: u64,
         randomness_value: &jubjub::Fr,
         serial: &jubjub::Fr,
         randomness_coin: &jubjub::Fr,
@@ -90,6 +91,7 @@ impl SpendRevealedValues {
                 .to_state()
                 .update(&public.to_bytes())
                 .update(&value.to_le_bytes())
+                .update(&asset_id.to_le_bytes())
                 .update(&serial.to_bytes())
                 .update(&randomness_coin.to_bytes())
                 .finalize()
@@ -170,7 +172,9 @@ fn main() {
     use rand::rngs::OsRng;
 
     let value = 110;
+    let asset_id = 1;
     let randomness_value: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
+    let randomness_asset: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
 
     let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let randomness_coin: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
@@ -195,7 +199,9 @@ fn main() {
     let (proof, revealed) = create_spend_proof(
         &params,
         value,
+        asset_id,
         randomness_value,
+        randomness_asset,
         serial,
         randomness_coin,
         secret,

+ 3 - 1
src/bin/tx.rs

@@ -158,10 +158,11 @@ fn main() {
     let builder = tx::TransactionBuilder {
         clear_inputs: vec![tx::TransactionBuilderClearInputInfo {
             value: 110,
+            asset_id: 1,
             signature_secret: cashier_secret,
         }],
         inputs: vec![],
-        outputs: vec![tx::TransactionBuilderOutputInfo { value: 110, public }],
+        outputs: vec![tx::TransactionBuilderOutputInfo { value: 110, asset_id: 1, public }],
     };
 
     // We will 'compile' the tx, and then serialize it to this Vec<u8>
@@ -276,6 +277,7 @@ fn main() {
         // The only constraint is that sum(value in) == sum(value out)
         outputs: vec![tx::TransactionBuilderOutputInfo {
             value: 110,
+            asset_id: 1,
             public: public2,
         }],
     };

+ 40 - 2
src/circuit/mint_contract.rs

@@ -15,7 +15,9 @@ use zcash_proofs::circuit::{ecc, pedersen_hash};
 
 pub struct MintContract {
     pub value: Option<u64>,
+    pub asset_id: Option<u64>,
     pub randomness_value: Option<jubjub::Fr>,
+    pub randomness_asset: Option<jubjub::Fr>,
     pub serial: Option<jubjub::Fr>,
     pub randomness_coin: Option<jubjub::Fr>,
     pub public: Option<jubjub::SubgroupPoint>,
@@ -31,12 +33,24 @@ impl Circuit<bls12_381::Scalar> for MintContract {
             self.value,
         )?;
 
+        // Line 19: u64_as_binary_le asset_id param:asset_id
+        let asset_id = boolean::u64_into_boolean_vec_le(
+            cs.namespace(|| "Line 19: u64_as_binary_le asset_id param:asset_id"),
+            self.asset_id,
+        )?;
+
         // Line 19: fr_as_binary_le randomness_value param:randomness_value
         let randomness_value = boolean::field_into_boolean_vec_le(
             cs.namespace(|| "Line 19: fr_as_binary_le randomness_value param:randomness_value"),
             self.randomness_value,
         )?;
 
+        // Line 19: fr_as_binary_le randomness_asset param:randomness_asset
+        let randomness_asset = boolean::field_into_boolean_vec_le(
+            cs.namespace(|| "Line 19: fr_as_binary_le randomness_asset param:randomness_asset"),
+            self.randomness_asset,
+        )?;
+
         // Line 20: fr_as_binary_le serial param:serial
         let serial = boolean::field_into_boolean_vec_le(
             cs.namespace(|| "Line 20: fr_as_binary_le serial param:serial"),
@@ -78,6 +92,27 @@ impl Circuit<bls12_381::Scalar> for MintContract {
         // Line 33: emit_ec cv
         cv.inputize(cs.namespace(|| "Line 33: emit_ec cv"))?;
 
+        // Line 29: ec_mul_const vca asset_id G_VCV
+        let vca = ecc::fixed_base_multiplication(
+            cs.namespace(|| "Line 29: ec_mul_const vca asset_id G_VCV"),
+            &zcash_proofs::constants::VALUE_COMMITMENT_VALUE_GENERATOR,
+            &asset_id,
+        )?;
+
+        // Line 47: ec_mul_const rca randomness_asset G_VCR
+        let rca = ecc::fixed_base_multiplication(
+            cs.namespace(|| "Line 47: ec_mul_const rca randomness_asset G_VCR"),
+            &zcash_proofs::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
+            &randomness_asset,
+        )?;
+
+        // Line 48: ec_add ca vca rca
+        let ca = vca.add(cs.namespace(|| "Line 48: ec_add ca vca rca"), &rca)?;
+
+        // Line 50: emit_ec ca
+        ca.inputize(cs.namespace(|| "Line 50: emit_ec ca"))?;
+
+
         // Line 39: alloc_binary preimage
         let mut preimage = vec![];
 
@@ -90,6 +125,9 @@ impl Circuit<bls12_381::Scalar> for MintContract {
         // Line 46: binary_extend preimage value
         preimage.extend(value);
 
+        // Line 47: binary_extend preimage asset_id
+        preimage.extend(asset_id);
+
         // Line 53: binary_extend preimage serial
         preimage.extend(serial);
 
@@ -112,8 +150,8 @@ impl Circuit<bls12_381::Scalar> for MintContract {
             preimage.push(zero_bit);
         }
 
-        // Line 89: static_assert_binary_size preimage 832
-        assert_eq!(preimage.len(), 832);
+        // Line 89: static_assert_binary_size preimage 896
+        assert_eq!(preimage.len(), 896);
 
         // Line 90: blake2s coin preimage CRH_IVK
         let mut coin = blake2s::blake2s(

+ 40 - 2
src/circuit/spend_contract.rs

@@ -17,7 +17,9 @@ use crate::crypto::merkle_node::SAPLING_COMMITMENT_TREE_DEPTH;
 
 pub struct SpendContract {
     pub value: Option<u64>,
+    pub asset_id: Option<u64>,
     pub randomness_value: Option<jubjub::Fr>,
+    pub randomness_asset: Option<jubjub::Fr>,
     pub serial: Option<jubjub::Fr>,
     pub randomness_coin: Option<jubjub::Fr>,
     pub secret: Option<jubjub::Fr>,
@@ -36,12 +38,24 @@ impl Circuit<bls12_381::Scalar> for SpendContract {
             self.value,
         )?;
 
+        // Line 41: u64_as_binary_le asset_id param:asset_id
+        let asset_id = boolean::u64_into_boolean_vec_le(
+            cs.namespace(|| "Line 41: u64_as_binary_le value param:value"),
+            self.asset_id,
+        )?;
+
         // Line 41: fr_as_binary_le randomness_value param:randomness_value
         let randomness_value = boolean::field_into_boolean_vec_le(
             cs.namespace(|| "Line 41: fr_as_binary_le randomness_value param:randomness_value"),
             self.randomness_value,
         )?;
 
+        // Line 41: fr_as_binary_le randomness_asset param:randomness_asset
+        let randomness_asset = boolean::field_into_boolean_vec_le(
+            cs.namespace(|| "Line 41: fr_as_binary_le randomness_asset param:randomness_asset"),
+            self.randomness_asset,
+        )?;
+
         // Line 46: ec_mul_const vcv value G_VCV
         let vcv = ecc::fixed_base_multiplication(
             cs.namespace(|| "Line 46: ec_mul_const vcv value G_VCV"),
@@ -62,6 +76,27 @@ impl Circuit<bls12_381::Scalar> for SpendContract {
         // Line 50: emit_ec cv
         cv.inputize(cs.namespace(|| "Line 50: emit_ec cv"))?;
 
+        // Line 46: ec_mul_const vca asset_id G_VCV
+        let vca = ecc::fixed_base_multiplication(
+            cs.namespace(|| "Line 46: ec_mul_const vca asset_id G_VCV"),
+            &zcash_proofs::constants::VALUE_COMMITMENT_VALUE_GENERATOR,
+            &asset_id,
+        )?;
+
+        // Line 47: ec_mul_const rca randomness_asset G_VCR
+        let rca = ecc::fixed_base_multiplication(
+            cs.namespace(|| "Line 47: ec_mul_const rca randomness_asset G_VCR"),
+            &zcash_proofs::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
+            &randomness_asset,
+        )?;
+
+        // Line 48: ec_add ca vca rca
+        let ca = vca.add(cs.namespace(|| "Line 48: ec_add ca vca rca"), &rca)?;
+
+        // Line 50: emit_ec ca
+        ca.inputize(cs.namespace(|| "Line 50: emit_ec ca"))?;
+
+
         // Line 54: fr_as_binary_le serial param:serial
         let serial = boolean::field_into_boolean_vec_le(
             cs.namespace(|| "Line 54: fr_as_binary_le serial param:serial"),
@@ -175,6 +210,9 @@ impl Circuit<bls12_381::Scalar> for SpendContract {
         // Line 120: binary_extend preimage value
         preimage.extend(value);
 
+        // Line 121: binary_extend preimage asset_id
+        preimage.extend(asset_id);
+
         // Line 123: binary_extend preimage serial
         preimage.extend(serial);
 
@@ -229,8 +267,8 @@ impl Circuit<bls12_381::Scalar> for SpendContract {
         // Line 151: binary_push preimage zero_bit
         preimage.push(zero_bit);
 
-        // Line 159: static_assert_binary_size preimage 832
-        assert_eq!(preimage.len(), 832);
+        // Line 159: static_assert_binary_size preimage 896
+        assert_eq!(preimage.len(), 896);
 
         // Line 160: blake2s coin preimage CRH_IVK
         let mut coin = blake2s::blake2s(

+ 1 - 1
src/crypto/merkle_node.rs

@@ -9,7 +9,7 @@ use crate::impl_vec;
 use crate::serial::{Decodable, Encodable, VarInt};
 use crate::{Error, Result};
 
-pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 6;
+pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
 
 /// Compute a parent node in the Sapling commitment tree given its two children.
 pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> bls12_381::Scalar {

+ 32 - 6
src/crypto/mint_proof.rs

@@ -13,13 +13,16 @@ use crate::serial::{Decodable, Encodable};
 
 pub struct MintRevealedValues {
     pub value_commit: jubjub::SubgroupPoint,
+    pub asset_commit: jubjub::SubgroupPoint,
     pub coin: [u8; 32],
 }
 
 impl MintRevealedValues {
     fn compute(
         value: u64,
+        asset_id: u64,
         randomness_value: &jubjub::Fr,
+        randomness_asset: &jubjub::Fr,
         serial: &jubjub::Fr,
         randomness_coin: &jubjub::Fr,
         public: &jubjub::SubgroupPoint,
@@ -29,6 +32,11 @@ impl MintRevealedValues {
             + (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR
                 * randomness_value);
 
+        let asset_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
+            * jubjub::Fr::from(asset_id))
+            + (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR
+                * randomness_asset);
+
         let mut coin = [0; 32];
         coin.copy_from_slice(
             Blake2sParams::new()
@@ -37,17 +45,18 @@ impl MintRevealedValues {
                 .to_state()
                 .update(&public.to_bytes())
                 .update(&value.to_le_bytes())
+                .update(&asset_id.to_le_bytes())
                 .update(&serial.to_bytes())
                 .update(&randomness_coin.to_bytes())
                 .finalize()
                 .as_bytes(),
         );
 
-        MintRevealedValues { value_commit, coin }
+        MintRevealedValues { value_commit, asset_commit, coin }
     }
 
-    fn make_outputs(&self) -> [bls12_381::Scalar; 4] {
-        let mut public_input = [bls12_381::Scalar::zero(); 4];
+    fn make_outputs(&self) -> [bls12_381::Scalar; 6] {
+        let mut public_input = [bls12_381::Scalar::zero(); 6];
 
         {
             let result = jubjub::ExtendedPoint::from(self.value_commit);
@@ -59,6 +68,15 @@ impl MintRevealedValues {
             public_input[1] = v;
         }
 
+        {
+            let result = jubjub::ExtendedPoint::from(self.asset_commit);
+            let affine = result.to_affine();
+            let u = affine.get_u();
+            let v = affine.get_v();
+            public_input[2] = u;
+            public_input[3] = v;
+        }
+
         {
             // Pack the hash as inputs for proof verification.
             let hash = multipack::bytes_to_bits_le(&self.coin);
@@ -67,8 +85,8 @@ impl MintRevealedValues {
             // There are 2 chunks for a blake hash
             assert_eq!(hash.len(), 2);
 
-            public_input[2] = hash[0];
-            public_input[3] = hash[1];
+            public_input[4] = hash[0];
+            public_input[5] = hash[1];
         }
 
         public_input
@@ -79,6 +97,7 @@ impl Encodable for MintRevealedValues {
     fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
         let mut len = 0;
         len += self.value_commit.encode(&mut s)?;
+        len += self.asset_commit.encode(&mut s)?;
         len += self.coin.encode(&mut s)?;
         Ok(len)
     }
@@ -88,6 +107,7 @@ impl Decodable for MintRevealedValues {
     fn decode<D: io::Read>(mut d: D) -> Result<Self> {
         Ok(Self {
             value_commit: Decodable::decode(&mut d)?,
+            asset_commit: Decodable::decode(&mut d)?,
             coin: Decodable::decode(d)?,
         })
     }
@@ -99,7 +119,9 @@ pub fn setup_mint_prover() -> groth16::Parameters<Bls12> {
     let params = {
         let c = MintContract {
             value: None,
+            asset_id: None,
             randomness_value: None,
+            randomness_asset: None,
             serial: None,
             randomness_coin: None,
             public: None,
@@ -113,17 +135,21 @@ pub fn setup_mint_prover() -> groth16::Parameters<Bls12> {
 pub fn create_mint_proof(
     params: &groth16::Parameters<Bls12>,
     value: u64,
+    asset_id: u64,
     randomness_value: jubjub::Fr,
+    randomness_asset: jubjub::Fr,
     serial: jubjub::Fr,
     randomness_coin: jubjub::Fr,
     public: jubjub::SubgroupPoint,
 ) -> (groth16::Proof<Bls12>, MintRevealedValues) {
     let revealed =
-        MintRevealedValues::compute(value, &randomness_value, &serial, &randomness_coin, &public);
+        MintRevealedValues::compute(value, asset_id, &randomness_value, &randomness_asset, &serial, &randomness_coin, &public);
 
     let c = MintContract {
         value: Some(value),
+        asset_id: Some(asset_id),
         randomness_value: Some(randomness_value),
+        randomness_asset: Some(randomness_asset),
         serial: Some(serial),
         randomness_coin: Some(randomness_coin),
         public: Some(public),

+ 6 - 0
src/crypto/note.rs

@@ -9,6 +9,7 @@ use crate::serial::{Decodable, Encodable, ReadExt, WriteExt};
 
 pub const NOTE_PLAINTEXT_SIZE: usize = 32 + // serial
     8 + // value
+    8 + // asset_id
     32 + // coin_blind
     32; // valcom_blind
 pub const AEAD_TAG_SIZE: usize = 16;
@@ -18,6 +19,7 @@ pub const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE;
 pub struct Note {
     pub serial: jubjub::Fr,
     pub value: u64,
+    pub asset_id: u64,
     pub coin_blind: jubjub::Fr,
     pub valcom_blind: jubjub::Fr,
 }
@@ -27,6 +29,7 @@ impl Encodable for Note {
         let mut len = 0;
         len += self.serial.encode(&mut s)?;
         len += self.value.encode(&mut s)?;
+        len += self.asset_id.encode(&mut s)?;
         len += self.coin_blind.encode(&mut s)?;
         len += self.valcom_blind.encode(&mut s)?;
         Ok(len)
@@ -38,6 +41,7 @@ impl Decodable for Note {
         Ok(Self {
             serial: Decodable::decode(&mut d)?,
             value: Decodable::decode(&mut d)?,
+            asset_id: Decodable::decode(&mut d)?,
             coin_blind: Decodable::decode(&mut d)?,
             valcom_blind: Decodable::decode(d)?,
         })
@@ -123,6 +127,7 @@ fn test_note_encdec() {
     let note = Note {
         serial: jubjub::Fr::random(&mut OsRng),
         value: 110,
+        asset_id: 1,
         coin_blind: jubjub::Fr::random(&mut OsRng),
         valcom_blind: jubjub::Fr::random(&mut OsRng),
     };
@@ -133,4 +138,5 @@ fn test_note_encdec() {
     let encrypted_note = note.encrypt(&public).unwrap();
     let note2 = encrypted_note.decrypt(&secret).unwrap();
     assert_eq!(note.value, note2.value);
+    assert_eq!(note.asset_id, note2.asset_id);
 }

+ 38 - 7
src/crypto/spend_proof.rs

@@ -16,6 +16,7 @@ use crate::serial::{Decodable, Encodable};
 
 pub struct SpendRevealedValues {
     pub value_commit: jubjub::SubgroupPoint,
+    pub asset_commit: jubjub::SubgroupPoint,
     pub nullifier: Nullifier,
     // This should not be here, we just have it for debugging
     //coin: [u8; 32],
@@ -26,7 +27,9 @@ pub struct SpendRevealedValues {
 impl SpendRevealedValues {
     fn compute(
         value: u64,
+        asset_id: u64,
         randomness_value: &jubjub::Fr,
+        randomness_asset: &jubjub::Fr,
         serial: &jubjub::Fr,
         randomness_coin: &jubjub::Fr,
         secret: &jubjub::Fr,
@@ -38,6 +41,11 @@ impl SpendRevealedValues {
             + (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR
                 * randomness_value);
 
+        let asset_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
+            * jubjub::Fr::from(asset_id))
+            + (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR
+                * randomness_asset);
+
         let mut nullifier = [0; 32];
         nullifier.copy_from_slice(
             Blake2sParams::new()
@@ -63,6 +71,7 @@ impl SpendRevealedValues {
                 .to_state()
                 .update(&public.to_bytes())
                 .update(&value.to_le_bytes())
+                .update(&asset_id.to_le_bytes())
                 .update(&serial.to_bytes())
                 .update(&randomness_coin.to_bytes())
                 .finalize()
@@ -89,14 +98,15 @@ impl SpendRevealedValues {
 
         SpendRevealedValues {
             value_commit,
+            asset_commit,
             nullifier,
             merkle_root,
             signature_public,
         }
     }
 
-    fn make_outputs(&self) -> [bls12_381::Scalar; 7] {
-        let mut public_input = [bls12_381::Scalar::zero(); 7];
+    fn make_outputs(&self) -> [bls12_381::Scalar; 9] {
+        let mut public_input = [bls12_381::Scalar::zero(); 9];
 
         // CV
         {
@@ -109,6 +119,17 @@ impl SpendRevealedValues {
             public_input[1] = v;
         }
 
+        // CA
+        {
+            let result = jubjub::ExtendedPoint::from(self.asset_commit);
+            let affine = result.to_affine();
+            //let (u, v) = (affine.get_u(), affine.get_v());
+            let u = affine.get_u();
+            let v = affine.get_v();
+            public_input[2] = u;
+            public_input[3] = v;
+        }
+
         // NF
         {
             // Pack the hash as inputs for proof verification.
@@ -118,8 +139,8 @@ impl SpendRevealedValues {
             // There are 2 chunks for a blake hash
             assert_eq!(hash.len(), 2);
 
-            public_input[2] = hash[0];
-            public_input[3] = hash[1];
+            public_input[4] = hash[0];
+            public_input[5] = hash[1];
         }
 
         // Not revealed. We leave this code here for debug
@@ -136,7 +157,7 @@ impl SpendRevealedValues {
             public_input[5] = hash[1];
         }*/
 
-        public_input[4] = self.merkle_root.into();
+        public_input[6] = self.merkle_root.into();
 
         {
             let result = jubjub::ExtendedPoint::from(self.signature_public);
@@ -144,8 +165,8 @@ impl SpendRevealedValues {
             //let (u, v) = (affine.get_u(), affine.get_v());
             let u = affine.get_u();
             let v = affine.get_v();
-            public_input[5] = u;
-            public_input[6] = v;
+            public_input[7] = u;
+            public_input[8] = v;
         }
 
         public_input
@@ -156,6 +177,7 @@ impl Encodable for SpendRevealedValues {
     fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
         let mut len = 0;
         len += self.value_commit.encode(&mut s)?;
+        len += self.asset_commit.encode(&mut s)?;
         len += self.nullifier.encode(&mut s)?;
         len += self.merkle_root.encode(&mut s)?;
         len += self.signature_public.encode(s)?;
@@ -167,6 +189,7 @@ impl Decodable for SpendRevealedValues {
     fn decode<D: io::Read>(mut d: D) -> Result<Self> {
         Ok(Self {
             value_commit: Decodable::decode(&mut d)?,
+            asset_commit: Decodable::decode(&mut d)?,
             nullifier: Decodable::decode(&mut d)?,
             merkle_root: Decodable::decode(&mut d)?,
             signature_public: Decodable::decode(d)?,
@@ -180,7 +203,9 @@ pub fn setup_spend_prover() -> groth16::Parameters<Bls12> {
     let params = {
         let c = SpendContract {
             value: None,
+            asset_id: None,
             randomness_value: None,
+            randomness_asset: None,
             serial: None,
             randomness_coin: None,
             secret: None,
@@ -199,7 +224,9 @@ pub fn setup_spend_prover() -> groth16::Parameters<Bls12> {
 pub fn create_spend_proof(
     params: &groth16::Parameters<Bls12>,
     value: u64,
+    asset_id: u64,
     randomness_value: jubjub::Fr,
+    randomness_asset: jubjub::Fr,
     serial: jubjub::Fr,
     randomness_coin: jubjub::Fr,
     secret: jubjub::Fr,
@@ -215,7 +242,9 @@ pub fn create_spend_proof(
     }
     let c = SpendContract {
         value: Some(value),
+        asset_id: Some(asset_id),
         randomness_value: Some(randomness_value),
+        randomness_asset: Some(randomness_asset),
         serial: Some(serial),
         randomness_coin: Some(randomness_coin),
         secret: Some(secret),
@@ -232,7 +261,9 @@ pub fn create_spend_proof(
 
     let revealed = SpendRevealedValues::compute(
         value,
+        asset_id,
         &randomness_value,
+        &randomness_asset,
         &serial,
         &randomness_coin,
         &secret,

+ 4 - 0
src/state.rs

@@ -34,6 +34,7 @@ pub enum VerifyFailed {
     ClearInputSignature(usize),
     InputSignature(usize),
     MissingFunds,
+    AssetMismatch,
 }
 
 impl std::error::Error for VerifyFailed {}
@@ -59,6 +60,9 @@ impl fmt::Display for VerifyFailed {
             VerifyFailed::MissingFunds => {
                 f.write_str("Money in does not match money out (value commits)")
             }
+            VerifyFailed::AssetMismatch => {
+                f.write_str("Assets don't match some inputs or outputs (asset commits)")
+            }
         }
     }
 }

+ 11 - 0
src/tx/builder.rs

@@ -21,6 +21,7 @@ pub struct TransactionBuilder {
 
 pub struct TransactionBuilderClearInputInfo {
     pub value: u64,
+    pub asset_id: u64,
     pub signature_secret: jubjub::Fr,
 }
 
@@ -32,6 +33,7 @@ pub struct TransactionBuilderInputInfo {
 
 pub struct TransactionBuilderOutputInfo {
     pub value: u64,
+    pub asset_id: u64,
     pub public: jubjub::SubgroupPoint,
 }
 
@@ -64,6 +66,7 @@ impl TransactionBuilder {
         spend_params: &groth16::Parameters<Bls12>,
     ) -> Transaction {
         let mut clear_inputs = vec![];
+        let asset_commit_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
         for input in &self.clear_inputs {
             let signature_public =
                 zcash_primitives::constants::SPENDING_KEY_GENERATOR * input.signature_secret;
@@ -71,7 +74,9 @@ impl TransactionBuilder {
             let valcom_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
             let clear_input = PartialTransactionClearInput {
                 value: input.value,
+                asset_id: input.asset_id,
                 valcom_blind,
+                asset_commit_blind,
                 signature_public,
             };
             clear_inputs.push(clear_input);
@@ -98,7 +103,9 @@ impl TransactionBuilder {
             let (proof, revealed) = create_spend_proof(
                 &spend_params,
                 input.note.value,
+                input.note.asset_id,
                 input.note.valcom_blind,
+                asset_commit_blind,
                 input.note.serial,
                 input.note.coin_blind,
                 input.secret,
@@ -119,6 +126,7 @@ impl TransactionBuilder {
 
         let mut outputs = vec![];
         let mut output_blinds = vec![];
+
         for (i, output) in self.outputs.iter().enumerate() {
             let valcom_blind = if i == self.outputs.len() - 1 {
                 Self::compute_remainder_blind(&clear_inputs, &input_blinds, &output_blinds)
@@ -133,7 +141,9 @@ impl TransactionBuilder {
             let (mint_proof, revealed) = create_mint_proof(
                 mint_params,
                 output.value,
+                output.asset_id,
                 valcom_blind.clone(),
+                asset_commit_blind.clone(),
                 serial.clone(),
                 coin_blind.clone(),
                 output.public.clone(),
@@ -144,6 +154,7 @@ impl TransactionBuilder {
             let note = Note {
                 serial,
                 value: output.value,
+                asset_id: output.asset_id,
                 coin_blind,
                 valcom_blind,
             };

+ 27 - 2
src/tx/mod.rs

@@ -29,7 +29,9 @@ pub struct Transaction {
 
 pub struct TransactionClearInput {
     pub value: u64,
+    pub asset_id: u64,
     pub valcom_blind: jubjub::Fr,
+    pub asset_commit_blind: jubjub::Fr,
     pub signature_public: jubjub::SubgroupPoint,
     pub signature: schnorr::Signature,
 }
@@ -55,13 +57,23 @@ impl Transaction {
         Ok(len)
     }
 
-    fn compute_value_commit(value: u64, blind: &jubjub::Fr) -> jubjub::SubgroupPoint {
+    fn compute_pedersen_commit(value: u64, blind: &jubjub::Fr) -> jubjub::SubgroupPoint {
         let value_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
             * jubjub::Fr::from(value))
             + (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * blind);
         value_commit
     }
 
+    fn verify_asset_commitments(&self) -> bool {
+        assert_ne!(self.outputs.len(), 0);
+        let asset_commit_value = self.outputs[0].revealed.asset_commit;
+
+        let mut failed = self.inputs.iter().any(|input| input.revealed.asset_commit != asset_commit_value);
+        failed = failed || self.outputs.iter().any(|output| output.revealed.asset_commit != asset_commit_value);
+        failed = failed || self.clear_inputs.iter().any(|input| Self::compute_pedersen_commit(input.asset_id, &input.asset_commit_blind) != asset_commit_value);
+        !failed
+    }
+
     pub fn verify(
         &self,
         mint_pvk: &groth16::PreparedVerifyingKey<Bls12>,
@@ -69,7 +81,7 @@ impl Transaction {
     ) -> state::VerifyResult<()> {
         let mut valcom_total = jubjub::SubgroupPoint::identity();
         for input in &self.clear_inputs {
-            valcom_total += Self::compute_value_commit(input.value, &input.valcom_blind);
+            valcom_total += Self::compute_pedersen_commit(input.value, &input.valcom_blind);
         }
         for (i, input) in self.inputs.iter().enumerate() {
             if !verify_spend_proof(spend_pvk, &input.spend_proof, &input.revealed) {
@@ -88,6 +100,11 @@ impl Transaction {
             return Err(state::VerifyFailed::MissingFunds);
         }
 
+        // Verify asset commitments match
+        if !self.verify_asset_commitments() {
+            return Err(state::VerifyFailed::AssetMismatch);
+        }
+
         // Verify signatures
         let mut unsigned_tx_data = vec![];
         self.encode_without_signature(&mut unsigned_tx_data)
@@ -113,7 +130,9 @@ impl TransactionClearInput {
     fn from_partial(partial: PartialTransactionClearInput, signature: schnorr::Signature) -> Self {
         Self {
             value: partial.value,
+            asset_id: partial.asset_id,
             valcom_blind: partial.valcom_blind,
+            asset_commit_blind: partial.asset_commit_blind,
             signature_public: partial.signature_public,
             signature,
         }
@@ -122,7 +141,9 @@ impl TransactionClearInput {
     fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
         let mut len = 0;
         len += self.value.encode(&mut s)?;
+        len += self.asset_id.encode(&mut s)?;
         len += self.valcom_blind.encode(&mut s)?;
+        len += self.asset_commit_blind.encode(&mut s)?;
         len += self.signature_public.encode(s)?;
         Ok(len)
     }
@@ -169,7 +190,9 @@ impl Encodable for TransactionClearInput {
     fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
         let mut len = 0;
         len += self.value.encode(&mut s)?;
+        len += self.asset_id.encode(&mut s)?;
         len += self.valcom_blind.encode(&mut s)?;
+        len += self.asset_commit_blind.encode(&mut s)?;
         len += self.signature_public.encode(&mut s)?;
         len += self.signature.encode(s)?;
         Ok(len)
@@ -180,7 +203,9 @@ impl Decodable for TransactionClearInput {
     fn decode<D: io::Read>(mut d: D) -> Result<Self> {
         Ok(Self {
             value: Decodable::decode(&mut d)?,
+            asset_id: Decodable::decode(&mut d)?,
             valcom_blind: Decodable::decode(&mut d)?,
+            asset_commit_blind: Decodable::decode(&mut d)?,
             signature_public: Decodable::decode(&mut d)?,
             signature: Decodable::decode(d)?,
         })

+ 6 - 0
src/tx/partial.rs

@@ -16,7 +16,9 @@ pub struct PartialTransaction {
 
 pub struct PartialTransactionClearInput {
     pub value: u64,
+    pub asset_id: u64,
     pub valcom_blind: jubjub::Fr,
+    pub asset_commit_blind: jubjub::Fr,
     pub signature_public: jubjub::SubgroupPoint,
 }
 
@@ -49,7 +51,9 @@ impl Encodable for PartialTransactionClearInput {
     fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
         let mut len = 0;
         len += self.value.encode(&mut s)?;
+        len += self.asset_id.encode(&mut s)?;
         len += self.valcom_blind.encode(&mut s)?;
+        len += self.asset_commit_blind.encode(&mut s)?;
         len += self.signature_public.encode(&mut s)?;
         Ok(len)
     }
@@ -58,7 +62,9 @@ impl Decodable for PartialTransactionClearInput {
     fn decode<D: io::Read>(mut d: D) -> Result<Self> {
         Ok(Self {
             value: Decodable::decode(&mut d)?,
+            asset_id: Decodable::decode(&mut d)?,
             valcom_blind: Decodable::decode(&mut d)?,
+            asset_commit_blind: Decodable::decode(&mut d)?,
             signature_public: Decodable::decode(&mut d)?,
         })
     }

+ 4 - 2
src/wallet/walletdb.rs

@@ -52,15 +52,17 @@ impl WalletDB {
         let coin_blind = self.get_value_serialized(&note.coin_blind).await?;
         let valcom_blind = self.get_value_serialized(&note.valcom_blind).await?;
         let value = self.get_value_serialized(&note.value).await?;
+        let asset_id = self.get_value_serialized(&note.asset_id).await?;
         let conn = Connection::open(&self.path)?;
         let witness = self.get_value_serialized(&self.own_coins[0].3).await?;
         conn.execute(
-            "INSERT INTO coins(coin, serial, value, coin_blind, valcom_blind, witness, key_id)
-            VALUES (NULL, :coin, :serial, :value, :coin_blind, :valcom_blind, :witness, :key_id)",
+            "INSERT INTO coins(coin, serial, value, asset_id, coin_blind, valcom_blind, witness, key_id)
+            VALUES (NULL, :coin, :serial, :value, :asset_id, :coin_blind, :valcom_blind, :witness, :key_id)",
             named_params! {
             ":coin": coin,
             ":serial": serial,
             ":value": value,
+            ":asset_id": asset_id,
             ":coin_blind": coin_blind,
             ":valcom_blind": valcom_blind,
             ":witness": witness,