Skip to content

Commit

Permalink
[feature] hyperledger#4285: Verifiable Random Function in Sumeragi
Browse files Browse the repository at this point in the history
Signed-off-by: Sam H. Smith <sam.henning.smith@protonmail.com>
  • Loading branch information
SamHSmith committed Apr 4, 2024
1 parent bc6be98 commit 3b6abab
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 37 deletions.
52 changes: 52 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions config/src/parameters/user.rs
Expand Up @@ -422,6 +422,9 @@ impl Sumeragi {
} = self;

let trusted_peers = construct_unique_vec(trusted_peers.unwrap_or(vec![]))?;
for peer in &trusted_peers {
assert!(peer.public_key.algorithm() == iroha_crypto::Algorithm::Secp256k1);
}

Ok(actual::Sumeragi {
trusted_peers,
Expand Down
1 change: 1 addition & 0 deletions core/Cargo.toml
Expand Up @@ -66,6 +66,7 @@ nonzero_ext = { workspace = true }

uuid = { version = "1.4.1", features = ["v4"] }
indexmap = "2.1.0"
vrf = "0.2.4"

[dev-dependencies]
criterion = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion core/benches/blocks/common.rs
Expand Up @@ -40,7 +40,7 @@ pub fn create_block(
topology.clone(),
Vec::new(),
)
.chain(0, state)
.chain(0, Vec::new(), state)
.sign(key_pair)
.unpack(|_| {})
.commit(&topology)
Expand Down
4 changes: 2 additions & 2 deletions core/benches/kura.rs
Expand Up @@ -13,7 +13,7 @@ use iroha_core::{
state::{State, World},
sumeragi::network_topology::Topology,
};
use iroha_crypto::KeyPair;
use iroha_crypto::{KeyPair, VRFState};
use iroha_data_model::{prelude::*, transaction::TransactionLimits};
use iroha_primitives::unique_vec::UniqueVec;
use tokio::{fs, runtime::Runtime};
Expand Down Expand Up @@ -54,7 +54,7 @@ async fn measure_block_size_for_n_executors(n_executors: u32) {
let mut block = {
let mut state_block = state.block();
BlockBuilder::new(vec![tx], topology, Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(&KeyPair::random())
.unpack(|_| {})
};
Expand Down
8 changes: 6 additions & 2 deletions core/benches/validation.rs
Expand Up @@ -12,6 +12,7 @@ use iroha_core::{
sumeragi::network_topology::Topology,
tx::TransactionExecutor,
};
use iroha_crypto::VRFState;
use iroha_data_model::{isi::InstructionBox, prelude::*, transaction::TransactionLimits};
use iroha_primitives::unique_vec::UniqueVec;

Expand Down Expand Up @@ -179,8 +180,11 @@ fn sign_blocks(criterion: &mut Criterion) {
let mut count = 0;

let mut state_block = state.block();
let block =
BlockBuilder::new(vec![transaction], topology, Vec::new()).chain(0, &mut state_block);
let block = BlockBuilder::new(vec![transaction], topology, Vec::new()).chain(
0,
Vec::new(),
&mut state_block,
);

let _ = criterion.bench_function("sign_block", |b| {
b.iter_batched(
Expand Down
28 changes: 24 additions & 4 deletions core/src/block.rs
Expand Up @@ -20,7 +20,11 @@ use thiserror::Error;

pub(crate) use self::event::WithEvents;
pub use self::{chained::Chained, commit::CommittedBlock, valid::ValidBlock};
use crate::{prelude::*, sumeragi::network_topology::Topology, tx::AcceptTransactionFail};
use crate::{
prelude::*,
sumeragi::{main_loop::verify_vrf, network_topology::Topology},
tx::AcceptTransactionFail,
};

/// Error during transaction validation
#[derive(Debug, displaydoc::Display, Error)]
Expand Down Expand Up @@ -74,6 +78,8 @@ pub enum BlockValidationError {
SignatureVerification(#[from] SignatureVerificationError),
/// Received view change index is too large
ViewChangeIndexTooLarge,
/// Block has an invalid VRF state
InvalidVRF,
}

/// Error during signature verification
Expand Down Expand Up @@ -148,11 +154,13 @@ mod pending {
previous_height: u64,
prev_block_hash: Option<HashOf<SignedBlock>>,
view_change_index: u64,
vrf_state: Vec<u8>,
transactions: &[TransactionValue],
) -> BlockHeader {
BlockHeader {
height: previous_height + 1,
previous_block_hash: prev_block_hash,
vrf_state,
transactions_hash: transactions
.iter()
.map(|value| value.as_ref().hash())
Expand Down Expand Up @@ -206,6 +214,7 @@ mod pending {
pub fn chain(
self,
view_change_index: u64,
vrf_state: Vec<u8>,
state: &mut StateBlock<'_>,
) -> BlockBuilder<Chained> {
let transactions = Self::categorize_transactions(self.0.transactions, state);
Expand All @@ -215,6 +224,7 @@ mod pending {
state.height(),
state.latest_block_hash(),
view_change_index,
vrf_state,
&transactions,
),
transactions,
Expand Down Expand Up @@ -320,6 +330,15 @@ mod valid {
)));
}

let leader_pk = &topology.ordered_peers[0].public_key;
if !verify_vrf(
&topology.get_vrf_state(),
&block.header().vrf_state,
leader_pk,
) {
return WithEvents::new(Err((block, BlockValidationError::InvalidVRF)));
}

if topology
.filter_signatures_by_roles(&[Role::Leader], block.signatures())
.is_empty()
Expand Down Expand Up @@ -491,6 +510,7 @@ mod valid {
.as_millis()
.try_into()
.expect("Time should fit into u64"),
vrf_state: Vec::new(),
},
transactions: Vec::new(),
commit_topology: UniqueVec::new(),
Expand Down Expand Up @@ -842,7 +862,7 @@ mod tests {
let transactions = vec![tx.clone(), tx];
let topology = Topology::new(UniqueVec::new());
let valid_block = BlockBuilder::new(transactions, topology, Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(&alice_keys)
.unpack(|_| {});

Expand Down Expand Up @@ -917,7 +937,7 @@ mod tests {
let transactions = vec![tx0, tx, tx2];
let topology = Topology::new(UniqueVec::new());
let valid_block = BlockBuilder::new(transactions, topology, Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(&alice_keys)
.unpack(|_| {});

Expand Down Expand Up @@ -987,7 +1007,7 @@ mod tests {
let transactions = vec![tx_fail, tx_accept];
let topology = Topology::new(UniqueVec::new());
let valid_block = BlockBuilder::new(transactions, topology, Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(&alice_keys)
.unpack(|_| {});

Expand Down
6 changes: 3 additions & 3 deletions core/src/smartcontracts/isi/query.rs
Expand Up @@ -314,7 +314,7 @@ mod tests {

let topology = Topology::new(UniqueVec::new());
let first_block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(&ALICE_KEYS)
.unpack(|_| {})
.commit(&topology)
Expand All @@ -326,7 +326,7 @@ mod tests {

for _ in 1u64..blocks {
let block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(&ALICE_KEYS)
.unpack(|_| {})
.commit(&topology)
Expand Down Expand Up @@ -468,7 +468,7 @@ mod tests {

let topology = Topology::new(UniqueVec::new());
let vcb = BlockBuilder::new(vec![va_tx.clone()], topology.clone(), Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(&ALICE_KEYS)
.unpack(|_| {})
.commit(&topology)
Expand Down
44 changes: 40 additions & 4 deletions core/src/sumeragi/main_loop.rs
Expand Up @@ -5,6 +5,10 @@ use iroha_crypto::HashOf;
use iroha_data_model::{block::*, events::pipeline::PipelineEventBox, peer::PeerId};
use iroha_p2p::UpdateTopology;
use tracing::{span, Level};
use vrf::{
openssl::{CipherSuite, ECVRF},
VRF,
};

use super::{view_change::ProofBuilder, *};
use crate::{block::*, sumeragi::tracing::instrument};
Expand Down Expand Up @@ -270,8 +274,13 @@ impl Sumeragi {
.expect("Genesis invalid");

let mut state_block = state.block();
// Here is the only place in sumeragi it is okay to have a bogus unchecked vrf state.
let genesis = BlockBuilder::new(transactions, self.current_topology.clone(), vec![])
.chain(0, &mut state_block)
.chain(
0,
self.current_topology.get_vrf_state().clone(),
&mut state_block,
)
.sign(&self.key_pair)
.unpack(|e| self.send_event(e));

Expand Down Expand Up @@ -315,6 +324,7 @@ impl Sumeragi {
role=%self.current_topology.role(&self.peer_id),
block_height=%block.as_ref().header().height,
block_hash=%block.as_ref().hash(),
vrf_state=%HashOf::new(&block.as_ref().header().vrf_state),
"{}", Strategy::LOG_MESSAGE,
);

Expand Down Expand Up @@ -651,6 +661,16 @@ impl Sumeragi {
info!(%addr, txns=%transactions.len(), "Creating block...");
let create_block_start_time = Instant::now();

let new_vrf_state: Vec<u8> = perform_vrf(
&state
.view()
.latest_block_ref()
.expect("Genesis committed")
.header()
.vrf_state,
&self.key_pair,
);

// TODO: properly process triggers!
let mut state_block = state.block();
let event_recommendations = Vec::new();
Expand All @@ -659,7 +679,7 @@ impl Sumeragi {
self.current_topology.clone(),
event_recommendations,
)
.chain(current_view_change_index, &mut state_block)
.chain(current_view_change_index, new_vrf_state, &mut state_block)
.sign(&self.key_pair)
.unpack(|e| self.send_event(e));

Expand Down Expand Up @@ -1208,7 +1228,7 @@ mod tests {

// Creating a block of two identical transactions and validating it
let block = BlockBuilder::new(vec![tx.clone(), tx], topology.clone(), Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(leader_key_pair)
.unpack(|_| {});

Expand Down Expand Up @@ -1253,7 +1273,7 @@ mod tests {

// Creating a block of two identical transactions and validating it
BlockBuilder::new(vec![tx1, tx2], topology.clone(), Vec::new())
.chain(0, &mut state_block)
.chain(0, Vec::new(), &mut state_block)
.sign(leader_key_pair)
.unpack(|_| {})
};
Expand Down Expand Up @@ -1456,3 +1476,19 @@ mod tests {
))
}
}

/// Perform the verifiable random function
pub fn perform_vrf(old_state: &Vec<u8>, kp: &KeyPair) -> Vec<u8> {
assert!(kp.algorithm() == iroha_crypto::Algorithm::Secp256k1);
let mut ctx = ECVRF::from_suite(CipherSuite::SECP256K1_SHA256_TAI).expect("Cannot fail");
ctx.prove(&kp.private_key().to_bytes().1, old_state.as_ref())
.expect("Is not allowed to fail")
}
/// Verify the verifiable random function
pub fn verify_vrf(old_state: &Vec<u8>, new_state: &Vec<u8>, pk: &PublicKey) -> bool {
assert!(pk.algorithm() == iroha_crypto::Algorithm::Secp256k1);

let mut ctx = ECVRF::from_suite(CipherSuite::SECP256K1_SHA256_TAI).expect("Cannot fail");
ctx.verify(&pk.to_bytes().1, new_state.as_ref(), old_state.as_ref())
.is_ok()
}

0 comments on commit 3b6abab

Please sign in to comment.