use canic_contracts::{
cycles::Cycles,
ids::{
CyclesFundingBudget, FleetFundingProfile, FleetSubnetRootFundingAuthority,
FleetSubnetRootFundingPolicy,
},
};
use ic_testkit::pic::{PocketIc, PocketIcBuilder, PocketIcBuilderExt, PocketIcStartupConfig};
use std::{
path::{Path, PathBuf},
time::{Duration, SystemTime, UNIX_EPOCH},
};
const POCKET_IC_SERVER_URL_ENV: &str = "IC_TESTKIT_POCKET_IC_URL";
const POCKET_IC_STARTUP_TIMEOUT: Duration = Duration::from_secs(30);
pub fn start_pocket_ic(builder: PocketIcBuilder) -> PocketIc {
let server_url = std::env::var(POCKET_IC_SERVER_URL_ENV)
.ok()
.filter(|value| !value.is_empty())
.unwrap_or_else(|| {
panic!(
"{POCKET_IC_SERVER_URL_ENV} must name the governed PocketIC server; use the workspace test runner"
)
});
builder
.try_build(PocketIcStartupConfig::connect(
server_url,
POCKET_IC_STARTUP_TIMEOUT,
))
.unwrap_or_else(|error| panic!("start governed PocketIC instance: {error}"))
}
pub fn fleet_subnet_root_funding_authority() -> FleetSubnetRootFundingAuthority {
FleetSubnetRootFundingAuthority {
root_funding: FleetSubnetRootFundingPolicy {
funding_profile: FleetFundingProfile::SingleSubnet,
request_threshold: Cycles::new(10_000_000_000_000),
target_balance: Cycles::new(30_000_000_000_000),
cooldown_secs: 30 * 24 * 60 * 60,
budget: CyclesFundingBudget {
window_secs: 90 * 24 * 60 * 60,
maximum_cycles: Cycles::new(30_000_000_000_000),
},
maximum_automatic_grants: 4,
maximum_automatic_cycles: Cycles::new(120_000_000_000_000),
},
icp_refill: None,
}
}
pub fn temp_dir(prefix: &str) -> PathBuf {
let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time after epoch")
.as_nanos();
std::env::temp_dir().join(format!("{prefix}-{}-{unique}", std::process::id()))
}
#[track_caller]
pub fn generate_fixture_lockfile(root: &Path) {
let output = crate::cargo_command()
.current_dir(root)
.args(["generate-lockfile", "--offline"])
.output()
.expect("generate fixture lockfile");
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
}
#[cfg(unix)]
pub fn create_fifo(path: &Path) {
let status = std::process::Command::new("mkfifo")
.args(["-m", "600"])
.arg(path)
.status()
.expect("run mkfifo");
assert!(status.success(), "mkfifo failed with {status}");
}
pub fn tool_script(template: &str) -> String {
template
.replace("@ICP_VERSION@", icp_cli_version())
.replace(
"@IC_WASM_IDENTITY@",
crate::ic_wasm::IC_WASM_VERSION_IDENTITY,
)
.replace(
"@BINARYEN_IDENTITY@",
crate::binaryen::BINARYEN_VERSION_IDENTITY,
)
}
pub fn icp_cli_version() -> &'static str {
ic_tool_pin("icp", "linux-x86_64", 1)
}
pub fn ic_tool_pin(tool: &str, host: &str, column: usize) -> &'static str {
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../ci/ic-tools.tsv"
))
.lines()
.find_map(|line| {
let fields: Vec<_> = line.split('\t').collect();
(fields.first() == Some(&tool) && fields.get(2) == Some(&host)).then(|| fields[column])
})
.expect("common repository IC tool pin")
}
#[cfg(unix)]
pub fn synthetic_identity_pem(seed: u8) -> String {
let mut der = vec![
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04,
0x20,
];
der.extend([seed; 32]);
let alphabet = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut body = String::new();
for chunk in der.as_chunks::<3>().0 {
let value = (u32::from(chunk[0]) << 16) | (u32::from(chunk[1]) << 8) | u32::from(chunk[2]);
for shift in [18, 12, 6, 0] {
body.push(char::from(alphabet[((value >> shift) & 63) as usize]));
}
}
format!("-----BEGIN PRIVATE KEY-----\n{body}\n-----END PRIVATE KEY-----\n")
}