Skip to content

Commit 5445657

Browse files
authored
Merge pull request #983 from openmina/faster-vrf
Allocate `num-bigint` on the stack
2 parents 5b6117d + 9ebdbce commit 5445657

File tree

9 files changed

+203
-115
lines changed

9 files changed

+203
-115
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ ark-ec = { git = "https://github.com/openmina/algebra", rev = "d0343f5" } # bran
8787
ark-poly = { git = "https://github.com/openmina/algebra", rev = "d0343f5" } # branch: fix-openmina-webnode
8888
ark-serialize = { git = "https://github.com/openmina/algebra", rev = "d0343f5" } # branch: fix-openmina-webnode
8989

90+
num-bigint = { git = "https://github.com/openmina/num-bigint", rev = "8bb5ee4" } # branch: on-stack
91+
num-rational = { git = "https://github.com/openmina/num-rational", rev = "336f11d" } # branch: on-stack
92+
9093
[profile.test.package."*"]
9194
opt-level = 3
9295
debug-assertions = true

mina-p2p-messages/src/bigint.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl BigInt {
4747
}
4848

4949
pub fn from_decimal(s: &str) -> Result<Self, InvalidDecimalNumber> {
50-
num_bigint::BigInt::parse_bytes(s.as_bytes(), 10)
50+
num_bigint::BigInt::<4>::parse_bytes(s.as_bytes(), 10)
5151
.map(|num| {
5252
let mut bytes = num.to_bytes_be().1;
5353
bytes.reverse();
@@ -238,7 +238,7 @@ impl<'de> Deserialize<'de> for BigInt {
238238
}),
239239
None => {
240240
// Try to parse as a decimal number
241-
num_bigint::BigInt::parse_bytes(v.as_bytes(), 10)
241+
num_bigint::BigInt::<4>::parse_bytes(v.as_bytes(), 10)
242242
.map(|num| {
243243
let mut bytes = num.to_bytes_be().1;
244244
bytes.reverse();

node/common/src/service/block_producer/vrf_evaluator.rs

Lines changed: 104 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use node::{
88
core::channels::mpsc::{UnboundedReceiver, UnboundedSender},
99
event_source::Event,
1010
};
11+
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
1112
use vrf::{VrfEvaluationInput, VrfEvaluationOutput};
1213

1314
use crate::NodeService;
@@ -18,29 +19,46 @@ pub fn vrf_evaluator(
1819
keypair: Keypair,
1920
) {
2021
while let Some(vrf_evaluator_input) = vrf_evaluation_receiver.blocking_recv() {
21-
let mut vrf_result = VrfEvaluationOutput::SlotLost(vrf_evaluator_input.global_slot);
22-
23-
for (index, (pub_key, stake)) in vrf_evaluator_input.delegator_table.iter() {
24-
let vrf_input = VrfEvaluationInput::new(
25-
keypair.clone(),
26-
vrf_evaluator_input.epoch_seed.clone(),
27-
pub_key.clone(),
28-
vrf_evaluator_input.global_slot,
29-
*index,
30-
(*stake).into(),
31-
vrf_evaluator_input.total_currency.into(),
32-
);
33-
vrf_result = vrf::evaluate_vrf(vrf_input).unwrap();
34-
35-
// the first delegate that won the slot
36-
if let VrfEvaluationOutput::SlotWon(_) = vrf_result {
37-
break;
38-
}
39-
}
22+
// let bytes = serde_json::to_string(&vrf_evaluator_input).unwrap();
23+
// openmina_core::http::download("vrf.json".to_string(), bytes.as_bytes().to_vec()).unwrap();
24+
25+
let keypair = &keypair;
26+
let VrfEvaluatorInput {
27+
epoch_seed,
28+
delegator_table,
29+
global_slot,
30+
total_currency,
31+
staking_ledger_hash: _,
32+
} = &vrf_evaluator_input;
33+
34+
let vrf_result = delegator_table
35+
.par_iter()
36+
.find_map_any(|(index, (pub_key, stake))| {
37+
let vrf_input = VrfEvaluationInput {
38+
producer_key: keypair.clone(),
39+
global_slot: *global_slot,
40+
epoch_seed: epoch_seed.clone(),
41+
account_pub_key: pub_key.clone(),
42+
delegator_index: *index,
43+
delegated_stake: (*stake).into(),
44+
total_currency: (*total_currency).into(),
45+
};
46+
47+
let vrf_result = vrf::evaluate_vrf(vrf_input).unwrap();
48+
49+
// the first delegate that won the slot
50+
if let VrfEvaluationOutput::SlotWon(_) = vrf_result {
51+
return Some(vrf_result);
52+
}
53+
None
54+
})
55+
.unwrap_or(VrfEvaluationOutput::SlotLost(*global_slot));
56+
4057
let vrf_result_with_hash = VrfEvaluationOutputWithHash::new(
4158
vrf_result,
4259
vrf_evaluator_input.staking_ledger_hash.clone(),
4360
);
61+
4462
// send the result back to the state machine
4563
let _ = event_sender.send(
4664
BlockProducerEvent::VrfEvaluator(BlockProducerVrfEvaluatorEvent::Evaluated(
@@ -60,3 +78,70 @@ impl node::block_producer_effectful::vrf_evaluator_effectful::BlockProducerVrfEv
6078
}
6179
}
6280
}
81+
82+
#[cfg(test)]
83+
mod tests {
84+
use std::str::FromStr;
85+
86+
// use mina_signer::keypair;
87+
use node::account::AccountSecretKey;
88+
89+
use super::*;
90+
91+
#[test]
92+
#[ignore]
93+
fn test_vrf() {
94+
let json = std::fs::read_to_string("/tmp/vrf.json").unwrap();
95+
let vrf_evaluator_input: VrfEvaluatorInput = serde_json::from_str(&json).unwrap();
96+
97+
let private = "SOME_KEY";
98+
let private = AccountSecretKey::from_str(private).unwrap();
99+
let keypair: Keypair = private.into();
100+
101+
let VrfEvaluatorInput {
102+
epoch_seed,
103+
delegator_table,
104+
global_slot,
105+
total_currency,
106+
staking_ledger_hash: _,
107+
} = &vrf_evaluator_input;
108+
109+
let now = std::time::Instant::now();
110+
111+
let vrf_result = delegator_table
112+
.par_iter()
113+
.map(|(index, (pub_key, stake))| {
114+
let vrf_input = VrfEvaluationInput {
115+
producer_key: keypair.clone(),
116+
global_slot: *global_slot,
117+
epoch_seed: epoch_seed.clone(),
118+
account_pub_key: pub_key.clone(),
119+
delegator_index: *index,
120+
delegated_stake: (*stake).into(),
121+
total_currency: (*total_currency).into(),
122+
};
123+
// let now = redux::Instant::now();
124+
let vrf_result = vrf::evaluate_vrf(vrf_input).unwrap();
125+
// let elapsed = now.elapsed();
126+
// let slot = global_slot;
127+
// eprintln!("vrf::evaluate_vrf: {elapsed:?} slot:{slot:?} index:{index:?}");
128+
// openmina_core::info!(openmina_core::log::system_time(); "vrf::evaluate_vrf: {elapsed:?} slot:{slot:?} index:{index:?}");
129+
130+
// nevaluated.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
131+
132+
// the first delegate that won the slot
133+
if let VrfEvaluationOutput::SlotWon(_) = vrf_result {
134+
return Some(vrf_result);
135+
}
136+
None
137+
})
138+
.collect::<Vec<_>>();
139+
140+
let elapsed = now.elapsed();
141+
let slot = vrf_evaluator_input.global_slot;
142+
let ndelegator = vrf_evaluator_input.delegator_table.len();
143+
// let nevaluated = nevaluated.load(std::sync::atomic::Ordering::Relaxed);
144+
eprintln!("TOTAL vrf::evaluate_vrf: {elapsed:?} slot:{slot:?} ndelegators:{ndelegator:?}");
145+
dbg!(vrf_result);
146+
}
147+
}

producer-dashboard/src/evaluator/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ impl Evaluator {
5151
.db
5252
.store_slot(global_slot, &SlotData::new_lost(global_slot, timestamp));
5353
for (index, delegate) in &delegates {
54-
let vrf_input = VrfEvaluationInput::new(
55-
self.key.clone().into(),
56-
epoch_seed.clone(),
57-
pub_key.clone(),
54+
let vrf_input = VrfEvaluationInput {
55+
producer_key: self.key.clone().into(),
5856
global_slot,
59-
(*index).into(),
60-
delegate.balance.clone().into(),
61-
total_currency.clone(),
62-
);
57+
epoch_seed: epoch_seed.clone(),
58+
account_pub_key: pub_key.clone(),
59+
delegator_index: (*index).into(),
60+
delegated_stake: delegate.balance.clone().into(),
61+
total_currency: total_currency.clone(),
62+
};
6363

6464
if let Ok(VrfEvaluationOutput::SlotWon(_)) = vrf::evaluate_vrf(vrf_input) {
6565
println!("Won slot: {global_slot}");

vrf/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ poseidon = { workspace = true }
2222
ark-ff = { workspace = true }
2323
ark-ec = { version = "0.3.0", features = [ "std" ] }
2424
ark-serialize = { version = "0.3.0", features = [ "std" ] }
25-
num = "0.4"
25+
num = {version = "0.4", features = [ "std" ] }
26+
# num = "0.4"
2627
itertools = "0.12"
2728
sha2 = "0.10"
2829
bs58 = "0.4.0"

vrf/src/lib.rs

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ark_ff::PrimeField;
33
use ledger::AccountIndex;
44
use message::VrfMessage;
55
use mina_p2p_messages::v2::EpochSeed;
6-
use num::{BigInt, ToPrimitive};
6+
use num::{rational::Ratio, BigInt, ToPrimitive};
77
use openmina_node_account::AccountPublicKey;
88
use output::VrfOutput;
99
use serde::{Deserialize, Serialize};
@@ -52,6 +52,17 @@ pub enum VrfError {
5252
IvalidWitness,
5353
}
5454

55+
/// 256 bits
56+
pub(crate) type BigInt256 = BigInt<4>;
57+
58+
/// 2048 bits
59+
pub(crate) type BigInt2048 = BigInt<32>;
60+
pub(crate) type BigRational2048 = Ratio<BigInt2048>;
61+
62+
/// 4096 bits
63+
pub(crate) type BigInt4096 = BigInt<64>;
64+
pub(crate) type BigRational4096 = Ratio<BigInt4096>;
65+
5566
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5667
pub struct VrfWonSlot {
5768
pub producer: AccountPublicKey,
@@ -79,35 +90,13 @@ impl VrfEvaluationOutput {
7990

8091
#[derive(Debug, Clone, PartialEq)]
8192
pub struct VrfEvaluationInput {
82-
producer_key: Keypair,
83-
global_slot: u32,
84-
epoch_seed: EpochSeed,
85-
account_pub_key: AccountPublicKey,
86-
delegator_index: AccountIndex,
87-
delegated_stake: BigInt,
88-
total_currency: BigInt,
89-
}
90-
91-
impl VrfEvaluationInput {
92-
pub fn new(
93-
producer_key: Keypair,
94-
epoch_seed: EpochSeed,
95-
account_pub_key: AccountPublicKey,
96-
global_slot: u32,
97-
delegator_index: AccountIndex,
98-
delegated_stake: BigInt,
99-
total_currency: BigInt,
100-
) -> Self {
101-
Self {
102-
producer_key,
103-
global_slot,
104-
epoch_seed,
105-
delegator_index,
106-
delegated_stake,
107-
total_currency,
108-
account_pub_key,
109-
}
110-
}
93+
pub producer_key: Keypair,
94+
pub global_slot: u32,
95+
pub epoch_seed: EpochSeed,
96+
pub account_pub_key: AccountPublicKey,
97+
pub delegator_index: AccountIndex,
98+
pub delegated_stake: BigInt,
99+
pub total_currency: BigInt,
111100
}
112101

113102
/// Generates the VRF output for the genesis block

vrf/src/output.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use ark_ec::short_weierstrass_jacobian::GroupAffine;
22
use ark_ff::{BigInteger, BigInteger256, PrimeField};
3+
use ledger::proofs::transaction::field_to_bits;
34
use ledger::{AppendToInputs, ToInputs};
45
use mina_p2p_messages::v2::ConsensusVrfOutputTruncatedStableV1;
56
use num::{BigInt, BigRational, One, ToPrimitive};
@@ -8,7 +9,7 @@ use poseidon::hash::params::MINA_VRF_OUTPUT;
89
use serde::{Deserialize, Serialize};
910
use sha2::{Digest, Sha256};
1011

11-
use crate::{BaseField, ScalarField};
12+
use crate::{BaseField, BigInt2048, ScalarField};
1213

1314
use super::serialize::{ark_deserialize, ark_serialize};
1415

@@ -62,7 +63,8 @@ impl VrfOutput {
6263
}
6364

6465
pub fn truncated(&self) -> ScalarField {
65-
let bits = self.hash().to_bits();
66+
let hash = self.hash();
67+
let bits = field_to_bits::<_, 255>(hash);
6668

6769
let repr = BigInteger256::from_bits_le(&bits[..bits.len() - 3]);
6870
ScalarField::from_repr(repr).unwrap()
@@ -89,7 +91,7 @@ impl VrfOutput {
8991
// Field.size_in_bits = 255
9092
let two_tpo_256 = BigInt::one() << 253u32;
9193

92-
let vrf_out = BigInt::from_bytes_be(
94+
let vrf_out: BigInt2048 = BigInt2048::from_bytes_be(
9395
num::bigint::Sign::Plus,
9496
&self.truncated().into_repr().to_bytes_be(),
9597
);

0 commit comments

Comments
 (0)