Skip to content

Tb/fortuna/use requested v2 event #2845

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

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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 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 apps/fortuna/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fortuna"
version = "8.2.6"
version = "9.0.0"
edition = "2021"

[lib]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Remove callback_failed, callback_return_value, and callback_gas_used from the requests table.
ALTER TABLE request DROP COLUMN callback_failed;
ALTER TABLE request DROP COLUMN callback_return_value;
ALTER TABLE request DROP COLUMN callback_gas_used;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Add callback_failed, callback_return_value, and callback_gas_used to the requests table.
ALTER TABLE request ADD COLUMN callback_failed INTEGER;
ALTER TABLE request ADD COLUMN callback_return_value VARCHAR;
ALTER TABLE request ADD COLUMN callback_gas_used VARCHAR(100);
49 changes: 17 additions & 32 deletions apps/fortuna/src/chain/ethereum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
use {
crate::{
api::ChainId,
chain::reader::{
self, BlockNumber, BlockStatus, EntropyReader, EntropyRequestInfo,
RequestedWithCallbackEvent,
},
chain::reader::{self, BlockNumber, BlockStatus, EntropyReader, RequestedV2Event},
config::EthereumConfig,
eth_utils::{
eth_gas_oracle::EthProviderOracle,
Expand Down Expand Up @@ -274,17 +271,14 @@ impl<T: JsonRpcClient + 'static> EntropyReader for PythRandom<Provider<T>> {
.get_request_v2(provider_address, sequence_number)
.call()
.await?;
if request.sequence_number == 0 {
Ok(None)
} else {
Ok(Some(reader::Request {
provider: request.provider,
sequence_number: request.sequence_number,
block_number: request.block_number,
use_blockhash: request.use_blockhash,
callback_status: reader::RequestCallbackStatus::try_from(request.callback_status)?,
}))
}
Ok(Some(reader::Request {
provider: request.provider,
sequence_number: request.sequence_number,
block_number: request.block_number,
use_blockhash: request.use_blockhash,
callback_status: reader::RequestCallbackStatus::try_from(request.callback_status)?,
gas_limit_10k: request.gas_limit_1_0k,
}))
}

async fn get_block_number(&self, confirmed_block_status: BlockStatus) -> Result<BlockNumber> {
Expand All @@ -306,33 +300,24 @@ impl<T: JsonRpcClient + 'static> EntropyReader for PythRandom<Provider<T>> {
from_block: BlockNumber,
to_block: BlockNumber,
provider: Address,
) -> Result<Vec<RequestedWithCallbackEvent>> {
let mut event = self.requested_with_callback_filter();
) -> Result<Vec<RequestedV2Event>> {
let mut event = self.requested_2_filter();
event.filter = event
.filter
.address(self.address())
.from_block(from_block)
.to_block(to_block)
.topic1(provider);

let res: Vec<(RequestedWithCallbackFilter, LogMeta)> = event.query_with_meta().await?;
let res: Vec<(Requested2Filter, LogMeta)> = event.query_with_meta().await?;
Ok(res
.into_iter()
.map(|(r, meta)| RequestedWithCallbackEvent {
.map(|(r, meta)| RequestedV2Event {
sequence_number: r.sequence_number,
user_random_number: r.user_random_number,
provider_address: r.request.provider,
requestor: r.requestor,
request: EntropyRequestInfo {
provider: r.request.provider,
sequence_number: r.request.sequence_number,
num_hashes: r.request.num_hashes,
commitment: r.request.commitment,
block_number: r.request.block_number,
requester: r.request.requester,
use_blockhash: r.request.use_blockhash,
is_request_with_callback: r.request.is_request_with_callback,
},
user_random_number: r.user_contribution,
provider_address: r.provider,
sender: r.caller,
gas_limit: r.gas_limit,
log_meta: meta,
})
.filter(|r| r.provider_address == provider)
Expand Down
14 changes: 9 additions & 5 deletions apps/fortuna/src/chain/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ pub struct EntropyRequestInfo {
}

#[derive(Clone)]
pub struct RequestedWithCallbackEvent {
pub struct RequestedV2Event {
pub sequence_number: u64,
pub user_random_number: [u8; 32],
pub provider_address: Address,
pub requestor: Address,
pub request: EntropyRequestInfo,
pub sender: Address,
pub gas_limit: u32,
pub log_meta: LogMeta,
}

Expand All @@ -73,7 +73,7 @@ pub trait EntropyReader: Send + Sync {
from_block: BlockNumber,
to_block: BlockNumber,
provider: Address,
) -> Result<Vec<RequestedWithCallbackEvent>>;
) -> Result<Vec<RequestedV2Event>>;

/// Estimate the gas required to reveal a random number with a callback.
async fn estimate_reveal_with_callback_gas(
Expand All @@ -97,6 +97,8 @@ pub struct Request {
pub block_number: BlockNumber,
pub use_blockhash: bool,
pub callback_status: RequestCallbackStatus,
/// The gas limit for the request, in 10k gas units. (i.e., 2 = 20k gas).
pub gas_limit_10k: u16,
}

/// Status values for Request.callback_status
Expand Down Expand Up @@ -169,6 +171,7 @@ pub mod mock {
block_number: b,
use_blockhash: u,
callback_status: RequestCallbackStatus::CallbackNotNecessary,
gas_limit_10k: 0,
})
.collect(),
),
Expand All @@ -189,6 +192,7 @@ pub mod mock {
block_number,
use_blockhash,
callback_status: RequestCallbackStatus::CallbackNotNecessary,
gas_limit_10k: 0,
});
self
}
Expand Down Expand Up @@ -227,7 +231,7 @@ pub mod mock {
_from_block: BlockNumber,
_to_block: BlockNumber,
_provider: Address,
) -> Result<Vec<super::RequestedWithCallbackEvent>> {
) -> Result<Vec<super::RequestedV2Event>> {
Ok(vec![])
}

Expand Down
21 changes: 19 additions & 2 deletions apps/fortuna/src/eth_utils/utils.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use {
crate::{
chain::ethereum::InstrumentedSignablePythContract, eth_utils::nonce_manager::NonceManaged,
chain::ethereum::{InstrumentedSignablePythContract, PythRandomEvents, Revealed2Filter},
eth_utils::nonce_manager::NonceManaged,
},
anyhow::{anyhow, Result},
backoff::ExponentialBackoff,
ethabi::ethereum_types::U64,
ethers::{
contract::{ContractCall, ContractError},
contract::{ContractCall, ContractError, EthLogDecode},
middleware::Middleware,
providers::{MiddlewareError, ProviderError},
signers::Signer,
Expand All @@ -30,6 +31,7 @@ pub struct SubmitTxResult {
pub fee_multiplier: u64,
pub duration: Duration,
pub receipt: TransactionReceipt,
pub revealed_event: Revealed2Filter,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -186,11 +188,26 @@ pub async fn submit_tx_with_backoff<T: Middleware + NonceManaged + 'static>(
let duration = start_time.elapsed();
let num_retries = num_retries.load(std::sync::atomic::Ordering::Relaxed);

let revealed_event: Revealed2Filter = {
let mut found_event = None;
for log in &success.logs {
if let Ok(PythRandomEvents::Revealed2Filter(decoded_event)) =
PythRandomEvents::decode_log(&log.clone().into())
{
found_event = Some(decoded_event);
break;
}
}
// A successful reveal will always emit a Revealed v2 event, so theoretically we should never get here.
found_event.ok_or_else(|| SubmitTxError::ReceiptError(call.tx.clone(), success.clone()))?
};

Ok(SubmitTxResult {
num_retries,
fee_multiplier: escalation_policy.get_fee_multiplier_pct(num_retries),
duration,
receipt: success,
revealed_event,
})
}

Expand Down
56 changes: 50 additions & 6 deletions apps/fortuna/src/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use {
ethers::{
core::utils::hex::ToHex,
prelude::TxHash,
types::{Address, U256},
types::{Address, Bytes, U256},
utils::keccak256,
},
serde::Serialize,
Expand Down Expand Up @@ -45,6 +45,17 @@ pub enum RequestEntryState {
#[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
#[serde_as(as = "serde_with::hex::Hex")]
combined_random_number: [u8; 32],
/// Whether the callback to the caller failed.
callback_failed: bool,
/// Return value from the callback. If the callback failed, this field contains
/// the error code and any additional returned data. Note that "" often indicates an out-of-gas error.
/// If the callback returns more than 256 bytes, only the first 256 bytes of the callback return value are included.
/// NOTE: This field is the raw bytes returned from the callback, not hex-decoded. The client should decode it as needed.
callback_return_value: Bytes,
/// How much gas the callback used.
#[schema(example = "567890", value_type = String)]
#[serde(with = "crate::serde::u32")]
callback_gas_used: u32,
},
Failed {
reason: String,
Expand Down Expand Up @@ -78,8 +89,7 @@ pub struct RequestStatus {
/// Gas limit for the callback in the smallest unit of the chain.
/// For example, if the native currency is ETH, this will be in wei.
#[schema(example = "500000", value_type = String)]
#[serde(with = "crate::serde::u256")]
pub gas_limit: U256,
pub gas_limit: u32,
/// The user contribution to the random number.
#[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
#[serde_as(as = "serde_with::hex::Hex")]
Expand Down Expand Up @@ -121,6 +131,9 @@ struct RequestRow {
provider_random_number: Option<String>,
gas_used: Option<String>,
info: Option<String>,
callback_failed: Option<i64>,
callback_return_value: Option<String>,
callback_gas_used: Option<String>,
}

impl TryFrom<RequestRow> for RequestStatus {
Expand All @@ -139,7 +152,9 @@ impl TryFrom<RequestRow> for RequestStatus {
let user_random_number = hex::FromHex::from_hex(row.user_random_number)?;
let request_tx_hash = row.request_tx_hash.parse()?;
let sender = row.sender.parse()?;
let gas_limit = U256::from_dec_str(&row.gas_limit)
let gas_limit = row
.gas_limit
.parse::<u32>()
.map_err(|_| anyhow::anyhow!("Failed to parse gas limit"))?;

let state = match row.state.as_str() {
Expand Down Expand Up @@ -173,6 +188,18 @@ impl TryFrom<RequestRow> for RequestStatus {
&user_random_number,
&provider_random_number,
),
// Sqlx::Any doesn't support boolean types, so we need to convert from integer
// https://github.com/launchbadge/sqlx/issues/2778
callback_failed: row.callback_failed.unwrap_or(0) == 1,
callback_return_value: row
.callback_return_value
.map(|s| s.parse::<Bytes>().unwrap_or_default())
.unwrap_or_default(),
callback_gas_used: row
.callback_gas_used
.unwrap_or_default()
.parse::<u32>()
.map_err(|_| anyhow::anyhow!("Failed to parse callback_gas_used"))?,
}
}
"Failed" => RequestEntryState::Failed {
Expand Down Expand Up @@ -312,18 +339,29 @@ impl History {
provider_random_number,
gas_used,
combined_random_number: _,
callback_failed,
callback_return_value,
callback_gas_used,
} => {
let reveal_block_number = reveal_block_number as i64;
let reveal_tx_hash: String = reveal_tx_hash.encode_hex();
let provider_random_number: String = provider_random_number.encode_hex();
let gas_used: String = gas_used.to_string();
let result = sqlx::query("UPDATE request SET state = $1, last_updated_at = $2, reveal_block_number = $3, reveal_tx_hash = $4, provider_random_number = $5, gas_used = $6 WHERE network_id = $7 AND sequence = $8 AND provider = $9 AND request_tx_hash = $10")
// Sqlx::Any doesn't support boolean types, so we need to convert to integer
// https://github.com/launchbadge/sqlx/issues/2778
let callback_failed: i64 = if callback_failed { 1 } else { 0 };
let callback_return_value: String = callback_return_value.encode_hex();
let callback_gas_used: String = callback_gas_used.to_string();
let result = sqlx::query("UPDATE request SET state = $1, last_updated_at = $2, reveal_block_number = $3, reveal_tx_hash = $4, provider_random_number = $5, gas_used = $6, callback_failed = $7, callback_return_value = $8, callback_gas_used = $9 WHERE network_id = $10 AND sequence = $11 AND provider = $12 AND request_tx_hash = $13")
.bind("Completed")
.bind(new_status.last_updated_at.timestamp())
.bind(reveal_block_number)
.bind(reveal_tx_hash)
.bind(provider_random_number)
.bind(gas_used)
.bind(callback_failed)
.bind(callback_return_value)
.bind(callback_gas_used)
.bind(network_id)
.bind(sequence)
.bind(provider.clone())
Expand Down Expand Up @@ -646,7 +684,7 @@ mod test {
user_random_number: [20; 32],
sender: Address::random(),
state: RequestEntryState::Pending,
gas_limit: U256::from(500_000),
gas_limit: 500_000,
}
}

Expand All @@ -665,6 +703,9 @@ mod test {
&status.user_random_number,
&[40; 32],
),
callback_failed: false,
callback_return_value: Default::default(),
callback_gas_used: 100_000,
};
History::update_request_status(&history.pool, status.clone()).await;

Expand Down Expand Up @@ -876,6 +917,9 @@ mod test {
&status.user_random_number,
&[40; 32],
),
callback_failed: false,
callback_return_value: Default::default(),
callback_gas_used: 0,
};
History::update_request_status(&history.pool, status.clone()).await;
let mut failed_status = status.clone();
Expand Down
Loading