Jayant Krishnamurthy 2 жил өмнө
parent
commit
a608e913c7

+ 7 - 0
.pre-commit-config.yaml

@@ -65,6 +65,13 @@ repos:
         entry: cargo +nightly-2023-07-23 fmt --manifest-path ./hermes/Cargo.toml --all -- --config-path rustfmt.toml
         pass_filenames: false
         files: hermes
+      # Hooks for pyth-rng
+      - id: cargo-fmt-pyth-rng
+        name: Cargo format for Pyth RNG
+        language: "rust"
+        entry: cargo +nightly-2023-07-23 fmt --manifest-path ./pyth-rng/Cargo.toml --all -- --config-path rustfmt.toml
+        pass_filenames: false
+        files: pyth-rng
       # Hooks for message buffer contract
       - id: cargo-fmt-message-buffer
         name: Cargo format for message buffer contract

+ 36 - 28
pyth-rng/src/api.rs

@@ -1,32 +1,33 @@
-use std::sync::Arc;
-
-use axum::{
-    http::StatusCode,
-    response::{
-        IntoResponse,
-        Response,
+use {
+    crate::{
+        ethereum::PythContract,
+        state::HashChainState,
+    },
+    axum::{
+        http::StatusCode,
+        response::{
+            IntoResponse,
+            Response,
+        },
     },
+    ethers::core::types::Address,
+    std::sync::Arc,
 };
-use ethers::core::types::Address;
-
 pub use {
     index::*,
     revelation::*,
 };
 
-use crate::ethereum::PythContract;
-use crate::state::HashChainState;
-
-mod revelation;
 mod index;
+mod revelation;
 
 /// The state of the randomness service for a single blockchain.
 #[derive(Clone)]
 pub struct ApiState {
     /// The hash chain(s) required to serve random numbers for this blockchain
-    pub state: Arc<HashChainState>,
+    pub state:            Arc<HashChainState>,
     /// The EVM contract where the protocol is running.
-    pub contract: Arc<PythContract>,
+    pub contract:         Arc<PythContract>,
     /// The EVM address of the provider that this server is operating for.
     pub provider_address: Address,
 }
@@ -48,19 +49,26 @@ pub enum RestError {
 impl IntoResponse for RestError {
     fn into_response(self) -> Response {
         match self {
-            RestError::InvalidSequenceNumber => {
-                (StatusCode::BAD_REQUEST, "The sequence number is out of the permitted range").into_response()
-            }
-            RestError::NoPendingRequest => {
-                (StatusCode::FORBIDDEN, "The random value cannot currently be retrieved").into_response()
-            }
-            RestError::TemporarilyUnavailable => {
-                (StatusCode::SERVICE_UNAVAILABLE, "This service is temporarily unavailable").into_response()
-            }
-            RestError::Unknown => {
-                (StatusCode::INTERNAL_SERVER_ERROR, "An unknown error occurred processing the request").into_response()
-            },
+            RestError::InvalidSequenceNumber => (
+                StatusCode::BAD_REQUEST,
+                "The sequence number is out of the permitted range",
+            )
+                .into_response(),
+            RestError::NoPendingRequest => (
+                StatusCode::FORBIDDEN,
+                "The random value cannot currently be retrieved",
+            )
+                .into_response(),
+            RestError::TemporarilyUnavailable => (
+                StatusCode::SERVICE_UNAVAILABLE,
+                "This service is temporarily unavailable",
+            )
+                .into_response(),
+            RestError::Unknown => (
+                StatusCode::INTERNAL_SERVER_ERROR,
+                "An unknown error occurred processing the request",
+            )
+                .into_response(),
         }
     }
 }
-

+ 1 - 3
pyth-rng/src/api/index.rs

@@ -7,7 +7,5 @@ use axum::{
 ///
 /// TODO: Dynamically generate this list if possible.
 pub async fn index() -> impl IntoResponse {
-    Json([
-        "/v1/revelation",
-    ])
+    Json(["/v1/revelation"])
 }

+ 20 - 8
pyth-rng/src/api/revelation.rs

@@ -1,18 +1,17 @@
-use pythnet_sdk::wire::array;
-
 use {
+    crate::api::RestError,
     anyhow::Result,
     axum::{
         extract::State,
         Json,
     },
+    pythnet_sdk::wire::array,
     serde_qs::axum::QsQuery,
     utoipa::{
         IntoParams,
         ToSchema,
     },
 };
-use crate::api::RestError;
 
 // TODO: this should probably take path parameters /v1/revelation/<chain_id>/<sequence_number>
 /// Reveal the random value for a given sequence number.
@@ -34,13 +33,26 @@ pub async fn revelation(
     State(state): State<crate::api::ApiState>,
     QsQuery(params): QsQuery<GetRandomValueQueryParams>,
 ) -> Result<Json<GetRandomValueResponse>, RestError> {
-    let sequence: u64 = params.sequence.try_into().map_err(|_| RestError::InvalidSequenceNumber)?;
+    let sequence: u64 = params
+        .sequence
+        .try_into()
+        .map_err(|_| RestError::InvalidSequenceNumber)?;
 
-    let r = state.contract.get_request(state.provider_address, sequence).call().await.map_err(|e| RestError::TemporarilyUnavailable)?;
+    let r = state
+        .contract
+        .get_request(state.provider_address, sequence)
+        .call()
+        .await
+        .map_err(|e| RestError::TemporarilyUnavailable)?;
 
     if r.sequence_number != 0 {
-        let value = &state.state.reveal(sequence).map_err(|_| RestError::Unknown)?;
-        Ok(Json(GetRandomValueResponse { value: (*value).clone() }))
+        let value = &state
+            .state
+            .reveal(sequence)
+            .map_err(|_| RestError::Unknown)?;
+        Ok(Json(GetRandomValueResponse {
+            value: (*value).clone(),
+        }))
     } else {
         Err(RestError::NoPendingRequest)
     }
@@ -56,5 +68,5 @@ pub struct GetRandomValueQueryParams {
 pub struct GetRandomValueResponse {
     // TODO: choose serialization format
     #[serde(with = "array")]
-    pub value:      [u8; 32],
+    pub value: [u8; 32],
 }

+ 9 - 7
pyth-rng/src/command.rs

@@ -1,11 +1,13 @@
+mod generate;
+mod get_request;
 mod register_provider;
 mod request_randomness;
-mod get_request;
-mod generate;
 mod run;
 
-pub use register_provider::register_provider;
-pub use request_randomness::request_randomness;
-pub use get_request::get_request;
-pub use generate::generate;
-pub use run::run;
+pub use {
+    generate::generate,
+    get_request::get_request,
+    register_provider::register_provider,
+    request_randomness::request_randomness,
+    run::run,
+};

+ 38 - 15
pyth-rng/src/command/generate.rs

@@ -1,12 +1,16 @@
-use std::error::Error;
-use std::sync::Arc;
-
-use ethers::core::types::Address;
-use sha3::Digest;
-use crate::api::GetRandomValueResponse;
-
-use crate::config::GenerateOptions;
-use crate::ethereum::PythContract;
+use {
+    crate::{
+        api::GetRandomValueResponse,
+        config::GenerateOptions,
+        ethereum::PythContract,
+    },
+    ethers::core::types::Address,
+    sha3::Digest,
+    std::{
+        error::Error,
+        sync::Arc,
+    },
+};
 
 /// Run the entire random number generation protocol to produce a random number.
 pub async fn generate(opts: &GenerateOptions) -> Result<(), Box<dyn Error>> {
@@ -16,22 +20,41 @@ pub async fn generate(opts: &GenerateOptions) -> Result<(), Box<dyn Error>> {
     let provider = opts.provider.parse::<Address>()?;
 
     // Request a random number on the contract
-    let sequence_number = contract.request_wrapper(&provider, &user_randomness, opts.blockhash).await?;
-    println!("Requested the random number with sequence number {:#?}", sequence_number);
+    let sequence_number = contract
+        .request_wrapper(&provider, &user_randomness, opts.blockhash)
+        .await?;
+    println!(
+        "Requested the random number with sequence number {:#?}",
+        sequence_number
+    );
 
     // Get the committed value from the provider
     let client = reqwest::Client::new();
-    let request_url = client.get(format!("{}/v1/revelation", &opts.url)).query(&[("sequence", sequence_number)]).build()?;
-    let resp = client.execute(request_url)
+    let request_url = client
+        .get(format!("{}/v1/revelation", &opts.url))
+        .query(&[("sequence", sequence_number)])
+        .build()?;
+    let resp = client
+        .execute(request_url)
         .await?
         .json::<GetRandomValueResponse>()
         .await?;
 
-    println!("Retrieved the provider's random value. Server response: {:#?}", resp);
+    println!(
+        "Retrieved the provider's random value. Server response: {:#?}",
+        resp
+    );
     let provider_randomness = resp.value;
 
     // Submit the provider's and our values to the contract to reveal the random number.
-    let random_value = contract.reveal_wrapper(&provider, sequence_number, &user_randomness, &provider_randomness).await?;
+    let random_value = contract
+        .reveal_wrapper(
+            &provider,
+            sequence_number,
+            &user_randomness,
+            &provider_randomness,
+        )
+        .await?;
 
     println!("Generated random number: {:#?}", random_value);
 

+ 17 - 9
pyth-rng/src/command/get_request.rs

@@ -1,20 +1,28 @@
-use crate::config::GetRequestOptions;
-use crate::state::PebbleHashChain;
-use ethers::core::types::Address;
-use std::error::Error;
-use std::sync::Arc;
-use crate::ethereum::PythContract;
+use {
+    crate::{
+        config::GetRequestOptions,
+        ethereum::PythContract,
+        state::PebbleHashChain,
+    },
+    ethers::core::types::Address,
+    std::{
+        error::Error,
+        sync::Arc,
+    },
+};
 
 /// Get the on-chain request metadata for a provider and sequence number.
 pub async fn get_request(opts: &GetRequestOptions) -> Result<(), Box<dyn Error>> {
     // Initialize a Provider to interface with the EVM contract.
     let contract = Arc::new(PythContract::from_opts(&opts.ethereum).await?);
 
-    if let r = contract.get_request(opts.provider.parse::<Address>()?, opts.sequence).call().await? {
+    if let r = contract
+        .get_request(opts.provider.parse::<Address>()?, opts.sequence)
+        .call()
+        .await?
+    {
         println!("Found request: {:?}", r);
     }
 
     Ok(())
 }
-
-

+ 18 - 7
pyth-rng/src/command/register_provider.rs

@@ -1,9 +1,15 @@
-use crate::ethereum::PythContract;
-use crate::config::RegisterProviderOptions;
-use crate::state::PebbleHashChain;
-use ethers::core::types::U256;
-use std::error::Error;
-use std::sync::Arc;
+use {
+    crate::{
+        config::RegisterProviderOptions,
+        ethereum::PythContract,
+        state::PebbleHashChain,
+    },
+    ethers::core::types::U256,
+    std::{
+        error::Error,
+        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.
@@ -24,7 +30,12 @@ pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<(), Box
     let commitment_length = opts.randomness.chain_length;
 
     if let Some(r) = contract
-        .register(fee_in_wei, commitment, commitment_metadata, commitment_length)
+        .register(
+            fee_in_wei,
+            commitment,
+            commitment_metadata,
+            commitment_length,
+        )
         .send()
         .await?
         .await?

+ 15 - 10
pyth-rng/src/command/request_randomness.rs

@@ -1,12 +1,15 @@
-use std::error::Error;
-use std::sync::Arc;
-
-use ethers::core::types::Address;
-use sha3::Digest;
-
-use crate::config::RequestRandomnessOptions;
-
-use crate::ethereum::PythContract;
+use {
+    crate::{
+        config::RequestRandomnessOptions,
+        ethereum::PythContract,
+    },
+    ethers::core::types::Address,
+    sha3::Digest,
+    std::{
+        error::Error,
+        sync::Arc,
+    },
+};
 
 pub async fn request_randomness(opts: &RequestRandomnessOptions) -> Result<(), Box<dyn Error>> {
     let contract = Arc::new(PythContract::from_opts(&opts.ethereum).await?);
@@ -14,7 +17,9 @@ pub async fn request_randomness(opts: &RequestRandomnessOptions) -> Result<(), B
     let user_randomness = rand::random::<[u8; 32]>();
     let provider = opts.provider.parse::<Address>()?;
 
-    let sequence_number = contract.request_wrapper(&provider, &user_randomness, false).await?;
+    let sequence_number = contract
+        .request_wrapper(&provider, &user_randomness, false)
+        .await?;
 
     println!("sequence number: {:#?}", sequence_number);
 

+ 30 - 18
pyth-rng/src/command/run.rs

@@ -1,27 +1,31 @@
-use anyhow::anyhow;
-use std::error::Error;
-
-use clap::Parser;
-
 use {
-    anyhow::Result,
+    crate::{
+        api,
+        config::RunOptions,
+        ethereum::PythContract,
+        state::{
+            HashChainState,
+            PebbleHashChain,
+        },
+    },
+    anyhow::{
+        anyhow,
+        Result,
+    },
     axum::{
-        Router,
         routing::get,
+        Router,
     },
-    crate::config::RunOptions,
-    std::sync::{
-        Arc,
+    clap::Parser,
+    ethers::core::types::Address,
+    std::{
+        error::Error,
+        sync::Arc,
     },
     tower_http::cors::CorsLayer,
     utoipa::OpenApi,
     utoipa_swagger_ui::SwaggerUi,
 };
-use ethers::core::types::Address;
-
-use crate::api;
-use crate::state::{HashChainState, PebbleHashChain};
-use crate::ethereum::PythContract;
 
 pub async fn run(opts: &RunOptions) -> Result<(), Box<dyn Error>> {
     #[derive(OpenApi)]
@@ -51,17 +55,25 @@ pub async fn run(opts: &RunOptions) -> Result<(), Box<dyn Error>> {
     let random: [u8; 32] = provider_info.commitment_metadata;
     let mut chain = PebbleHashChain::from_config(&opts.randomness, random)?;
     let chain_state = HashChainState {
-        offsets: vec![provider_info.original_commitment_sequence_number.try_into()?],
+        offsets:     vec![provider_info
+            .original_commitment_sequence_number
+            .try_into()?],
         hash_chains: vec![chain],
     };
 
-    if chain_state.reveal(provider_info.original_commitment_sequence_number)? != provider_info.original_commitment {
+    if chain_state.reveal(provider_info.original_commitment_sequence_number)?
+        != provider_info.original_commitment
+    {
         return Err(anyhow!("The root of the generated hash chain does not match the commitment. Is the secret configured correctly?").into());
     } else {
         println!("Root of chain matches commitment");
     }
 
-    let mut state = api::ApiState { state: Arc::new(chain_state), contract, provider_address: provider_addr };
+    let mut state = api::ApiState {
+        state: Arc::new(chain_state),
+        contract,
+        provider_address: provider_addr,
+    };
 
     // Initialize Axum Router. Note the type here is a `Router<State>` due to the use of the
     // `with_state` method which replaces `Body` with `State` in the type signature.

+ 17 - 13
pyth-rng/src/config.rs

@@ -1,22 +1,26 @@
-use clap::crate_authors;
-use clap::crate_description;
-use clap::crate_name;
-use clap::crate_version;
-use clap::Parser;
-use clap::Args;
+use clap::{
+    crate_authors,
+    crate_description,
+    crate_name,
+    crate_version,
+    Args,
+    Parser,
+};
 
 
+mod generate;
+mod get_request;
 mod register_provider;
 mod request_randomness;
 mod run;
-mod get_request;
-mod generate;
 
-pub use register_provider::RegisterProviderOptions;
-pub use request_randomness::RequestRandomnessOptions;
-pub use get_request::GetRequestOptions;
-pub use run::RunOptions;
-pub use generate::GenerateOptions;
+pub use {
+    generate::GenerateOptions,
+    get_request::GetRequestOptions,
+    register_provider::RegisterProviderOptions,
+    request_randomness::RequestRandomnessOptions,
+    run::RunOptions,
+};
 
 const DEFAULT_RPC_ADDR: &str = "127.0.0.1:34000";
 const DEFAULT_HTTP_ADDR: &str = "http://127.0.0.1:34000";

+ 8 - 4
pyth-rng/src/config/generate.rs

@@ -1,6 +1,10 @@
-use clap::Args;
-
-use crate::config::{EthereumOptions, RandomnessOptions};
+use {
+    crate::config::{
+        EthereumOptions,
+        RandomnessOptions,
+    },
+    clap::Args,
+};
 
 #[derive(Args, Clone, Debug)]
 #[command(next_help_heading = "Generate Options")]
@@ -19,5 +23,5 @@ pub struct GenerateOptions {
     pub url: String,
 
     #[arg(short = 'b')]
-    pub blockhash: bool
+    pub blockhash: bool,
 }

+ 4 - 2
pyth-rng/src/config/get_request.rs

@@ -1,5 +1,7 @@
-use clap::Args;
-use crate::config::EthereumOptions;
+use {
+    crate::config::EthereumOptions,
+    clap::Args,
+};
 
 #[derive(Args, Clone, Debug)]
 #[command(next_help_heading = "Get Request Options")]

+ 7 - 2
pyth-rng/src/config/register_provider.rs

@@ -1,5 +1,10 @@
-use clap::Args;
-use crate::config::{EthereumOptions, RandomnessOptions};
+use {
+    crate::config::{
+        EthereumOptions,
+        RandomnessOptions,
+    },
+    clap::Args,
+};
 
 #[derive(Args, Clone, Debug)]
 #[command(next_help_heading = "Register Provider Options")]

+ 7 - 3
pyth-rng/src/config/request_randomness.rs

@@ -1,6 +1,10 @@
-use clap::Args;
-
-use crate::config::{EthereumOptions, RandomnessOptions};
+use {
+    crate::config::{
+        EthereumOptions,
+        RandomnessOptions,
+    },
+    clap::Args,
+};
 
 #[derive(Args, Clone, Debug)]
 #[command(next_help_heading = "Request Randomness Options")]

+ 8 - 3
pyth-rng/src/config/run.rs

@@ -1,6 +1,11 @@
-use clap::Args;
-use std::net::SocketAddr;
-use crate::config::{EthereumOptions, RandomnessOptions};
+use {
+    crate::config::{
+        EthereumOptions,
+        RandomnessOptions,
+    },
+    clap::Args,
+    std::net::SocketAddr,
+};
 
 /// Run the webservice
 #[derive(Args, Clone, Debug)]

+ 51 - 24
pyth-rng/src/ethereum.rs

@@ -1,19 +1,33 @@
-use ethers::contract::abigen;
-use ethers::core::types::Address;
-use ethers::middleware::SignerMiddleware;
-use ethers::providers::Http;
-use ethers::providers::Middleware;
-use ethers::providers::Provider;
-use ethers::signers::LocalWallet;
-use ethers::signers::Signer;
-use ethers::abi::RawLog;
-use ethers::contract::EthLogDecode;
-use std::error::Error;
-use std::sync::Arc;
-use crate::config::EthereumOptions;
-use anyhow::anyhow;
-use sha3::Keccak256;
-use sha3::Digest;
+use {
+    crate::config::EthereumOptions,
+    anyhow::anyhow,
+    ethers::{
+        abi::RawLog,
+        contract::{
+            abigen,
+            EthLogDecode,
+        },
+        core::types::Address,
+        middleware::SignerMiddleware,
+        providers::{
+            Http,
+            Middleware,
+            Provider,
+        },
+        signers::{
+            LocalWallet,
+            Signer,
+        },
+    },
+    sha3::{
+        Digest,
+        Keccak256,
+    },
+    std::{
+        error::Error,
+        sync::Arc,
+    },
+};
 
 // TODO: Programatically generate this so we don't have to keep committed ABI in sync with the
 // contract in the same repo.
@@ -22,20 +36,28 @@ abigen!(PythRandom, "src/abi.json");
 pub type PythContract = PythRandom<SignerMiddleware<Provider<Http>, LocalWallet>>;
 
 impl PythContract {
-
     pub async fn from_opts(opts: &EthereumOptions) -> Result<PythContract, Box<dyn Error>> {
         let provider = Provider::<Http>::try_from(&opts.geth_rpc_addr)?;
         let chain_id = provider.get_chainid().await?;
-        let wallet__ = opts.private_key.clone().ok_or(anyhow!("No private key specified"))?.parse::<LocalWallet>()?.with_chain_id(chain_id.as_u64());
+        let wallet__ = opts
+            .private_key
+            .clone()
+            .ok_or(anyhow!("No private key specified"))?
+            .parse::<LocalWallet>()?
+            .with_chain_id(chain_id.as_u64());
 
         Ok(PythRandom::new(
             opts.contract_addr.parse::<Address>()?,
             Arc::new(SignerMiddleware::new(provider, wallet__)),
-        )
-        )
+        ))
     }
 
-    pub async fn request_wrapper(&self, provider: &Address, user_randomness: &[u8; 32], use_blockhash: bool) -> Result<u64, Box<dyn Error>> {
+    pub async fn request_wrapper(
+        &self,
+        provider: &Address,
+        user_randomness: &[u8; 32],
+        use_blockhash: bool,
+    ) -> Result<u64, Box<dyn Error>> {
         let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into();
 
         if let Some(r) = self
@@ -57,7 +79,13 @@ impl PythContract {
         }
     }
 
-    pub async fn reveal_wrapper(&self, provider: &Address, sequence_number: u64, user_randomness: &[u8; 32], provider_randomness: &[u8; 32]) -> Result<[u8;32], Box<dyn Error>> {
+    pub async fn reveal_wrapper(
+        &self,
+        provider: &Address,
+        sequence_number: u64,
+        user_randomness: &[u8; 32],
+        provider_randomness: &[u8; 32],
+    ) -> Result<[u8; 32], Box<dyn Error>> {
         if let Some(r) = self
             .reveal(
                 *provider,
@@ -79,6 +107,5 @@ impl PythContract {
         } else {
             Err(anyhow!("Request failed").into())
         }
-
     }
-}
+}

+ 4 - 7
pyth-rng/src/main.rs

@@ -1,20 +1,17 @@
 #![allow(clippy::just_underscores_and_digits)]
 #![feature(slice_flatten)]
 
-use std::error::Error;
-
-use clap::Parser;
-
 use {
+    crate::state::PebbleHashChain,
     anyhow::Result,
+    clap::Parser,
+    std::error::Error,
     utoipa::OpenApi,
 };
 
-use crate::state::PebbleHashChain;
-
 pub mod api;
-pub mod config;
 pub mod command;
+pub mod config;
 pub mod ethereum;
 pub mod state;
 

+ 14 - 9
pyth-rng/src/state.rs

@@ -1,10 +1,15 @@
-use anyhow::ensure;
-use anyhow::Result;
-use sha3::Digest;
-use sha3::Keccak256;
-use std::error::Error;
-
-use crate::config::RandomnessOptions;
+use {
+    crate::config::RandomnessOptions,
+    anyhow::{
+        ensure,
+        Result,
+    },
+    sha3::{
+        Digest,
+        Keccak256,
+    },
+    std::error::Error,
+};
 
 /// A HashChain.
 pub struct PebbleHashChain {
@@ -59,7 +64,7 @@ impl PebbleHashChain {
 /// which requires tracking multiple hash chains here.
 pub struct HashChainState {
     // The sequence number where the hash chain starts. Must be stored in sorted order.
-    pub offsets: Vec<usize>,
+    pub offsets:     Vec<usize>,
     pub hash_chains: Vec<PebbleHashChain>,
 }
 
@@ -69,4 +74,4 @@ impl HashChainState {
         let chain_index = self.offsets.partition_point(|x| x <= &sequence_number) - 1;
         self.hash_chains[chain_index].reveal_ith(sequence_number - self.offsets[chain_index])
     }
-}
+}