Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Fortuna): add finality checks #1322

Merged
merged 13 commits into from
Feb 27, 2024
Merged
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
2 changes: 1 addition & 1 deletion fortuna/Cargo.lock

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

2 changes: 1 addition & 1 deletion fortuna/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fortuna"
version = "3.2.4"
version = "3.3.4"
edition = "2021"

[dependencies]
Expand Down
35 changes: 22 additions & 13 deletions fortuna/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use {
crate::{
chain::reader::{
BlockNumber,
BlockStatus,
EntropyReader,
},
state::HashChainState,
Expand Down Expand Up @@ -73,14 +74,17 @@ impl ApiState {
#[derive(Clone)]
pub struct BlockchainState {
/// The hash chain(s) required to serve random numbers for this blockchain
pub state: Arc<HashChainState>,
pub state: Arc<HashChainState>,
/// The contract that the server is fulfilling requests for.
pub contract: Arc<dyn EntropyReader>,
pub contract: Arc<dyn EntropyReader>,
/// The address of the provider that this server is operating for.
pub provider_address: Address,
pub provider_address: Address,
/// The server will wait for this many block confirmations of a request before revealing
/// the random number.
pub reveal_delay_blocks: BlockNumber,
pub reveal_delay_blocks: BlockNumber,
/// The BlockStatus of the block that is considered to be confirmed on the blockchain.
/// For eg., Finalized, Safe
pub confirmed_block_status: BlockStatus,
}

pub struct Metrics {
Expand Down Expand Up @@ -203,7 +207,10 @@ mod test {
BlockchainState,
GetRandomValueResponse,
},
chain::reader::mock::MockEntropyReader,
chain::reader::{
mock::MockEntropyReader,
BlockStatus,
},
state::{
HashChainState,
PebbleHashChain,
Expand Down Expand Up @@ -238,19 +245,21 @@ mod test {
let eth_read = Arc::new(MockEntropyReader::with_requests(10, &[]));

let eth_state = BlockchainState {
state: ETH_CHAIN.clone(),
contract: eth_read.clone(),
provider_address: PROVIDER,
reveal_delay_blocks: 1,
state: ETH_CHAIN.clone(),
contract: eth_read.clone(),
provider_address: PROVIDER,
reveal_delay_blocks: 1,
confirmed_block_status: BlockStatus::Latest,
};

let avax_read = Arc::new(MockEntropyReader::with_requests(10, &[]));

let avax_state = BlockchainState {
state: AVAX_CHAIN.clone(),
contract: avax_read.clone(),
provider_address: PROVIDER,
reveal_delay_blocks: 2,
state: AVAX_CHAIN.clone(),
contract: avax_read.clone(),
provider_address: PROVIDER,
reveal_delay_blocks: 2,
confirmed_block_status: BlockStatus::Latest,
};

let api_state = ApiState::new(&[
Expand Down
4 changes: 3 additions & 1 deletion fortuna/src/api/revelation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ pub async fn revelation(

let maybe_request_fut = state.contract.get_request(state.provider_address, sequence);

let current_block_number_fut = state.contract.get_block_number();
let current_block_number_fut = state
.contract
.get_block_number(state.confirmed_block_status);

let (maybe_request, current_block_number) =
try_join!(maybe_request_fut, current_block_number_fut).map_err(|e| {
Expand Down
31 changes: 22 additions & 9 deletions fortuna/src/chain/ethereum.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use {
crate::{
chain::{
reader,
reader::{
BlockNumber,
EntropyReader,
},
chain::reader::{
self,
BlockNumber,
BlockStatus,
EntropyReader,
},
config::EthereumConfig,
},
anyhow::{
anyhow,
Error,
Result,
},
axum::async_trait,
Expand Down Expand Up @@ -39,7 +39,10 @@ use {
LocalWallet,
Signer,
},
types::transaction::eip2718::TypedTransaction,
types::{
transaction::eip2718::TypedTransaction,
BlockNumber as EthersBlockNumber,
},
},
sha3::{
Digest,
Expand Down Expand Up @@ -209,7 +212,17 @@ impl EntropyReader for PythContract {
}
}

async fn get_block_number(&self) -> Result<BlockNumber> {
Ok(self.client().get_block_number().await?.as_u64())
async fn get_block_number(&self, confirmed_block_status: BlockStatus) -> Result<BlockNumber> {
let block_number: EthersBlockNumber = confirmed_block_status.into();
0xfirefist marked this conversation as resolved.
Show resolved Hide resolved
let block = self
.client()
.get_block(block_number)
.await?
.ok_or_else(|| Error::msg("pending block confirmation"))?;

Ok(block
.number
.ok_or_else(|| Error::msg("pending confirmation"))?
.as_u64())
}
}
36 changes: 33 additions & 3 deletions fortuna/src/chain/reader.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
use {
anyhow::Result,
axum::async_trait,
ethers::types::Address,
ethers::types::{
Address,
BlockNumber as EthersBlockNumber,
},
};

pub type BlockNumber = u64;

#[derive(
Copy, Clone, Debug, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize,
)]
pub enum BlockStatus {
/// Latest block
#[default]
Latest,
/// Finalized block accepted as canonical
Finalized,
/// Safe head block
Safe,
}

impl Into<EthersBlockNumber> for BlockStatus {
fn into(self) -> EthersBlockNumber {
match self {
BlockStatus::Latest => EthersBlockNumber::Latest,
BlockStatus::Finalized => EthersBlockNumber::Finalized,
BlockStatus::Safe => EthersBlockNumber::Safe,
}
}
}

/// EntropyReader is the read-only interface of the Entropy contract.
#[async_trait]
pub trait EntropyReader: Send + Sync {
Expand All @@ -15,7 +41,7 @@ pub trait EntropyReader: Send + Sync {
async fn get_request(&self, provider: Address, sequence_number: u64)
-> Result<Option<Request>>;

async fn get_block_number(&self) -> Result<BlockNumber>;
async fn get_block_number(&self, confirmed_block_status: BlockStatus) -> Result<BlockNumber>;
}

/// An in-flight request stored in the contract.
Expand All @@ -36,6 +62,7 @@ pub mod mock {
use {
crate::chain::reader::{
BlockNumber,
BlockStatus,
EntropyReader,
Request,
},
Expand Down Expand Up @@ -114,7 +141,10 @@ pub mod mock {
.map(|r| (*r).clone()))
}

async fn get_block_number(&self) -> Result<BlockNumber> {
async fn get_block_number(
&self,
confirmed_block_status: BlockStatus,
) -> Result<BlockNumber> {
Ok(*self.block_number.read().unwrap())
}
}
Expand Down
1 change: 1 addition & 0 deletions fortuna/src/command/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
contract,
provider_address: opts.provider,
reveal_delay_blocks: chain_config.reveal_delay_blocks,
confirmed_block_status: chain_config.confirmed_block_status,
};

chains.insert(chain_id.clone(), state);
Expand Down
15 changes: 13 additions & 2 deletions fortuna/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use {
crate::{
api::ChainId,
chain::reader::BlockNumber,
chain::reader::{
BlockNumber,
BlockStatus,
},
},
anyhow::{
anyhow,
Expand Down Expand Up @@ -131,10 +134,18 @@ pub struct EthereumConfig {
/// Address of a Pyth Randomness contract to interact with.
pub contract_addr: Address,

/// How many blocks to wait before revealing the random number.
/// reveal_delay_blocks - The difference between the block number with the
/// confirmed_block_status(see below) and the block number of a request to
/// Entropy should be greater than `reveal_delay_blocks` for Fortuna to reveal
/// its commitment.
pub reveal_delay_blocks: BlockNumber,

/// Use the legacy transaction format (for networks without EIP 1559)
#[serde(default)]
pub legacy_tx: bool,

/// The BlockStatus of the block that is considered confirmed.
/// For example, Finalized, Safe, Latest
#[serde(default)]
pub confirmed_block_status: BlockStatus,
}
Loading