mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
implement signature checking for proposal inputs
This commit is contained in:
@@ -2,7 +2,14 @@ use crate::{
|
||||
dao_contract::{DaoBulla, State},
|
||||
demo::{CallDataBase, StateRegistry, Transaction},
|
||||
};
|
||||
use darkfi::crypto::{merkle_node::MerkleNode, types::DrkCircuitField};
|
||||
use darkfi::{
|
||||
crypto::{
|
||||
keypair::PublicKey, merkle_node::MerkleNode, schnorr, schnorr::SchnorrPublic,
|
||||
types::DrkCircuitField, Proof,
|
||||
},
|
||||
util::serial::{Encodable, SerialDecodable, SerialEncodable, VarInt},
|
||||
Error as DarkFiError,
|
||||
};
|
||||
use log::{debug, error};
|
||||
use pasta_curves::{
|
||||
arithmetic::CurveAffine,
|
||||
@@ -11,20 +18,53 @@ use pasta_curves::{
|
||||
};
|
||||
use std::any::{Any, TypeId};
|
||||
|
||||
use super::wallet::{Partial, PartialInput};
|
||||
|
||||
const TARGET: &str = "dao_contract::propose::validate::state_transition()";
|
||||
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Invalid DAO merkle root")]
|
||||
InvalidDaoMerkleRoot,
|
||||
|
||||
#[error("Signature verification failed")]
|
||||
SignatureVerifyFailed,
|
||||
|
||||
#[error("DarkFi error: {0}")]
|
||||
DarkFiError(String),
|
||||
}
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl From<DarkFiError> for Error {
|
||||
fn from(err: DarkFiError) -> Self {
|
||||
Self::DarkFiError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CallData {
|
||||
pub dao_merkle_root: MerkleNode,
|
||||
pub token_commit: pallas::Base,
|
||||
// TODO: compute from sum of input commits
|
||||
pub total_funds_commit: pallas::Point,
|
||||
|
||||
pub inputs: Vec<Input>,
|
||||
}
|
||||
|
||||
impl CallData {
|
||||
fn encode_without_signature<S: std::io::Write>(
|
||||
&self,
|
||||
mut s: S,
|
||||
proofs: &Vec<Proof>,
|
||||
) -> Result<usize> {
|
||||
let mut len = 0;
|
||||
len += self.dao_merkle_root.encode(&mut s)?;
|
||||
len += self.token_commit.encode(&mut s)?;
|
||||
//len += self.value_blind.encode(&mut s)?;
|
||||
//len += self.token_blind.encode(&mut s)?;
|
||||
len += self.inputs.encode_without_signature(&mut s)?;
|
||||
len += proofs.encode(s)?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl CallDataBase for CallData {
|
||||
@@ -44,6 +84,48 @@ impl CallDataBase for CallData {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Input {
|
||||
pub signature_public: PublicKey,
|
||||
pub signature: schnorr::Signature,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn from_partial(partial: PartialInput, signature: schnorr::Signature) -> Self {
|
||||
Self { signature_public: partial.signature_public, signature }
|
||||
}
|
||||
|
||||
fn encode_without_signature<S: std::io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let mut len = 0;
|
||||
//len += self.value.encode(&mut s)?;
|
||||
//len += self.token_id.encode(&mut s)?;
|
||||
//len += self.value_blind.encode(&mut s)?;
|
||||
//len += self.token_blind.encode(&mut s)?;
|
||||
len += self.signature_public.encode(s)?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
trait EncodableWithoutSignature {
|
||||
fn encode_without_signature<S: std::io::Write>(&self, s: S) -> Result<usize>;
|
||||
}
|
||||
|
||||
macro_rules! impl_vec_without_signature {
|
||||
($type: ty) => {
|
||||
impl EncodableWithoutSignature for Vec<$type> {
|
||||
#[inline]
|
||||
fn encode_without_signature<S: std::io::Write>(&self, mut s: S) -> Result<usize> {
|
||||
let mut len = 0;
|
||||
len += VarInt(self.len() as u64).encode(&mut s)?;
|
||||
for c in self.iter() {
|
||||
len += c.encode_without_signature(&mut s)?;
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_vec_without_signature!(Input);
|
||||
|
||||
pub fn state_transition(
|
||||
states: &StateRegistry,
|
||||
func_call_index: usize,
|
||||
@@ -65,6 +147,17 @@ pub fn state_transition(
|
||||
return Err(Error::InvalidDaoMerkleRoot)
|
||||
}
|
||||
|
||||
// Verify the available signatures
|
||||
let mut unsigned_tx_data = vec![];
|
||||
call_data.encode_without_signature(&mut unsigned_tx_data, &func_call.proofs)?;
|
||||
|
||||
for (i, input) in call_data.inputs.iter().enumerate() {
|
||||
let public = &input.signature_public;
|
||||
if !public.verify(&unsigned_tx_data[..], &input.signature) {
|
||||
return Err(Error::SignatureVerifyFailed)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Update {})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use halo2_proofs::circuit::Value;
|
||||
use pasta_curves::{
|
||||
arithmetic::CurveAffine,
|
||||
group::{ff::Field, Curve},
|
||||
group::{ff::Field, Curve, Group},
|
||||
pallas,
|
||||
};
|
||||
use rand::rngs::OsRng;
|
||||
@@ -26,13 +26,13 @@ use darkfi::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
dao_contract::propose::validate::CallData,
|
||||
dao_contract::propose::validate::{CallData, Input},
|
||||
demo::{CallDataBase, FuncCall, StateRegistry, ZkContractInfo, ZkContractTable},
|
||||
money_contract,
|
||||
util::poseidon_hash,
|
||||
};
|
||||
|
||||
pub struct Input {
|
||||
pub struct BuilderInput {
|
||||
pub secret: SecretKey,
|
||||
pub note: money_contract::transfer::wallet::Note,
|
||||
pub leaf_position: incrementalmerkletree::Position,
|
||||
@@ -57,7 +57,7 @@ pub struct DaoParams {
|
||||
}
|
||||
|
||||
pub struct Builder {
|
||||
pub inputs: Vec<Input>,
|
||||
pub inputs: Vec<BuilderInput>,
|
||||
pub proposal: Proposal,
|
||||
pub dao: DaoParams,
|
||||
pub dao_leaf_position: incrementalmerkletree::Position,
|
||||
@@ -67,8 +67,56 @@ pub struct Builder {
|
||||
|
||||
impl Builder {
|
||||
pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
|
||||
let total_funds = 110;
|
||||
let total_funds_blind = pallas::Scalar::random(&mut OsRng);
|
||||
let mut inputs = vec![];
|
||||
let mut total_funds = 0;
|
||||
let mut input_funds_blinds = vec![];
|
||||
let mut signature_secrets = vec![];
|
||||
for input in self.inputs {
|
||||
let funds_blind = pallas::Scalar::random(&mut OsRng);
|
||||
input_funds_blinds.push(funds_blind);
|
||||
|
||||
let signature_secret = SecretKey::random(&mut OsRng);
|
||||
|
||||
//let zk_info = zk_bins.lookup(&"money-transfer-burn".to_string()).unwrap();
|
||||
//let zk_info = if let ZkContractInfo::Native(info) = zk_info {
|
||||
// info
|
||||
//} else {
|
||||
// panic!("Not native info")
|
||||
//};
|
||||
//let burn_pk = &zk_info.proving_key;
|
||||
|
||||
//// Note from the previous output
|
||||
//let note = input.note;
|
||||
|
||||
//let (burn_proof, revealed) = create_burn_proof(
|
||||
// burn_pk,
|
||||
// note.value,
|
||||
// note.token_id,
|
||||
// value_blind,
|
||||
// token_blind,
|
||||
// note.serial,
|
||||
// note.spend_hook,
|
||||
// note.user_data,
|
||||
// input.user_data_blind,
|
||||
// note.coin_blind,
|
||||
// input.secret,
|
||||
// input.leaf_position,
|
||||
// input.merkle_path,
|
||||
// signature_secret,
|
||||
//)?;
|
||||
//proofs.push(burn_proof);
|
||||
|
||||
// First we make the tx then sign after
|
||||
signature_secrets.push(signature_secret);
|
||||
|
||||
let input = PartialInput { signature_public: PublicKey::from_secret(signature_secret) };
|
||||
inputs.push(input);
|
||||
}
|
||||
|
||||
let mut total_funds_blind = pallas::Scalar::from(0);
|
||||
for blind in &input_funds_blinds {
|
||||
total_funds_blind += blind;
|
||||
}
|
||||
let total_funds_commit = pedersen_commitment_u64(total_funds, total_funds_blind);
|
||||
let total_funds_coords = total_funds_commit.to_affine().coordinates().unwrap();
|
||||
let total_funds_x = *total_funds_coords.x();
|
||||
@@ -145,14 +193,59 @@ impl Builder {
|
||||
let main_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
|
||||
.expect("DAO::propose() proving error!");
|
||||
|
||||
let call_data =
|
||||
CallData { dao_merkle_root: self.dao_merkle_root, token_commit, total_funds_commit };
|
||||
// Create the input signatures
|
||||
let proofs = vec![main_proof];
|
||||
|
||||
let partial = Partial {
|
||||
dao_merkle_root: self.dao_merkle_root,
|
||||
token_commit,
|
||||
total_funds_commit,
|
||||
inputs,
|
||||
proofs,
|
||||
};
|
||||
|
||||
let mut unsigned_tx_data = vec![];
|
||||
partial.encode(&mut unsigned_tx_data).expect("failed to encode data");
|
||||
|
||||
let mut inputs = vec![];
|
||||
for (input, signature_secret) in
|
||||
partial.inputs.into_iter().zip(signature_secrets.into_iter())
|
||||
{
|
||||
let signature = signature_secret.sign(&unsigned_tx_data[..]);
|
||||
let input = Input::from_partial(input, signature);
|
||||
inputs.push(input);
|
||||
}
|
||||
|
||||
let call_data = CallData {
|
||||
dao_merkle_root: self.dao_merkle_root,
|
||||
token_commit,
|
||||
total_funds_commit,
|
||||
inputs: vec![],
|
||||
};
|
||||
|
||||
FuncCall {
|
||||
contract_id: "DAO".to_string(),
|
||||
func_id: "DAO::propose()".to_string(),
|
||||
call_data: Box::new(call_data),
|
||||
proofs: vec![main_proof],
|
||||
proofs: partial.proofs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, SerialEncodable, SerialDecodable)]
|
||||
pub struct Partial {
|
||||
pub dao_merkle_root: MerkleNode,
|
||||
pub token_commit: pallas::Base,
|
||||
// TODO: compute from sum of input commits
|
||||
pub total_funds_commit: pallas::Point,
|
||||
|
||||
pub inputs: Vec<PartialInput>,
|
||||
|
||||
pub proofs: Vec<Proof>,
|
||||
}
|
||||
|
||||
#[derive(Clone, SerialEncodable, SerialDecodable)]
|
||||
pub struct PartialInput {
|
||||
/// Public key for the signature
|
||||
pub signature_public: PublicKey,
|
||||
}
|
||||
|
||||
@@ -577,7 +577,7 @@ pub async fn demo() -> Result<()> {
|
||||
|
||||
// TODO: is it possible for an invalid transfer() to be constructed on exec()?
|
||||
// need to look into this
|
||||
let input = dao_contract::propose::wallet::Input {
|
||||
let input = dao_contract::propose::wallet::BuilderInput {
|
||||
secret: gov_keypair_1.secret,
|
||||
note: gov_recv[0].note.clone(),
|
||||
leaf_position: money_leaf_position,
|
||||
|
||||
Reference in New Issue
Block a user