Skip to content

Commit

Permalink
feat: interval mining
Browse files Browse the repository at this point in the history
  • Loading branch information
Wodann committed Nov 24, 2023
1 parent 79f26ee commit 8ceda5b
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 33 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions crates/edr_napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,13 @@ export interface MemPoolConfig {
order: MineOrdering
}
export interface IntervalRange {
min: number
max: number
min: bigint
max: bigint
}
/** Configuration for the provider's miner. */
export interface MiningConfig {
autoMine: boolean
interval: number | IntervalRange
interval?: bigint | IntervalRange
memPool: MemPoolConfig
}
/** Configuration for a provider */
Expand Down
40 changes: 26 additions & 14 deletions crates/edr_napi/src/provider/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ pub struct MemPoolConfig {

#[napi(object)]
pub struct IntervalRange {
pub min: i64,
pub max: i64,
pub min: BigInt,
pub max: BigInt,
}

/// Configuration for the provider's miner.
#[napi(object)]
pub struct MiningConfig {
pub auto_mine: bool,
pub interval: Either<i64, IntervalRange>,
pub interval: Option<Either<BigInt, IntervalRange>>,
pub mem_pool: MemPoolConfig,
}

Expand Down Expand Up @@ -99,22 +99,34 @@ impl From<MemPoolConfig> for edr_provider::MemPoolConfig {
}
}

impl From<MiningConfig> for edr_provider::MiningConfig {
fn from(value: MiningConfig) -> Self {
impl TryFrom<MiningConfig> for edr_provider::MiningConfig {
type Error = napi::Error;

fn try_from(value: MiningConfig) -> Result<Self, Self::Error> {
let mem_pool = value.mem_pool.into();

let interval = match value.interval {
Either::A(interval) => edr_provider::IntervalConfig::Fixed(interval),
Either::B(IntervalRange { min, max }) => {
edr_provider::IntervalConfig::Range { min, max }
}
};
let interval = value
.interval
.map(|interval| {
let interval = match interval {
Either::A(interval) => {
edr_provider::IntervalConfig::Fixed(interval.try_cast()?)
}
Either::B(IntervalRange { min, max }) => edr_provider::IntervalConfig::Range {
min: min.try_cast()?,
max: max.try_cast()?,
},
};

napi::Result::Ok(interval)
})
.transpose()?;

Self {
Ok(Self {
auto_mine: value.auto_mine,
interval,
mem_pool,
}
})
}
}

Expand Down Expand Up @@ -152,7 +164,7 @@ impl TryFrom<ProviderConfig> for edr_provider::ProviderConfig {
napi::Result::Ok(SystemTime::UNIX_EPOCH + elapsed_since_epoch)
})
.transpose()?,
mining: value.mining.into(),
mining: value.mining.try_into()?,
network_id: value.network_id.try_cast()?,
})
}
Expand Down
2 changes: 2 additions & 0 deletions crates/edr_provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ edr_evm = { version = "0.2.0-dev", path = "../edr_evm" }
rpc_hardhat = { package = "edr_rpc_hardhat", version = "0.2.0-dev", path = "../edr_rpc_hardhat" }
indexmap = { version = "2.0.0", default-features = false, features = ["std"] }
k256 = { version = "0.13.1", default-features = false, features = ["arithmetic", "ecdsa", "pkcs8", "precomputed-tables", "std"] }
log = { version = "0.4.20", default-features = false }
parking_lot = { version = "0.12.1", default-features = false }
rand = { version = "0.8.5", default-features = false }
rlp = { version = "0.5.2", default-features = false }
serde = { version = "1.0.147", default-features = false, features = ["derive"] }
serde_json = { version = "1.0.89" }
Expand Down
21 changes: 17 additions & 4 deletions crates/edr_provider/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ use std::{path::PathBuf, time::SystemTime};

use edr_eth::{AccountInfo, Address, HashMap, SpecId, U256};
use edr_evm::MineOrdering;
use rand::Rng;
use rpc_hardhat::config::ForkConfig;

/// Configuration for interval mining.
#[derive(Clone)]
pub enum IntervalConfig {
Fixed(i64),
Range { min: i64, max: i64 },
Fixed(u64),
Range { min: u64, max: u64 },
}

impl IntervalConfig {
/// Generates a (random) interval based on the configuration.
pub fn generate_interval(&self) -> u64 {
match self {
IntervalConfig::Fixed(interval) => *interval,
IntervalConfig::Range { min, max } => rand::thread_rng().gen_range(*min..=*max),
}
}
}

