Skip to content
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
6 changes: 6 additions & 0 deletions apps/fortuna/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub use {

mod chain_ids;
mod config;
pub mod examples;
mod explorer;
mod index;
mod live;
Expand All @@ -40,10 +41,15 @@ pub type ChainId = String;
pub type NetworkId = u64;

#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, utoipa::ToSchema)]
#[schema(example = "Completed")]
pub enum StateTag {
/// Request is pending and waiting to be fulfilled
Pending,
/// Request failed to be fulfilled
Failed,
/// Request was successfully completed
Completed,
/// Request was completed but the callback to the caller failed
CallbackErrored,
}

Expand Down
16 changes: 15 additions & 1 deletion apps/fortuna/src/api/chain_ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,28 @@ use {
crate::api::{ChainId, RestError},
anyhow::Result,
axum::{extract::State, Json},
utoipa::ToSchema,
};

/// Response containing the list of supported chain IDs
#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct GetChainIdsResponse(
/// List of supported blockchain identifiers
#[schema(example = json!(["ethereum", "avalanche", "arbitrum", "optimism"]))]
Vec<ChainId>,
);

/// Get the list of supported chain ids
///
/// Returns all blockchain identifiers that this Fortuna instance supports.
/// Each chain_id can be used in other API endpoints to specify which blockchain to interact with.
#[utoipa::path(
get,
path = "/v1/chains",
responses(
(status = 200, description = "Successfully retrieved the list of chain ids", body = GetRandomValueResponse),
(status = 200, description = "Successfully retrieved the list of chain ids", body = [String],
example = json!(["ethereum", "avalanche", "arbitrum", "optimism"])
),
)
)]
pub async fn chain_ids(
Expand Down
89 changes: 89 additions & 0 deletions apps/fortuna/src/api/examples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Example values for the utoipa API docs.
// Note that each of these expressions is only evaluated once when the documentation is created,
// so the examples don't auto-update over time.

/// Example value for a chain ID
pub fn chain_id_example() -> &'static str {
"abstract"
}

/// Example value for a sequence number
pub fn sequence_example() -> u64 {
42381
}

/// Example value for a block number
pub fn block_number_example() -> u64 {
19000000
}

/// Example value for a random value in hex format (32 bytes)
pub fn random_value_hex_example() -> &'static str {
"a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe"
}

/// Example value for a transaction hash
pub fn tx_hash_example() -> &'static str {
"0xfe5f880ac10c0aae43f910b5a17f98a93cdd2eb2dce3a5ae34e5827a3a071a32"
}

/// Example value for an Ethereum address
pub fn address_example() -> &'static str {
"0x6cc14824ea2918f5de5c2f75a9da968ad4bd6344"
}

/// Example value for a timestamp in ISO 8601 format
pub fn timestamp_example() -> &'static str {
"2023-10-01T00:00:00Z"
}

/// Example value for a network ID (Ethereum mainnet)
pub fn network_id_example() -> u64 {
1
}

/// Example value for gas limit
pub fn gas_limit_example() -> u32 {
500000
}

/// Example value for gas used
pub fn gas_used_example() -> &'static str {
"567890"
}

/// Example value for callback gas used
pub fn callback_gas_used_example() -> u32 {
100000
}

/// Example value for callback return value (error code example)
pub fn callback_return_value_example() -> &'static str {
"0x4e487b710000000000000000000000000000000000000000000000000000000000000011"
}

/// Example list of chain IDs
pub fn chain_ids_example() -> Vec<&'static str> {
vec!["monad", "avalanche", "arbitrum", "optimism"]
}

/// Example value for binary encoding type
pub fn encoding_example() -> &'static str {
"hex"
}

/// Example value for limit parameter
pub fn limit_example() -> u64 {
100
}

/// Example value for offset parameter
pub fn offset_example() -> u64 {
0
}

/// Example value for total results count
pub fn total_results_example() -> i64 {

Check warning on line 86 in apps/fortuna/src/api/examples.rs

View workflow job for this annotation

GitHub Actions / test

Diff in /home/runner/work/pyth-crosschain/pyth-crosschain/apps/fortuna/src/api/examples.rs
42
}

45 changes: 38 additions & 7 deletions apps/fortuna/src/api/explorer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{
api::{ApiBlockChainState, NetworkId, RestError, StateTag},
api::{examples, ApiBlockChainState, NetworkId, RestError, StateTag},
config::LATENCY_BUCKETS,
history::{RequestQueryBuilder, RequestStatus, SearchField},
},
Expand Down Expand Up @@ -93,23 +93,28 @@ pub struct ExplorerQueryParams {
#[param(value_type = Option<String>, example = "2033-10-01T00:00:00Z")]
pub max_timestamp: Option<DateTime<Utc>>,
/// The query string to search for. This can be a transaction hash, sender address, or sequence number.
#[param(example = "0xfe5f880ac10c0aae43f910b5a17f98a93cdd2eb2dce3a5ae34e5827a3a071a32")]
pub query: Option<String>,
/// The network ID to filter the results by.
#[param(value_type = Option<u64>)]
/// The network ID to filter the results by (e.g., 1 for Ethereum mainnet, 43114 for Avalanche).
#[param(value_type = Option<u64>, example = examples::network_id_example)]
pub network_id: Option<NetworkId>,
/// The state to filter the results by.
/// The state to filter the results by (Pending, Completed, Failed, or CallbackErrored).
#[param(example = "Completed")]
pub state: Option<StateTag>,
/// The maximum number of logs to return. Max value is 1000.
#[param(default = 1000)]
#[param(default = 1000, example = examples::limit_example)]
pub limit: Option<u64>,
/// The offset to start returning logs from.
#[param(default = 0)]
#[param(default = 0, example = examples::offset_example)]
pub offset: Option<u64>,
}

