tempo_commonware_node/
validators.rs1use std::{
2 collections::HashMap,
3 net::{IpAddr, SocketAddr},
4};
5
6use alloy_consensus::BlockHeader;
7use alloy_primitives::{Address, B256};
8use commonware_codec::DecodeExt as _;
9use commonware_cryptography::ed25519::PublicKey;
10use commonware_p2p::Ingress;
11use commonware_utils::{TryFromIterator, ordered};
12use eyre::{OptionExt as _, WrapErr as _};
13use reth_ethereum::evm::revm::{State, database::StateProviderDatabase};
14use reth_node_builder::ConfigureEvm as _;
15use reth_provider::{HeaderProvider as _, StateProviderFactory as _};
16use tempo_node::TempoFullNode;
17use tempo_precompiles::{
18 storage::StorageCtx,
19 validator_config_v2::{IValidatorConfigV2, ValidatorConfigV2},
20};
21
22use tracing::{Level, instrument, warn};
23
24use crate::utils::public_key_to_b256;
25
26pub(crate) fn read_active_and_known_peers_at_block_hash(
31 node: &TempoFullNode,
32 known: &ordered::Set<PublicKey>,
33 hash: B256,
34) -> eyre::Result<ordered::Map<PublicKey, commonware_p2p::Address>> {
35 read_validator_config_at_block_hash(node, hash, |config: &ValidatorConfigV2| {
36 let mut all = HashMap::new();
37 for raw in config
38 .get_active_validators()
39 .wrap_err("failed getting active validator set")?
40 {
41 if let Ok(decoded) = DecodedValidatorV2::decode_from_contract(raw)
42 && all
43 .insert(decoded.public_key.clone(), decoded.to_address())
44 .is_some()
45 {
46 warn!(
47 duplicate = %decoded.public_key,
48 "found duplicate public keys",
49 );
50 }
51 }
52 for member in known {
53 if !all.contains_key(member) {
54 let decoded = config
55 .validator_by_public_key(public_key_to_b256(member))
56 .map_err(eyre::Report::new)
57 .and_then(DecodedValidatorV2::decode_from_contract)
58 .expect(
59 "invariant: known peers must have an entry in the \
60 smart contract and be well formed",
61 );
62 all.insert(decoded.public_key.clone(), decoded.to_address());
63 }
64 }
65 Ok(ordered::Map::try_from_iter(all).expect("hashmaps don't contain duplicates"))
66 })
67 .map(|(_height, _hash, value)| value)
68}
69
70pub(crate) fn read_validator_config_at_block_hash<C, T>(
72 node: &TempoFullNode,
73 block_hash: B256,
74 read_fn: impl FnOnce(&C) -> eyre::Result<T>,
75) -> eyre::Result<(u64, B256, T)>
76where
77 C: Default,
78{
79 let header = node
80 .provider
81 .header(block_hash)
82 .map_err(eyre::Report::new)
83 .and_then(|maybe| maybe.ok_or_eyre("execution layer returned empty header"))
84 .wrap_err_with(|| format!("failed reading block with hash `{block_hash}`"))?;
85
86 let db = State::builder()
87 .with_database(StateProviderDatabase::new(
88 node.provider
89 .state_by_block_hash(block_hash)
90 .wrap_err_with(|| {
91 format!("failed to get state from node provider for hash `{block_hash}`")
92 })?,
93 ))
94 .build();
95
96 let mut evm = node
97 .evm_config
98 .evm_for_block(db, &header)
99 .wrap_err("failed instantiating evm for block")?;
100
101 let ctx = evm.ctx_mut();
102 let res = StorageCtx::enter_evm(
103 &mut ctx.journaled_state,
104 &ctx.block,
105 &ctx.cfg,
106 &ctx.tx,
107 || read_fn(&C::default()),
108 )?;
109 Ok((header.number(), block_hash, res))
110}
111
112pub(crate) struct DecodedValidatorV2 {
115 public_key: PublicKey,
116 ingress: SocketAddr,
117 egress: IpAddr,
118 added_at_height: u64,
119 deleted_at_height: u64,
120 index: u64,
121 address: Address,
122}
123
124impl DecodedValidatorV2 {
125 #[instrument(ret(Display, level = Level::DEBUG), err(level = Level::WARN))]
126 pub(crate) fn decode_from_contract(
127 IValidatorConfigV2::Validator {
128 publicKey,
129 validatorAddress: address,
130 ingress,
131 egress,
132 index,
133 addedAtHeight: added_at_height,
134 deactivatedAtHeight: deleted_at_height,
135 ..
136 }: IValidatorConfigV2::Validator,
137 ) -> eyre::Result<Self> {
138 let public_key = PublicKey::decode(publicKey.as_ref())
139 .wrap_err("failed decoding publicKey field as ed25519 public key")?;
140 let ingress = ingress.parse().wrap_err("ingress was not valid")?;
141 let egress = egress.parse().wrap_err("egress was not valid")?;
142 Ok(Self {
143 public_key,
144 ingress,
145 egress,
146 added_at_height,
147 deleted_at_height,
148 index,
149 address,
150 })
151 }
152
153 fn to_address(&self) -> commonware_p2p::Address {
154 commonware_p2p::Address::Asymmetric {
157 ingress: Ingress::Socket(self.ingress),
158 egress: SocketAddr::from((self.egress, 0)),
159 }
160 }
161}
162impl std::fmt::Display for DecodedValidatorV2 {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 f.write_fmt(format_args!(
165 "public key = `{}`, ingress = `{}`, egress = `{}`, added_at_height: `{}`, deleted_at_height = `{}`, index = `{}`, address = `{}`",
166 self.public_key,
167 self.ingress,
168 self.egress,
169 self.added_at_height,
170 self.deleted_at_height,
171 self.index,
172 self.address
173 ))
174 }
175}