/// Configuration for the provider's mempool.
Expand All @@ -17,7 +30,7 @@ pub struct MemPoolConfig {
/// Configuration for the provider's miner.
pub struct MiningConfig {
pub auto_mine: bool,
pub interval: IntervalConfig,
pub interval: Option<IntervalConfig>,
pub mem_pool: MemPoolConfig,
}

Expand Down Expand Up @@ -60,7 +73,7 @@ impl Default for MiningConfig {
fn default() -> Self {
Self {
auto_mine: true,
interval: IntervalConfig::Fixed(0),
interval: None,
mem_pool: MemPoolConfig::default(),
}
}
Expand Down
25 changes: 25 additions & 0 deletions crates/edr_provider/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,31 @@ impl ProviderData {
&self.instance_id
}

pub fn interval_mine(&mut self) -> Result<bool, ProviderError> {
let result = self.mine_and_commit_block(None)?;

let header = result.block.header();
let is_empty = result.block.transactions().is_empty();
if is_empty {
self.logger.print_interval_mined_block_number(
header.number,
is_empty,
header.base_fee_per_gas,
);
} else {
log::error!("TODO: interval_mine: log mined block");

self.logger
.print_interval_mined_block_number(header.number, is_empty, None);

if self.logger.print_logs() {
self.logger.print_empty_line();
}
}

Ok(true)
}

pub fn logger(&self) -> &Logger {
&self.logger
}
Expand Down
65 changes: 65 additions & 0 deletions crates/edr_provider/src/interval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::{
sync::Arc,
thread::{self, JoinHandle},
};

use parking_lot::Mutex;
use tokio::sync::oneshot::{self, error::TryRecvError};

use crate::{data::ProviderData, IntervalConfig};

/// Type for interval mining on a separate thread.
pub struct IntervalMiner {
inner: Option<Inner>,
}

/// Inner type for interval mining on a separate thread, required for
/// implementation of `Drop`.
struct Inner {
cancellation_sender: oneshot::Sender<()>,
handle: JoinHandle<()>,
}

impl IntervalMiner {
pub fn new(config: IntervalConfig, data: Arc<Mutex<ProviderData>>) -> Self {
let (cancellation_sender, mut cancellation_receiver) = oneshot::channel();
let handle = thread::spawn(move || loop {
let delay = config.generate_interval();
thread::sleep(std::time::Duration::from_secs(delay));

match cancellation_receiver.try_recv() {
Ok(_) | Err(TryRecvError::Closed) => return,
Err(TryRecvError::Empty) => {}
}

let mut data = data.lock();
if let Err(error) = data.interval_mine() {
log::error!("Unexpected error while performing interval mining: {error}");
return;
}
});

Self {
inner: Some(Inner {
cancellation_sender,
handle,
}),
}
}
}

impl Drop for IntervalMiner {
fn drop(&mut self) {
if let Some(Inner {
cancellation_sender,
handle,
}) = self.inner.take()
{
cancellation_sender
.send(())
.expect("Failed to send cancellation signal");

handle.join().expect("Failed to join interval miner thread");
}
}
}
25 changes: 20 additions & 5 deletions crates/edr_provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@ mod config;
mod data;
mod error;
mod filter;
mod interval;
mod logger;
mod requests;
/// Test utilities
#[cfg(test)]
pub mod test_utils;

use data::{CreationError, ProviderData};
use std::sync::Arc;

use parking_lot::Mutex;
use requests::{eth, hardhat};
use tokio::runtime;

use self::requests::{EthRequest, Request};
pub use self::{config::*, error::ProviderError, requests::ProviderRequest};
use self::{
data::{CreationError, ProviderData},
interval::IntervalMiner,
requests::{eth, hardhat, EthRequest, Request},
};

/// A JSON-RPC provider for Ethereum.
///
Expand Down Expand Up @@ -47,7 +52,9 @@ pub use self::{config::*, error::ProviderError, requests::ProviderRequest};
/// }
/// ```
pub struct Provider {
data: Mutex<ProviderData>,
data: Arc<Mutex<ProviderData>>,
/// Interval miner runs in the background, if enabled.
_interval_miner: Option<IntervalMiner>,
}

impl Provider {
Expand All @@ -57,9 +64,17 @@ impl Provider {
config: &ProviderConfig,
) -> Result<Self, CreationError> {
let data = ProviderData::new(runtime, config).await?;
let data = Arc::new(Mutex::new(data));

let interval_miner = config
.mining
.interval
.as_ref()
.map(|config| IntervalMiner::new(config.clone(), data.clone()));

Ok(Self {
data: Mutex::new(data),
data,
_interval_miner: interval_miner,
})
}

Expand Down
4 changes: 3 additions & 1 deletion crates/edr_provider/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ impl Logger {
pub fn print_method(&self, _method: &str) {}

/// Prints all accumulated logs.
pub fn print_logs(&self) {}
pub fn print_logs(&self) -> bool {
false
}

/// Prints the block number of an interval-mined block.
pub fn print_interval_mined_block_number(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import {
SuccessReason,
IntervalRange,
} from "@ignored/edr";
import { isNumber } from "lodash";
import { fromBigIntLike, toHex } from "../../../util/bigint";
import { HardforkName, hardforkGte } from "../../../util/hardforks";
import {
Expand Down Expand Up @@ -262,13 +261,18 @@ export function ethereumjsHeaderDataToEdrBlockOptions(

export function ethereumjsIntervalMiningConfigToEdr(
config: IntervalMiningConfig
): number | IntervalRange {
if (isNumber(config)) {
return config;
): bigint | IntervalRange | undefined {
if (typeof config === "number") {
// Is interval mining disabled?
if (config === 0) {
return undefined;
} else {
return BigInt(config);
}
} else {
return {
min: config[0],
max: config[1],
min: BigInt(config[0]),
max: BigInt(config[1]),
};
}
}
Expand Down

0 comments on commit 8ceda5b

Please sign in to comment.