api.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. use {
  2. crate::{
  3. chain::reader::{BlockNumber, BlockStatus, EntropyReader},
  4. },
  5. anyhow::Result,
  6. axum::{
  7. body::Body,
  8. http::StatusCode,
  9. response::{IntoResponse, Response},
  10. routing::get,
  11. Router,
  12. },
  13. ethers::core::types::Address,
  14. prometheus_client::{
  15. encoding::EncodeLabelSet,
  16. metrics::{counter::Counter, family::Family},
  17. registry::Registry,
  18. },
  19. std::{collections::HashMap, sync::Arc},
  20. tokio::sync::RwLock,
  21. url::Url,
  22. };
  23. pub use {index::*, live::*, metrics::*, ready::*};
  24. mod index;
  25. mod live;
  26. mod metrics;
  27. mod ready;
  28. pub type ChainId = String;
  29. #[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelSet)]
  30. pub struct RequestLabel {
  31. pub value: String,
  32. }
  33. pub struct ApiMetrics {
  34. pub http_requests: Family<RequestLabel, Counter>,
  35. }
  36. #[derive(Clone)]
  37. pub struct ApiState {
  38. pub chains: Arc<HashMap<ChainId, BlockchainState>>,
  39. pub metrics_registry: Arc<RwLock<Registry>>,
  40. /// Prometheus metrics
  41. pub metrics: Arc<ApiMetrics>,
  42. }
  43. impl ApiState {
  44. pub async fn new(
  45. chains: HashMap<ChainId, BlockchainState>,
  46. metrics_registry: Arc<RwLock<Registry>>,
  47. ) -> ApiState {
  48. let metrics = ApiMetrics {
  49. http_requests: Family::default(),
  50. };
  51. let http_requests = metrics.http_requests.clone();
  52. metrics_registry.write().await.register(
  53. "http_requests",
  54. "Number of HTTP requests received",
  55. http_requests,
  56. );
  57. ApiState {
  58. chains: Arc::new(chains),
  59. metrics: Arc::new(metrics),
  60. metrics_registry,
  61. }
  62. }
  63. }
  64. /// The state of the randomness service for a single blockchain.
  65. #[derive(Clone)]
  66. pub struct BlockchainState {
  67. /// The chain id for this blockchain, useful for logging
  68. pub id: ChainId,
  69. /// The contract that the server is fulfilling requests for.
  70. pub contract: Arc<dyn EntropyReader>,
  71. /// The address of the provider that this server is operating for.
  72. pub provider_address: Address,
  73. /// The server will wait for this many block confirmations of a request before revealing
  74. /// the random number.
  75. pub reveal_delay_blocks: BlockNumber,
  76. /// The BlockStatus of the block that is considered to be confirmed on the blockchain.
  77. /// For eg., Finalized, Safe
  78. pub confirmed_block_status: BlockStatus,
  79. }
  80. pub enum RestError {
  81. /// The caller passed a sequence number that isn't within the supported range
  82. InvalidSequenceNumber,
  83. /// The caller passed an unsupported chain id
  84. InvalidChainId,
  85. /// The caller requested a random value that can't currently be revealed (because it
  86. /// hasn't been committed to on-chain)
  87. NoPendingRequest,
  88. /// The request exists, but the server is waiting for more confirmations (more blocks
  89. /// to be mined) before revealing the random number.
  90. PendingConfirmation,
  91. /// The server cannot currently communicate with the blockchain, so is not able to verify
  92. /// which random values have been requested.
  93. TemporarilyUnavailable,
  94. /// A catch-all error for all other types of errors that could occur during processing.
  95. Unknown,
  96. }
  97. impl IntoResponse for RestError {
  98. fn into_response(self) -> Response {
  99. match self {
  100. RestError::InvalidSequenceNumber => (
  101. StatusCode::BAD_REQUEST,
  102. "The sequence number is out of the permitted range",
  103. )
  104. .into_response(),
  105. RestError::InvalidChainId => {
  106. (StatusCode::BAD_REQUEST, "The chain id is not supported").into_response()
  107. }
  108. RestError::NoPendingRequest => (
  109. StatusCode::FORBIDDEN,
  110. "The request with the given sequence number has not been made yet, or the random value has already been revealed on chain.",
  111. ).into_response(),
  112. RestError::PendingConfirmation => (
  113. StatusCode::FORBIDDEN,
  114. "The request needs additional confirmations before the random value can be retrieved. Try your request again later.",
  115. )
  116. .into_response(),
  117. RestError::TemporarilyUnavailable => (
  118. StatusCode::SERVICE_UNAVAILABLE,
  119. "This service is temporarily unavailable",
  120. )
  121. .into_response(),
  122. RestError::Unknown => (
  123. StatusCode::INTERNAL_SERVER_ERROR,
  124. "An unknown error occurred processing the request",
  125. )
  126. .into_response(),
  127. }
  128. }
  129. }
  130. pub fn routes(state: ApiState) -> Router<(), Body> {
  131. Router::new()
  132. .route("/", get(index))
  133. .route("/live", get(live))
  134. .route("/metrics", get(metrics))
  135. .route("/ready", get(ready))
  136. .with_state(state)
  137. }
  138. /// We are registering the provider on chain with the following url:
  139. /// `{base_uri}/v1/chains/{chain_id}`
  140. /// The path and API are highly coupled. Please be sure to keep them consistent.
  141. pub fn get_register_uri(base_uri: &str, chain_id: &str) -> Result<String> {
  142. let base_uri = Url::parse(base_uri)?;
  143. let path = format!("/v1/chains/{}", chain_id);
  144. let uri = base_uri.join(&path)?;
  145. Ok(uri.to_string())
  146. }