diff --git a/README.md b/README.md index 39e82bd..d0f2e37 100644 --- a/README.md +++ b/README.md @@ -102,10 +102,13 @@ Make sure the `service` binary and the `glove.eif` file are in the same director will both be in `target/release`: ```shell -target/release/service --address= --proxy-secret-phrase= --node-endpoint= dynamodb --table-name= +target/release/service --address= --proxy-secret-phrase= --node-endpoint= --subscan-api-key= dynamodb --table-name= ``` -To understand what these arguments mean and others, you will need to first read the help with `--help`. +The service makes use of [Subscan](https://support.subscan.io/) and it is recommended an API key be provided, though +not required. + +Full documenation of the arguments, along with other flags, can be found with `--help`. You can check the enclave is running with: diff --git a/client-interface/src/subscan.rs b/client-interface/src/subscan.rs index 8502749..286e9b8 100644 --- a/client-interface/src/subscan.rs +++ b/client-interface/src/subscan.rs @@ -14,15 +14,17 @@ use common::ExtrinsicLocation; #[derive(Clone)] pub struct Subscan { - http_client: reqwest::Client, network: String, + api_key: Option, + http_client: reqwest::Client, } impl Subscan { - pub fn new(network: String) -> Self { + pub fn new(network: String, api_key: Option) -> Self { Self { - http_client: reqwest::Client::new(), network, + api_key, + http_client: reqwest::Client::new(), } } @@ -87,8 +89,13 @@ impl Subscan { end_point: &str, request: &(impl Serialize + ?Sized) ) -> Result<(HeaderMap, Resp), Error> { - let http_response = self.http_client - .post(format!("https://{}.api.subscan.io/api/scan/{}", self.network, end_point)) + let request_builder = self.http_client + .post(format!("https://{}.api.subscan.io/api/scan/{}", self.network, end_point)); + let request_builder = match &self.api_key { + Some(api_key) => request_builder.header("X-API-Key", api_key), + None => request_builder + }; + let http_response = request_builder .json(&request) .send().await?; let headers = http_response.headers().clone(); diff --git a/client/src/lib.rs b/client/src/lib.rs index e628fea..e974737 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -33,7 +33,7 @@ use common::{attestation, Conviction, SignedVoteRequest, VoteRequest}; use common::attestation::{Attestation, EnclaveInfo}; use RuntimeError::Proxy; -use crate::verify::{Error, try_verify_glove_result}; +use crate::verify::try_verify_glove_result; pub mod verify; @@ -125,51 +125,9 @@ async fn vote( if response.status() != StatusCode::OK { bail!(response.text().await?) } - if cmd.await_glove_proof { - listen_for_glove_votes(&network, &cmd, nonce, &service_info.proxy_account).await?; - } return Ok(SuccessOutput::Voted { nonce }); } -// TODO Stop waiting when the poll is closed. -async fn listen_for_glove_votes( - network: &CallableSubstrateNetwork, - vote_cmd: &VoteCmd, - nonce: u32, - proxy_account: &AccountId32 -) -> Result<()> { - network.subscribe_successful_extrinsics(|extrinsic, _| async move { - let verification_result = try_verify_glove_result( - &network, - &extrinsic, - proxy_account, - vote_cmd.poll_index, - ).await; - let verified_glove_proof = match verification_result { - Ok(None) => return Ok(()), // Not what we're looking for - Ok(Some(verified_glove_proof)) => verified_glove_proof, - Err(Error::Subxt(subxt_error)) => return Err(subxt_error.into()), - Err(error) => { - eprintln!("Error verifying Glove proof: {}", error); - return Ok(()); - } - }; - if let Some(balance) = verified_glove_proof.get_vote_balance(&network.account(), nonce) { - println!("Glove vote {:?} with balance {}", - verified_glove_proof.result.direction, network.token.amount(balance)); - if let Some(_) = &verified_glove_proof.enclave_info { - // TODO Check measurement - } else { - eprintln!("WARNING: Secure enclave wasn't used"); - } - } else { - eprintln!("WARNING: Received Glove proof for poll, but vote was not included"); - } - Ok(()) - }).await?; - Ok(()) -} - async fn remove_vote( service_info: ServiceInfo, cmd: RemoveVoteCmd, @@ -203,7 +161,7 @@ async fn verify_vote(service_info: ServiceInfo, cmd: VerifyVoteCmd) -> Result, /// Optional, the nonce value used in the most recent vote request. #[arg(long, short, verbatim_doc_comment)] - nonce: Option + nonce: Option, + /// Optional, API key to use with Subscan + #[arg(long, short, verbatim_doc_comment)] + subscan_api_key: Option } #[derive(Debug, Parser)] diff --git a/client/src/verify.rs b/client/src/verify.rs index f94a2a4..f8ae977 100644 --- a/client/src/verify.rs +++ b/client/src/verify.rs @@ -27,7 +27,6 @@ pub struct VerifiedGloveProof { pub attested_data: AttestedData } -// TODO API for checking EnclaveInfo for expected measurements impl VerifiedGloveProof { pub fn get_assigned_balance(&self, account: &AccountId32) -> Option { self.result @@ -246,5 +245,3 @@ pub enum Error { #[error("Invalid attestation: {0}")] Attestation(#[from] attestation::Error) } - -// TODO Tests diff --git a/common/Cargo.toml b/common/Cargo.toml index da546b2..8202b34 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -25,4 +25,3 @@ subxt.workspace = true [dev-dependencies] serde_json.workspace = true subxt-signer.workspace = true -# TODO client-interface and enclave-interface should be merged into common but behind feature flags diff --git a/service/src/main.rs b/service/src/main.rs index fbe3c8d..3de9ab6 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -74,6 +74,10 @@ struct Args { #[arg(long, verbatim_doc_comment)] node_endpoint: String, + /// API key to use when querying Subscan. + #[arg(long, verbatim_doc_comment)] + subscan_api_key: Option, + /// The storage to use for the service. #[clap(subcommand, verbatim_doc_comment)] storage: Storage, @@ -129,7 +133,6 @@ enum EnclaveMode { Mock } -// TODO Probably need to specify API key for subscan // TODO Sign the enclave image // TODO Permantely ban accounts which vote directly // TODO Endpoint for poll end time and other info? @@ -196,7 +199,7 @@ async fn main() -> anyhow::Result<()> { state: GloveState::default() }); - let subscan = Subscan::new(glove_context.network.network_name.clone()); + let subscan = Subscan::new(glove_context.network.network_name.clone(), args.subscan_api_key); if args.regular_mix { warn!("Regular mixing of votes is enabled. This is not suitable for production."); } else { @@ -532,9 +535,6 @@ async fn submit_glove_result_on_chain( signed_requests: &Vec, signed_glove_result: SignedGloveResult ) -> Result<(), ProxyError> { - // TODO Should the enclave produce the extrinic calls structs? It would prove the enclave - // intiated the abstain votes. Otherwise, users are trusting the host service is correctly - // interpreting the enclave's None mixing output. let mut batched_calls = Vec::with_capacity(signed_requests.len() + 1); let glove_result = &signed_glove_result.result; diff --git a/stress-tool/src/main.rs b/stress-tool/src/main.rs index 00257ef..8f880ae 100644 --- a/stress-tool/src/main.rs +++ b/stress-tool/src/main.rs @@ -167,7 +167,7 @@ async fn extrinsic(extrinsic_location: ExtrinsicLocation, args: Args) -> Result< .send().await? .error_for_status()? .json::().await?; - let subscan = Subscan::new(service_info.network_name); + let subscan = Subscan::new(service_info.network_name, None); match subscan.get_extrinsic(extrinsic_location).await? { Some(extrinsic) => println!("{:#?}", extrinsic), None => bail!("Extrinsic not found")