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 Mar 28, 2024
1 parent 8d0157a commit 06f6ebd
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 35 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 @@ -387,6 +387,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 @@ -69,6 +69,7 @@ derive_more = { 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, wsv)
.chain(0, Vec::new(), wsv)
.sign(key_pair)
.commit(&topology)
.unwrap();
Expand Down
4 changes: 2 additions & 2 deletions core/benches/kura.rs
Expand Up @@ -13,7 +13,7 @@ use iroha_core::{
sumeragi::network_topology::Topology,
wsv::World,
};
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 @@ -52,7 +52,7 @@ async fn measure_block_size_for_n_executors(n_executors: u32) {
let mut wsv = WorldStateView::new(World::new(), kura, query_handle);
let topology = Topology::new(UniqueVec::new());
let mut block = BlockBuilder::new(vec![tx], topology, Vec::new())
.chain(0, &mut wsv)
.chain(0, VRFState::generate_new_random_state(), &mut wsv)
.sign(&KeyPair::random());

for _ in 1..n_executors {
Expand Down
7 changes: 6 additions & 1 deletion core/benches/validation.rs
Expand Up @@ -12,6 +12,7 @@ use iroha_core::{
tx::TransactionExecutor,
wsv::World,
};
use iroha_crypto::VRFState;
use iroha_data_model::{isi::InstructionBox, prelude::*, transaction::TransactionLimits};
use iroha_primitives::unique_vec::UniqueVec;

Expand Down Expand Up @@ -174,7 +175,11 @@ fn sign_blocks(criterion: &mut Criterion) {

let mut count = 0;

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

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

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 @@ -66,6 +70,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 @@ -137,6 +143,7 @@ mod pending {
previous_height: u64,
previous_block_hash: Option<HashOf<SignedBlock>>,
view_change_index: u64,
vrf_state: Vec<u8>,
transactions: &[TransactionValue],
) -> BlockHeader {
BlockHeader {
Expand All @@ -151,6 +158,7 @@ mod pending {
height: previous_height + 1,
view_change_index,
previous_block_hash,
vrf_state,
transactions_hash: transactions
.iter()
.map(|value| value.as_ref().hash())
Expand Down Expand Up @@ -191,6 +199,7 @@ mod pending {
pub fn chain(
self,
view_change_index: u64,
vrf_state: Vec<u8>,
wsv: &mut WorldStateView,
) -> BlockBuilder<Chained> {
let transactions = Self::categorize_transactions(self.0.transactions, wsv);
Expand All @@ -200,6 +209,7 @@ mod pending {
wsv.height(),
wsv.latest_block_hash(),
view_change_index,
vrf_state,
&transactions,
),
transactions,
Expand Down Expand Up @@ -278,6 +288,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 Err((block, BlockValidationError::InvalidVRF));
}

if topology
.filter_signatures_by_roles(&[Role::Leader], block.signatures())
.is_empty()
Expand Down Expand Up @@ -447,6 +466,7 @@ mod valid {
height: 2,
view_change_index: 0,
previous_block_hash: None,
vrf_state: Vec::new(),
transactions_hash: None,
},
transactions: Vec::new(),
Expand Down Expand Up @@ -724,7 +744,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 wsv)
.chain(0, Vec::new(), &mut wsv)
.sign(&alice_keys);

// The first transaction should be confirmed
Expand Down Expand Up @@ -785,7 +805,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 wsv)
.chain(0, Vec::new(), &mut wsv)
.sign(&alice_keys);

// The first transaction should fail
Expand Down Expand Up @@ -841,7 +861,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 wsv)
.chain(0, Vec::new(), &mut wsv)
.sign(&alice_keys);

// The first transaction should be rejected
Expand Down
6 changes: 3 additions & 3 deletions core/src/smartcontracts/isi/query.rs
Expand Up @@ -294,7 +294,7 @@ mod tests {

let topology = Topology::new(UniqueVec::new());
let first_block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new())
.chain(0, &mut wsv)
.chain(0, Vec::new(), &mut wsv)
.sign(&ALICE_KEYS)
.commit(&topology)
.expect("Block is valid");
Expand All @@ -304,7 +304,7 @@ mod tests {

for _ in 1u64..blocks {
let block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new())
.chain(0, &mut wsv)
.chain(0, Vec::new(), &mut wsv)
.sign(&ALICE_KEYS)
.commit(&topology)
.expect("Block is valid");
Expand Down Expand Up @@ -435,7 +435,7 @@ mod tests {

let topology = Topology::new(UniqueVec::new());
let vcb = BlockBuilder::new(vec![va_tx.clone()], topology.clone(), Vec::new())
.chain(0, &mut wsv)
.chain(0, Vec::new(), &mut wsv)
.sign(&ALICE_KEYS)
.commit(&topology)
.expect("Block is valid");
Expand Down
44 changes: 40 additions & 4 deletions core/src/sumeragi/main_loop.rs
Expand Up @@ -8,6 +8,10 @@ use iroha_data_model::{
};
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 @@ -291,8 +295,13 @@ impl Sumeragi {
.expect("Genesis invalid");

let mut new_wsv = self.wsv.clone();
// 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 new_wsv)
.chain(
0,
self.current_topology.get_vrf_state().clone(),
&mut new_wsv,
)
.sign(&self.key_pair);

let genesis_msg = BlockCreated::from(genesis.clone()).into();
Expand Down Expand Up @@ -334,6 +343,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 @@ -638,6 +648,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(
&self
.wsv
.latest_block_ref()
.expect("Genesis committed")
.header()
.vrf_state,
&self.key_pair,
);

// TODO: properly process triggers!
let mut new_wsv = self.wsv.clone();
let event_recommendations = Vec::new();
Expand All @@ -646,7 +666,7 @@ impl Sumeragi {
self.current_topology.clone(),
event_recommendations,
)
.chain(current_view_change_index, &mut new_wsv)
.chain(current_view_change_index, new_vrf_state, &mut new_wsv)
.sign(&self.key_pair);

let created_in = create_block_start_time.elapsed();
Expand Down Expand Up @@ -1224,7 +1244,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 wsv)
.chain(0, Vec::new(), &mut wsv)
.sign(leader_key_pair);

let genesis = block.commit(topology).expect("Block is valid");
Expand Down Expand Up @@ -1262,7 +1282,7 @@ mod tests {

// Creating a block of two identical transactions and validating it
let block = BlockBuilder::new(vec![tx1, tx2], topology.clone(), Vec::new())
.chain(0, &mut wsv.clone())
.chain(0, Vec::new(), &mut wsv.clone())
.sign(leader_key_pair);

(wsv, kura, block.into())
Expand Down Expand Up @@ -1460,3 +1480,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 06f6ebd

Please sign in to comment.