Skip to content

Commit 20a4ecd

Browse files
authored
Merge pull request #202 from bnb-chain/improve-mev-rpc
perf(mev-rpc): fix address derivation on startup
2 parents 4036d83 + 794e255 commit 20a4ecd

File tree

3 files changed

+75
-64
lines changed

3 files changed

+75
-64
lines changed

src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ fn main() -> eyre::Result<()> {
149149
mining_config.gas_limit = Some(gas_limit);
150150
}
151151

152+
if let Err(err) = mining_config.derive_validator_address_from_keys() {
153+
return Err(eyre::eyre!(err));
154+
}
155+
152156
// Ensure keys are available if enabled but none provided
153157
mining_config = mining_config.ensure_keys_available();
154158

src/node/miner/config.rs

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,33 @@ impl MiningConfig {
162162
}.ensure_keys_available()
163163
}
164164

165+
/// Ensures `validator_address` is populated by deriving it from the configured key material.
166+
/// Returns an error if a key source is configured but cannot be decoded.
167+
pub fn derive_validator_address_from_keys(&mut self) -> Result<(), String> {
168+
if self.validator_address.is_some() {
169+
return Ok(());
170+
}
171+
172+
if let Some(ref pk_hex) = self.private_key_hex {
173+
let signing_key = keystore::load_private_key_from_hex(pk_hex)
174+
.map_err(|err| format!("Failed to load mining private key: {err}"))?;
175+
self.validator_address = Some(keystore::get_validator_address(&signing_key));
176+
return Ok(());
177+
}
178+
179+
if let Some(ref path) = self.keystore_path {
180+
let password = self
181+
.keystore_password
182+
.as_deref()
183+
.ok_or_else(|| "Keystore password must be provided when deriving validator address".to_string())?;
184+
let signing_key = keystore::load_private_key_from_keystore(path, password)
185+
.map_err(|err| format!("Failed to decrypt keystore {}: {err}", path.display()))?;
186+
self.validator_address = Some(keystore::get_validator_address(&signing_key));
187+
}
188+
189+
Ok(())
190+
}
191+
165192
/// Load configuration from environment variables
166193
pub fn from_env() -> Self {
167194
let enabled = std::env::var("BSC_MINING_ENABLED")
@@ -197,17 +224,8 @@ impl MiningConfig {
197224
..Default::default()
198225
};
199226

200-
// If a private key is present but validator_address is not, derive it automatically.
201-
if cfg.validator_address.is_none() {
202-
if let Some(ref pk_hex) = cfg.private_key_hex {
203-
if let Ok(sk) = keystore::load_private_key_from_hex(pk_hex) {
204-
cfg.validator_address = Some(keystore::get_validator_address(&sk));
205-
}
206-
} else if let (Some(ref path), Some(ref pass)) = (&cfg.keystore_path, &cfg.keystore_password) {
207-
if let Ok(sk) = keystore::load_private_key_from_keystore(path, pass) {
208-
cfg.validator_address = Some(keystore::get_validator_address(&sk));
209-
}
210-
}
227+
if let Err(err) = cfg.derive_validator_address_from_keys() {
228+
tracing::warn!("Failed to derive validator address from configured keys: {err}");
211229
}
212230

213231
cfg.ensure_keys_available()
@@ -286,6 +304,23 @@ pub mod keystore {
286304
#[cfg(test)]
287305
mod tests {
288306
use super::*;
307+
use std::{fs, io::Write, path::PathBuf};
308+
use uuid::Uuid;
309+
310+
const SAMPLE_KEYSTORE_JSON: &str = r#"{"address":"bcdd0d2cda5f6423e57b6a4dcd75decbe31aecf0","crypto":{"cipher":"aes-128-ctr","ciphertext":"f7505ced32fe3037d6dc25ae7e9716858e516bacfb0dc28c9995f01cf7fee84a","cipherparams":{"iv":"d93e5314f18ccfc8330e1ad37534fa29"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"1fb8f954f70fb0ddb8be5b1fb57d821df21dda700682f382baed311dde95a6c7"},"mac":"c501bb033f94cb9efc3c63fe1547555fc1412d3abb8b400cacda37bd9de888ec"},"id":"7246dc9c-d170-4009-8878-8628be169836","version":3}"#;
311+
const SAMPLE_KEYSTORE_PASSWORD: &str = "0123456789";
312+
const SAMPLE_KEYSTORE_ADDRESS: &str = "0xbcdd0d2cda5f6423e57b6a4dcd75decbe31aecf0";
313+
314+
fn write_sample_keystore() -> PathBuf {
315+
let mut path: PathBuf = std::env::temp_dir();
316+
let fname =
317+
format!("UTC--test-{}--bcdd0d2cda5f6423e57b6a4dcd75decbe31aecf0", Uuid::new_v4());
318+
path.push(fname);
319+
let mut file = fs::File::create(&path).unwrap();
320+
file.write_all(SAMPLE_KEYSTORE_JSON.as_bytes()).unwrap();
321+
file.sync_all().unwrap();
322+
path
323+
}
289324

290325
#[test]
291326
fn test_mining_config_validation() {
@@ -314,37 +349,39 @@ mod tests {
314349

315350
#[test]
316351
fn test_load_private_key_from_keystore_file() {
317-
use std::fs;
318-
use std::io::Write;
319-
use std::path::PathBuf;
320-
use uuid::Uuid;
321-
322-
// This is a real V3 keystore JSON (address bcdd0d2c...) with password "0123456789"
323-
let keystore_json = r#"{"address":"bcdd0d2cda5f6423e57b6a4dcd75decbe31aecf0","crypto":{"cipher":"aes-128-ctr","ciphertext":"f7505ced32fe3037d6dc25ae7e9716858e516bacfb0dc28c9995f01cf7fee84a","cipherparams":{"iv":"d93e5314f18ccfc8330e1ad37534fa29"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"1fb8f954f70fb0ddb8be5b1fb57d821df21dda700682f382baed311dde95a6c7"},"mac":"c501bb033f94cb9efc3c63fe1547555fc1412d3abb8b400cacda37bd9de888ec"},"id":"7246dc9c-d170-4009-8878-8628be169836","version":3}"#;
324-
325352
// Write to a temporary file
326-
let mut path: PathBuf = std::env::temp_dir();
327-
let fname = format!("UTC--test-{}--bcdd0d2cda5f6423e57b6a4dcd75decbe31aecf0", Uuid::new_v4());
328-
path.push(fname);
329-
{
330-
let mut f = fs::File::create(&path).unwrap();
331-
f.write_all(keystore_json.as_bytes()).unwrap();
332-
f.sync_all().unwrap();
333-
}
353+
let path = write_sample_keystore();
334354

335355
// Decrypt with the known password
336-
let signing_key = keystore::load_private_key_from_keystore(&path, "0123456789").unwrap();
356+
let signing_key =
357+
keystore::load_private_key_from_keystore(&path, SAMPLE_KEYSTORE_PASSWORD).unwrap();
337358
// convert signing_key to hex string
338359
let signing_key_hex = alloy_primitives::hex::encode(signing_key.to_bytes());
339360
println!("signing_key: 0x{}", signing_key_hex);
340361

341362
let address = keystore::get_validator_address(&signing_key);
342363

343364
// Expect derived address to match the keystore address
344-
let expected: Address = "0xbcdd0d2cda5f6423e57b6a4dcd75decbe31aecf0".parse().unwrap();
365+
let expected: Address = SAMPLE_KEYSTORE_ADDRESS.parse().unwrap();
345366
assert_eq!(address, expected);
346367

347368
// Cleanup best-effort
348369
let _ = fs::remove_file(&path);
349370
}
371+
372+
#[test]
373+
fn derive_validator_address_from_keystore_config() {
374+
let path = write_sample_keystore();
375+
let mut cfg = MiningConfig {
376+
enabled: true,
377+
validator_address: None,
378+
keystore_path: Some(path.clone()),
379+
keystore_password: Some(SAMPLE_KEYSTORE_PASSWORD.to_string()),
380+
..Default::default()
381+
};
382+
383+
cfg.derive_validator_address_from_keys().unwrap();
384+
assert_eq!(cfg.validator_address, Some(SAMPLE_KEYSTORE_ADDRESS.parse().unwrap()));
385+
let _ = fs::remove_file(path);
386+
}
350387
}

src/rpc/mev.rs

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use alloy_primitives::Address;
1414
use alloy_consensus::BlobTransactionSidecar;
1515
use alloy_consensus::{TxEip4844WithSidecar, transaction::RlpEcdsaDecodableTx};
1616
use crate::node::miner::config::MiningConfig;
17-
use crate::node::miner::config::keystore;
1817

1918
/// Raw bid data structure from builder
2019
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
@@ -90,39 +89,10 @@ impl MevApiImpl {
9089
MiningConfig::from_env()
9190
};
9291

93-
let mut validator_address = mining_config.validator_address.unwrap_or(Address::ZERO);
94-
95-
// Try to load signing key and derive validator address
96-
if let Some(keystore_path) = &mining_config.keystore_path {
97-
let password = mining_config.keystore_password.as_deref().unwrap_or("");
98-
if let Ok(signing_key) = keystore::load_private_key_from_keystore(keystore_path, password) {
99-
let derived_address = keystore::get_validator_address(&signing_key);
100-
if derived_address != validator_address && validator_address != Address::ZERO {
101-
tracing::warn!(
102-
"Validator address mismatch, configured: {}, derived: {}",
103-
validator_address, derived_address
104-
);
105-
}
106-
validator_address = derived_address;
107-
tracing::info!("Derived validator address from keystore: {}", derived_address);
108-
} else {
109-
tracing::warn!("Failed to load signing key from keystore");
110-
}
111-
} else if let Some(hex_key) = &mining_config.private_key_hex {
112-
if let Ok(signing_key) = keystore::load_private_key_from_hex(hex_key) {
113-
let derived_address = keystore::get_validator_address(&signing_key);
114-
if derived_address != validator_address && validator_address != Address::ZERO {
115-
tracing::warn!(
116-
"Validator address mismatch, configured: {}, derived: {}",
117-
validator_address, derived_address
118-
);
119-
}
120-
validator_address = derived_address;
121-
tracing::info!("Derived validator address from hex key: {}", derived_address);
122-
} else {
123-
tracing::warn!("Failed to load signing key from hex");
124-
}
125-
}
92+
let validator_address = mining_config.validator_address.unwrap_or_else(|| {
93+
tracing::warn!("Validator address not configured; MEV API will default to Address::ZERO");
94+
Address::ZERO
95+
});
12696

12797
Self { snapshot_provider, chain_spec, validator_address }
12898
}
@@ -625,4 +595,4 @@ impl BscMevApiServer for MevApiImpl {
625595

626596
Ok(bid_hash)
627597
}
628-
}
598+
}

0 commit comments

Comments
 (0)