Преглед изворни кода

daod/ example_contract: create working example to illustrate smart contract architecture

ihateface пре 4 година
родитељ
комит
55c8d9adf5

+ 0 - 2
bin/daod/src/dao_contract/state.rs

@@ -118,8 +118,6 @@ impl State {
         self.proposal_votes.get_mut(&HashableBase(proposal_bulla))
         self.proposal_votes.get_mut(&HashableBase(proposal_bulla))
     }
     }
 
 
-    //pub fn add_proposal_vote(&mut self,
-
     pub fn is_valid_dao_merkle(&self, root: &MerkleNode) -> bool {
     pub fn is_valid_dao_merkle(&self, root: &MerkleNode) -> bool {
         self.dao_roots.iter().any(|m| m == root)
         self.dao_roots.iter().any(|m| m == root)
     }
     }

+ 43 - 0
bin/daod/src/demo.rs

@@ -211,7 +211,49 @@ impl StateRegistry {
     }
     }
 }
 }
 
 
+///////////////////////////////////////////////////
+///// Example contract
+///////////////////////////////////////////////////
+pub async fn example() -> Result<()> {
+    debug!(target: "demo", "Stage 0. Example contract");
+    // Lookup table for smart contract states
+    let mut states = StateRegistry::new();
+
+    // Initialize ZK binary table
+    let mut zk_bins = ZkContractTable::new();
+
+    let zk_example_foo_bincode = include_bytes!("../proof/foo.zk.bin");
+    let zk_example_foo_bin = ZkBinary::decode(zk_example_foo_bincode)?;
+    zk_bins.add_contract("example-foo".to_string(), zk_example_foo_bin, 13);
+
+    let example_state = example_contract::state::State::new();
+    states.register("EXAMPLE".to_string(), example_state);
+
+    let foo = example_contract::foo::wallet::Foo { a: 5, b: 10 };
+
+    let builder = example_contract::foo::wallet::Builder { foo };
+    let func_call = builder.build(&zk_bins);
+    let tx = Transaction { func_calls: vec![func_call] };
+
+    for (idx, func_call) in tx.func_calls.iter().enumerate() {
+        if func_call.func_id == "EXAMPLE::foo()" {
+            debug!("example_contract::foo::state_transition()");
+
+            let update = example_contract::foo::validate::state_transition(&states, idx, &tx)
+                .expect("example_contract::foo::validate::state_transition() failed!");
+            example_contract::foo::validate::apply(&mut states, update);
+        }
+    }
+
+    tx.zk_verify(&zk_bins);
+
+    Ok(())
+}
 pub async fn demo() -> Result<()> {
 pub async fn demo() -> Result<()> {
+    // Example smart contract
+    //// TODO: this will be moved to a different file
+    example().await?;
+
     // Money parameters
     // Money parameters
     let xdrk_supply = 1_000_000;
     let xdrk_supply = 1_000_000;
     let xdrk_token_id = pallas::Base::random(&mut OsRng);
     let xdrk_token_id = pallas::Base::random(&mut OsRng);
@@ -230,6 +272,7 @@ pub async fn demo() -> Result<()> {
 
 
     // Initialize ZK binary table
     // Initialize ZK binary table
     let mut zk_bins = ZkContractTable::new();
     let mut zk_bins = ZkContractTable::new();
+
     debug!(target: "demo", "Loading dao-mint.zk");
     debug!(target: "demo", "Loading dao-mint.zk");
     let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
     let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
     let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
     let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;

+ 0 - 1
bin/daod/src/example_contract/foo/mod.rs

@@ -1,3 +1,2 @@
-#![allow(unused)]
 pub mod validate;
 pub mod validate;
 pub mod wallet;
 pub mod wallet;

+ 48 - 9
bin/daod/src/example_contract/foo/validate.rs

@@ -1,13 +1,25 @@
-use darkfi::{crypto::types::DrkCircuitField, Error as DarkFiError};
+use pasta_curves::pallas;
 
 
-use std::any::Any;
+use darkfi::{
+    crypto::types::DrkCircuitField,
+    util::serial::{SerialDecodable, SerialEncodable},
+    Error as DarkFiError,
+};
 
 
-use crate::demo::{CallDataBase, StateRegistry, Transaction};
+use std::any::{Any, TypeId};
+
+use crate::{
+    demo::{CallDataBase, StateRegistry, Transaction},
+    example_contract::state::State,
+};
 
 
 type Result<T> = std::result::Result<T, Error>;
 type Result<T> = std::result::Result<T, Error>;
 
 
 #[derive(Debug, Clone, thiserror::Error)]
 #[derive(Debug, Clone, thiserror::Error)]
 pub enum Error {
 pub enum Error {
+    #[error("ValueExists")]
+    ValueExists,
+
     #[error("DarkFi error: {0}")]
     #[error("DarkFi error: {0}")]
     DarkFiError(String),
     DarkFiError(String),
 }
 }
@@ -18,29 +30,56 @@ impl From<DarkFiError> for Error {
     }
     }
 }
 }
 
 
-pub struct CallData {}
+pub struct CallData {
+    pub header: Header,
+}
 
 
 impl CallDataBase for CallData {
 impl CallDataBase for CallData {
     fn zk_public_values(&self) -> Vec<Vec<DrkCircuitField>> {
     fn zk_public_values(&self) -> Vec<Vec<DrkCircuitField>> {
-        vec![]
+        vec![vec![self.header.public_c]]
     }
     }
     fn zk_proof_addrs(&self) -> Vec<String> {
     fn zk_proof_addrs(&self) -> Vec<String> {
-        vec![]
+        vec!["example-foo".to_string()]
     }
     }
     fn as_any(&self) -> &dyn Any {
     fn as_any(&self) -> &dyn Any {
         self
         self
     }
     }
 }
 }
 
 
