|
@@ -1,8 +1,13 @@
|
|
|
use {
|
|
use {
|
|
|
- crate::api::ChainId,
|
|
|
|
|
|
|
+ crate::api::{ChainId, NetworkId},
|
|
|
anyhow::Result,
|
|
anyhow::Result,
|
|
|
chrono::{DateTime, NaiveDateTime},
|
|
chrono::{DateTime, NaiveDateTime},
|
|
|
- ethers::{core::utils::hex::ToHex, prelude::TxHash, types::Address},
|
|
|
|
|
|
|
+ ethers::{
|
|
|
|
|
+ core::utils::hex::ToHex,
|
|
|
|
|
+ prelude::TxHash,
|
|
|
|
|
+ types::{Address, U256},
|
|
|
|
|
+ utils::keccak256,
|
|
|
|
|
+ },
|
|
|
serde::Serialize,
|
|
serde::Serialize,
|
|
|
serde_with::serde_as,
|
|
serde_with::serde_as,
|
|
|
sqlx::{migrate, Pool, Sqlite, SqlitePool},
|
|
sqlx::{migrate, Pool, Sqlite, SqlitePool},
|
|
@@ -17,6 +22,7 @@ use {
|
|
|
pub enum RequestEntryState {
|
|
pub enum RequestEntryState {
|
|
|
Pending,
|
|
Pending,
|
|
|
Completed {
|
|
Completed {
|
|
|
|
|
+ /// The block number of the reveal transaction.
|
|
|
reveal_block_number: u64,
|
|
reveal_block_number: u64,
|
|
|
/// The transaction hash of the reveal transaction.
|
|
/// The transaction hash of the reveal transaction.
|
|
|
#[schema(example = "0xfe5f880ac10c0aae43f910b5a17f98a93cdd2eb2dce3a5ae34e5827a3a071a32", value_type = String)]
|
|
#[schema(example = "0xfe5f880ac10c0aae43f910b5a17f98a93cdd2eb2dce3a5ae34e5827a3a071a32", value_type = String)]
|
|
@@ -25,9 +31,22 @@ pub enum RequestEntryState {
|
|
|
#[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
|
|
#[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
|
|
|
#[serde_as(as = "serde_with::hex::Hex")]
|
|
#[serde_as(as = "serde_with::hex::Hex")]
|
|
|
provider_random_number: [u8; 32],
|
|
provider_random_number: [u8; 32],
|
|
|
|
|
+ /// The gas used for the reveal transaction in the smallest unit of the chain.
|
|
|
|
|
+ /// For example, if the native currency is ETH, this will be in wei.
|
|
|
|
|
+ #[schema(example = "567890", value_type = String)]
|
|
|
|
|
+ #[serde(with = "crate::serde::u256")]
|
|
|
|
|
+ gas_used: U256,
|
|
|
|
|
+ /// The combined random number generated from the user and provider contributions.
|
|
|
|
|
+ #[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
|
|
|
|
|
+ #[serde_as(as = "serde_with::hex::Hex")]
|
|
|
|
|
+ combined_random_number: [u8; 32],
|
|
|
},
|
|
},
|
|
|
Failed {
|
|
Failed {
|
|
|
reason: String,
|
|
reason: String,
|
|
|
|
|
+ /// The provider contribution to the random number.
|
|
|
|
|
+ #[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
|
|
|
|
|
+ #[serde_as(as = "Option<serde_with::hex::Hex>")]
|
|
|
|
|
+ provider_random_number: Option<[u8; 32]>,
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -37,6 +56,9 @@ pub struct RequestStatus {
|
|
|
/// The chain ID of the request.
|
|
/// The chain ID of the request.
|
|
|
#[schema(example = "ethereum", value_type = String)]
|
|
#[schema(example = "ethereum", value_type = String)]
|
|
|
pub chain_id: ChainId,
|
|
pub chain_id: ChainId,
|
|
|
|
|
+ /// The network ID of the request. This is the response of eth_chainId rpc call.
|
|
|
|
|
+ #[schema(example = "1", value_type = u64)]
|
|
|
|
|
+ pub network_id: NetworkId,
|
|
|
#[schema(example = "0x6cc14824ea2918f5de5c2f75a9da968ad4bd6344", value_type = String)]
|
|
#[schema(example = "0x6cc14824ea2918f5de5c2f75a9da968ad4bd6344", value_type = String)]
|
|
|
pub provider: Address,
|
|
pub provider: Address,
|
|
|
pub sequence: u64,
|
|
pub sequence: u64,
|
|
@@ -48,6 +70,11 @@ pub struct RequestStatus {
|
|
|
/// The transaction hash of the request transaction.
|
|
/// The transaction hash of the request transaction.
|
|
|
#[schema(example = "0x5a3a984f41bb5443f5efa6070ed59ccb25edd8dbe6ce7f9294cf5caa64ed00ae", value_type = String)]
|
|
#[schema(example = "0x5a3a984f41bb5443f5efa6070ed59ccb25edd8dbe6ce7f9294cf5caa64ed00ae", value_type = String)]
|
|
|
pub request_tx_hash: TxHash,
|
|
pub request_tx_hash: TxHash,
|
|
|
|
|
+ /// 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,
|
|
|
/// The user contribution to the random number.
|
|
/// The user contribution to the random number.
|
|
|
#[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
|
|
#[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
|
|
|
#[serde_as(as = "serde_with::hex::Hex")]
|
|
#[serde_as(as = "serde_with::hex::Hex")]
|
|
@@ -58,9 +85,22 @@ pub struct RequestStatus {
|
|
|
pub state: RequestEntryState,
|
|
pub state: RequestEntryState,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+impl RequestStatus {
|
|
|
|
|
+ pub fn generate_combined_random_number(
|
|
|
|
|
+ user_random_number: &[u8; 32],
|
|
|
|
|
+ provider_random_number: &[u8; 32],
|
|
|
|
|
+ ) -> [u8; 32] {
|
|
|
|
|
+ let mut concat: [u8; 96] = [0; 96]; // last 32 bytes are for the block hash which is not used here
|
|
|
|
|
+ concat[0..32].copy_from_slice(user_random_number);
|
|
|
|
|
+ concat[32..64].copy_from_slice(provider_random_number);
|
|
|
|
|
+ keccak256(concat)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
#[derive(Clone, Debug, Serialize, ToSchema, PartialEq)]
|
|
#[derive(Clone, Debug, Serialize, ToSchema, PartialEq)]
|
|
|
struct RequestRow {
|
|
struct RequestRow {
|
|
|
chain_id: String,
|
|
chain_id: String,
|
|
|
|
|
+ network_id: i64,
|
|
|
provider: String,
|
|
provider: String,
|
|
|
sequence: i64,
|
|
sequence: i64,
|
|
|
created_at: NaiveDateTime,
|
|
created_at: NaiveDateTime,
|
|
@@ -70,9 +110,11 @@ struct RequestRow {
|
|
|
request_tx_hash: String,
|
|
request_tx_hash: String,
|
|
|
user_random_number: String,
|
|
user_random_number: String,
|
|
|
sender: String,
|
|
sender: String,
|
|
|
|
|
+ gas_limit: String,
|
|
|
reveal_block_number: Option<i64>,
|
|
reveal_block_number: Option<i64>,
|
|
|
reveal_tx_hash: Option<String>,
|
|
reveal_tx_hash: Option<String>,
|
|
|
provider_random_number: Option<String>,
|
|
provider_random_number: Option<String>,
|
|
|
|
|
+ gas_used: Option<String>,
|
|
|
info: Option<String>,
|
|
info: Option<String>,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -81,6 +123,7 @@ impl TryFrom<RequestRow> for RequestStatus {
|
|
|
|
|
|
|
|
fn try_from(row: RequestRow) -> Result<Self, Self::Error> {
|
|
fn try_from(row: RequestRow) -> Result<Self, Self::Error> {
|
|
|
let chain_id = row.chain_id;
|
|
let chain_id = row.chain_id;
|
|
|
|
|
+ let network_id = row.network_id as u64;
|
|
|
let provider = row.provider.parse()?;
|
|
let provider = row.provider.parse()?;
|
|
|
let sequence = row.sequence as u64;
|
|
let sequence = row.sequence as u64;
|
|
|
let created_at = row.created_at.and_utc();
|
|
let created_at = row.created_at.and_utc();
|
|
@@ -89,6 +132,8 @@ impl TryFrom<RequestRow> for RequestStatus {
|
|
|
let user_random_number = hex::FromHex::from_hex(row.user_random_number)?;
|
|
let user_random_number = hex::FromHex::from_hex(row.user_random_number)?;
|
|
|
let request_tx_hash = row.request_tx_hash.parse()?;
|
|
let request_tx_hash = row.request_tx_hash.parse()?;
|
|
|
let sender = row.sender.parse()?;
|
|
let sender = row.sender.parse()?;
|
|
|
|
|
+ let gas_limit = U256::from_dec_str(&row.gas_limit)
|
|
|
|
|
+ .map_err(|_| anyhow::anyhow!("Failed to parse gas limit"))?;
|
|
|
|
|
|
|
|
let state = match row.state.as_str() {
|
|
let state = match row.state.as_str() {
|
|
|
"Pending" => RequestEntryState::Pending,
|
|
"Pending" => RequestEntryState::Pending,
|
|
@@ -107,19 +152,36 @@ impl TryFrom<RequestRow> for RequestStatus {
|
|
|
))?;
|
|
))?;
|
|
|
let provider_random_number: [u8; 32] =
|
|
let provider_random_number: [u8; 32] =
|
|
|
hex::FromHex::from_hex(provider_random_number)?;
|
|
hex::FromHex::from_hex(provider_random_number)?;
|
|
|
|
|
+ let gas_used = row
|
|
|
|
|
+ .gas_used
|
|
|
|
|
+ .ok_or(anyhow::anyhow!("Gas used is missing for completed request"))?;
|
|
|
|
|
+ let gas_used = U256::from_dec_str(&gas_used)
|
|
|
|
|
+ .map_err(|_| anyhow::anyhow!("Failed to parse gas used"))?;
|
|
|
RequestEntryState::Completed {
|
|
RequestEntryState::Completed {
|
|
|
reveal_block_number,
|
|
reveal_block_number,
|
|
|
reveal_tx_hash,
|
|
reveal_tx_hash,
|
|
|
provider_random_number,
|
|
provider_random_number,
|
|
|
|
|
+ gas_used,
|
|
|
|
|
+ combined_random_number: Self::generate_combined_random_number(
|
|
|
|
|
+ &user_random_number,
|
|
|
|
|
+ &provider_random_number,
|
|
|
|
|
+ ),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
"Failed" => RequestEntryState::Failed {
|
|
"Failed" => RequestEntryState::Failed {
|
|
|
reason: row.info.unwrap_or_default(),
|
|
reason: row.info.unwrap_or_default(),
|
|
|
|
|
+ provider_random_number: match row.provider_random_number {
|
|
|
|
|
+ Some(provider_random_number) => {
|
|
|
|
|
+ Some(hex::FromHex::from_hex(provider_random_number)?)
|
|
|
|
|
+ }
|
|
|
|
|
+ None => None,
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
_ => return Err(anyhow::anyhow!("Unknown request state: {}", row.state)),
|
|
_ => return Err(anyhow::anyhow!("Unknown request state: {}", row.state)),
|
|
|
};
|
|
};
|
|
|
Ok(Self {
|
|
Ok(Self {
|
|
|
chain_id,
|
|
chain_id,
|
|
|
|
|
+ network_id,
|
|
|
provider,
|
|
provider,
|
|
|
sequence,
|
|
sequence,
|
|
|
created_at,
|
|
created_at,
|
|
@@ -129,6 +191,7 @@ impl TryFrom<RequestRow> for RequestStatus {
|
|
|
request_tx_hash,
|
|
request_tx_hash,
|
|
|
user_random_number,
|
|
user_random_number,
|
|
|
sender,
|
|
sender,
|
|
|
|
|
+ gas_limit,
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -185,15 +248,18 @@ impl History {
|
|
|
async fn update_request_status(pool: &Pool<Sqlite>, new_status: RequestStatus) {
|
|
async fn update_request_status(pool: &Pool<Sqlite>, new_status: RequestStatus) {
|
|
|
let sequence = new_status.sequence as i64;
|
|
let sequence = new_status.sequence as i64;
|
|
|
let chain_id = new_status.chain_id;
|
|
let chain_id = new_status.chain_id;
|
|
|
|
|
+ let network_id = new_status.network_id as i64;
|
|
|
let request_tx_hash: String = new_status.request_tx_hash.encode_hex();
|
|
let request_tx_hash: String = new_status.request_tx_hash.encode_hex();
|
|
|
let provider: String = new_status.provider.encode_hex();
|
|
let provider: String = new_status.provider.encode_hex();
|
|
|
|
|
+ let gas_limit = new_status.gas_limit.to_string();
|
|
|
let result = match new_status.state {
|
|
let result = match new_status.state {
|
|
|
RequestEntryState::Pending => {
|
|
RequestEntryState::Pending => {
|
|
|
let block_number = new_status.request_block_number as i64;
|
|
let block_number = new_status.request_block_number as i64;
|
|
|
let sender: String = new_status.sender.encode_hex();
|
|
let sender: String = new_status.sender.encode_hex();
|
|
|
let user_random_number: String = new_status.user_random_number.encode_hex();
|
|
let user_random_number: String = new_status.user_random_number.encode_hex();
|
|
|
- sqlx::query!("INSERT INTO request(chain_id, provider, sequence, created_at, last_updated_at, state, request_block_number, request_tx_hash, user_random_number, sender) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
|
|
|
|
|
+ sqlx::query!("INSERT INTO request(chain_id, network_id, provider, sequence, created_at, last_updated_at, state, request_block_number, request_tx_hash, user_random_number, sender, gas_limit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
|
chain_id,
|
|
chain_id,
|
|
|
|
|
+ network_id,
|
|
|
provider,
|
|
provider,
|
|
|
sequence,
|
|
sequence,
|
|
|
new_status.created_at,
|
|
new_status.created_at,
|
|
@@ -202,7 +268,9 @@ impl History {
|
|
|
block_number,
|
|
block_number,
|
|
|
request_tx_hash,
|
|
request_tx_hash,
|
|
|
user_random_number,
|
|
user_random_number,
|
|
|
- sender)
|
|
|
|
|
|
|
+ sender,
|
|
|
|
|
+ gas_limit
|
|
|
|
|
+ )
|
|
|
.execute(pool)
|
|
.execute(pool)
|
|
|
.await
|
|
.await
|
|
|
}
|
|
}
|
|
@@ -210,29 +278,45 @@ impl History {
|
|
|
reveal_block_number,
|
|
reveal_block_number,
|
|
|
reveal_tx_hash,
|
|
reveal_tx_hash,
|
|
|
provider_random_number,
|
|
provider_random_number,
|
|
|
|
|
+ gas_used,
|
|
|
|
|
+ combined_random_number: _,
|
|
|
} => {
|
|
} => {
|
|
|
let reveal_block_number = reveal_block_number as i64;
|
|
let reveal_block_number = reveal_block_number as i64;
|
|
|
let reveal_tx_hash: String = reveal_tx_hash.encode_hex();
|
|
let reveal_tx_hash: String = reveal_tx_hash.encode_hex();
|
|
|
let provider_random_number: String = provider_random_number.encode_hex();
|
|
let provider_random_number: String = provider_random_number.encode_hex();
|
|
|
- sqlx::query!("UPDATE request SET state = ?, last_updated_at = ?, reveal_block_number = ?, reveal_tx_hash = ?, provider_random_number = ? WHERE chain_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ?",
|
|
|
|
|
|
|
+ let gas_used: String = gas_used.to_string();
|
|
|
|
|
+ let result = sqlx::query!("UPDATE request SET state = ?, last_updated_at = ?, reveal_block_number = ?, reveal_tx_hash = ?, provider_random_number =?, gas_used = ? WHERE network_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ?",
|
|
|
"Completed",
|
|
"Completed",
|
|
|
new_status.last_updated_at,
|
|
new_status.last_updated_at,
|
|
|
reveal_block_number,
|
|
reveal_block_number,
|
|
|
reveal_tx_hash,
|
|
reveal_tx_hash,
|
|
|
provider_random_number,
|
|
provider_random_number,
|
|
|
- chain_id,
|
|
|
|
|
|
|
+ gas_used,
|
|
|
|
|
+ network_id,
|
|
|
sequence,
|
|
sequence,
|
|
|
provider,
|
|
provider,
|
|
|
request_tx_hash)
|
|
request_tx_hash)
|
|
|
.execute(pool)
|
|
.execute(pool)
|
|
|
- .await
|
|
|
|
|
|
|
+ .await;
|
|
|
|
|
+ if let Ok(query_result) = &result {
|
|
|
|
|
+ if query_result.rows_affected() == 0 {
|
|
|
|
|
+ tracing::error!("Failed to update request status to complete: No rows affected. Chain ID: {}, Sequence: {}, Request TX Hash: {}", network_id, sequence, request_tx_hash);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ result
|
|
|
}
|
|
}
|
|
|
- RequestEntryState::Failed { reason } => {
|
|
|
|
|
- sqlx::query!("UPDATE request SET state = ?, last_updated_at = ?, info = ? WHERE chain_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ? AND state = 'Pending'",
|
|
|
|
|
|
|
+ RequestEntryState::Failed {
|
|
|
|
|
+ reason,
|
|
|
|
|
+ provider_random_number,
|
|
|
|
|
+ } => {
|
|
|
|
|
+ let provider_random_number: Option<String> = provider_random_number
|
|
|
|
|
+ .map(|provider_random_number| provider_random_number.encode_hex());
|
|
|
|
|
+ sqlx::query!("UPDATE request SET state = ?, last_updated_at = ?, info = ?, provider_random_number = ? WHERE network_id = ? AND sequence = ? AND provider = ? AND request_tx_hash = ? AND state = 'Pending'",
|
|
|
"Failed",
|
|
"Failed",
|
|
|
new_status.last_updated_at,
|
|
new_status.last_updated_at,
|
|
|
reason,
|
|
reason,
|
|
|
- chain_id,
|
|
|
|
|
|
|
+ provider_random_number,
|
|
|
|
|
+ network_id,
|
|
|
sequence,
|
|
sequence,
|
|
|
provider,
|
|
provider,
|
|
|
request_tx_hash)
|
|
request_tx_hash)
|
|
@@ -271,16 +355,17 @@ impl History {
|
|
|
pub async fn get_requests_by_sender(
|
|
pub async fn get_requests_by_sender(
|
|
|
&self,
|
|
&self,
|
|
|
sender: Address,
|
|
sender: Address,
|
|
|
- chain_id: Option<ChainId>,
|
|
|
|
|
|
|
+ network_id: Option<NetworkId>,
|
|
|
) -> Result<Vec<RequestStatus>> {
|
|
) -> Result<Vec<RequestStatus>> {
|
|
|
let sender: String = sender.encode_hex();
|
|
let sender: String = sender.encode_hex();
|
|
|
- let rows = match chain_id {
|
|
|
|
|
- Some(chain_id) => {
|
|
|
|
|
|
|
+ let rows = match network_id {
|
|
|
|
|
+ Some(network_id) => {
|
|
|
|
|
+ let network_id = network_id as i64;
|
|
|
sqlx::query_as!(
|
|
sqlx::query_as!(
|
|
|
RequestRow,
|
|
RequestRow,
|
|
|
- "SELECT * FROM request WHERE sender = ? AND chain_id = ?",
|
|
|
|
|
|
|
+ "SELECT * FROM request WHERE sender = ? AND network_id = ?",
|
|
|
sender,
|
|
sender,
|
|
|
- chain_id,
|
|
|
|
|
|
|
+ network_id,
|
|
|
)
|
|
)
|
|
|
.fetch_all(&self.pool)
|
|
.fetch_all(&self.pool)
|
|
|
.await
|
|
.await
|
|
@@ -301,16 +386,17 @@ impl History {
|
|
|
pub async fn get_requests_by_sequence(
|
|
pub async fn get_requests_by_sequence(
|
|
|
&self,
|
|
&self,
|
|
|
sequence: u64,
|
|
sequence: u64,
|
|
|
- chain_id: Option<ChainId>,
|
|
|
|
|
|
|
+ network_id: Option<NetworkId>,
|
|
|
) -> Result<Vec<RequestStatus>> {
|
|
) -> Result<Vec<RequestStatus>> {
|
|
|
let sequence = sequence as i64;
|
|
let sequence = sequence as i64;
|
|
|
- let rows = match chain_id {
|
|
|
|
|
- Some(chain_id) => {
|
|
|
|
|
|
|
+ let rows = match network_id {
|
|
|
|
|
+ Some(network_id) => {
|
|
|
|
|
+ let network_id = network_id as i64;
|
|
|
sqlx::query_as!(
|
|
sqlx::query_as!(
|
|
|
RequestRow,
|
|
RequestRow,
|
|
|
- "SELECT * FROM request WHERE sequence = ? AND chain_id = ?",
|
|
|
|
|
|
|
+ "SELECT * FROM request WHERE sequence = ? AND network_id = ?",
|
|
|
sequence,
|
|
sequence,
|
|
|
- chain_id,
|
|
|
|
|
|
|
+ network_id,
|
|
|
)
|
|
)
|
|
|
.fetch_all(&self.pool)
|
|
.fetch_all(&self.pool)
|
|
|
.await
|
|
.await
|
|
@@ -334,8 +420,9 @@ impl History {
|
|
|
|
|
|
|
|
pub async fn get_requests_by_time(
|
|
pub async fn get_requests_by_time(
|
|
|
&self,
|
|
&self,
|
|
|
- chain_id: Option<ChainId>,
|
|
|
|
|
|
|
+ network_id: Option<NetworkId>,
|
|
|
limit: u64,
|
|
limit: u64,
|
|
|
|
|
+ offset: u64,
|
|
|
min_timestamp: Option<DateTime<chrono::Utc>>,
|
|
min_timestamp: Option<DateTime<chrono::Utc>>,
|
|
|
max_timestamp: Option<DateTime<chrono::Utc>>,
|
|
max_timestamp: Option<DateTime<chrono::Utc>>,
|
|
|
) -> Result<Vec<RequestStatus>> {
|
|
) -> Result<Vec<RequestStatus>> {
|
|
@@ -352,20 +439,23 @@ impl History {
|
|
|
.unwrap(),
|
|
.unwrap(),
|
|
|
);
|
|
);
|
|
|
let limit = limit as i64;
|
|
let limit = limit as i64;
|
|
|
- let rows = match chain_id {
|
|
|
|
|
- Some(chain_id) => {
|
|
|
|
|
- let chain_id = chain_id.to_string();
|
|
|
|
|
- sqlx::query_as!(RequestRow, "SELECT * FROM request WHERE chain_id = ? AND created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?",
|
|
|
|
|
- chain_id,
|
|
|
|
|
|
|
+ let offset = offset as i64;
|
|
|
|
|
+ let rows = match network_id {
|
|
|
|
|
+ Some(network_id) => {
|
|
|
|
|
+ let network_id = network_id as i64;
|
|
|
|
|
+ sqlx::query_as!(RequestRow, "SELECT * FROM request WHERE network_id = ? AND created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ? OFFSET ?",
|
|
|
|
|
+ network_id,
|
|
|
min_timestamp,
|
|
min_timestamp,
|
|
|
max_timestamp,
|
|
max_timestamp,
|
|
|
- limit).fetch_all(&self.pool).await
|
|
|
|
|
|
|
+ limit,
|
|
|
|
|
+ offset).fetch_all(&self.pool).await
|
|
|
}
|
|
}
|
|
|
None => {
|
|
None => {
|
|
|
- sqlx::query_as!(RequestRow, "SELECT * FROM request WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ?",
|
|
|
|
|
|
|
+ sqlx::query_as!(RequestRow, "SELECT * FROM request WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT ? OFFSET ?",
|
|
|
min_timestamp,
|
|
min_timestamp,
|
|
|
max_timestamp,
|
|
max_timestamp,
|
|
|
- limit).fetch_all(&self.pool).await
|
|
|
|
|
|
|
+ limit,
|
|
|
|
|
+ offset).fetch_all(&self.pool).await
|
|
|
}
|
|
}
|
|
|
}.map_err(|e| {
|
|
}.map_err(|e| {
|
|
|
tracing::error!("Failed to fetch request by time: {}", e);
|
|
tracing::error!("Failed to fetch request by time: {}", e);
|
|
@@ -382,6 +472,7 @@ mod test {
|
|
|
fn get_random_request_status() -> RequestStatus {
|
|
fn get_random_request_status() -> RequestStatus {
|
|
|
RequestStatus {
|
|
RequestStatus {
|
|
|
chain_id: "ethereum".to_string(),
|
|
chain_id: "ethereum".to_string(),
|
|
|
|
|
+ network_id: 121,
|
|
|
provider: Address::random(),
|
|
provider: Address::random(),
|
|
|
sequence: 1,
|
|
sequence: 1,
|
|
|
created_at: chrono::Utc::now(),
|
|
created_at: chrono::Utc::now(),
|
|
@@ -391,6 +482,7 @@ mod test {
|
|
|
user_random_number: [20; 32],
|
|
user_random_number: [20; 32],
|
|
|
sender: Address::random(),
|
|
sender: Address::random(),
|
|
|
state: RequestEntryState::Pending,
|
|
state: RequestEntryState::Pending,
|
|
|
|
|
+ gas_limit: U256::from(500_000),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -404,11 +496,16 @@ mod test {
|
|
|
reveal_block_number: 1,
|
|
reveal_block_number: 1,
|
|
|
reveal_tx_hash,
|
|
reveal_tx_hash,
|
|
|
provider_random_number: [40; 32],
|
|
provider_random_number: [40; 32],
|
|
|
|
|
+ gas_used: U256::from(567890),
|
|
|
|
|
+ combined_random_number: RequestStatus::generate_combined_random_number(
|
|
|
|
|
+ &status.user_random_number,
|
|
|
|
|
+ &[40; 32],
|
|
|
|
|
+ ),
|
|
|
};
|
|
};
|
|
|
History::update_request_status(&history.pool, status.clone()).await;
|
|
History::update_request_status(&history.pool, status.clone()).await;
|
|
|
|
|
|
|
|
let logs = history
|
|
let logs = history
|
|
|
- .get_requests_by_sequence(status.sequence, Some(status.chain_id.clone()))
|
|
|
|
|
|
|
+ .get_requests_by_sequence(status.sequence, Some(status.network_id))
|
|
|
.await
|
|
.await
|
|
|
.unwrap();
|
|
.unwrap();
|
|
|
assert_eq!(logs, vec![status.clone()]);
|
|
assert_eq!(logs, vec![status.clone()]);
|
|
@@ -432,7 +529,7 @@ mod test {
|
|
|
assert_eq!(logs, vec![status.clone()]);
|
|
assert_eq!(logs, vec![status.clone()]);
|
|
|
|
|
|
|
|
let logs = history
|
|
let logs = history
|
|
|
- .get_requests_by_sender(status.sender, Some(status.chain_id.clone()))
|
|
|
|
|
|
|
+ .get_requests_by_sender(status.sender, Some(status.network_id))
|
|
|
.await
|
|
.await
|
|
|
.unwrap();
|
|
.unwrap();
|
|
|
assert_eq!(logs, vec![status.clone()]);
|
|
assert_eq!(logs, vec![status.clone()]);
|
|
@@ -445,14 +542,82 @@ mod test {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
#[tokio::test]
|
|
|
|
|
+ async fn test_no_transition_from_completed_to_failed() {
|
|
|
|
|
+ let history = History::new_in_memory().await.unwrap();
|
|
|
|
|
+ let reveal_tx_hash = TxHash::random();
|
|
|
|
|
+ let mut status = get_random_request_status();
|
|
|
|
|
+ History::update_request_status(&history.pool, status.clone()).await;
|
|
|
|
|
+ status.state = RequestEntryState::Completed {
|
|
|
|
|
+ reveal_block_number: 1,
|
|
|
|
|
+ reveal_tx_hash,
|
|
|
|
|
+ provider_random_number: [40; 32],
|
|
|
|
|
+ gas_used: U256::from(567890),
|
|
|
|
|
+ combined_random_number: RequestStatus::generate_combined_random_number(
|
|
|
|
|
+ &status.user_random_number,
|
|
|
|
|
+ &[40; 32],
|
|
|
|
|
+ ),
|
|
|
|
|
+ };
|
|
|
|
|
+ History::update_request_status(&history.pool, status.clone()).await;
|
|
|
|
|
+ let mut failed_status = status.clone();
|
|
|
|
|
+ failed_status.state = RequestEntryState::Failed {
|
|
|
|
|
+ reason: "Failed".to_string(),
|
|
|
|
|
+ provider_random_number: None,
|
|
|
|
|
+ };
|
|
|
|
|
+ History::update_request_status(&history.pool, failed_status).await;
|
|
|
|
|
+
|
|
|
|
|
+ let logs = history
|
|
|
|
|
+ .get_requests_by_tx_hash(reveal_tx_hash)
|
|
|
|
|
+ .await
|
|
|
|
|
+ .unwrap();
|
|
|
|
|
+ assert_eq!(logs, vec![status.clone()]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #[tokio::test]
|
|
|
|
|
+ async fn test_failed_state() {
|
|
|
|
|
+ let history = History::new_in_memory().await.unwrap();
|
|
|
|
|
+ let mut status = get_random_request_status();
|
|
|
|
|
+ History::update_request_status(&history.pool, status.clone()).await;
|
|
|
|
|
+ status.state = RequestEntryState::Failed {
|
|
|
|
|
+ reason: "Failed".to_string(),
|
|
|
|
|
+ provider_random_number: Some([40; 32]),
|
|
|
|
|
+ };
|
|
|
|
|
+ History::update_request_status(&history.pool, status.clone()).await;
|
|
|
|
|
+ let logs = history
|
|
|
|
|
+ .get_requests_by_tx_hash(status.request_tx_hash)
|
|
|
|
|
+ .await
|
|
|
|
|
+ .unwrap();
|
|
|
|
|
+ assert_eq!(logs, vec![status.clone()]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #[tokio::test]
|
|
|
|
|
+ async fn test_generate_combined_random_number() {
|
|
|
|
|
+ let user_random_number = hex::FromHex::from_hex(
|
|
|
|
|
+ "0000000000000000000000006c8ac03d388d5572f77aca84573628ee87a7a4da",
|
|
|
|
|
+ )
|
|
|
|
|
+ .unwrap();
|
|
|
|
|
+ let provider_random_number = hex::FromHex::from_hex(
|
|
|
|
|
+ "deeb67cb894c33f7b20ae484228a9096b51e8db11461fcb0975c681cf0875d37",
|
|
|
|
|
+ )
|
|
|
|
|
+ .unwrap();
|
|
|
|
|
+ let combined_random_number = RequestStatus::generate_combined_random_number(
|
|
|
|
|
+ &user_random_number,
|
|
|
|
|
+ &provider_random_number,
|
|
|
|
|
+ );
|
|
|
|
|
+ let expected_combined_random_number: [u8; 32] = hex::FromHex::from_hex(
|
|
|
|
|
+ "1c26ffa1f8430dc91cb755a98bf37ce82ac0e2cfd961e10111935917694609d5",
|
|
|
|
|
+ )
|
|
|
|
|
+ .unwrap();
|
|
|
|
|
+ assert_eq!(combined_random_number, expected_combined_random_number,);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ #[tokio::test]
|
|
|
async fn test_history_filter_irrelevant_logs() {
|
|
async fn test_history_filter_irrelevant_logs() {
|
|
|
let history = History::new_in_memory().await.unwrap();
|
|
let history = History::new_in_memory().await.unwrap();
|
|
|
let status = get_random_request_status();
|
|
let status = get_random_request_status();
|
|
|
History::update_request_status(&history.pool, status.clone()).await;
|
|
History::update_request_status(&history.pool, status.clone()).await;
|
|
|
|
|
|
|
|
let logs = history
|
|
let logs = history
|
|
|
- .get_requests_by_sequence(status.sequence, Some("not-ethereum".to_string()))
|
|
|
|
|
|
|
+ .get_requests_by_sequence(status.sequence, Some(123))
|
|
|
.await
|
|
.await
|
|
|
.unwrap();
|
|
.unwrap();
|
|
|
assert_eq!(logs, vec![]);
|
|
assert_eq!(logs, vec![]);
|
|
@@ -470,7 +635,7 @@ mod test {
|
|
|
assert_eq!(logs, vec![]);
|
|
assert_eq!(logs, vec![]);
|
|
|
|
|
|
|
|
let logs = history
|
|
let logs = history
|
|
|
- .get_requests_by_sender(Address::zero(), Some(status.chain_id.clone()))
|
|
|
|
|
|
|
+ .get_requests_by_sender(Address::zero(), Some(status.network_id))
|
|
|
.await
|
|
.await
|
|
|
.unwrap();
|
|
.unwrap();
|
|
|
assert_eq!(logs, vec![]);
|
|
assert_eq!(logs, vec![]);
|
|
@@ -487,12 +652,13 @@ mod test {
|
|
|
let history = History::new_in_memory().await.unwrap();
|
|
let history = History::new_in_memory().await.unwrap();
|
|
|
let status = get_random_request_status();
|
|
let status = get_random_request_status();
|
|
|
History::update_request_status(&history.pool, status.clone()).await;
|
|
History::update_request_status(&history.pool, status.clone()).await;
|
|
|
- for chain_id in [None, Some("ethereum".to_string())] {
|
|
|
|
|
|
|
+ for network_id in [None, Some(121)] {
|
|
|
// min = created_at = max
|
|
// min = created_at = max
|
|
|
let logs = history
|
|
let logs = history
|
|
|
.get_requests_by_time(
|
|
.get_requests_by_time(
|
|
|
- chain_id.clone(),
|
|
|
|
|
|
|
+ network_id,
|
|
|
10,
|
|
10,
|
|
|
|
|
+ 0,
|
|
|
Some(status.created_at),
|
|
Some(status.created_at),
|
|
|
Some(status.created_at),
|
|
Some(status.created_at),
|
|
|
)
|
|
)
|
|
@@ -503,8 +669,9 @@ mod test {
|
|
|
// min = created_at + 1
|
|
// min = created_at + 1
|
|
|
let logs = history
|
|
let logs = history
|
|
|
.get_requests_by_time(
|
|
.get_requests_by_time(
|
|
|
- chain_id.clone(),
|
|
|
|
|
|
|
+ network_id,
|
|
|
10,
|
|
10,
|
|
|
|
|
+ 0,
|
|
|
Some(status.created_at + Duration::seconds(1)),
|
|
Some(status.created_at + Duration::seconds(1)),
|
|
|
None,
|
|
None,
|
|
|
)
|
|
)
|
|
@@ -515,8 +682,9 @@ mod test {
|
|
|
// max = created_at - 1
|
|
// max = created_at - 1
|
|
|
let logs = history
|
|
let logs = history
|
|
|
.get_requests_by_time(
|
|
.get_requests_by_time(
|
|
|
- chain_id.clone(),
|
|
|
|
|
|
|
+ network_id,
|
|
|
10,
|
|
10,
|
|
|
|
|
+ 0,
|
|
|
None,
|
|
None,
|
|
|
Some(status.created_at - Duration::seconds(1)),
|
|
Some(status.created_at - Duration::seconds(1)),
|
|
|
)
|
|
)
|
|
@@ -526,7 +694,7 @@ mod test {
|
|
|
|
|
|
|
|
// no min or max
|
|
// no min or max
|
|
|
let logs = history
|
|
let logs = history
|
|
|
- .get_requests_by_time(chain_id.clone(), 10, None, None)
|
|
|
|
|
|
|
+ .get_requests_by_time(network_id, 10, 0, None, None)
|
|
|
.await
|
|
.await
|
|
|
.unwrap();
|
|
.unwrap();
|
|
|
assert_eq!(logs, vec![status.clone()]);
|
|
assert_eq!(logs, vec![status.clone()]);
|
|
@@ -541,7 +709,7 @@ mod test {
|
|
|
// wait for the writer thread to write to the db
|
|
// wait for the writer thread to write to the db
|
|
|
sleep(std::time::Duration::from_secs(1)).await;
|
|
sleep(std::time::Duration::from_secs(1)).await;
|
|
|
let logs = history
|
|
let logs = history
|
|
|
- .get_requests_by_sequence(1, Some("ethereum".to_string()))
|
|
|
|
|
|
|
+ .get_requests_by_sequence(1, Some(121))
|
|
|
.await
|
|
.await
|
|
|
.unwrap();
|
|
.unwrap();
|
|
|
assert_eq!(logs, vec![status]);
|
|
assert_eq!(logs, vec![status]);
|