Skip to content

Commit

Permalink
saving my place
Browse files Browse the repository at this point in the history
  • Loading branch information
lastmjs committed May 15, 2023
1 parent 1968c5b commit 9660889
Show file tree
Hide file tree
Showing 13 changed files with 536 additions and 30 deletions.
359 changes: 359 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
members = [
"kybra/compiler/kybra_generate",
"kybra/compiler/kybra_vm_value_derive",
"kybra/compiler/kybra_modules_init"
"kybra/compiler/kybra_modules_init",
"kybra/compiler/kybra_deployer"
]
exclude = ["examples"]
edition = "2018"
2 changes: 1 addition & 1 deletion examples/outgoing_http_requests/dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"canisters": {
"outgoing_http_requests": {
"type": "custom",
"build": "python -m kybra outgoing_http_requests src/main.py src/main.did",
"build": "python -m kybra outgoing_http_requests src/main.py src/main.did --verbose",
"post_install": ".kybra/outgoing_http_requests/post_install.sh",
"candid": "src/main.did",
"wasm": ".kybra/outgoing_http_requests/outgoing_http_requests.wasm.gz",
Expand Down
36 changes: 32 additions & 4 deletions kybra/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def main():
shutil.rmtree(paths["canister"])
shutil.copytree(paths["compiler"], paths["canister"], dirs_exist_ok=True)
create_file(f"{paths['canister']}/Cargo.toml", generate_cargo_toml(canister_name))
create_file(f"{paths['canister']}/Cargo.lock", generate_cargo_lock())
# create_file(f"{paths['canister']}/Cargo.lock", generate_cargo_lock())
create_file(
f"{paths['canister']}/post_install.sh",
generate_post_install_script(canister_name, kybra.__rust_version__),
Expand Down Expand Up @@ -389,14 +389,18 @@ def build_wasm_binary_or_exit(

shutil.copy(
f"{paths['global_kybra_target_dir']}/wasm32-wasi/release/{canister_name}.wasm",
paths["wasm"],
f"{paths['canister']}/{canister_name}_final.wasm",
)

candid_file = generate_candid_file(paths)
candid_file = generate_candid_file(paths, canister_name)
create_file(paths["did"], candid_file)

wasi2ic_result = subprocess.run(
[f"{paths['global_kybra_bin_dir']}/wasi2ic", paths["wasm"], paths["wasm"]],
[
f"{paths['global_kybra_bin_dir']}/wasi2ic",
f"{paths['canister']}/{canister_name}_final.wasm",
f"{paths['canister']}/{canister_name}_final.wasm",
],
capture_output=not verbose,
env=cargo_env,
)
Expand All @@ -407,6 +411,30 @@ def build_wasm_binary_or_exit(
print("💀 Build failed")
sys.exit(1)

kybra_deployer_build_result = subprocess.run(
[
f"{paths['global_kybra_bin_dir']}/cargo",
"build",
f"--manifest-path={paths['canister']}/kybra_deployer/Cargo.toml",
"--target=wasm32-unknown-unknown",
f"--package=kybra_deployer",
"--release",
],
capture_output=not verbose,
env=cargo_env,
)

if kybra_deployer_build_result.returncode != 0:
print(red("\n💣 Error building Wasm binary:"))
print(cargo_build_result.stderr.decode("utf-8"))
print("💀 Build failed")
sys.exit(1)

shutil.copy(
f"{paths['global_kybra_target_dir']}/wasm32-unknown-unknown/release/kybra_deployer.wasm",
f"{paths['canister']}/{canister_name}.wasm",
)


@timed_inline
def optimize_wasm_binary_or_exit(
Expand Down
4 changes: 2 additions & 2 deletions kybra/candid.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
)


def generate_candid_file(paths) -> str:
file = open(paths["wasm"], "rb")
def generate_candid_file(paths, canister_name) -> str:
file = open(f"{paths['canister']}/{canister_name}_final.wasm", "rb")
wasm_buffer = file.read()
file.close()

Expand Down
8 changes: 3 additions & 5 deletions kybra/cargotoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ def generate_cargo_toml(canister_name: str) -> str:
opt-level = 'z'
codegen-units = 1
lto = "fat"
# strip = true
panic = "abort"
[lib]
crate-type = ["cdylib"]
Expand All @@ -19,8 +21,6 @@ def generate_cargo_toml(canister_name: str) -> str:
ic-cdk-timers = "0.1.2"
candid = {{ version = "0.9.0-beta.2", features = ["parser"] }}
kybra-vm-value-derive = {{ path = "./kybra_vm_value_derive" }}
# TODO add this back once we support the full stdlib: https://github.com/demergent-labs/kybra/issues/12
# rustpython = {{ git = "https://github.com/demergent-labs/RustPython", rev = "b7b0a4994d7871bf1e21fedb6bd0f0e5639fa874", default-features = false, features = ["stdlib", "freeze-stdlib"] }}
rustpython = {{ git = "https://github.com/demergent-labs/RustPython", rev = "b7b0a4994d7871bf1e21fedb6bd0f0e5639fa874", default-features = false, features = ["stdlib", "encodings"] }}
rustpython-vm = {{ git = "https://github.com/demergent-labs/RustPython", rev = "b7b0a4994d7871bf1e21fedb6bd0f0e5639fa874", default-features = false, features = ["ic"] }}
Expand All @@ -41,12 +41,10 @@ def generate_cargo_toml(canister_name: str) -> str:
# TODO add this back once we support the full stdlib: https://github.com/demergent-labs/kybra/issues/12
# rustpython-pylib = {{ git = "https://github.com/demergent-labs/RustPython", rev = "b7b0a4994d7871bf1e21fedb6bd0f0e5639fa874", default-features = false, features = ["freeze-stdlib"] }}
# rustpython = {{ path = "../../../../../../RustPython", default-features = false, features = [] }}
getrandom = {{ version = "0.2.3", features = ["custom"] }}
serde = "1.0.137"
serde = {{ version = "1.0.137", default-features = false, features = [] }}
async-recursion = "1.0.0"
ic-stable-structures = "0.5.2"
slotmap = "1.0.6"
rand = "0.8.5"
ic-wasi-polyfill = {{ git = "https://github.com/wasm-forge/ic-wasi-polyfill", rev = "01dc79c84dc1de37bba104ac0a87f4faf7308937" }}
[patch.crates-io]
Expand Down
7 changes: 4 additions & 3 deletions kybra/compiler/install_rust_dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function run() {
mkdir -p "$global_kybra_logs_dir"

install_rustup
install_wasm32_wasi
install_wasm32
install_wasi2ic
install_ic_wasm "$ic_wasm_already_installed"
else
Expand All @@ -46,10 +46,11 @@ function update_rustup() {
"$global_kybra_rustup_bin" update "$rust_version" &> "$global_kybra_logs_dir"/rustup_update
}

function install_wasm32_wasi() {
echo -e "2/4) Installing wasm32-wasi"
function install_wasm32() {
echo -e "2/4) Installing wasm32"

"$global_kybra_rustup_bin" target add wasm32-wasi &> "$global_kybra_logs_dir"/install_wasm32_wasi
"$global_kybra_rustup_bin" target add wasm32-unknown-unknown &> "$global_kybra_logs_dir"/install_wasm32_unknown_unknown
}

function install_wasi2ic() {
Expand Down
11 changes: 11 additions & 0 deletions kybra/compiler/kybra_deployer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "kybra_deployer"
version = "0.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
ic-cdk = "0.8.0-beta"
ic-cdk-macros = "0.6.10"
39 changes: 39 additions & 0 deletions kybra/compiler/kybra_deployer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
thread_local! {
static WASM_REF_CELL: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(vec![]);
// static STDLIB_REF_CELL
}

#[ic_cdk_macros::update]
pub fn upload_wasm_chunk(bytes: Vec<u8>) {
WASM_REF_CELL.with(|wasm_ref_cell| {
let mut wasm_ref_mut = wasm_ref_cell.borrow_mut();
wasm_ref_mut.extend(bytes);
});
}

// TODO we want to use notify
// TODO figure out args
// TODO it will probably be similar to kybra_modules_init
// TODO I think for the init stuff...I wonder if I can just store the args as binary
#[ic_cdk_macros::update]
pub async fn install_wasm() {
let wasm_module = WASM_REF_CELL.with(|wasm_ref_cell| wasm_ref_cell.borrow().clone());

// TODO figure out init/post_upgrade
let result = ic_cdk::api::management_canister::main::install_code(
ic_cdk::api::management_canister::main::InstallCodeArgument {
mode: ic_cdk::api::management_canister::main::CanisterInstallMode::Upgrade,
canister_id: ic_cdk::api::id(),
wasm_module,
arg: vec![],
},
)
.await;

match result {
Ok(_) => {}
Err(err) => {
panic!("{:#?}", err);
}
}
}
1 change: 0 additions & 1 deletion kybra/compiler/kybra_generate/src/header/use_statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pub fn generate() -> TokenStream {
quote::quote! {
use candid::{Decode, Encode};
use kybra_vm_value_derive::{CdkActTryIntoVmValue, CdkActTryFromVmValue};
use rand::Rng as _KybraTraitRng;
use rustpython_vm::{
class::PyClassImpl as _KybraTraitPyClassImpl,
convert::ToPyObject as _KybraTraitToPyObject,
Expand Down
15 changes: 9 additions & 6 deletions kybra/compiler/kybra_generate/src/kybra_modules_init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,19 @@ pub fn generate(
static INSTALLER_PRINCIPAL_REF_CELL: std::cell::RefCell<Option<ic_cdk::export::Principal>> = std::cell::RefCell::new(None);
}

// TODO I think I can just check who the controllers are here, that's probably better actually
// TODO make sure to actually manually test this authorization
// TODO we might want an automated test for this as well
// TODO this just got more difficult, as we need to figure out the installing identity
// TODO as separate from the calling identity now, since the canister itself now installs
fn authorize_python_module_init() {
INSTALLER_PRINCIPAL_REF_CELL.with(|installer_principal_ref_cell| {
let installer_principal = installer_principal_ref_cell.borrow().expect("Installer Principal must be set");
// INSTALLER_PRINCIPAL_REF_CELL.with(|installer_principal_ref_cell| {
// let installer_principal = installer_principal_ref_cell.borrow().expect("Installer Principal must be set");

if ic_cdk::api::caller().to_text() != installer_principal.to_text() {
panic!("Not authorized to initialize Python modules");
}
});
// if ic_cdk::api::caller().to_text() != installer_principal.to_text() {
// panic!("Not authorized to initialize Python modules");
// }
// });
}

#[ic_cdk_macros::update]
Expand Down
2 changes: 2 additions & 0 deletions kybra/compiler/kybra_modules_init/src/Lib/uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
# The recognized platforms - known behaviors
if sys.platform in ('win32', 'darwin'):
_AIX = _LINUX = False
elif sys.platform in ('emscripten', 'wasi'): # XXX: RUSTPYTHON; patched to support those platforms
_AIX = _LINUX = False
else:
import platform
_platform_system = platform.system()
Expand Down
79 changes: 72 additions & 7 deletions kybra/compiler/kybra_modules_init/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,82 @@ use std::io::Write;
use std::process::Command;
use tempfile::NamedTempFile;

// TODO share with DFINITY how I'm doing this, maybe they can build that into dfx to overcome some of the Wasm binary limitations temporarily
fn main() {
let args: Vec<String> = std::env::args().collect();

let canister_name = &args[1];

let max_chunk_size = 2 * 1_000 * 1_000; // 2 MB

match std::env::current_dir() {
Ok(current_dir) => println!("Current working directory: {}", current_dir.display()),
Err(e) => println!("Error getting current directory: {}", e),
};

let wasm = std::fs::read(format!(".kybra/{canister_name}/{canister_name}_final.wasm")).unwrap();
let wasm_chunks = split_into_chunks(wasm, max_chunk_size);

for wasm_chunk in wasm_chunks {
upload_chunk(canister_name, wasm_chunk, "upload_wasm_chunk");
}

let canister_id_output = Command::new("dfx")
.arg("canister")
.arg("id")
.arg(canister_name)
.output()
.expect("Failed to execute the dfx canister id command");

let canister_id = String::from_utf8_lossy(&canister_id_output.stdout)
.trim()
.to_string();

// TODO we shouldn't just leave this...
// TODO we should check if it is already the controller
// TODO if it is, don't remove it. If it isn't add it and then remove it
let add_controller_output = Command::new("dfx")
.arg("canister")
.arg("update-settings")
.arg("--add-controller")
.arg(canister_id)
.arg(canister_name)
.output()
.expect("Failed to execute the dfx command");

if add_controller_output.status.success() {
println!(
"Output: {:?}",
String::from_utf8_lossy(&add_controller_output.stdout)
);
} else {
eprintln!(
"Error: {:?}",
String::from_utf8_lossy(&add_controller_output.stderr)
);
}

let install_output = Command::new("dfx")
.arg("canister")
.arg("call")
.arg(canister_name)
.arg("install_wasm")
.output()
.expect("Failed to execute the dfx command");

// TODO this will error out until we use notify
if install_output.status.success() {
println!(
"Output: {:?}",
String::from_utf8_lossy(&install_output.stdout)
);
} else {
eprintln!(
"Error: {:?}",
String::from_utf8_lossy(&install_output.stderr)
);
}

let python_source_modules = rustpython_vm::py_freeze!(dir = "../python_source");
let python_source_bytecode = python_source_modules.bytes.to_vec();
let python_source_bytecode_chunks = split_into_chunks(python_source_bytecode, max_chunk_size);
Expand Down Expand Up @@ -36,7 +105,7 @@ fn main() {

// Upload Python source bytecode
for python_source_bytecode_chunk in python_source_bytecode_chunks {
upload_python_bytecode_chunk(
upload_chunk(
canister_name,
python_source_bytecode_chunk,
"upload_python_source_bytecode_chunk",
Expand All @@ -45,7 +114,7 @@ fn main() {

// Upload Python stdlib bytecode
for python_stdlib_bytecode_chunk in python_stdlib_bytecode_chunks {
upload_python_bytecode_chunk(
upload_chunk(
canister_name,
python_stdlib_bytecode_chunk,
"upload_python_stdlib_bytecode_chunk",
Expand Down Expand Up @@ -73,11 +142,7 @@ fn main() {
}
}

fn upload_python_bytecode_chunk(
canister_name: &str,
bytecode_chunk: Vec<u8>,
canister_method_name: &str,
) {
fn upload_chunk(canister_name: &str, bytecode_chunk: Vec<u8>, canister_method_name: &str) {
let blob_string = vec_u8_to_blob_string(&bytecode_chunk);

let mut temp_file = NamedTempFile::new().expect("Failed to create temporary file");
Expand Down

0 comments on commit 9660889

Please sign in to comment.