implement signature checking for proposal inputs

This commit is contained in:
narodnik
2022-08-17 12:20:16 +02:00
parent 78766ef671
commit a3e73babe8
3 changed files with 197 additions and 11 deletions

View File

@@ -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 {})
}

View File

@@ -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,
}

View File

@@ -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,