Skip to content

Commit

Permalink
Require complete documentation coverage (#315)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamspofford-dfinity authored Mar 2, 2022
1 parent 10a1d31 commit e7f5050
Show file tree
Hide file tree
Showing 32 changed files with 633 additions and 37 deletions.
6 changes: 5 additions & 1 deletion ic-agent/src/agent/agent_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ use crate::{
use std::{sync::Arc, time::Duration};

/// A configuration for an agent.
#[derive(Debug)]
pub struct AgentConfig {
/// See [`with_nonce_factory`](super::AgentBuilder::with_nonce_factory).
pub nonce_factory: Arc<dyn NonceGenerator>,
/// See [`with_identity`](super::AgentBuilder::with_identity).
pub identity: Arc<dyn Identity>,
/// See [`with_ingress_expiry`](super::AgentBuilder::with_ingress_expiry).
pub ingress_expiry_duration: Option<Duration>,
/// The [`with_transport`](super::AgentBuilder::with_transport).
pub transport: Option<Arc<dyn ReplicaV2Transport>>,
}

Expand Down
62 changes: 60 additions & 2 deletions ic-agent/src/agent/agent_error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Errors that can occur when using the replica agent.
use crate::{agent::status::Status, hash_tree::Label, RequestIdError};
use leb128::read;
use std::{
Expand All @@ -6,122 +8,174 @@ use std::{
};
use thiserror::Error;

/// An error that occurred when using the agent.
#[derive(Error, Debug)]
pub enum AgentError {
/// The replica URL was invalid.
#[error(r#"Invalid Replica URL: "{0}""#)]
InvalidReplicaUrl(String),

/// The request timed out.
#[error("The request timed out.")]
TimeoutWaitingForResponse(),

/// The waiter was restarted without being started first.
#[error("The waiter was restarted without being started first.")]
WaiterRestartError(),

/// An error occurred when signing with the identity.
#[error("Identity had a signing error: {0}")]
SigningError(String),

/// The data fetched was invalid CBOR.
#[error("Invalid CBOR data, could not deserialize: {0}")]
InvalidCborData(#[from] serde_cbor::Error),

/// There was an error calculating a request ID.
#[error("Cannot calculate a RequestID: {0}")]
CannotCalculateRequestId(#[from] RequestIdError),

/// There was an error when de/serializing with Candid.
#[error("Candid returned an error: {0}")]
CandidError(Box<dyn Send + Sync + std::error::Error>),

/// There was an error parsing a URL.
#[error(r#"Cannot parse url: "{0}""#)]
UrlParseError(#[from] url::ParseError),

/// The HTTP method was invalid.
#[error(r#"Invalid method: "{0}""#)]
InvalidMethodError(#[from] http::method::InvalidMethod),

/// The principal string was not a valid principal.
#[error("Cannot parse Principal: {0}")]
PrincipalError(#[from] crate::export::PrincipalError),

/// The replica rejected the message.
#[error(r#"The Replica returned an error: code {reject_code}, message: "{reject_message}""#)]
ReplicaError {
/// The [reject code](https://smartcontracts.org/docs/interface-spec/index.html#reject-codes) returned by the replica.
reject_code: u64,
/// The rejection message.
reject_message: String,
},

/// The replica returned an HTTP error.
#[error("The replica returned an HTTP Error: {0}")]
HttpError(HttpErrorPayload),

/// Attempted to use HTTP authentication in a non-secure URL (either HTTPS or localhost).
#[error("HTTP Authentication cannot be used in a non-secure URL (either HTTPS or localhost)")]
CannotUseAuthenticationOnNonSecureUrl(),

/// The password manager returned an error.
#[error("Password Manager returned an error: {0}")]
AuthenticationError(String),

/// The status endpoint returned an invalid status.
#[error("Status endpoint returned an invalid status.")]
InvalidReplicaStatus,

/// The call was marked done, but no reply was provided.
#[error("Call was marked as done but we never saw the reply. Request ID: {0}")]
RequestStatusDoneNoReply(String),

/// A string error occurred in an external tool.
#[error("A tool returned a string message error: {0}")]
MessageError(String),

/// An error occurred in an external tool.
#[error("A tool returned a custom error: {0}")]
CustomError(#[from] Box<dyn Send + Sync + std::error::Error>),

/// There was an error reading a LEB128 value.
#[error("Error reading LEB128 value: {0}")]
Leb128ReadError(#[from] read::Error),

/// A string was invalid UTF-8.
#[error("Error in UTF-8 string: {0}")]
Utf8ReadError(#[from] Utf8Error),

/// The lookup path was absent in the certificate.
#[error("The lookup path ({0:?}) is absent in the certificate.")]
LookupPathAbsent(Vec<Label>),

/// The lookup path was unknown in the certificate.
#[error("The lookup path ({0:?}) is unknown in the certificate.")]
LookupPathUnknown(Vec<Label>),

/// The lookup path did not make sense for the certificate.
#[error("The lookup path ({0:?}) does not make sense for the certificate.")]
LookupPathError(Vec<Label>),

/// The request status at the requested path was invalid.
#[error("The request status ({1}) at path {0:?} is invalid.")]
InvalidRequestStatus(Vec<Label>, String),

/// The certificate verification failed.
#[error("Certificate verification failed.")]
CertificateVerificationFailed(),

/// There was a length mismatch between the expected and actual length of the BLS DER-encoded public key.
#[error(
r#"BLS DER-encoded public key must be ${expected} bytes long, but is {actual} bytes long."#
)]
DerKeyLengthMismatch { expected: usize, actual: usize },
DerKeyLengthMismatch {
/// The expected length of the key.
expected: usize,
/// The actual length of the key.
actual: usize,
},

/// There was a mismatch between the expected and actual prefix of the BLS DER-encoded public key.
#[error("BLS DER-encoded public key is invalid. Expected the following prefix: ${expected:?}, but got ${actual:?}")]
DerPrefixMismatch { expected: Vec<u8>, actual: Vec<u8> },
DerPrefixMismatch {
/// The expected key prefix.
expected: Vec<u8>,
/// The actual key prefix.
actual: Vec<u8>,
},

/// The status response did not contain a root key.
#[error("The status response did not contain a root key. Status: {0}")]
NoRootKeyInStatus(Status),

/// Could not read the replica root key.
#[error("Could not read the root key")]
CouldNotReadRootKey(),

/// Failed to initialize the BLS library.
#[error("Failed to initialize the BLS library")]
BlsInitializationFailure(),

/// The invocation to the wallet call forward method failed with an error.
#[error("The invocation to the wallet call forward method failed with the error: {0}")]
WalletCallFailed(String),

/// The wallet operation failed.
#[error("The wallet operation failed: {0}")]
WalletError(String),

/// The wallet canister must be upgraded. See [`dfx wallet upgrade`](https://smartcontracts.org/docs/developers-guide/cli-reference/dfx-wallet.html)
#[error("The wallet canister must be upgraded: {0}")]
WalletUpgradeRequired(String),

/// The transport was not specified in the [`AgentBuilder`](super::AgentBuilder).
#[error("Missing replica transport in the Agent Builder.")]
MissingReplicaTransport(),

/// An unknown error occurred during communication with the replica.
#[error("An error happened during communication with the replica: {0}")]
TransportError(Box<dyn std::error::Error + Send + Sync>),

/// There was a mismatch between the expected and actual CBOR data during inspection.
#[error("There is a mismatch between the CBOR encoded call and the arguments: field {field}, value in argument is {value_arg}, value in CBOR is {value_cbor}")]
CallDataMismatch {
/// The field that was mismatched.
field: String,
/// The value that was expected to be in the CBOR.
value_arg: String,
/// The value that was actually in the CBOR.
value_cbor: String,
},
}
Expand All @@ -134,9 +188,13 @@ impl PartialEq for AgentError {
}
}

/// A HTTP error from the replica.
pub struct HttpErrorPayload {
/// The HTTP status code.
pub status: u16,
/// The MIME type of `content`.
pub content_type: Option<String>,
/// The body of the error.
pub content: Vec<u8>,
}

Expand Down
2 changes: 2 additions & 0 deletions ic-agent/src/agent/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use crate::{
};
use std::sync::Arc;

/// A builder for an [`Agent`].
#[derive(Debug)]
pub struct AgentBuilder {
config: AgentConfig,
}
Expand Down
7 changes: 7 additions & 0 deletions ic-agent/src/agent/http_transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ pub trait PasswordManager: Send + Sync {
fn required(&self, url: &str) -> Result<(String, String), String>;
}

impl_debug_empty!(dyn PasswordManager);

/// A [ReplicaV2Transport] using Reqwest to make HTTP calls to the internet computer.
#[derive(Debug)]
pub struct ReqwestHttpReplicaV2Transport {
url: reqwest::Url,
client: reqwest::Client,
Expand All @@ -33,6 +36,7 @@ const IC0_DOMAIN: &str = "ic0.app";
const IC0_SUB_DOMAIN: &str = ".ic0.app";

impl ReqwestHttpReplicaV2Transport {
/// Creates a replica transport from a HTTP URL.
pub fn create<U: Into<String>>(url: U) -> Result<Self, AgentError> {
let mut tls_config = rustls::ClientConfig::builder()
.with_safe_defaults()
Expand Down Expand Up @@ -64,6 +68,7 @@ impl ReqwestHttpReplicaV2Transport {
})
}

/// Sets a password manager to use with HTTP authentication.
pub fn with_password_manager<P: 'static + PasswordManager>(self, password_manager: P) -> Self {
ReqwestHttpReplicaV2Transport {
password_manager: Some(Arc::new(password_manager)),
Expand All @@ -72,6 +77,7 @@ impl ReqwestHttpReplicaV2Transport {
}
}

/// Same as [`with_password_manager`], but providing the Arc so one does not have to be created.
pub fn with_arc_password_manager(self, password_manager: Arc<dyn PasswordManager>) -> Self {
ReqwestHttpReplicaV2Transport {
password_manager: Some(password_manager),
Expand All @@ -80,6 +86,7 @@ impl ReqwestHttpReplicaV2Transport {
}
}

/// Gets the set password manager, if one exists. Otherwise returns None.
pub fn password_manager(&self) -> Option<&dyn PasswordManager> {
self.password_manager.as_deref()
}
Expand Down
Loading

0 comments on commit e7f5050

Please sign in to comment.