feat(Fortuna): add finality checks (#1322)

This commit is contained in:
Dev Kalra 2024-02-27 19:08:16 +05:30 committed by GitHub
parent 281ef68753
commit eaaa74a44e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 96 additions and 30 deletions

2
fortuna/Cargo.lock generated
View File

@ -1486,7 +1486,7 @@ dependencies = [
[[package]]
name = "fortuna"
version = "3.2.4"
version = "3.3.4"
dependencies = [
"anyhow",
"axum",

View File

@ -1,6 +1,6 @@
[package]
name = "fortuna"
version = "3.2.4"
version = "3.3.4"
edition = "2021"
[dependencies]

View File

@ -2,6 +2,7 @@ use {
crate::{
chain::reader::{
BlockNumber,
BlockStatus,
EntropyReader,
},
state::HashChainState,
@ -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 {
@ -203,7 +207,10 @@ mod test {
BlockchainState,
GetRandomValueResponse,
},
chain::reader::mock::MockEntropyReader,
chain::reader::{
mock::MockEntropyReader,
BlockStatus,
},
state::{
HashChainState,
PebbleHashChain,
@ -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(&[

View File

@ -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| {

View File

@ -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,
@ -39,7 +39,10 @@ use {
LocalWallet,
Signer,
},
types::transaction::eip2718::TypedTransaction,
types::{
transaction::eip2718::TypedTransaction,
BlockNumber as EthersBlockNumber,
},
},
sha3::{
Digest,
@ -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();
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())
}
}

View File

@ -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 {
@ -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.
@ -36,6 +62,7 @@ pub mod mock {
use {
crate::chain::reader::{
BlockNumber,
BlockStatus,
EntropyReader,
Request,
},
@ -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())
}
}

View File

@ -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);

View File

@ -1,7 +1,10 @@
use {
crate::{
api::ChainId,
chain::reader::BlockNumber,
chain::reader::{
BlockNumber,
BlockStatus,
},
},
anyhow::{
anyhow,
@ -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,
}