Skip to content

Commit 2adf0dc

Browse files
authored
Merge pull request #288 from input-output-hk/golddydev/vrf-validation
feat: add block vrf validator module
2 parents dffa20b + a77a3ed commit 2adf0dc

File tree

34 files changed

+2564
-111
lines changed

34 files changed

+2564
-111
lines changed

Cargo.lock

Lines changed: 247 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ members = [
2626
"modules/consensus", # Chooses favoured chain across multiple options
2727
"modules/chain_store", # Tracks historical information about blocks and TXs
2828
"modules/tx_submitter", # Submits TXs to peers
29+
"modules/block_vrf_validator", # Validate the VRF calculation in the block header
2930

3031
# Process builds
3132
"processes/omnibus", # All-inclusive omnibus process
@@ -52,6 +53,7 @@ opentelemetry = { version = "0.30.0", features = ["trace"] }
5253
opentelemetry-otlp = { version = "0.30.0", features = ["grpc-tonic", "trace", "tls"] }
5354
opentelemetry_sdk = { version = "0.30.0", features = ["rt-tokio"] }
5455
pallas = "0.33.0"
56+
pallas-math = "0.33.0"
5557
pallas-primitives = "0.33.0"
5658
pallas-traverse = "0.33.0"
5759
serde = { version = "1.0.214", features = ["derive"] }

common/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ rayon = "1.11.0"
4040
cryptoxide = "0.5.1"
4141
thiserror = "2.0.17"
4242
sha2 = "0.10.8"
43-
caryatid_process.workspace = true
44-
config.workspace = true
43+
caryatid_process = { workspace = true }
44+
config = { workspace = true }
4545

4646
[lib]
4747
crate-type = ["rlib"]

common/src/genesis_values.rs

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
use crate::calculations::{
2-
epoch_to_first_slot_with_shelley_params, slot_to_epoch_with_shelley_params,
3-
slot_to_timestamp_with_params,
1+
use std::str::FromStr;
2+
3+
use crate::{
4+
calculations::{
5+
epoch_to_first_slot_with_shelley_params, slot_to_epoch_with_shelley_params,
6+
slot_to_timestamp_with_params,
7+
},
8+
hash::Hash,
9+
GenesisDelegates,
410
};
511
const MAINNET_SHELLEY_GENESIS_HASH: &str =
612
"1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81";
@@ -10,7 +16,8 @@ pub struct GenesisValues {
1016
pub byron_timestamp: u64,
1117
pub shelley_epoch: u64,
1218
pub shelley_epoch_len: u64,
13-
pub shelley_genesis_hash: [u8; 32],
19+
pub shelley_genesis_hash: Hash<32>,
20+
pub genesis_delegs: GenesisDelegates,
1421
}
1522

1623
impl GenesisValues {
@@ -19,10 +26,59 @@ impl GenesisValues {
1926
byron_timestamp: 1506203091,
2027
shelley_epoch: 208,
2128
shelley_epoch_len: 432000,
22-
shelley_genesis_hash: hex::decode(MAINNET_SHELLEY_GENESIS_HASH)
23-
.unwrap()
24-
.try_into()
25-
.unwrap(),
29+
shelley_genesis_hash: Hash::<32>::from_str(MAINNET_SHELLEY_GENESIS_HASH).unwrap(),
30+
genesis_delegs: GenesisDelegates::try_from(vec![
31+
(
32+
"ad5463153dc3d24b9ff133e46136028bdc1edbb897f5a7cf1b37950c",
33+
(
34+
"d9e5c76ad5ee778960804094a389f0b546b5c2b140a62f8ec43ea54d",
35+
"64fa87e8b29a5b7bfbd6795677e3e878c505bc4a3649485d366b50abadec92d7",
36+
),
37+
),
38+
(
39+
"b9547b8a57656539a8d9bc42c008e38d9c8bd9c8adbb1e73ad529497",
40+
(
41+
"855d6fc1e54274e331e34478eeac8d060b0b90c1f9e8a2b01167c048",
42+
"66d5167a1f426bd1adcc8bbf4b88c280d38c148d135cb41e3f5a39f948ad7fcc",
43+
),
44+
),
45+
(
46+
"60baee25cbc90047e83fd01e1e57dc0b06d3d0cb150d0ab40bbfead1",
47+
(
48+
"7f72a1826ae3b279782ab2bc582d0d2958de65bd86b2c4f82d8ba956",
49+
"c0546d9aa5740afd569d3c2d9c412595cd60822bb6d9a4e8ce6c43d12bd0f674",
50+
),
51+
),
52+
(
53+
"f7b341c14cd58fca4195a9b278cce1ef402dc0e06deb77e543cd1757",
54+
(
55+
"69ae12f9e45c0c9122356c8e624b1fbbed6c22a2e3b4358cf0cb5011",
56+
"6394a632af51a32768a6f12dac3485d9c0712d0b54e3f389f355385762a478f2",
57+
),
58+
),
59+
(
60+
"162f94554ac8c225383a2248c245659eda870eaa82d0ef25fc7dcd82",
61+
(
62+
"4485708022839a7b9b8b639a939c85ec0ed6999b5b6dc651b03c43f6",
63+
"aba81e764b71006c515986bf7b37a72fbb5554f78e6775f08e384dbd572a4b32",
64+
),
65+
),
66+
(
67+
"2075a095b3c844a29c24317a94a643ab8e22d54a3a3a72a420260af6",
68+
(
69+
"6535db26347283990a252313a7903a45e3526ec25ddba381c071b25b",
70+
"fcaca997b8105bd860876348fc2c6e68b13607f9bbd23515cd2193b555d267af",
71+
),
72+
),
73+
(
74+
"268cfc0b89e910ead22e0ade91493d8212f53f3e2164b2e4bef0819b",
75+
(
76+
"1d4f2e1fda43070d71bb22a5522f86943c7c18aeb4fa47a362c27e23",
77+
"63ef48bc5355f3e7973100c371d6a095251c80ceb40559f4750aa7014a6fb6db",
78+
),
79+
),
80+
])
81+
.unwrap(),
2682
}
2783
}
2884

common/src/messages.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use crate::commands::transactions::{TransactionsCommand, TransactionsCommandResponse};
77
use crate::genesis_values::GenesisValues;
88
use crate::ledger_state::SPOState;
9-
use crate::protocol_params::{NonceHash, ProtocolParams};
9+
use crate::protocol_params::{Nonce, ProtocolParams};
1010
use crate::queries::parameters::{ParametersStateQuery, ParametersStateQueryResponse};
1111
use crate::queries::spdd::{SPDDStateQuery, SPDDStateQueryResponse};
1212
use crate::queries::utxos::{UTxOStateQuery, UTxOStateQueryResponse};
@@ -182,7 +182,7 @@ pub struct EpochActivityMessage {
182182
pub spo_blocks: Vec<(PoolId, usize)>,
183183

184184
/// Nonce
185-
pub nonce: Option<NonceHash>,
185+
pub nonce: Option<Nonce>,
186186
}
187187

188188
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]

common/src/protocol_params.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use anyhow::{bail, Result};
99
use blake2::{digest::consts::U32, Blake2b, Digest};
1010
use chrono::{DateTime, Utc};
1111
use serde_with::{hex::Hex, serde_as};
12-
use std::collections::HashMap;
1312
use std::ops::Deref;
13+
use std::{collections::HashMap, fmt::Display};
1414

1515
#[derive(Debug, Default, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
1616
pub struct ProtocolParams {
@@ -272,6 +272,15 @@ pub struct Nonce {
272272
pub hash: Option<NonceHash>,
273273
}
274274

275+
impl Display for Nonce {
276+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277+
match self.hash {
278+
Some(hash) => write!(f, "{}", hex::encode(hash)),
279+
None => write!(f, "NeutralNonce"),
280+
}
281+
}
282+
}
283+
275284
impl Default for Nonce {
276285
fn default() -> Self {
277286
Self {
@@ -290,6 +299,34 @@ impl From<NonceHash> for Nonce {
290299
}
291300
}
292301

302+
impl Nonce {
303+
pub fn from_number(n: u64) -> Self {
304+
let mut hasher = Blake2b::<U32>::new();
305+
hasher.update(n.to_be_bytes());
306+
let hash: NonceHash = hasher.finalize().into();
307+
Self::from(hash)
308+
}
309+
310+
pub fn neutral() -> Self {
311+
Self {
312+
tag: NonceVariant::NeutralNonce,
313+
hash: None,
314+
}
315+
}
316+
317+
/// Seed constant for eta (randomness/entropy) computation
318+
/// Used when generating the epoch nonce
319+
pub fn seed_eta() -> Self {
320+
Self::from_number(0)
321+
}
322+
323+
/// Seed constant for leader (L) computation
324+
/// Used when determining if a stake pool is the slot leader
325+
pub fn seed_l() -> Self {
326+
Self::from_number(1)
327+
}
328+
}
329+
293330
impl From<BlockHash> for Nonce {
294331
fn from(hash: BlockHash) -> Self {
295332
Self {

common/src/rational_number.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ mod tests {
159159
fn test_chameleon_serialization() -> Result<()> {
160160
for n in 0..=1000 {
161161
let ch = [
162-
&ChameleonFraction::Float(f32::from_str(&format!("0.{:03}", n))?),
162+
&ChameleonFraction::Float(f32::from_str(&format!("0.{n:03}"))?),
163163
&ChameleonFraction::Fraction {
164164
numerator: n,
165165
denominator: 1000,

common/src/types.rs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use hex::decode;
1616
use regex::Regex;
1717
use serde::{Deserialize, Serialize};
1818
use serde_with::{hex::Hex, serde_as};
19+
use std::collections::BTreeMap;
1920
use std::{
2021
cmp::Ordering,
2122
collections::{HashMap, HashSet},
@@ -115,7 +116,7 @@ impl TryFrom<u8> for Era {
115116

116117
impl Display for Era {
117118
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
118-
write!(f, "{:?}", self)
119+
write!(f, "{self:?}")
119120
}
120121
}
121122

@@ -656,6 +657,9 @@ impl TxOutRef {
656657
}
657658
}
658659

660+
/// Slot
661+
pub type Slot = u64;
662+
659663
/// Amount of Ada, in Lovelace
660664
pub type Lovelace = u64;
661665
pub type LovelaceDelta = i64;
@@ -735,14 +739,10 @@ impl Credential {
735739
let key_hash = decode(hex_str.to_owned().into_bytes())?;
736740
if key_hash.len() != 28 {
737741
Err(anyhow!(
738-
"Invalid hash length for {:?}, expected 28 bytes",
739-
hex_str
742+
"Invalid hash length for {hex_str:?}, expected 28 bytes"
740743
))
741744
} else {
742-
key_hash
743-
.as_slice()
744-
.try_into()
745-
.map_err(|e| anyhow!("Failed to convert to KeyHash {}", e))
745+
key_hash.as_slice().try_into().map_err(|e| anyhow!("Failed to convert to KeyHash {e}"))
746746
}
747747
}
748748

@@ -753,16 +753,15 @@ impl Credential {
753753
Ok(Credential::AddrKeyHash(Self::hex_string_to_hash(hash)?))
754754
} else {
755755
Err(anyhow!(
756-
"Incorrect credential {}, expected scriptHash- or keyHash- prefix",
757-
credential
756+
"Incorrect credential {credential}, expected scriptHash- or keyHash- prefix"
758757
))
759758
}
760759
}
761760

762761
pub fn to_json_string(&self) -> String {
763762
match self {
764-
Self::ScriptHash(hash) => format!("scriptHash-{}", hash),
765-
Self::AddrKeyHash(hash) => format!("keyHash-{}", hash),
763+
Self::ScriptHash(hash) => format!("scriptHash-{hash}"),
764+
Self::AddrKeyHash(hash) => format!("keyHash-{hash}"),
766765
}
767766
}
768767

@@ -788,8 +787,7 @@ impl Credential {
788787
"drep" => Ok(Credential::AddrKeyHash(hash)),
789788
"drep_script" => Ok(Credential::ScriptHash(hash)),
790789
_ => Err(anyhow!(
791-
"Invalid HRP for DRep Bech32, expected 'drep' or 'drep_script', got '{}'",
792-
hrp
790+
"Invalid HRP for DRep Bech32, expected 'drep' or 'drep_script', got '{hrp}'"
793791
)),
794792
}
795793
}
@@ -1296,7 +1294,7 @@ impl GovActionId {
12961294
let (hrp, data) = bech32::decode(bech32_str)?;
12971295

12981296
if hrp != Hrp::parse("gov_action")? {
1299-
return Err(anyhow!("Invalid HRP, expected 'gov_action', got: {}", hrp));
1297+
return Err(anyhow!("Invalid HRP, expected 'gov_action', got: {hrp}"));
13001298
}
13011299

13021300
if data.len() < 33 {
@@ -1328,7 +1326,7 @@ impl GovActionId {
13281326
impl Display for GovActionId {
13291327
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
13301328
match self.to_bech32() {
1331-
Ok(s) => write!(f, "{}", s),
1329+
Ok(s) => write!(f, "{s}"),
13321330
Err(e) => {
13331331
tracing::error!("GovActionId to_bech32 failed: {:?}", e);
13341332
write!(f, "<invalid-govactionid>")
@@ -1426,7 +1424,36 @@ pub struct GenesisDelegate {
14261424
#[serde_as(as = "Hex")]
14271425
pub delegate: Hash<28>,
14281426
#[serde_as(as = "Hex")]
1429-
pub vrf: Vec<u8>,
1427+
pub vrf: VrfKeyHash,
1428+
}
1429+
1430+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1431+
pub struct GenesisDelegates(pub BTreeMap<GenesisKeyhash, GenesisDelegate>);
1432+
1433+
impl TryFrom<Vec<(&str, (&str, &str))>> for GenesisDelegates {
1434+
type Error = anyhow::Error;
1435+
fn try_from(entries: Vec<(&str, (&str, &str))>) -> Result<Self, Self::Error> {
1436+
Ok(GenesisDelegates(
1437+
entries
1438+
.into_iter()
1439+
.map(|(genesis_key_str, (delegate_str, vrf_str))| {
1440+
let genesis_key = GenesisKeyhash::from_str(genesis_key_str)
1441+
.map_err(|e| anyhow::anyhow!("Invalid genesis key hash: {e}"))?;
1442+
let delegate = Hash::<28>::from_str(delegate_str)
1443+
.map_err(|e| anyhow::anyhow!("Invalid genesis delegate: {e}"))?;
1444+
let vrf = VrfKeyHash::from_str(vrf_str)
1445+
.map_err(|e| anyhow::anyhow!("Invalid genesis VRF: {e}"))?;
1446+
Ok((genesis_key, GenesisDelegate { delegate, vrf }))
1447+
})
1448+
.collect::<Result<_, Self::Error>>()?,
1449+
))
1450+
}
1451+
}
1452+
1453+
impl AsRef<BTreeMap<GenesisKeyhash, GenesisDelegate>> for GenesisDelegates {
1454+
fn as_ref(&self) -> &BTreeMap<GenesisKeyhash, GenesisDelegate> {
1455+
&self.0
1456+
}
14301457
}
14311458

14321459
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
@@ -1767,8 +1794,8 @@ impl Voter {
17671794
impl Display for Voter {
17681795
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
17691796
match self.to_bech32() {
1770-
Ok(addr) => write!(f, "{}", addr),
1771-
Err(e) => write!(f, "<invalid voter: {}>", e),
1797+
Ok(addr) => write!(f, "{addr}"),
1798+
Err(e) => write!(f, "<invalid voter: {e}>"),
17721799
}
17731800
}
17741801
}

0 commit comments

Comments
 (0)