From d096bfaf730920b50a7fb44d959ddf8c883f04d0 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 20 Oct 2023 09:22:20 -0700 Subject: [PATCH] [pyth-rng] A few small improvements (#1114) * more stuff * build * cleanup * Update pyth-rng/src/command/generate.rs Co-authored-by: Reisen * Update pyth-rng/src/command/generate.rs Co-authored-by: Reisen * Update pyth-rng/src/command/generate.rs Co-authored-by: Reisen * tracing --------- Co-authored-by: Reisen --- pyth-rng/Cargo.lock | 135 ++++++++++++++++++++- pyth-rng/Cargo.toml | 3 + pyth-rng/src/api.rs | 50 ++++++++ pyth-rng/src/api/live.rs | 11 ++ pyth-rng/src/api/metrics.rs | 20 +++ pyth-rng/src/api/ready.rs | 13 ++ pyth-rng/src/api/revelation.rs | 9 ++ pyth-rng/src/command/generate.rs | 21 ++-- pyth-rng/src/command/get_request.rs | 10 +- pyth-rng/src/command/register_provider.rs | 10 +- pyth-rng/src/command/request_randomness.rs | 10 +- pyth-rng/src/command/run.rs | 14 ++- pyth-rng/src/config.rs | 11 +- pyth-rng/src/ethereum.rs | 18 +-- pyth-rng/src/main.rs | 21 +++- pyth-rng/src/state.rs | 11 +- 16 files changed, 305 insertions(+), 62 deletions(-) create mode 100644 pyth-rng/src/api/live.rs create mode 100644 pyth-rng/src/api/metrics.rs create mode 100644 pyth-rng/src/api/ready.rs diff --git a/pyth-rng/Cargo.lock b/pyth-rng/Cargo.lock index 277d456b2..8f52ed3de 100644 --- a/pyth-rng/Cargo.lock +++ b/pyth-rng/Cargo.lock @@ -863,6 +863,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + [[package]] name = "dunce" version = "1.0.4" @@ -1961,7 +1967,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax", + "regex-syntax 0.7.5", "string_cache", "term", "tiny-keccak", @@ -2008,6 +2014,15 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -2099,6 +2114,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num" version = "0.4.1" @@ -2296,6 +2321,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parity-scale-codec" version = "3.6.5" @@ -2606,6 +2637,29 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prometheus-client" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c99afa9a01501019ac3a14d71d9f94050346f55ca471ce90c799a15c58f61e2" +dependencies = [ + "dtoa", + "itoa", + "parking_lot", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "pyth-rng" version = "0.1.0" @@ -2619,6 +2673,7 @@ dependencies = [ "ethabi", "ethers", "hex", + "prometheus-client", "pythnet-sdk", "rand", "reqwest", @@ -2629,6 +2684,8 @@ dependencies = [ "sha3", "tokio", "tower-http", + "tracing", + "tracing-subscriber", "utoipa", "utoipa-swagger-ui", ] @@ -2754,8 +2811,17 @@ checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.3.9", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -2766,9 +2832,15 @@ checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.7.5" @@ -3243,6 +3315,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shellexpand" version = "2.1.2" @@ -3557,6 +3638,16 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.29" @@ -3808,6 +3899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", + "valuable", ] [[package]] @@ -3820,6 +3912,35 @@ dependencies = [ "tracing", ] +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "try-lock" version = "0.2.4" @@ -3986,6 +4107,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/pyth-rng/Cargo.toml b/pyth-rng/Cargo.toml index be3d16e2f..d5abdbc49 100644 --- a/pyth-rng/Cargo.toml +++ b/pyth-rng/Cargo.toml @@ -13,6 +13,7 @@ clap = { version = "4.4.6", features = ["derive", "cargo", "env"] } ethabi = "18.0.0" ethers = "2.0.10" hex = "0.4.3" +prometheus-client = { version = "0.21.2" } pythnet-sdk = { git = "https://github.com/pyth-network/pyth-crosschain", version = "2.0.0", features = ["strum"] } rand = "0.8.5" reqwest = { version = "0.11.22", features = ["json", "blocking"] } @@ -23,5 +24,7 @@ serde_yaml = "0.9.25" sha3 = "0.10.8" tokio = { version = "1.33.0", features = ["full"] } tower-http = { version = "0.4.0", features = ["cors"] } +tracing = { version = "0.1.37", features = ["log"] } +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } utoipa = { version = "3.4.0", features = ["axum_extras"] } utoipa-swagger-ui = { version = "3.1.4", features = ["axum"] } diff --git a/pyth-rng/src/api.rs b/pyth-rng/src/api.rs index 548200c44..f5a36ccd2 100644 --- a/pyth-rng/src/api.rs +++ b/pyth-rng/src/api.rs @@ -11,19 +11,34 @@ use { }, }, ethers::core::types::Address, + prometheus_client::{ + encoding::EncodeLabelSet, + metrics::{ + counter::Counter, + family::Family, + }, + registry::Registry, + }, std::{ collections::HashMap, sync::Arc, }, + tokio::sync::RwLock, }; pub use { chain_ids::*, index::*, + live::*, + metrics::*, + ready::*, revelation::*, }; mod chain_ids; mod index; +mod live; +mod metrics; +mod ready; mod revelation; pub type ChainId = String; @@ -31,6 +46,9 @@ pub type ChainId = String; #[derive(Clone)] pub struct ApiState { pub chains: Arc>, + + /// Prometheus metrics + pub metrics: Arc, } /// The state of the randomness service for a single blockchain. @@ -43,6 +61,38 @@ pub struct BlockchainState { pub provider_address: Address, } +pub struct Metrics { + pub registry: RwLock, + // TODO: track useful metrics. this counter is just a placeholder to get things set up. + pub request_counter: Family, +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelSet)] +pub struct Label { + value: String, +} + +impl Metrics { + pub fn new() -> Self { + let mut metrics_registry = Registry::default(); + let http_requests = Family::::default(); + + // Register the metric family with the registry. + metrics_registry.register( + // With the metric name. + "http_requests", + // And the metric help text. + "Number of HTTP requests received", + http_requests.clone(), + ); + + Metrics { + registry: RwLock::new(metrics_registry), + request_counter: http_requests, + } + } +} + pub enum RestError { /// The caller passed a sequence number that isn't within the supported range InvalidSequenceNumber, diff --git a/pyth-rng/src/api/live.rs b/pyth-rng/src/api/live.rs new file mode 100644 index 000000000..66343cc6f --- /dev/null +++ b/pyth-rng/src/api/live.rs @@ -0,0 +1,11 @@ +use axum::{ + http::StatusCode, + response::{ + IntoResponse, + Response, + }, +}; + +pub async fn live() -> Response { + (StatusCode::OK, "OK").into_response() +} diff --git a/pyth-rng/src/api/metrics.rs b/pyth-rng/src/api/metrics.rs new file mode 100644 index 000000000..b211adec0 --- /dev/null +++ b/pyth-rng/src/api/metrics.rs @@ -0,0 +1,20 @@ +//! Exposing prometheus metrics via HTTP in openmetrics format. + +use { + axum::{ + extract::State, + response::IntoResponse, + }, + prometheus_client::encoding::text::encode, +}; + +pub async fn metrics(State(state): State) -> impl IntoResponse { + let registry = state.metrics.registry.read().await; + let mut buffer = String::new(); + + // Should not fail if the metrics are valid and there is memory available + // to write to the buffer. + encode(&mut buffer, ®istry).unwrap(); + + buffer +} diff --git a/pyth-rng/src/api/ready.rs b/pyth-rng/src/api/ready.rs new file mode 100644 index 000000000..e3834b94c --- /dev/null +++ b/pyth-rng/src/api/ready.rs @@ -0,0 +1,13 @@ +use axum::{ + http::StatusCode, + response::{ + IntoResponse, + Response, + }, +}; + +pub async fn ready() -> Response { + // TODO: are there useful checks here? At the moment, everything important (specifically hash + // chain computation) occurs synchronously on startup. + (StatusCode::OK, "OK").into_response() +} diff --git a/pyth-rng/src/api/revelation.rs b/pyth-rng/src/api/revelation.rs index f7740eae6..2f7296f6c 100644 --- a/pyth-rng/src/api/revelation.rs +++ b/pyth-rng/src/api/revelation.rs @@ -1,6 +1,7 @@ use { crate::api::{ ChainId, + Label, RestError, }, anyhow::Result, @@ -39,6 +40,14 @@ pub async fn revelation( State(state): State, Path(GetRandomValueQueryParams { chain_id, sequence }): Path, ) -> Result, RestError> { + state + .metrics + .request_counter + .get_or_create(&Label { + value: "/v1/chains/{chain_id}/revelations/{sequence}".to_string(), + }) + .inc(); + let sequence: u64 = sequence .try_into() .map_err(|_| RestError::InvalidSequenceNumber)?; diff --git a/pyth-rng/src/command/generate.rs b/pyth-rng/src/command/generate.rs index 8f0b20d27..1e9f7814a 100644 --- a/pyth-rng/src/command/generate.rs +++ b/pyth-rng/src/command/generate.rs @@ -7,14 +7,12 @@ use { }, ethereum::SignablePythContract, }, - std::{ - error::Error, - sync::Arc, - }, + anyhow::Result, + std::sync::Arc, }; /// Run the entire random number generation protocol to produce a random number. -pub async fn generate(opts: &GenerateOptions) -> Result<(), Box> { +pub async fn generate(opts: &GenerateOptions) -> Result<()> { let contract = Arc::new( SignablePythContract::from_config( &Config::load(&opts.config.config)?.get_chain_config(&opts.chain_id)?, @@ -30,10 +28,8 @@ pub async fn generate(opts: &GenerateOptions) -> Result<(), Box> { let sequence_number = contract .request_wrapper(&provider, &user_randomness, opts.blockhash) .await?; - println!( - "Requested the random number with sequence number {:#?}", - sequence_number - ); + + tracing::info!(sequence_number = sequence_number, "random number requested",); // Get the committed value from the provider let resp = reqwest::get(opts.url.join(&format!( @@ -44,10 +40,7 @@ pub async fn generate(opts: &GenerateOptions) -> Result<(), Box> { .json::() .await?; - println!( - "Retrieved the provider's random value. Server response: {:#?}", - resp - ); + tracing::info!(response = resp, "Retrieved the provider's random value.",); let provider_randomness = resp.value; // Submit the provider's and our values to the contract to reveal the random number. @@ -60,7 +53,7 @@ pub async fn generate(opts: &GenerateOptions) -> Result<(), Box> { ) .await?; - println!("Generated random number: {:#?}", random_value); + tracing::info!(number = random_value, "Random number generated."); Ok(()) } diff --git a/pyth-rng/src/command/get_request.rs b/pyth-rng/src/command/get_request.rs index 86e794703..4b6f538b5 100644 --- a/pyth-rng/src/command/get_request.rs +++ b/pyth-rng/src/command/get_request.rs @@ -6,14 +6,12 @@ use { }, ethereum::PythContract, }, - std::{ - error::Error, - sync::Arc, - }, + anyhow::Result, + std::sync::Arc, }; /// Get the on-chain request metadata for a provider and sequence number. -pub async fn get_request(opts: &GetRequestOptions) -> Result<(), Box> { +pub async fn get_request(opts: &GetRequestOptions) -> Result<()> { // Initialize a Provider to interface with the EVM contract. let contract = Arc::new(PythContract::from_config( &Config::load(&opts.config.config)?.get_chain_config(&opts.chain_id)?, @@ -23,7 +21,7 @@ pub async fn get_request(opts: &GetRequestOptions) -> Result<(), Box> .get_request(opts.provider, opts.sequence) .call() .await?; - println!("Found request: {:?}", r); + tracing::info!("Found request: {:?}", r); Ok(()) } diff --git a/pyth-rng/src/command/register_provider.rs b/pyth-rng/src/command/register_provider.rs index b1c84fd9d..513a3938d 100644 --- a/pyth-rng/src/command/register_provider.rs +++ b/pyth-rng/src/command/register_provider.rs @@ -7,15 +7,13 @@ use { ethereum::SignablePythContract, state::PebbleHashChain, }, - std::{ - error::Error, - sync::Arc, - }, + anyhow::Result, + std::sync::Arc, }; /// Register as a randomness provider. This method will generate and commit to a new random /// hash chain from the configured secret & a newly generated random value. -pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<(), Box> { +pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<()> { // Initialize a Provider to interface with the EVM contract. let contract = Arc::new( SignablePythContract::from_config( @@ -48,7 +46,7 @@ pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<(), Box .await? .await? { - println!("Registered provider: {:?}", r); + tracing::info!("Registered provider: {:?}", r); } Ok(()) diff --git a/pyth-rng/src/command/request_randomness.rs b/pyth-rng/src/command/request_randomness.rs index 212da300d..709b33d8d 100644 --- a/pyth-rng/src/command/request_randomness.rs +++ b/pyth-rng/src/command/request_randomness.rs @@ -6,13 +6,11 @@ use { }, ethereum::SignablePythContract, }, - std::{ - error::Error, - sync::Arc, - }, + anyhow::Result, + std::sync::Arc, }; -pub async fn request_randomness(opts: &RequestRandomnessOptions) -> Result<(), Box> { +pub async fn request_randomness(opts: &RequestRandomnessOptions) -> Result<()> { let contract = Arc::new( SignablePythContract::from_config( &Config::load(&opts.config.config)?.get_chain_config(&opts.chain_id)?, @@ -26,7 +24,7 @@ pub async fn request_randomness(opts: &RequestRandomnessOptions) -> Result<(), B .request_wrapper(&opts.provider, &user_randomness, false) .await?; - println!("sequence number: {:#?}", sequence_number); + tracing::info!("sequence number: {:#?}", sequence_number); Ok(()) } diff --git a/pyth-rng/src/command/run.rs b/pyth-rng/src/command/run.rs index 93b6cd727..16b38b527 100644 --- a/pyth-rng/src/command/run.rs +++ b/pyth-rng/src/command/run.rs @@ -21,7 +21,6 @@ use { }, std::{ collections::HashMap, - error::Error, sync::Arc, }, tower_http::cors::CorsLayer, @@ -29,7 +28,7 @@ use { utoipa_swagger_ui::SwaggerUi, }; -pub async fn run(opts: &RunOptions) -> Result<(), Box> { +pub async fn run(opts: &RunOptions) -> Result<()> { #[derive(OpenApi)] #[openapi( paths( @@ -75,7 +74,7 @@ pub async fn run(opts: &RunOptions) -> Result<(), Box> { { return Err(anyhow!("The root of the generated hash chain for chain id {} does not match the commitment. Are the secret and chain length configured correctly?", &chain_id).into()); } else { - println!("Root of chain id {} matches commitment", &chain_id); + tracing::info!("Root of chain id {} matches commitment", &chain_id); } let state = api::BlockchainState { @@ -87,8 +86,10 @@ pub async fn run(opts: &RunOptions) -> Result<(), Box> { chains.insert(chain_id.clone(), state); } + let metrics_registry = api::Metrics::new(); let api_state = api::ApiState { - chains: Arc::new(chains), + chains: Arc::new(chains), + metrics: Arc::new(metrics_registry), }; // Initialize Axum Router. Note the type here is a `Router` due to the use of the @@ -97,6 +98,9 @@ pub async fn run(opts: &RunOptions) -> Result<(), Box> { let app = app .merge(SwaggerUi::new("/docs").url("/docs/openapi.json", ApiDoc::openapi())) .route("/", get(api::index)) + .route("/live", get(api::live)) + .route("/metrics", get(api::metrics)) + .route("/ready", get(api::ready)) .route("/v1/chains", get(api::chain_ids)) .route( "/v1/chains/:chain_id/revelations/:sequence", @@ -107,7 +111,7 @@ pub async fn run(opts: &RunOptions) -> Result<(), Box> { .layer(CorsLayer::permissive()); - println!("Starting server on: {:?}", &opts.addr); + tracing::info!("Starting server on: {:?}", &opts.addr); // Binds the axum's server to the configured address and port. This is a blocking call and will // not return until the server is shutdown. axum::Server::try_bind(&opts.addr)? diff --git a/pyth-rng/src/config.rs b/pyth-rng/src/config.rs index b014accb8..11f84fc37 100644 --- a/pyth-rng/src/config.rs +++ b/pyth-rng/src/config.rs @@ -1,6 +1,9 @@ use { crate::api::ChainId, - anyhow::anyhow, + anyhow::{ + anyhow, + Result, + }, clap::{ crate_authors, crate_description, @@ -12,7 +15,6 @@ use { ethers::types::Address, std::{ collections::HashMap, - error::Error, fs, }, }; @@ -90,14 +92,15 @@ pub struct Config { } impl Config { - pub fn load(path: &str) -> Result> { + pub fn load(path: &str) -> Result { // Open and read the YAML file + // TODO: the default serde deserialization doesn't enforce unique keys let yaml_content = fs::read_to_string(path)?; let config: Config = serde_yaml::from_str(&yaml_content)?; Ok(config) } - pub fn get_chain_config(&self, chain_id: &ChainId) -> Result> { + pub fn get_chain_config(&self, chain_id: &ChainId) -> Result { self.chains .get(chain_id) .map(|x| x.clone()) diff --git a/pyth-rng/src/ethereum.rs b/pyth-rng/src/ethereum.rs index aecba6aa0..5a8fb2e48 100644 --- a/pyth-rng/src/ethereum.rs +++ b/pyth-rng/src/ethereum.rs @@ -1,6 +1,9 @@ use { crate::config::EthereumConfig, - anyhow::anyhow, + anyhow::{ + anyhow, + Result, + }, ethers::{ abi::RawLog, contract::{ @@ -23,10 +26,7 @@ use { Digest, Keccak256, }, - std::{ - error::Error, - sync::Arc, - }, + std::sync::Arc, }; // TODO: Programatically generate this so we don't have to keep committed ABI in sync with the @@ -40,7 +40,7 @@ impl SignablePythContract { pub async fn from_config( chain_config: &EthereumConfig, private_key: &str, - ) -> Result> { + ) -> Result { let provider = Provider::::try_from(&chain_config.geth_rpc_addr)?; let chain_id = provider.get_chainid().await?; @@ -64,7 +64,7 @@ impl SignablePythContract { provider: &Address, user_randomness: &[u8; 32], use_blockhash: bool, - ) -> Result> { + ) -> Result { let fee = self.get_fee(*provider).call().await?; let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into(); @@ -98,7 +98,7 @@ impl SignablePythContract { sequence_number: u64, user_randomness: &[u8; 32], provider_randomness: &[u8; 32], - ) -> Result<[u8; 32], Box> { + ) -> Result<[u8; 32]> { if let Some(r) = self .reveal( *provider, @@ -124,7 +124,7 @@ impl SignablePythContract { } impl PythContract { - pub fn from_config(chain_config: &EthereumConfig) -> Result> { + pub fn from_config(chain_config: &EthereumConfig) -> Result { let provider = Provider::::try_from(&chain_config.geth_rpc_addr)?; Ok(PythRandom::new( diff --git a/pyth-rng/src/main.rs b/pyth-rng/src/main.rs index d48f25ac5..fb2180b4d 100644 --- a/pyth-rng/src/main.rs +++ b/pyth-rng/src/main.rs @@ -4,7 +4,7 @@ use { anyhow::Result, clap::Parser, - std::error::Error, + std::io::IsTerminal, }; pub mod api; @@ -15,14 +15,25 @@ pub mod state; // Server TODO list: // - Tests -// - Metrics / liveness / readiness endpoints -// - replace println! with proper logging // - Reduce memory requirements for storing hash chains to increase scalability // - Name things nicely (service name, API resource names) // - README -// - use anyhow::Result +// - Choose data formats for binary data #[tokio::main] -async fn main() -> Result<(), Box> { +#[tracing::instrument] +async fn main() -> Result<()> { + // Initialize a Tracing Subscriber + tracing::subscriber::set_global_default( + tracing_subscriber::fmt() + .compact() + .with_file(false) + .with_line_number(true) + .with_thread_ids(true) + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_ansi(std::io::stderr().is_terminal()) + .finish(), + )?; + match config::Options::parse() { config::Options::GetRequest(opts) => command::get_request(&opts).await, config::Options::Generate(opts) => command::generate(&opts).await, diff --git a/pyth-rng/src/state.rs b/pyth-rng/src/state.rs index 9895c3b0e..8f72264ce 100644 --- a/pyth-rng/src/state.rs +++ b/pyth-rng/src/state.rs @@ -11,7 +11,6 @@ use { Digest, Keccak256, }, - std::error::Error, }; /// A HashChain. @@ -39,7 +38,7 @@ impl PebbleHashChain { opts: &RandomnessOptions, chain_id: &ChainId, random: [u8; 32], - ) -> Result> { + ) -> Result { let mut input: Vec = vec![]; input.extend_from_slice(&hex::decode(opts.secret.clone())?); input.extend_from_slice(&chain_id.as_bytes()); @@ -79,7 +78,13 @@ pub struct HashChainState { impl HashChainState { pub fn reveal(&self, sequence_number: u64) -> Result<[u8; 32]> { let sequence_number: usize = sequence_number.try_into()?; - let chain_index = self.offsets.partition_point(|x| x <= &sequence_number) - 1; + let chain_index = self + .offsets + .partition_point(|x| x <= &sequence_number) + .checked_sub(1) + .ok_or(anyhow::anyhow!( + "Hash chain for the requested sequence number is not available." + ))?; self.hash_chains[chain_index].reveal_ith(sequence_number - self.offsets[chain_index]) } }