+#[derive(Clone, SerialEncodable, SerialDecodable)]
+pub struct Header {
+    pub public_c: pallas::Base,
+}
+
 pub fn state_transition(
 pub fn state_transition(
     states: &StateRegistry,
     states: &StateRegistry,
     func_call_index: usize,
     func_call_index: usize,
     parent_tx: &Transaction,
     parent_tx: &Transaction,
 ) -> Result<Update> {
 ) -> Result<Update> {
-    Ok(Update {})
+    let func_call = &parent_tx.func_calls[func_call_index];
+    let call_data = func_call.call_data.as_any();
+
+    assert_eq!((&*call_data).type_id(), TypeId::of::<CallData>());
+    let call_data = call_data.downcast_ref::<CallData>();
+
+    // This will be inside wasm so unwrap is fine.
+    let call_data = call_data.unwrap();
+
+    let example_state = states.lookup::<State>(&"EXAMPLE".to_string()).unwrap();
+
+    if example_state.public_exists(&call_data.header.public_c) {
+        return Err(Error::ValueExists)
+    }
+
+    Ok(Update { public_value: call_data.header.public_c })
 }
 }
 
 
 #[derive(Clone)]
 #[derive(Clone)]
-pub struct Update {}
+pub struct Update {
+    public_value: pallas::Base,
+}
 
 
-pub fn apply(states: &mut StateRegistry, mut update: Update) {}
+pub fn apply(states: &mut StateRegistry, update: Update) {
+    let example_state = states.lookup_mut::<State>(&"EXAMPLE".to_string()).unwrap();
+    example_state.add_public_value(update.public_value);
+}

+ 53 - 5
bin/daod/src/example_contract/foo/wallet.rs

@@ -1,16 +1,64 @@
-use std::any::Any;
+use log::debug;
+use rand::rngs::OsRng;
 
 
-use crate::example_contract::foo::validate::CallData;
+use halo2_proofs::circuit::Value;
+use pasta_curves::pallas;
 
 
-use crate::demo::{/*CallDataBase, StateRegistry, ZkContractInfo, */ FuncCall, ZkContractTable,};
+use darkfi::{
+    crypto::Proof,
+    zk::vm::{Witness, ZkCircuit},
+};
 
 
-pub struct Builder {}
+use crate::{
+    demo::{FuncCall, ZkContractInfo, ZkContractTable},
+    example_contract::foo::validate::{CallData, Header},
+};
+
+pub struct Foo {
+    pub a: u64,
+    pub b: u64,
+}
+
+pub struct Builder {
+    pub foo: Foo,
+}
 
 
 impl Builder {
 impl Builder {
     pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
     pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
+        debug!(target: "example_contract::foo::wallet::Builder", "build()");
         let mut proofs = vec![];
         let mut proofs = vec![];
 
 
-        let call_data = CallData {};
+        let zk_info = zk_bins.lookup(&"example-foo".to_string()).unwrap();
+        let zk_info = if let ZkContractInfo::Binary(info) = zk_info {
+            info
+        } else {
+            panic!("Not binary info")
+        };
+
+        let zk_bin = zk_info.bincode.clone();
+
+        let prover_witnesses = vec![
+            Witness::Base(Value::known(pallas::Base::from(self.foo.a))),
+            Witness::Base(Value::known(pallas::Base::from(self.foo.b))),
+        ];
+
+        let a = pallas::Base::from(self.foo.a);
+        let b = pallas::Base::from(self.foo.b);
+
+        let c = a + b;
+
+        let public_inputs = vec![c];
+
+        let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
+        debug!(target: "example_contract::foo::wallet::Builder", "input_proof Proof::create()");
+        let proving_key = &zk_info.proving_key;
+        let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
+            .expect("EXAMPLE::foo() proving error!)");
+        proofs.push(input_proof);
+
+        let header = Header { public_c: c };
+
+        let call_data = CallData { header };
 
 
         FuncCall {
         FuncCall {
             contract_id: "EXAMPLE".to_string(),
             contract_id: "EXAMPLE".to_string(),

+ 0 - 2
bin/daod/src/example_contract/mod.rs

@@ -1,5 +1,3 @@
-#![allow(unused)]
-
 // foo()
 // foo()
 pub mod foo;
 pub mod foo;
 pub mod state;
 pub mod state;

+ 14 - 2
bin/daod/src/example_contract/state.rs

@@ -1,9 +1,21 @@
 use std::any::Any;
 use std::any::Any;
 
 
-pub struct State {}
+use pasta_curves::pallas;
+
+pub struct State {
+    pub public_values: Vec<pallas::Base>,
+}
 
 
 impl State {
 impl State {
     pub fn new() -> Box<dyn Any> {
     pub fn new() -> Box<dyn Any> {
-        Box::new(Self {})
+        Box::new(Self { public_values: Vec::new() })
+    }
+
+    pub fn add_public_value(&mut self, public_value: pallas::Base) {
+        self.public_values.push(public_value)
+    }
+
+    pub fn public_exists(&self, public_value: &pallas::Base) -> bool {
+        self.public_values.iter().any(|v| v == public_value)
     }
     }
 }
 }