|
1 | 1 | use blake2::{Blake2b512, Digest}; |
2 | | -use serde::Deserialize; |
| 2 | +use kimchi::o1_utils::FieldHelpers; |
| 3 | +use mina_p2p_messages::{hash::MinaHash, v2::MinaStateProtocolStateValueStableV2}; |
3 | 4 |
|
4 | | -#[derive(Clone, Debug, PartialEq)] |
5 | | -pub struct ConsensusState { |
6 | | - pub block_height: u32, |
7 | | - pub last_vrf_output: String, |
| 5 | +#[derive(PartialEq)] |
| 6 | +pub enum LongerChainResult { |
| 7 | + Tip, |
| 8 | + Candidate, |
8 | 9 | } |
9 | 10 |
|
10 | | -#[derive(Clone, Deserialize)] |
11 | | -#[serde(rename_all = "camelCase")] |
12 | | -pub struct ConsensusStateQuery { |
13 | | - pub block_height: String, |
14 | | - pub last_vrf_output: String, |
15 | | -} |
16 | | - |
17 | | -impl From<ConsensusStateQuery> for ConsensusState { |
18 | | - fn from(value: ConsensusStateQuery) -> Self { |
19 | | - Self { |
20 | | - block_height: u32::from_str_radix(&value.block_height, 10).unwrap(), |
21 | | - last_vrf_output: value.last_vrf_output, |
22 | | - } |
23 | | - } |
24 | | -} |
| 11 | +pub fn select_longer_chain( |
| 12 | + tip: &MinaStateProtocolStateValueStableV2, |
| 13 | + candidate: &MinaStateProtocolStateValueStableV2, |
| 14 | +) -> LongerChainResult { |
| 15 | + let tip_block_height = &tip.body.consensus_state.blockchain_length.as_u32(); |
| 16 | + let candidate_block_height = &candidate.body.consensus_state.blockchain_length.as_u32(); |
25 | 17 |
|
26 | | -impl ConsensusState { |
27 | | - pub fn from_json(mina_consensus_state_query: &str) -> Result<Self, String> { |
28 | | - let mina_consensus_state_query: serde_json::Map<String, serde_json::Value> = |
29 | | - serde_json::from_str(mina_consensus_state_query) |
30 | | - .map_err(|err| format!("Could not parse mina state consensus query: {err}"))?; |
31 | | - |
32 | | - let consensus_state_query_value = mina_consensus_state_query |
33 | | - .get("data") |
34 | | - .and_then(|d| d.get("block")) |
35 | | - .and_then(|d| d.get("protocolState")) |
36 | | - .and_then(|d| d.get("consensusState")) |
37 | | - .ok_or("Could not parse consensus state: JSON structure is unexpected")? |
38 | | - .to_owned(); |
39 | | - |
40 | | - let consensus_state_query: ConsensusStateQuery = |
41 | | - serde_json::from_value(consensus_state_query_value) |
42 | | - .map_err(|err| format!("Could not parse mina consensus state: {err}"))?; |
43 | | - |
44 | | - Ok(consensus_state_query.into()) |
| 18 | + if tip_block_height < candidate_block_height { |
| 19 | + return LongerChainResult::Candidate; |
45 | 20 | } |
46 | | - |
47 | | - pub fn select_longer_chain(&self, other: &Self) -> Self { |
48 | | - if self.block_height < other.block_height { |
49 | | - return other.clone(); |
50 | | - } |
51 | | - // tiebreak logic |
52 | | - else if self.block_height == other.block_height { |
53 | | - // compare last VRF digests lexicographically |
54 | | - if other.hash_last_vrf() > self.hash_last_vrf() { |
55 | | - return other.clone(); |
56 | | - } else if self.hash_last_vrf() == self.hash_last_vrf() { |
57 | | - // compare consensus state hashes lexicographically |
58 | | - // if other.hash_state() > self.hash_state() { |
59 | | - // return other.clone(); |
60 | | - // } |
61 | | - // FIXME: replace with logic defined above |
62 | | - return other.clone(); |
| 21 | + // tiebreak logic |
| 22 | + else if tip_block_height == candidate_block_height { |
| 23 | + // compare last VRF digests lexicographically |
| 24 | + if hash_last_vrf(tip) < hash_last_vrf(candidate) { |
| 25 | + return LongerChainResult::Candidate; |
| 26 | + } else if hash_last_vrf(tip) == hash_last_vrf(candidate) { |
| 27 | + // compare consensus state hashes lexicographically |
| 28 | + if hash_state(tip) < hash_state(candidate) { |
| 29 | + return LongerChainResult::Candidate; |
63 | 30 | } |
64 | 31 | } |
65 | | - |
66 | | - self.clone() |
67 | 32 | } |
68 | 33 |
|
69 | | - fn hash_last_vrf(&self) -> String { |
70 | | - let mut hasher = Blake2b512::new(); |
71 | | - hasher.update(self.last_vrf_output.clone()); |
72 | | - let digest = hasher.finalize().to_vec(); |
73 | | - |
74 | | - String::from_utf8(digest).unwrap() |
75 | | - } |
76 | | - |
77 | | - fn hash_state(&self) -> String { |
78 | | - todo!() |
79 | | - } |
| 34 | + LongerChainResult::Tip |
80 | 35 | } |
81 | 36 |
|
82 | | -#[cfg(test)] |
83 | | -mod tests { |
84 | | - use super::ConsensusState; |
| 37 | +fn hash_last_vrf(chain: &MinaStateProtocolStateValueStableV2) -> String { |
| 38 | + let mut hasher = Blake2b512::new(); |
| 39 | + hasher.update(chain.body.consensus_state.last_vrf_output.as_slice()); |
| 40 | + let digest = hasher.finalize().to_vec(); |
85 | 41 |
|
86 | | - const MINA_CONSENSUS_STATE_QUERY: &str = include_str!( |
87 | | - "../../../../batcher/aligned/test_files/mina/mina_mainnet_protocol_query.json" |
88 | | - ); |
89 | | - |
90 | | - #[test] |
91 | | - fn check_consensus_rules() { |
92 | | - let consensus_state_query = ConsensusState::from_json(MINA_CONSENSUS_STATE_QUERY).unwrap(); |
93 | | - let consensus_state: ConsensusState = consensus_state_query.into(); |
94 | | - dbg!(consensus_state.block_height); |
95 | | - let fake_chain_state = ConsensusState { |
96 | | - block_height: 1, |
97 | | - last_vrf_output: String::new(), |
98 | | - }; |
99 | | - |
100 | | - let best_chain = consensus_state.select_longer_chain(&fake_chain_state); |
| 42 | + String::from_utf8(digest).unwrap() |
| 43 | +} |
101 | 44 |
|
102 | | - assert_eq!(best_chain, consensus_state); |
103 | | - } |
| 45 | +fn hash_state(chain: &MinaStateProtocolStateValueStableV2) -> String { |
| 46 | + MinaHash::hash(chain).to_hex() |
104 | 47 | } |
0 commit comments