contract/consensus: use previous proposal/block hash in vrf input

This commit is contained in:
aggstam
2023-06-07 16:42:34 +03:00
parent e436e6d069
commit 4e521f2826
8 changed files with 37 additions and 13 deletions

View File

@@ -599,8 +599,7 @@ impl ConsensusState {
}
if hashes.is_empty() {
let (_, hash) = self.blockchain.last().unwrap();
hashes.push(hash);
hashes.push(self.genesis_block);
}
hashes

View File

@@ -115,6 +115,8 @@ pub struct ConsensusProposalCallBuilder {
pub coin: ConsensusOwnCoin,
/// Rewarded slot checkpoint
pub slot_checkpoint: SlotCheckpoint,
/// Extending fork last proposal/block hash
pub previous_hash: blake3::Hash,
/// Merkle tree of coins used to create inclusion proofs
pub tree: MerkleTree,
/// `Proposal_V1` zkas circuit ZkBinary
@@ -162,6 +164,7 @@ impl ConsensusProposalCallBuilder {
&input,
&output,
&self.slot_checkpoint,
self.previous_hash,
)?;
let input = ConsensusInput {
@@ -205,6 +208,7 @@ impl ConsensusProposalCallBuilder {
reward_blind,
new_serial_commit,
slot,
previous_hash: self.previous_hash,
vrf_proof,
y,
rho,
@@ -225,6 +229,7 @@ pub fn create_proposal_proof(
input: &ConsensusBurnInputInfo,
output: &ConsensusMintOutputInfo,
slot_checkpoint: &SlotCheckpoint,
previous_hash: blake3::Hash,
) -> Result<(Proof, ConsensusProposalRevealed)> {
// Proof parameters
let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
@@ -278,8 +283,9 @@ pub fn create_proposal_proof(
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 + 32);
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(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];

View File

@@ -95,10 +95,17 @@ pub(crate) fn consensus_proposal_get_metadata_v1(
};
let slot_checkpoint: SlotCheckpoint = deserialize(&slot_checkpoint)?;
// Verify proposal extends a known fork
if !slot_checkpoint.fork_hashes.contains(&params.previous_hash) {
msg!("[ConsensusProposalV1] Error: Proposal extends unknown fork {}", params.previous_hash);
return Err(ConsensusError::ProposalExtendsUnknownFork.into())
}
// Verify eta VRF proof
let slot_pallas = pallas::Base::from(slot_checkpoint.slot);
let mut vrf_input = Vec::with_capacity(32 + 32);
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(params.previous_hash.as_bytes());
vrf_input.extend_from_slice(&slot_pallas.to_repr());
let vrf_proof = &params.vrf_proof;
if !vrf_proof.verify(params.input.signature_public, &vrf_input) {

View File

@@ -23,6 +23,9 @@ pub enum ConsensusError {
#[error("Missing slot checkpoint from db")]
ProposalMissingSlotCheckpoint,
#[error("Proposal extends unknown fork")]
ProposalExtendsUnknownFork,
#[error("Eta VRF proof couldn't be verified")]
ProposalErroneousVrfProof,
@@ -37,9 +40,10 @@ impl From<ConsensusError> for ContractError {
fn from(e: ConsensusError) -> Self {
match e {
ConsensusError::ProposalMissingSlotCheckpoint => Self::Custom(1),
ConsensusError::ProposalErroneousVrfProof => Self::Custom(2),
ConsensusError::CoinStillInGracePeriod => Self::Custom(3),
ConsensusError::CoinNotInUnstakeSet => Self::Custom(4),
ConsensusError::ProposalExtendsUnknownFork => Self::Custom(2),
ConsensusError::ProposalErroneousVrfProof => Self::Custom(3),
ConsensusError::CoinStillInGracePeriod => Self::Custom(4),
ConsensusError::CoinNotInUnstakeSet => Self::Custom(5),
}
}
}

View File

@@ -49,6 +49,8 @@ pub struct ConsensusProposalParamsV1 {
pub new_serial_commit: pallas::Point,
/// Rewarded slot
pub slot: u64,
/// Extending fork last proposal/block hash
pub previous_hash: blake3::Hash,
/// VRF proof for eta calculation
pub vrf_proof: VrfProof,
/// Coin y

View File

@@ -112,7 +112,9 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> {
assert!(ALICE_INITIAL == alice_staked_oc.note.value);
// We simulate the proposal of genesis slot
let slot_checkpoint = th.get_slot_checkpoint_by_slot(current_slot).await?;
// We progress 1 slot and simulate its proposal
current_slot += 1;
let slot_checkpoint = th.generate_slot_checkpoint(current_slot).await?;
// With alice's current coin value she can become the slot proposer,
// so she creates a proposal transaction to burn her staked coin,
@@ -121,7 +123,7 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> {
info!(target: "consensus", "[Alice] Building proposal tx");
info!(target: "consensus", "[Alice] ====================");
let (proposal_tx, proposal_params, proposal_secret_key) =
th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone())?;
th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone()).await?;
info!(target: "consensus", "[Faucet] ===========================");
info!(target: "consensus", "[Faucet] Executing Alice proposal tx");

View File

@@ -511,7 +511,7 @@ impl ConsensusTestHarness {
Ok(())
}
pub fn proposal(
pub async fn proposal(
&mut self,
holder: Holder,
slot_checkpoint: SlotCheckpoint,
@@ -523,10 +523,14 @@ impl ConsensusTestHarness {
let tx_action_benchmark = self.tx_action_benchmarks.get_mut(&TxAction::Proposal).unwrap();
let timer = Instant::now();
// Proposals always extend genesis block
let previous_hash = wallet.state.read().await.consensus.genesis_block;
// Building Consensus::Unstake params
let proposal_call_debris = ConsensusProposalCallBuilder {
coin: staked_oc,
slot_checkpoint,
previous_hash,
tree: wallet.consensus_merkle_tree.clone(),
proposal_zkbin: proposal_zkbin.clone(),
proposal_pk: proposal_pk.clone(),

View File

@@ -102,7 +102,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
info!(target: "consensus", "[Malicious] Checking proposal before grace period");
info!(target: "consensus", "[Malicious] =====================================");
let (proposal_tx, _, _) =
th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone())?;
th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone()).await?;
th.execute_erroneous_proposal_txs(Holder::Alice, vec![proposal_tx], current_slot, 1).await?;
// We progress after grace period
@@ -116,7 +116,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
info!(target: "consensus", "[Alice] Building proposal tx");
info!(target: "consensus", "[Alice] ====================");
let (proposal_tx, proposal_params, proposal_secret_key) =
th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone())?;
th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone()).await?;
info!(target: "consensus", "[Faucet] ===========================");
info!(target: "consensus", "[Faucet] Executing Alice proposal tx");
@@ -194,7 +194,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
info!(target: "consensus", "[Malicious] Checking using unstaked coin in proposal");
info!(target: "consensus", "[Malicious] ========================================");
let (proposal_tx, _, _) =
th.proposal(Holder::Alice, slot_checkpoint, alice_unstake_request_oc.clone())?;
th.proposal(Holder::Alice, slot_checkpoint, alice_unstake_request_oc.clone()).await?;
th.execute_erroneous_proposal_txs(Holder::Alice, vec![proposal_tx], current_slot, 1).await?;
info!(target: "consensus", "[Malicious] =============================");