use crate::pocket_ic::RejectResponse;
use candid::Principal;
use hex;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::path::PathBuf;
pub type InstanceId = usize;
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct AutoProgressConfig {
pub artificial_delay_ms: Option<u64>,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum HttpGatewayBackend {
Replica(String),
PocketIcInstance(InstanceId),
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct HttpsConfig {
pub cert_path: String,
pub key_path: String,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct InstanceHttpGatewayConfig {
pub ip_addr: Option<String>,
pub port: Option<u16>,
pub domains: Option<Vec<String>>,
pub https_config: Option<HttpsConfig>,
pub domain_custom_provider_local_file: Option<String>,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct HttpGatewayConfig {
pub ip_addr: Option<String>,
pub port: Option<u16>,
pub forward_to: HttpGatewayBackend,
pub domains: Option<Vec<String>>,
pub https_config: Option<HttpsConfig>,
pub domain_custom_provider_local_file: Option<String>,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct HttpGatewayInfo {
pub instance_id: InstanceId,
pub port: u16,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CreateHttpGatewayResponse {
Created(HttpGatewayInfo),
Error { message: String },
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CreateInstanceResponse {
Created {
instance_id: InstanceId,
topology: Topology,
http_gateway_info: Option<HttpGatewayInfo>,
},
Error {
message: String,
},
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct RawTime {
pub nanos_since_epoch: u64,
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
pub enum RawEffectivePrincipal {
None,
SubnetId(
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
Vec<u8>,
),
CanisterId(
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
Vec<u8>,
),
}
impl std::fmt::Display for RawEffectivePrincipal {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RawEffectivePrincipal::None => write!(f, "None"),
RawEffectivePrincipal::SubnetId(subnet_id) => {
let principal = Principal::from_slice(subnet_id);
write!(f, "SubnetId({principal})")
}
RawEffectivePrincipal::CanisterId(canister_id) => {
let principal = Principal::from_slice(canister_id);
write!(f, "CanisterId({principal})")
}
}
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawMessageId {
pub effective_principal: RawEffectivePrincipal,
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub message_id: Vec<u8>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawIngressStatusArgs {
pub raw_message_id: RawMessageId,
pub raw_caller: Option<RawPrincipalId>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawSenderInfo {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub info: Vec<u8>,
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub signer: Vec<u8>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawCanisterCall {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub sender: Vec<u8>,
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub canister_id: Vec<u8>,
pub effective_principal: RawEffectivePrincipal,
pub method: String,
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub payload: Vec<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sender_info: Option<RawSenderInfo>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum RawCanisterResult {
Ok(
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
Vec<u8>,
),
Err(RejectResponse),
}
impl From<Result<Vec<u8>, RejectResponse>> for RawCanisterResult {
fn from(result: Result<Vec<u8>, RejectResponse>) -> Self {
match result {
Ok(data) => RawCanisterResult::Ok(data),
Err(reject_response) => RawCanisterResult::Err(reject_response),
}
}
}
impl From<RawCanisterResult> for Result<Vec<u8>, RejectResponse> {
fn from(result: RawCanisterResult) -> Self {
match result {
RawCanisterResult::Ok(data) => Ok(data),
RawCanisterResult::Err(reject_response) => Err(reject_response),
}
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawSetStableMemory {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub canister_id: Vec<u8>,
pub blob_id: BlobId,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawStableMemory {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub blob: Vec<u8>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct ApiError {
pub(crate) message: String,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct StartedOrBusyResponse {
pub state_label: String,
pub op_id: String,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawAddCycles {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub canister_id: Vec<u8>,
pub amount: u128,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawCycles {
pub cycles: u128,
}
#[derive(Clone, Serialize, Hash, Eq, PartialEq, Ord, PartialOrd, Deserialize, Debug)]
pub struct RawPrincipalId {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub principal_id: Vec<u8>,
}
impl From<Principal> for RawPrincipalId {
fn from(principal: Principal) -> Self {
Self {
principal_id: principal.as_slice().to_vec(),
}
}
}
impl From<RawPrincipalId> for Principal {
fn from(raw_principal_id: RawPrincipalId) -> Self {
Principal::from_slice(&raw_principal_id.principal_id)
}
}
#[derive(Clone, Serialize, Eq, PartialEq, Ord, PartialOrd, Deserialize, Debug)]
pub struct RawCanisterId {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub canister_id: Vec<u8>,
}
impl From<Principal> for RawCanisterId {
fn from(principal: Principal) -> Self {
Self {
canister_id: principal.as_slice().to_vec(),
}
}
}
impl From<RawCanisterId> for Principal {
fn from(raw_canister_id: RawCanisterId) -> Self {
Principal::from_slice(&raw_canister_id.canister_id)
}
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
pub struct RawSubnetId {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub subnet_id: Vec<u8>,
}
pub type SubnetId = Principal;
impl From<Principal> for RawSubnetId {
fn from(principal: Principal) -> Self {
Self {
subnet_id: principal.as_slice().to_vec(),
}
}
}
impl From<RawSubnetId> for Principal {
fn from(val: RawSubnetId) -> Self {
Principal::from_slice(&val.subnet_id)
}
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RawNodeId {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub node_id: Vec<u8>,
}
impl From<RawNodeId> for Principal {
fn from(val: RawNodeId) -> Self {
Principal::from_slice(&val.node_id)
}
}
impl From<Principal> for RawNodeId {
fn from(principal: Principal) -> Self {
Self {
node_id: principal.as_slice().to_vec(),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
pub struct RawTickConfigs {
pub blockmakers: Option<Vec<RawSubnetBlockmakers>>,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct RawSubnetBlockmakers {
pub subnet: RawSubnetId,
pub blockmaker: RawNodeId,
pub failed_blockmakers: Vec<RawNodeId>,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct BlobId(
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub Vec<u8>,
);
impl std::fmt::Display for BlobId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "BlobId{{{}}}", hex::encode(self.0.clone()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlobCompression {
Gzip,
NoCompression,
}
pub mod base64 {
use ::base64::prelude::*;
use serde::{Deserialize, Serialize};
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
let base64 = BASE64_STANDARD.encode(v);
String::serialize(&base64, s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let base64 = String::deserialize(d)?;
BASE64_STANDARD
.decode(base64.as_bytes())
.map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub enum SubnetKind {
Application,
Bitcoin,
CloudEngine,
Fiduciary,
II,
NNS,
SNS,
System,
TestThresholdKeys,
VerifiedApplication,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default)]
pub enum IcpFeaturesConfig {
#[default]
DefaultConfig,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default)]
pub struct IcpFeatures {
pub registry: Option<IcpFeaturesConfig>,
pub cycles_minting: Option<IcpFeaturesConfig>,
pub icp_token: Option<IcpFeaturesConfig>,
pub cycles_token: Option<IcpFeaturesConfig>,
pub ii: Option<IcpFeaturesConfig>,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum InitialTime {
Timestamp(RawTime),
AutoProgress(AutoProgressConfig),
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default)]
pub(crate) struct InstanceConfig {
pub subnet_config_set: ExtendedSubnetConfigSet,
pub http_gateway_config: Option<InstanceHttpGatewayConfig>,
pub state_dir: Option<PathBuf>,
pub icp_features: Option<IcpFeatures>,
pub initial_time: Option<InitialTime>,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default)]
pub(crate) struct ExtendedSubnetConfigSet {
pub nns: Option<SubnetSpec>,
pub sns: Option<SubnetSpec>,
pub ii: Option<SubnetSpec>,
pub fiduciary: Option<SubnetSpec>,
pub bitcoin: Option<SubnetSpec>,
pub test_threshold_keys: Option<SubnetSpec>,
pub system: Vec<SubnetSpec>,
pub application: Vec<SubnetSpec>,
#[serde(default)]
pub cloud_engine: Vec<SubnetSpec>,
pub verified_application: Vec<SubnetSpec>,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub(crate) struct SubnetSpec {
state_config: SubnetStateConfig,
instruction_config: SubnetInstructionConfig,
subnet_admins: Option<Vec<RawPrincipalId>>,
#[serde(default)]
cost_schedule: CanisterCyclesCostSchedule,
}
impl Default for SubnetSpec {
fn default() -> Self {
Self {
state_config: SubnetStateConfig::New,
instruction_config: SubnetInstructionConfig::Production,
subnet_admins: None,
cost_schedule: Default::default(),
}
}
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub enum SubnetInstructionConfig {
Production,
Benchmarking,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
enum SubnetStateConfig {
New,
}
#[derive(
Debug, Default, Hash, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize,
)]
pub enum CanisterCyclesCostSchedule {
#[default]
Normal,
Free,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct SubnetConfig {
pub subnet_kind: SubnetKind,
pub subnet_admins: Option<Vec<RawPrincipalId>>,
pub cost_schedule: CanisterCyclesCostSchedule,
pub subnet_seed: [u8; 32],
pub instruction_config: SubnetInstructionConfig,
pub canister_ranges: Vec<CanisterIdRange>,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct CanisterIdRange {
pub start: RawCanisterId,
pub end: RawCanisterId,
}
impl CanisterIdRange {
fn contains(&self, canister_id: Principal) -> bool {
Principal::from_slice(&self.start.canister_id) <= canister_id
&& canister_id <= Principal::from_slice(&self.end.canister_id)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Topology {
pub subnet_configs: BTreeMap<SubnetId, SubnetConfig>,
pub default_effective_canister_id: RawCanisterId,
}
impl Topology {
pub fn get_subnet(&self, canister_id: Principal) -> Option<SubnetId> {
self.subnet_configs
.iter()
.find(|(_, config)| {
config
.canister_ranges
.iter()
.any(|r| r.contains(canister_id))
})
.map(|(subnet_id, _)| subnet_id)
.copied()
}
pub fn get_app_subnets(&self) -> Vec<SubnetId> {
self.find_subnets(SubnetKind::Application, None)
}
pub fn get_cloud_engines(&self) -> Vec<SubnetId> {
self.find_subnets(SubnetKind::CloudEngine, None)
}
pub fn get_verified_app_subnets(&self) -> Vec<SubnetId> {
self.find_subnets(SubnetKind::VerifiedApplication, None)
}
pub fn get_benchmarking_app_subnets(&self) -> Vec<SubnetId> {
self.find_subnets(
SubnetKind::Application,
Some(SubnetInstructionConfig::Benchmarking),
)
}
pub fn get_bitcoin(&self) -> Option<SubnetId> {
self.find_subnet(SubnetKind::Bitcoin)
}
pub fn get_fiduciary(&self) -> Option<SubnetId> {
self.find_subnet(SubnetKind::Fiduciary)
}
pub fn get_ii(&self) -> Option<SubnetId> {
self.find_subnet(SubnetKind::II)
}
pub fn get_nns(&self) -> Option<SubnetId> {
self.find_subnet(SubnetKind::NNS)
}
pub fn get_sns(&self) -> Option<SubnetId> {
self.find_subnet(SubnetKind::SNS)
}
pub fn get_system_subnets(&self) -> Vec<SubnetId> {
self.find_subnets(SubnetKind::System, None)
}
fn find_subnets(
&self,
kind: SubnetKind,
instruction_config: Option<SubnetInstructionConfig>,
) -> Vec<SubnetId> {
self.subnet_configs
.iter()
.filter(|(_, config)| {
config.subnet_kind == kind
&& instruction_config
.as_ref()
.map(|instruction_config| config.instruction_config == *instruction_config)
.unwrap_or(true)
})
.map(|(id, _)| *id)
.collect()
}
fn find_subnet(&self, kind: SubnetKind) -> Option<SubnetId> {
self.subnet_configs
.iter()
.find(|(_, config)| config.subnet_kind == kind)
.map(|(id, _)| *id)
}
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum CanisterHttpMethod {
GET,
POST,
HEAD,
PUT,
DELETE,
PATCH,
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct CanisterHttpHeader {
pub name: String,
pub value: String,
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum CanisterHttpReplication {
FullyReplicated,
NonReplicated,
Flexible {
total_requests: u32,
min_responses: u32,
max_responses: u32,
},
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum CanisterHttpPricingVersion {
Legacy,
PayAsYouGo,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawCanisterHttpRequest {
pub subnet_id: RawSubnetId,
pub request_id: u64,
pub http_method: CanisterHttpMethod,
pub url: String,
pub headers: Vec<CanisterHttpHeader>,
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub body: Vec<u8>,
pub max_response_bytes: Option<u64>,
pub replication: CanisterHttpReplication,
pub pricing_version: CanisterHttpPricingVersion,
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct CanisterHttpRequest {
pub subnet_id: Principal,
pub request_id: u64,
pub http_method: CanisterHttpMethod,
pub url: String,
pub headers: Vec<CanisterHttpHeader>,
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub body: Vec<u8>,
pub max_response_bytes: Option<u64>,
pub replication: CanisterHttpReplication,
pub pricing_version: CanisterHttpPricingVersion,
}
impl From<RawCanisterHttpRequest> for CanisterHttpRequest {
fn from(raw_canister_http_request: RawCanisterHttpRequest) -> Self {
Self {
subnet_id: candid::Principal::from_slice(
&raw_canister_http_request.subnet_id.subnet_id,
),
request_id: raw_canister_http_request.request_id,
http_method: raw_canister_http_request.http_method,
url: raw_canister_http_request.url,
headers: raw_canister_http_request.headers,
body: raw_canister_http_request.body,
max_response_bytes: raw_canister_http_request.max_response_bytes,
replication: raw_canister_http_request.replication,
pricing_version: raw_canister_http_request.pricing_version,
}
}
}
impl From<CanisterHttpRequest> for RawCanisterHttpRequest {
fn from(canister_http_request: CanisterHttpRequest) -> Self {
Self {
subnet_id: canister_http_request.subnet_id.into(),
request_id: canister_http_request.request_id,
http_method: canister_http_request.http_method,
url: canister_http_request.url,
headers: canister_http_request.headers,
body: canister_http_request.body,
max_response_bytes: canister_http_request.max_response_bytes,
replication: canister_http_request.replication,
pricing_version: canister_http_request.pricing_version,
}
}
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct CanisterHttpReply {
pub status: u16,
pub headers: Vec<CanisterHttpHeader>,
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub body: Vec<u8>,
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct CanisterHttpReject {
pub reject_code: u64,
pub message: String,
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum CanisterHttpResponse {
CanisterHttpReply(CanisterHttpReply),
CanisterHttpReject(CanisterHttpReject),
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawMockCanisterHttpResponse {
pub subnet_id: RawSubnetId,
pub request_id: u64,
pub response: CanisterHttpResponse,
pub additional_responses: Vec<CanisterHttpResponse>,
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct MockCanisterHttpResponse {
pub subnet_id: Principal,
pub request_id: u64,
pub response: CanisterHttpResponse,
pub additional_responses: Vec<CanisterHttpResponse>,
}
impl From<RawMockCanisterHttpResponse> for MockCanisterHttpResponse {
fn from(raw_mock_canister_http_response: RawMockCanisterHttpResponse) -> Self {
Self {
subnet_id: candid::Principal::from_slice(
&raw_mock_canister_http_response.subnet_id.subnet_id,
),
request_id: raw_mock_canister_http_response.request_id,
response: raw_mock_canister_http_response.response,
additional_responses: raw_mock_canister_http_response.additional_responses,
}
}
}
impl From<MockCanisterHttpResponse> for RawMockCanisterHttpResponse {
fn from(mock_canister_http_response: MockCanisterHttpResponse) -> Self {
Self {
subnet_id: RawSubnetId {
subnet_id: mock_canister_http_response.subnet_id.as_slice().to_vec(),
},
request_id: mock_canister_http_response.request_id,
response: mock_canister_http_response.response,
additional_responses: mock_canister_http_response.additional_responses,
}
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawCanisterSnapshotDownload {
pub sender: RawPrincipalId,
pub canister_id: RawCanisterId,
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub snapshot_id: Vec<u8>,
pub snapshot_dir: PathBuf,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawCanisterSnapshotUpload {
pub sender: RawPrincipalId,
pub canister_id: RawCanisterId,
pub replace_snapshot: Option<RawCanisterSnapshotId>,
pub snapshot_dir: PathBuf,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct RawCanisterSnapshotId {
#[serde(deserialize_with = "base64::deserialize")]
#[serde(serialize_with = "base64::serialize")]
pub snapshot_id: Vec<u8>,
}