Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,605 changes: 2,083 additions & 1,522 deletions apps/fortuna/Cargo.lock

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions apps/fortuna/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ version = "7.4.1"
edition = "2021"

[dependencies]
alloy = { version = "0.11.0", features = ["full"] }
anyhow = "1.0.75"
axum = { version = "0.6.20", features = ["json", "ws", "macros"] }
axum-macros = { version = "0.3.8" }
axum = { version = "0.7.1", features = ["json", "ws", "macros"] }
axum-macros = { version = "0.5.0" }
base64 = { version = "0.21.0" }
bincode = "1.3.3"
byteorder = "1.5.0"
clap = { version = "4.4.6", features = ["derive", "cargo", "env"] }
ethabi = "18.0.0"
ethers = { version = "2.0.14", features = ["ws"] }
futures = { version = "0.3.28" }
hex = "0.4.3"
prometheus-client = { version = "0.21.2" }
Expand All @@ -26,11 +26,11 @@ serde_with = { version = "3.4.0", features = ["hex", "base64"] }
serde_yaml = "0.9.25"
sha3 = "0.10.8"
tokio = { version = "1.33.0", features = ["full"] }
tower-http = { version = "0.4.0", features = ["cors"] }
tower-http = { version = "0.5.0", features = ["cors"] }
tracing = { version = "0.1.37", features = ["log"] }
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
utoipa = { version = "3.4.0", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "3.1.4", features = ["axum"] }
utoipa = { version = "4", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "7", features = ["axum"] }
once_cell = "1.18.0"
lazy_static = "1.4.0"
url = "2.5.0"
Expand All @@ -41,7 +41,8 @@ chrono = { version = "0.4.38", features = [
backoff = { version = "0.4.0", features = ["futures", "tokio"] }
thiserror = "1.0.61"
futures-locks = "0.7.1"
eyre = "0.6.12"


[dev-dependencies]
axum-test = "13.1.1"
axum-test = "14"
11 changes: 5 additions & 6 deletions apps/fortuna/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ use {
chain::reader::{BlockNumber, BlockStatus, EntropyReader},
state::HashChainState,
},
alloy::primitives::Address,
anyhow::Result,
axum::{
body::Body,
http::StatusCode,
response::{IntoResponse, Response},
routing::get,
Router,
},
ethers::core::types::Address,
prometheus_client::{
encoding::EncodeLabelSet,
metrics::{counter::Counter, family::Family},
Expand Down Expand Up @@ -146,7 +145,7 @@ impl IntoResponse for RestError {
}
}

pub fn routes(state: ApiState) -> Router<(), Body> {
pub fn routes(state: ApiState) -> Router {
Router::new()
.route("/", get(index))
.route("/live", get(live))
Expand Down Expand Up @@ -178,18 +177,18 @@ mod test {
chain::reader::{mock::MockEntropyReader, BlockStatus},
state::{HashChainState, PebbleHashChain},
},
alloy::primitives::Address,
axum::http::StatusCode,
axum_test::{TestResponse, TestServer},
ethers::prelude::Address,
lazy_static::lazy_static,
prometheus_client::registry::Registry,
std::{collections::HashMap, sync::Arc},
tokio::sync::RwLock,
};

const PROVIDER: Address = Address::zero();
const PROVIDER: Address = Address::ZERO;
lazy_static! {
static ref OTHER_PROVIDER: Address = Address::from_low_u64_be(1);
static ref OTHER_PROVIDER: Address = Address::with_last_byte(1);
// Note: these chains are immutable. They are wrapped in Arc because we need Arcs to
// initialize the BlockchainStates below, but they aren't cloneable (nor do they need to be cloned).
static ref ETH_CHAIN: Arc<HashChainState> = Arc::new(HashChainState::from_chain_at_offset(
Expand Down
29 changes: 10 additions & 19 deletions apps/fortuna/src/chain/eth_gas_oracle.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use {
alloy::primitives::{I256, U256},
alloy::providers::Provider,
alloy::rpc::client::RpcClient,
axum::async_trait,
ethers::{
prelude::{
gas_oracle::{GasOracleError, Result},
GasOracle,
},
providers::Middleware,
types::{I256, U256},
},
eyre::Result,
};

// The default fee estimation logic in ethers.rs includes some hardcoded constants that do not
Expand All @@ -33,17 +29,15 @@ pub const SURGE_THRESHOLD_3: u64 = 200_000;
/// under it.
pub const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64 = 200;

/// Gas oracle from a [`Middleware`] implementation such as an
/// Ethereum RPC provider.
#[derive(Clone, Debug)]
#[must_use]
pub struct EthProviderOracle<M: Middleware> {
provider: M,
pub struct EthProviderOracle {
provider: Box<dyn Provider<RpcClient>>,
priority_fee_multiplier_pct: u64,
}

impl<M: Middleware> EthProviderOracle<M> {
pub fn new(provider: M, priority_fee_multiplier_pct: u64) -> Self {
impl EthProviderOracle {
pub fn new(provider: Box<dyn Provider<RpcClient>>, priority_fee_multiplier_pct: u64) -> Self {
Self {
provider,
priority_fee_multiplier_pct,
Expand All @@ -53,10 +47,7 @@ impl<M: Middleware> EthProviderOracle<M> {

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<M: Middleware> GasOracle for EthProviderOracle<M>
where
M::Error: 'static,
{
impl GasOracle for EthProviderOracle {
async fn fetch(&self) -> Result<U256> {
self.provider
.get_gas_price()
Expand All @@ -67,7 +58,7 @@ where
async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> {
let (max_fee_per_gas, max_priority_fee_per_gas) = self
.provider
.estimate_eip1559_fees(Some(eip1559_default_estimator))
.estimate_eip1559_fees(None)
.await
.map_err(|err| GasOracleError::ProviderError(Box::new(err)))?;

Expand Down
49 changes: 26 additions & 23 deletions apps/fortuna/src/chain/ethereum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,24 @@ use {
},
config::EthereumConfig,
},
alloy::{
primitives::Address,
providers::{Provider, ProviderBuilder},
sol,
sol_types::SolEventInterface,
},
anyhow::{anyhow, Error, Result},
axum::async_trait,
ethers::{
abi::RawLog,
contract::{abigen, ContractCall, EthLogDecode},
core::types::Address,
middleware::{gas_oracle::GasOracleMiddleware, MiddlewareError, SignerMiddleware},
prelude::{BlockId, JsonRpcClient, PendingTransaction, TransactionRequest},
providers::{Http, Middleware, Provider},
signers::{LocalWallet, Signer},
types::{transaction::eip2718::TypedTransaction, BlockNumber as EthersBlockNumber, U256},
},
sha3::{Digest, Keccak256},
std::sync::Arc,
thiserror::Error,
url::Url,
};

// TODO: Programmatically generate this so we don't have to keep committed ABI in sync with the
// contract in the same repo.
abigen!(
sol!(
#[sol(rpc)]
PythRandom,
"../../target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json"
);
Expand All @@ -39,7 +37,7 @@ pub type MiddlewaresWrapper<T> = LegacyTxMiddleware<
EthProviderOracle<Provider<T>>,
>,
>;
pub type SignablePythContractInner<T> = PythRandom<MiddlewaresWrapper<T>>;
pub type SignablePythContractInner<T> = PythRandom::PythRandomInstance<MiddlewaresWrapper<T>>;
pub type SignablePythContract = SignablePythContractInner<Http>;
pub type InstrumentedSignablePythContract = SignablePythContractInner<TracedClient>;

Expand All @@ -48,6 +46,8 @@ pub type PythContractCall = ContractCall<MiddlewaresWrapper<TracedClient>, ()>;
pub type PythContract = PythRandom<Provider<Http>>;
pub type InstrumentedPythContract = PythRandom<Provider<TracedClient>>;

pub type SignablePythContractInner<T, P> = PythRandom::PythRandomInstance<T, P>;

/// Middleware that converts a transaction into a legacy transaction if use_legacy_tx is true.
/// We can not use TransformerMiddleware because keeper calls fill_transaction first which bypasses
/// the transformer.
Expand Down Expand Up @@ -127,15 +127,15 @@ impl<M: Middleware> Middleware for LegacyTxMiddleware<M> {
}
}

impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
impl<T: JsonRpcClient + 'static + Clone, P: Provider> SignablePythContractInner<T, P> {
/// Get the wallet that signs transactions sent to this contract.
pub fn wallet(&self) -> LocalWallet {
self.client().inner().inner().inner().signer().clone()
}

/// Get the underlying provider that communicates with the blockchain.
pub fn provider(&self) -> Provider<T> {
self.client().inner().inner().inner().provider().clone()
pub fn provider(&self) -> Box<dyn Provider<T>> {
Box::new(self.client().provider().clone())
}

/// Submit a request for a random number to the contract.
Expand All @@ -148,7 +148,7 @@ impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
user_randomness: &[u8; 32],
use_blockhash: bool,
) -> Result<u64> {
let fee = self.get_fee(*provider).call().await?;
let fee = self.getFee(*provider).call().await?;

let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into();

Expand All @@ -161,8 +161,10 @@ impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
{
// Extract Log from TransactionReceipt.
let l: RawLog = r.logs[0].clone().into();
if let PythRandomEvents::RequestedFilter(r) = PythRandomEvents::decode_log(&l)? {
Ok(r.request.sequence_number)
if let PythRandom::PythRandomEvents::Requested(r) =
PythRandom::PythRandomEvents::decode_log(&l, true)?.data
{
Ok(r.request.sequenceNumber)
} else {
Err(anyhow!("No log with sequence number"))
}
Expand Down Expand Up @@ -193,10 +195,10 @@ impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
.await?
.await?
{
if let PythRandomEvents::RevealedFilter(r) =
PythRandomEvents::decode_log(&r.logs[0].clone().into())?
if let PythRandom::PythRandomEvents::Revealed(r) =
PythRandom::PythRandomEvents::decode_log(&r.logs[0].clone().into(), true)?.data
{
Ok(r.random_number)
Ok(r.randomNumber.into())
} else {
Err(anyhow!("No log with randomnumber"))
}
Expand Down Expand Up @@ -234,8 +236,9 @@ impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {

impl SignablePythContract {
pub async fn from_config(chain_config: &EthereumConfig, private_key: &str) -> Result<Self> {
let provider = Provider::<Http>::try_from(&chain_config.geth_rpc_addr)?;
Self::from_config_and_provider(chain_config, private_key, provider).await
let p = ProviderBuilder::new().on_http(Url::parse(&chain_config.geth_rpc_addr)?);
// let provider = Provider::<Http>::try_from(&chain_config.geth_rpc_addr)?;
Self::from_config_and_provider(chain_config, private_key, p).await
}
}

Expand Down
4 changes: 0 additions & 4 deletions apps/fortuna/src/chain/nonce_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@

use {
axum::async_trait,
ethers::{
providers::{Middleware, MiddlewareError, PendingTransaction},
types::{transaction::eip2718::TypedTransaction, *},
},
std::sync::atomic::{AtomicBool, AtomicU64, Ordering},
thiserror::Error,
};
Expand Down
13 changes: 7 additions & 6 deletions apps/fortuna/src/chain/reader.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use {
alloy::primitives::{Address, U256},
alloy::rpc::types::BlockNumberOrTag,
anyhow::Result,
axum::async_trait,
ethers::types::{Address, BlockNumber as EthersBlockNumber, U256},
};

pub type BlockNumber = u64;
Expand All @@ -19,12 +20,12 @@ pub enum BlockStatus {
Safe,
}

impl From<BlockStatus> for EthersBlockNumber {
impl From<BlockStatus> for BlockNumberOrTag {
fn from(val: BlockStatus) -> Self {
match val {
BlockStatus::Latest => EthersBlockNumber::Latest,
BlockStatus::Finalized => EthersBlockNumber::Finalized,
BlockStatus::Safe => EthersBlockNumber::Safe,
BlockStatus::Latest => BlockNumberOrTag::Latest,
BlockStatus::Finalized => BlockNumberOrTag::Finalized,
BlockStatus::Safe => BlockNumberOrTag::Safe,
}
}
}
Expand Down Expand Up @@ -80,9 +81,9 @@ pub struct Request {
pub mod mock {
use {
crate::chain::reader::{BlockNumber, BlockStatus, EntropyReader, Request},
alloy::primitives::{Address, U256},
anyhow::Result,
axum::async_trait,
ethers::types::{Address, U256},
std::sync::RwLock,
};

Expand Down
4 changes: 0 additions & 4 deletions apps/fortuna/src/chain/traced_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ use {
crate::api::ChainId,
anyhow::Result,
axum::async_trait,
ethers::{
prelude::Http,
providers::{HttpClientError, JsonRpcClient, Provider},
},
prometheus_client::{
encoding::EncodeLabelSet,
metrics::{counter::Counter, family::Family, histogram::Histogram},
Expand Down
5 changes: 0 additions & 5 deletions apps/fortuna/src/command/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ use {
config::{Config, EthereumConfig, InspectOptions},
},
anyhow::Result,
ethers::{
contract::Multicall,
middleware::Middleware,
prelude::{Http, Provider},
},
};

pub async fn inspect(opts: &InspectOptions) -> Result<()> {
Expand Down
17 changes: 7 additions & 10 deletions apps/fortuna/src/command/register_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ use {
config::{Config, EthereumConfig, ProviderConfig, RegisterProviderOptions},
state::PebbleHashChain,
},
anyhow::{anyhow, Result},
ethers::{
abi::Bytes,
signers::{LocalWallet, Signer},
types::U256,
alloy::{
primitives::{Address, Bytes, U256},
signers::local::PrivateKeySigner,
},
anyhow::{anyhow, Result},
std::sync::Arc,
};

Expand Down Expand Up @@ -55,7 +54,7 @@ pub async fn register_provider_from_config(
let chain = PebbleHashChain::from_config(
&secret,
chain_id,
&private_key_string.parse::<LocalWallet>()?.address(),
&Address::from(private_key_string.parse::<PrivateKeySigner>()?.address()),
&chain_config.contract_addr,
&random,
commitment_length,
Expand All @@ -76,11 +75,9 @@ pub async fn register_provider_from_config(
let call = contract.register(
fee_in_wei,
commitment,
bincode::serialize(&commitment_metadata)?.into(),
Bytes::from(bincode::serialize(&commitment_metadata)?),
commitment_length,
// Use Bytes to serialize the uri. Most users will be using JS/TS to deserialize this uri.
// Bincode is a different encoding mechanisms, and I didn't find any JS/TS library to parse bincode.
Bytes::from(uri.as_str()).into(),
Bytes::from(uri.as_bytes()),
);
let mut gas_estimate = call.estimate_gas().await?;
let gas_multiplier = U256::from(2); //TODO: smarter gas estimation
Expand Down
Loading
Loading