#[derive(Debug, serde::Serialize, utoipa::ToSchema)]
pub struct ExplorerResponse {
/// List of entropy request logs matching the query
pub requests: Vec<RequestStatus>,
/// Total number of results matching the query (may be more than returned due to limit)
#[schema(example = 42)]
pub total_results: i64,
}

Expand All @@ -120,7 +125,33 @@ pub struct ExplorerResponse {
#[utoipa::path(
get,
path = "/v1/logs",
responses((status = 200, description = "A list of Entropy request logs", body = ExplorerResponse)),
responses((status = 200, description = "A list of Entropy request logs", body = ExplorerResponse,
example = json!({
"requests": [{
"chain_id": "ethereum",
"network_id": 1,
"provider": "0x6cc14824ea2918f5de5c2f75a9da968ad4bd6344",
"sequence": 12345,
"created_at": "2023-10-01T00:00:00Z",
"last_updated_at": "2023-10-01T00:00:05Z",
"request_block_number": 19000000,
"request_tx_hash": "0x5a3a984f41bb5443f5efa6070ed59ccb25edd8dbe6ce7f9294cf5caa64ed00ae",
"gas_limit": 500000,
"user_random_number": "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe",
"sender": "0x78357316239040e19fc823372cc179ca75e64b81",
"state": "completed",
"reveal_block_number": 19000005,
"reveal_tx_hash": "0xfe5f880ac10c0aae43f910b5a17f98a93cdd2eb2dce3a5ae34e5827a3a071a32",
"provider_random_number": "deeb67cb894c33f7b20ae484228a9096b51e8db11461fcb0975c681cf0875d37",
"gas_used": "567890",
"combined_random_number": "1c26ffa1f8430dc91cb755a98bf37ce82ac0e2cfd961e10111935917694609d5",
"callback_failed": false,
"callback_return_value": "0x",
"callback_gas_used": 100000
}],
"total_results": 42
})
)),
params(ExplorerQueryParams)
)]
pub async fn explorer(
Expand Down
36 changes: 31 additions & 5 deletions apps/fortuna/src/api/revelation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{
api::{ApiBlockChainState, ChainId, RequestLabel, RestError},
api::{examples, ApiBlockChainState, ChainId, RequestLabel, RestError},
chain::reader::BlockNumber,
},
anyhow::Result,
Expand All @@ -25,8 +25,15 @@ use {
get,
path = "/v1/chains/{chain_id}/revelations/{sequence}",
responses(
(status = 200, description = "Random value successfully retrieved", body = GetRandomValueResponse),
(status = 403, description = "Random value cannot currently be retrieved", body = String)
(status = 200, description = "Random value successfully retrieved", body = GetRandomValueResponse,
example = json!({
"encoding": "hex",
"data": "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe"
})
),
(status = 403, description = "Random value cannot currently be retrieved", body = String,
example = json!("The request with the given sequence number has not been made yet, or the random value has already been revealed on chain.")
)
),
params(RevelationPathParams, RevelationQueryParams)
)]
Expand Down Expand Up @@ -130,48 +137,67 @@ pub async fn revelation(
#[derive(Debug, serde::Serialize, serde::Deserialize, IntoParams)]
#[into_params(parameter_in=Path)]
pub struct RevelationPathParams {
#[param(value_type = String)]
/// The unique identifier for the blockchain (e.g., "ethereum", "avalanche")
#[param(value_type = String, example = examples::chain_id_example)]
pub chain_id: ChainId,
/// The sequence number of the random value request
#[param(example = examples::sequence_example)]
pub sequence: u64,
}

#[derive(Debug, serde::Serialize, serde::Deserialize, IntoParams)]
#[into_params(parameter_in=Query)]
pub struct RevelationQueryParams {
/// The encoding format for the random value (hex, base64, or array)
#[param(example = examples::encoding_example)]
pub encoding: Option<BinaryEncoding>,
#[param(value_type = Option<u64>)]
/// Optional block number to verify the request was made at a specific block
#[param(value_type = Option<u64>, example = examples::block_number_example)]
pub block_number: Option<BlockNumber>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(rename_all = "kebab-case")]
#[schema(example = "hex")]
pub enum BinaryEncoding {
/// Hexadecimal encoding (default)
#[serde(rename = "hex")]
Hex,
/// Base64 encoding
#[serde(rename = "base64")]
Base64,
/// Raw byte array encoding
#[serde(rename = "array")]
Array,
}

#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema, PartialEq)]
pub struct GetRandomValueResponse {
/// The random value in the requested encoding format
pub value: Blob,
}

#[serde_as]
#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema, PartialEq)]
#[serde(tag = "encoding", rename_all = "kebab-case")]
pub enum Blob {
/// Random value encoded as hexadecimal string
Hex {
/// The 32-byte random value as a hex string
#[serde_as(as = "serde_with::hex::Hex")]
#[schema(example = "a905ab56567d31a7fda38ed819d97bc257f3ebe385fc5c72ce226d3bb855f0fe")]
data: [u8; 32],
},
/// Random value encoded as base64 string
Base64 {
/// The 32-byte random value as a base64 string
#[serde_as(as = "serde_with::base64::Base64")]
#[schema(example = "qQWrVlZ9MafaOO2BnZe8JX8z6+OFxccszibju4VfD+4=")]
data: [u8; 32],
},
/// Random value as a raw byte array
Array {
/// The 32-byte random value as an array of integers
#[serde(with = "array")]
data: [u8; 32],
},
Expand Down
Loading
Loading