contract/consensus: Clean up Proposal client API

This commit is contained in:
parazyd
2023-06-08 13:13:51 +02:00
parent 7fa2d8efc4
commit 7b24085790
6 changed files with 151 additions and 158 deletions

View File

@@ -30,27 +30,31 @@ use darkfi_money_contract::{
};
use darkfi_sdk::{
crypto::{
ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_base,
pedersen_commitment_u64, poseidon_hash, MerkleNode, MerkleTree, Nullifier, PublicKey,
SecretKey,
ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_u64,
poseidon_hash, Keypair, MerkleNode, MerkleTree, Nullifier, PublicKey, SecretKey,
},
incrementalmerkletree::{Hashable, Tree},
pasta::{group::ff::FromUniformBytes, pallas},
};
use log::debug;
use log::{debug, info};
use rand::rngs::OsRng;
use crate::{
client::common::{ConsensusBurnInputInfo, ConsensusMintOutputInfo},
model::{
ConsensusProposalParamsV1, HEADSTART, MU_RHO_PREFIX, MU_Y_PREFIX, REWARD, REWARD_PALLAS,
SEED_PREFIX, SERIAL_PREFIX,
ConsensusProposalParamsV1, HEADSTART, MU_RHO_PREFIX, MU_Y_PREFIX, REWARD, SEED_PREFIX,
SERIAL_PREFIX,
},
};
pub struct ConsensusProposalCallDebris {
/// Payload params
pub params: ConsensusProposalParamsV1,
/// ZK proofs
pub proofs: Vec<Proof>,
/// The new output keypair (used in the minted coin)
pub keypair: Keypair,
/// Secret key used to sign the transaction
pub signature_secret: SecretKey,
}
@@ -59,11 +63,10 @@ pub struct ConsensusProposalRevealed {
pub epoch: u64,
pub public_key: PublicKey,
pub merkle_root: MerkleNode,
pub value_commit: pallas::Point,
pub new_serial: pallas::Base,
pub new_serial_commit: pallas::Point,
pub new_value_commit: pallas::Point,
pub new_coin: Coin,
pub input_value_commit: pallas::Point,
pub reward: u64,
pub output_value_commit: pallas::Point,
pub output_coin: Coin,
pub vrf_proof: VrfProof,
pub mu_y: pallas::Base,
pub y: pallas::Base,
@@ -71,40 +74,36 @@ pub struct ConsensusProposalRevealed {
pub rho: pallas::Base,
pub sigma1: pallas::Base,
pub sigma2: pallas::Base,
pub headstart: pallas::Base,
}
impl ConsensusProposalRevealed {
pub fn to_vec(&self) -> Vec<pallas::Base> {
let epoch_palas = pallas::Base::from(self.epoch);
fn to_vec(&self) -> Vec<pallas::Base> {
let (pub_x, pub_y) = self.public_key.xy();
let value_coords = self.value_commit.to_affine().coordinates().unwrap();
let new_serial_coords = self.new_serial_commit.to_affine().coordinates().unwrap();
let reward_pallas = pallas::Base::from(REWARD);
let new_value_coords = self.new_value_commit.to_affine().coordinates().unwrap();
let input_value_coords = self.input_value_commit.to_affine().coordinates().unwrap();
let output_value_coords = self.output_value_commit.to_affine().coordinates().unwrap();
// NOTE: It's important to keep these in the same order
// as the `constrain_instance` calls in the zkas code.
vec![
self.nullifier.inner(),
epoch_palas,
pallas::Base::from(self.epoch),
pub_x,
pub_y,
self.merkle_root.inner(),
*value_coords.x(),
*value_coords.y(),
*new_serial_coords.x(),
*new_serial_coords.y(),
reward_pallas,
*new_value_coords.x(),
*new_value_coords.y(),
self.new_coin.inner(),
*input_value_coords.x(),
*input_value_coords.y(),
pallas::Base::from(self.reward),
*output_value_coords.x(),
*output_value_coords.y(),
self.output_coin.inner(),
self.mu_y,
self.y,
self.mu_rho,
self.rho,
self.sigma1,
self.sigma2,
HEADSTART,
self.headstart,
]
}
}
@@ -112,7 +111,7 @@ impl ConsensusProposalRevealed {
/// Struct holding necessary information to build a proposal transaction.
pub struct ConsensusProposalCallBuilder {
/// `ConsensusOwnCoin` we're given to use in this builder
pub coin: ConsensusOwnCoin,
pub owncoin: ConsensusOwnCoin,
/// Rewarded slot checkpoint
pub slot_checkpoint: SlotCheckpoint,
/// Extending fork last proposal/block hash
@@ -120,7 +119,7 @@ pub struct ConsensusProposalCallBuilder {
/// Extending fork second to last proposal/block hash
pub fork_previous_hash: blake3::Hash,
/// Merkle tree of coins used to create inclusion proofs
pub tree: MerkleTree,
pub merkle_tree: MerkleTree,
/// `Proposal_V1` zkas circuit ZkBinary
pub proposal_zkbin: ZkBinary,
/// Proving key for the `Proposal_V1` zk circuit
@@ -129,129 +128,148 @@ pub struct ConsensusProposalCallBuilder {
impl ConsensusProposalCallBuilder {
pub fn build(&self) -> Result<ConsensusProposalCallDebris> {
debug!("Building Consensus::ProposalBurnV1 contract call for proposal");
let value = self.coin.note.value;
assert!(value != 0);
info!("Building Consensus::ProposalBurnV1 contract call");
assert!(self.owncoin.note.value != 0);
debug!("Building Consensus::ProposalV1 anonymous input");
let leaf_position = self.coin.leaf_position;
let root = self.tree.root(0).unwrap();
let merkle_path = self.tree.authentication_path(leaf_position, &root).unwrap();
let root = self.merkle_tree.root(0).unwrap();
let merkle_path =
self.merkle_tree.authentication_path(self.owncoin.leaf_position, &root).unwrap();
let input = ConsensusBurnInputInfo {
leaf_position,
leaf_position: self.owncoin.leaf_position,
merkle_path,
secret: self.coin.secret,
note: self.coin.note.clone(),
secret: self.owncoin.secret,
note: self.owncoin.note.clone(),
value_blind: pallas::Scalar::random(&mut OsRng),
};
debug!("Building anonymous output");
let reward_blind = pallas::Scalar::random(&mut OsRng);
let new_value_blind = input.value_blind + reward_blind;
let new_coin_blind = pallas::Base::random(&mut OsRng);
let output = ConsensusMintOutputInfo {
value: self.coin.note.value + REWARD,
epoch: 0,
public_key: PublicKey::from_secret(self.coin.secret),
value_blind: new_value_blind,
serial: self.coin.note.serial,
coin_blind: new_coin_blind,
};
debug!("Finished building output");
debug!("Building Consensus::ProposalV1 anonymous output");
let output_coin_blind = pallas::Base::random(&mut OsRng);
let output_reward_blind = pallas::Scalar::random(&mut OsRng);
let output_value_blind = input.value_blind + output_reward_blind;
debug!("Building Consensus::ProposalV1 contract call for proposal");
// We create a new random keypair for the output
let output_keypair = Keypair::random(&mut OsRng);
// The output's serial is derived from the old serial
let new_serial =
poseidon_hash([SERIAL_PREFIX, self.owncoin.secret.inner(), self.owncoin.note.serial]);
let output = ConsensusMintOutputInfo {
value: self.owncoin.note.value + REWARD,
epoch: 0, // We set the epoch as 0 here to eliminate a potential timelock
public_key: output_keypair.public,
value_blind: output_value_blind,
serial: new_serial,
coin_blind: output_coin_blind,
};
info!("Building Consensus::ProposalV1 VRF proof");
let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
vrf_input.extend_from_slice(&self.slot_checkpoint.previous_eta.to_repr());
vrf_input.extend_from_slice(self.fork_previous_hash.as_bytes());
vrf_input.extend_from_slice(&pallas::Base::from(self.slot_checkpoint.slot).to_repr());
let vrf_proof = VrfProof::prove(input.secret, &vrf_input, &mut OsRng);
info!("Building Consensus::ProposalV1 ZK proof");
let (proof, public_inputs) = create_proposal_proof(
&self.proposal_zkbin,
&self.proposal_pk,
&input,
&output,
&self.slot_checkpoint,
self.fork_hash,
self.fork_previous_hash,
&vrf_proof,
)?;
let input = ConsensusInput {
epoch: self.coin.note.epoch,
coin: self.coin.coin,
value_commit: public_inputs.value_commit,
let tx_input = ConsensusInput {
epoch: input.note.epoch,
value_commit: public_inputs.input_value_commit,
nullifier: public_inputs.nullifier,
merkle_root: public_inputs.merkle_root,
signature_public: public_inputs.public_key,
};
// Encrypted note
// Output's encrypted note
let note = ConsensusNote {
serial: public_inputs.new_serial,
serial: output.serial,
value: output.value,
epoch: 0,
coin_blind: new_coin_blind,
value_blind: new_value_blind,
epoch: output.epoch,
coin_blind: output.coin_blind,
value_blind: output.value_blind,
reward: REWARD,
reward_blind,
reward_blind: output_reward_blind,
};
let encrypted_note = AeadEncryptedNote::encrypt(&note, &output.public_key, &mut OsRng)?;
let output = ConsensusOutput {
value_commit: public_inputs.new_value_commit,
coin: public_inputs.new_coin,
let tx_output = ConsensusOutput {
value_commit: public_inputs.output_value_commit,
coin: public_inputs.output_coin,
note: encrypted_note,
};
// We now fill this with necessary stuff
let new_serial_commit = public_inputs.new_serial_commit;
let slot = self.slot_checkpoint.slot;
let vrf_proof = public_inputs.vrf_proof;
let y = public_inputs.y;
let rho = public_inputs.rho;
// Construct params
let params = ConsensusProposalParamsV1 {
input,
output,
input: tx_input,
output: tx_output,
reward: REWARD,
reward_blind,
new_serial_commit,
slot,
reward_blind: output_reward_blind,
slot: self.slot_checkpoint.slot,
fork_hash: self.fork_hash,
fork_previous_hash: self.fork_previous_hash,
vrf_proof,
y,
rho,
y: public_inputs.y,
rho: public_inputs.rho,
};
let proofs = vec![proof];
// Now we should have all the params, zk proofs and signature secret.
// We return it all and let the caller deal with it.
let debris =
ConsensusProposalCallDebris { params, proofs, signature_secret: self.coin.secret };
// Construct debris
let debris = ConsensusProposalCallDebris {
params,
proofs: vec![proof],
keypair: output_keypair,
signature_secret: input.secret,
};
Ok(debris)
}
}
pub fn create_proposal_proof(
fn create_proposal_proof(
zkbin: &ZkBinary,
pk: &ProvingKey,
input: &ConsensusBurnInputInfo,
output: &ConsensusMintOutputInfo,
slot_checkpoint: &SlotCheckpoint,
_fork_hash: blake3::Hash,
fork_previous_hash: blake3::Hash,
checkpoint: &SlotCheckpoint,
vrf_proof: &VrfProof,
) -> Result<(Proof, ConsensusProposalRevealed)> {
// TODO: fork_hash to be used as part of rank constrain in the proof
// Proof parameters
// Calculate lottery parameters
let seed = poseidon_hash([SEED_PREFIX, input.note.serial]);
let mut eta = [0u8; 64];
eta[..blake3::OUT_LEN].copy_from_slice(vrf_proof.hash_output().as_bytes());
let eta = pallas::Base::from_uniform_bytes(&eta);
let mu_y = poseidon_hash([MU_Y_PREFIX, eta, pallas::Base::from(checkpoint.slot)]);
let y = poseidon_hash([seed, mu_y]);
let mu_rho = poseidon_hash([MU_RHO_PREFIX, eta, pallas::Base::from(checkpoint.slot)]);
let rho = poseidon_hash([seed, mu_rho]);
// Derive the input's nullifier
let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
let epoch = input.note.epoch;
let epoch_pallas = pallas::Base::from(epoch);
let value_pallas = pallas::Base::from(input.note.value);
let value_commit = pedersen_commitment_u64(input.note.value, input.value_blind);
// Create the value commitment for the input
let input_value_commit = pedersen_commitment_u64(input.note.value, input.value_blind);
// Merkle inclusion proof for the input
let public_key = PublicKey::from_secret(input.secret);
let (pub_x, pub_y) = public_key.xy();
// Burnt coin and its merkle_root
let coin = poseidon_hash([
pub_x,
pub_y,
value_pallas,
epoch_pallas,
pallas::Base::from(input.note.value),
pallas::Base::from(input.note.epoch),
input.note.serial,
input.note.coin_blind,
]);
@@ -270,79 +288,56 @@ pub fn create_proposal_proof(
current
};
// New coin
let new_serial = poseidon_hash([SERIAL_PREFIX, input.secret.inner(), input.note.serial]);
let new_serial_blind = pallas::Scalar::random(&mut OsRng);
let new_serial_commit = pedersen_commitment_base(new_serial, new_serial_blind);
let new_value_commit = pedersen_commitment_u64(output.value, output.value_blind);
let new_value_pallas = pallas::Base::from(output.value);
let (new_pub_x, new_pub_y) = output.public_key.xy();
let new_coin = Coin::from(poseidon_hash([
new_pub_x,
new_pub_y,
new_value_pallas,
pallas::Base::ZERO,
new_serial,
// Derive the new output coin
let (output_x, output_y) = output.public_key.xy();
let output_coin = Coin::from(poseidon_hash([
output_x,
output_y,
pallas::Base::from(output.value),
pallas::Base::from(output.epoch),
output.serial,
output.coin_blind,
]));
let slot_pallas = pallas::Base::from(slot_checkpoint.slot);
let seed = poseidon_hash([SEED_PREFIX, input.note.serial]);
let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
vrf_input.extend_from_slice(&slot_checkpoint.previous_eta.to_repr());
vrf_input.extend_from_slice(fork_previous_hash.as_bytes());
vrf_input.extend_from_slice(&slot_pallas.to_repr());
let vrf_proof = VrfProof::prove(input.secret, &vrf_input, &mut OsRng);
let mut eta = [0u8; 64];
eta[..blake3::OUT_LEN].copy_from_slice(vrf_proof.hash_output().as_bytes());
let eta = pallas::Base::from_uniform_bytes(&eta);
let mu_y = poseidon_hash([MU_Y_PREFIX, eta, slot_pallas]);
let y = poseidon_hash([seed, mu_y]);
let mu_rho = poseidon_hash([MU_RHO_PREFIX, eta, slot_pallas]);
let rho = poseidon_hash([seed, mu_rho]);
let (sigma1, sigma2) = (slot_checkpoint.sigma1, slot_checkpoint.sigma2);
// Generate public inputs, witnesses and proof
// Create the ZK proof
let public_inputs = ConsensusProposalRevealed {
nullifier,
epoch,
epoch: input.note.epoch,
public_key,
merkle_root,
value_commit,
new_serial,
new_serial_commit,
new_value_commit,
new_coin,
vrf_proof,
input_value_commit,
reward: REWARD,
output_value_commit: pedersen_commitment_u64(output.value, output.value_blind),
output_coin,
vrf_proof: *vrf_proof,
mu_y,
y,
mu_rho,
rho,
sigma1,
sigma2,
sigma1: checkpoint.sigma1,
sigma2: checkpoint.sigma2,
headstart: HEADSTART,
};
let prover_witnesses = vec![
Witness::Base(Value::known(input.secret.inner())),
Witness::Base(Value::known(input.note.serial)),
Witness::Base(Value::known(pallas::Base::from(input.note.value))),
Witness::Base(Value::known(epoch_pallas)),
Witness::Base(Value::known(REWARD_PALLAS)),
Witness::Base(Value::known(pallas::Base::from(input.note.epoch))),
Witness::Base(Value::known(pallas::Base::from(REWARD))),
Witness::Scalar(Value::known(input.value_blind)),
Witness::Base(Value::known(input.note.coin_blind)),
Witness::Uint32(Value::known(u64::from(input.leaf_position).try_into().unwrap())),
Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
Witness::Scalar(Value::known(new_serial_blind)),
Witness::Base(Value::known(new_pub_x)),
Witness::Base(Value::known(new_pub_y)),
Witness::Base(Value::known(output_x)),
Witness::Base(Value::known(output_y)),
Witness::Scalar(Value::known(output.value_blind)),
Witness::Base(Value::known(output.coin_blind)),
Witness::Base(Value::known(mu_y)),
Witness::Base(Value::known(mu_rho)),
Witness::Base(Value::known(sigma1)),
Witness::Base(Value::known(sigma2)),
Witness::Base(Value::known(HEADSTART)),
Witness::Base(Value::known(public_inputs.mu_y)),
Witness::Base(Value::known(public_inputs.mu_rho)),
Witness::Base(Value::known(public_inputs.sigma1)),
Witness::Base(Value::known(public_inputs.sigma2)),
Witness::Base(Value::known(public_inputs.headstart)),
];
let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());

View File

@@ -90,7 +90,6 @@ impl ConsensusUnstakeRequestCallBuilder {
let input = ConsensusInput {
epoch: self.coin.note.epoch,
coin: self.coin.coin,
value_commit: public_inputs.value_commit,
nullifier: public_inputs.nullifier,
merkle_root: public_inputs.merkle_root,

View File

@@ -82,7 +82,6 @@ impl ConsensusUnstakeCallBuilder {
let input = ConsensusInput {
epoch: self.coin.note.epoch,
coin: self.coin.coin,
value_commit: public_inputs.value_commit,
nullifier: public_inputs.nullifier,
merkle_root: public_inputs.merkle_root,

View File

@@ -36,6 +36,7 @@ pub struct ConsensusGenesisStakeParamsV1 {
/// Parameters for `Consensus::Proposal`
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
// ANCHOR: ConsensusProposalParams
pub struct ConsensusProposalParamsV1 {
/// Anonymous input
pub input: ConsensusInput,
@@ -43,10 +44,8 @@ pub struct ConsensusProposalParamsV1 {
pub output: ConsensusOutput,
/// Reward value
pub reward: u64,
/// Blinding factor for reward value
/// Revealed blinding factor for reward value
pub reward_blind: pallas::Scalar,
/// Pedersen commitment for the output's serial number
pub new_serial_commit: pallas::Point,
/// Rewarded slot
pub slot: u64,
/// Extending fork last proposal/block hash
@@ -60,6 +59,7 @@ pub struct ConsensusProposalParamsV1 {
/// Lottery rho used
pub rho: pallas::Base,
}
// ANCHOR_END: ConsensusProposalParams
/// State update for `Consensus::Proposal`
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
@@ -88,8 +88,8 @@ pub struct ConsensusUnstakeRequestParamsV1 {
pub const EPOCH_LENGTH: u64 = 10;
/// Slot time in seconds
pub const SLOT_TIME: u64 = 90;
/// Grace period days target
pub const GRACE_PERIOD_DAYS: u64 = 2;
// Stake/Unstake timelock length in epochs
pub const GRACE_PERIOD: u64 = calculate_grace_period();
/// Configured block reward (1 DRK == 1 * 10^8)
pub const REWARD: u64 = 100_000_000;
/// Reward `pallas::Base`, calculated by: pallas::Base::from(REWARD)
@@ -130,10 +130,13 @@ pub struct SlotCheckpoint {
pub sigma2: pallas::Base,
}
/// Auxiliary function to calculate the grace(locked) period, denominated
/// Auxiliary function to calculate the grace (locked) period, denominated
/// in epochs.
#[inline]
pub const fn calculate_grace_period() -> u64 {
// Grace period days target
const GRACE_PERIOD_DAYS: u64 = 2;
// 86400 seconds in a day
(86400 * GRACE_PERIOD_DAYS) / (SLOT_TIME * EPOCH_LENGTH)
}

View File

@@ -147,7 +147,6 @@ impl MoneyUnstakeCallBuilder {
let input = ConsensusInput {
epoch: self.coin.note.epoch,
coin: self.coin.coin,
value_commit: public_inputs.value_commit,
nullifier: self.nullifier,
merkle_root: self.merkle_root,

View File

@@ -101,8 +101,6 @@ pub struct Input {
pub struct ConsensusInput {
/// Epoch the coin was minted
pub epoch: u64,
/// The coin
pub coin: Coin,
/// Pedersen commitment for the staked coin's value
pub value_commit: pallas::Point,
/// Revealed nullifier