|
|
@@ -1,10 +1,22 @@
|
|
|
-use pasta_curves::{arithmetic::CurveAffine, group::Curve, pallas};
|
|
|
+use pasta_curves::{
|
|
|
+ arithmetic::CurveAffine,
|
|
|
+ group::{Curve, Group},
|
|
|
+ pallas,
|
|
|
+};
|
|
|
|
|
|
-use darkfi::{crypto::types::DrkCircuitField, Error as DarkFiError};
|
|
|
+use darkfi::{
|
|
|
+ crypto::{coin::Coin, types::DrkCircuitField},
|
|
|
+ Error as DarkFiError,
|
|
|
+};
|
|
|
|
|
|
use std::any::{Any, TypeId};
|
|
|
|
|
|
-use crate::demo::{CallDataBase, StateRegistry, Transaction, UpdateBase};
|
|
|
+use crate::{
|
|
|
+ dao_contract,
|
|
|
+ dao_contract::HashableBase,
|
|
|
+ demo::{CallDataBase, StateRegistry, Transaction, UpdateBase},
|
|
|
+ money_contract,
|
|
|
+};
|
|
|
|
|
|
type Result<T> = std::result::Result<T, Error>;
|
|
|
|
|
|
@@ -15,6 +27,27 @@ pub enum Error {
|
|
|
|
|
|
#[error("DarkFi error: {0}")]
|
|
|
DarkFiError(String),
|
|
|
+
|
|
|
+ #[error("InvalidNumberOfFuncCalls")]
|
|
|
+ InvalidNumberOfFuncCalls,
|
|
|
+
|
|
|
+ #[error("InvalidIndex")]
|
|
|
+ InvalidIndex,
|
|
|
+
|
|
|
+ #[error("InvalidCallData")]
|
|
|
+ InvalidCallData,
|
|
|
+
|
|
|
+ #[error("InvalidNumberOfOutputs")]
|
|
|
+ InvalidNumberOfOutputs,
|
|
|
+
|
|
|
+ #[error("InvalidOutput")]
|
|
|
+ InvalidOutput,
|
|
|
+
|
|
|
+ #[error("InvalidValueCommit")]
|
|
|
+ InvalidValueCommit,
|
|
|
+
|
|
|
+ #[error("InvalidVoteCommit")]
|
|
|
+ InvalidVoteCommit,
|
|
|
}
|
|
|
|
|
|
impl From<DarkFiError> for Error {
|
|
|
@@ -35,16 +68,10 @@ pub struct CallData {
|
|
|
impl CallDataBase for CallData {
|
|
|
fn zk_public_values(&self) -> Vec<(String, Vec<DrkCircuitField>)> {
|
|
|
let win_votes_coords = self.win_votes_commit.to_affine().coordinates().unwrap();
|
|
|
- let win_votes_commit_x = *win_votes_coords.x();
|
|
|
- let win_votes_commit_y = *win_votes_coords.y();
|
|
|
|
|
|
let total_votes_coords = self.total_votes_commit.to_affine().coordinates().unwrap();
|
|
|
- let total_votes_commit_x = *total_votes_coords.x();
|
|
|
- let total_votes_commit_y = *total_votes_coords.y();
|
|
|
|
|
|
let input_value_coords = self.input_value_commit.to_affine().coordinates().unwrap();
|
|
|
- let input_value_commit_x = *input_value_coords.x();
|
|
|
- let input_value_commit_y = *input_value_coords.y();
|
|
|
|
|
|
vec![(
|
|
|
"dao-exec".to_string(),
|
|
|
@@ -52,12 +79,12 @@ impl CallDataBase for CallData {
|
|
|
self.proposal,
|
|
|
self.coin_0,
|
|
|
self.coin_1,
|
|
|
- win_votes_commit_x,
|
|
|
- win_votes_commit_y,
|
|
|
- total_votes_commit_x,
|
|
|
- total_votes_commit_y,
|
|
|
- input_value_commit_x,
|
|
|
- input_value_commit_y,
|
|
|
+ *win_votes_coords.x(),
|
|
|
+ *win_votes_coords.y(),
|
|
|
+ *total_votes_coords.x(),
|
|
|
+ *total_votes_coords.y(),
|
|
|
+ *input_value_coords.x(),
|
|
|
+ *input_value_coords.y(),
|
|
|
*super::FUNC_ID,
|
|
|
pallas::Base::from(0),
|
|
|
pallas::Base::from(0),
|
|
|
@@ -85,27 +112,75 @@ pub fn state_transition(
|
|
|
let call_data = call_data.unwrap();
|
|
|
|
|
|
// Enforce tx has correct format:
|
|
|
- // 1. There should only be 2 calldata's
|
|
|
+ // 1. There should only be 2 func_call's
|
|
|
+ if parent_tx.func_calls.len() != 2 {
|
|
|
+ return Err(Error::InvalidNumberOfFuncCalls)
|
|
|
+ }
|
|
|
+
|
|
|
// 2. func_call_index == 1
|
|
|
+ if func_call_index != 1 {
|
|
|
+ return Err(Error::InvalidIndex)
|
|
|
+ }
|
|
|
+
|
|
|
// 3. First item should be a Money::transfer() calldata
|
|
|
+ let money_transfer_call_data = parent_tx.func_calls[0].call_data.as_any();
|
|
|
+ let money_transfer_call_data =
|
|
|
+ money_transfer_call_data.downcast_ref::<money_contract::transfer::validate::CallData>();
|
|
|
+ let money_transfer_call_data = money_transfer_call_data.unwrap();
|
|
|
+ if money_transfer_call_data.type_id() !=
|
|
|
+ TypeId::of::<money_contract::transfer::validate::CallData>()
|
|
|
+ {
|
|
|
+ return Err(Error::InvalidCallData)
|
|
|
+ }
|
|
|
+
|
|
|
// 4. Money::transfer() has exactly 2 outputs
|
|
|
+ if money_transfer_call_data.outputs.len() != 2 {
|
|
|
+ return Err(Error::InvalidNumberOfOutputs)
|
|
|
+ }
|
|
|
|
|
|
// Checks:
|
|
|
-
|
|
|
// 1. Check both coins in Money::transfer() are equal to our coin_0, coin_1
|
|
|
+ if money_transfer_call_data.outputs[0].revealed.coin != Coin(call_data.coin_0) {
|
|
|
+ return Err(Error::InvalidOutput)
|
|
|
+ }
|
|
|
+ if money_transfer_call_data.outputs[1].revealed.coin != Coin(call_data.coin_1) {
|
|
|
+ return Err(Error::InvalidOutput)
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut input_value_commits = pallas::Point::identity();
|
|
|
+
|
|
|
// 2. sum of Money::transfer() calldata input_value_commits == our input value commit
|
|
|
+ for input in &money_transfer_call_data.inputs {
|
|
|
+ input_value_commits += input.revealed.value_commit;
|
|
|
+ }
|
|
|
+ if input_value_commits != call_data.input_value_commit {
|
|
|
+ return Err(Error::InvalidValueCommit)
|
|
|
+ }
|
|
|
+
|
|
|
// 3. get the ProposalVote from DAO::State
|
|
|
+ let state = states
|
|
|
+ .lookup::<dao_contract::State>(&"DAO".to_string())
|
|
|
+ .expect("Return type is not of type State");
|
|
|
+ let proposal_votes = state.proposal_votes.get(&HashableBase(call_data.proposal)).unwrap();
|
|
|
+
|
|
|
// 4. check win/total_vote_commit is the same as in ProposalVote
|
|
|
+ if proposal_votes.vote_commits != call_data.win_votes_commit {
|
|
|
+ return Err(Error::InvalidVoteCommit)
|
|
|
+ }
|
|
|
|
|
|
- // We need the proposal in here
|
|
|
- Ok(Box::new(Update {}))
|
|
|
+ Ok(Box::new(Update { proposal: call_data.proposal }))
|
|
|
}
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
-pub struct Update {}
|
|
|
+pub struct Update {
|
|
|
+ pub proposal: pallas::Base,
|
|
|
+}
|
|
|
|
|
|
impl UpdateBase for Update {
|
|
|
fn apply(mut self: Box<Self>, states: &mut StateRegistry) {
|
|
|
- // Delete the ProposalVotes from DAO::State hashmap
|
|
|
+ let mut state = states
|
|
|
+ .lookup_mut::<dao_contract::State>(&"DAO".to_string())
|
|
|
+ .expect("Return type is not of type State");
|
|
|
+ state.proposal_votes.remove(&HashableBase(self.proposal)).unwrap();
|
|
|
}
|
|
|
}
|