jam_pvm_builder/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Builder logic for creating PVM code blobs for execution on the JAM PVM instances (service code
//! and authorizer code).

#![allow(clippy::unwrap_used)]

use std::{fs, path::PathBuf, process::Command};

pub enum BlobType {
	Service,
	Authorizer,
}

impl BlobType {
	pub fn dispatch_table(&self) -> Vec<Vec<u8>> {
		match self {
			Self::Service =>
				vec![b"refine_ext".into(), b"accumulate_ext".into(), b"on_transfer_ext".into()],
			Self::Authorizer => vec![b"is_authorized_ext".into()],
		}
	}
}

/// Build the service crate in the parent path for the RISCV target, convert to PVM code and finish
/// by creating a `.pvm` blob file for inclusion into the `lib.rs` of the present crate using the
/// `pvm_binary` macro.
pub fn build_service(crate_dir: PathBuf, crate_name: &str) {
	let out_dir: PathBuf = std::env::var("OUT_DIR").expect("No OUT_DIR").into();
	let output_file = out_dir.join(format!("{}.jam", &crate_name));

	println!("cargo:rerun-if-changed={}", crate_dir.to_str().unwrap());
	build_pvm_blob(crate_dir, crate_name, BlobType::Service, &output_file, &out_dir);
}

/// Build the authorizer crate in the parent path for the RISCV target, convert to PVM code and
/// finish by creating a `.pvm` blob file for inclusion into the `lib.rs` of the present crate using
/// the `pvm_binary` macro.
pub fn build_authorizer(crate_dir: PathBuf, crate_name: &str) {
	let out_dir: PathBuf = std::env::var("OUT_DIR").expect("No OUT_DIR").into();
	let output_file = out_dir.join(format!("{}.jam", &crate_name));

	println!("cargo:rerun-if-changed={}", crate_dir.to_str().unwrap());
	build_pvm_blob(crate_dir, crate_name, BlobType::Authorizer, &output_file, &out_dir);
}

/// Build the PVM crate in `crate_dir` called `crate_name` for the RISCV target, convert to PVM
/// code and finish by creating a `.pvm` blob file for inclusion into the `lib.rs` of the present
/// crate (called `this_crate_name`) using the `pvm_binary` macro. The crate is either a service
/// or an authorizer, defined by `blob_type`.
pub fn build_pvm_blob(
	crate_dir: PathBuf,
	crate_name: &str,
	blob_type: BlobType,
	output_file: &PathBuf,
	out_dir: &PathBuf,
) {
	let is_64_bit = cfg!(feature = "use-64-bit-pvm");
	let (target_name, target_json_path) = if is_64_bit {
		("riscv64emac-unknown-none-polkavm", polkavm_linker::target_json_64_path().unwrap())
	} else {
		("riscv32emac-unknown-none-polkavm", polkavm_linker::target_json_32_path().unwrap())
	};

	let build_res = Command::new("cargo")
		.current_dir(&crate_dir)
		.env_clear()
		.env("PATH", std::env::var("PATH").unwrap())
		.env("RUSTFLAGS", "-C panic=abort")
		.env("CARGO_TARGET_DIR", &out_dir)
		.args(["build", "-Z", "build-std=core,alloc", "--release", "--target"])
		.arg(target_json_path)
		.output()
		.expect("Failed to execute rustup process");

	if !build_res.status.success() {
		let stderr = String::from_utf8_lossy(&build_res.stderr);
		eprintln!("{}", stderr);
		std::process::exit(1);
	}

	// Post processing
	let mut config = polkavm_linker::Config::default();
	config.set_strip(true);
	config.set_dispatch_table(blob_type.dispatch_table());
	let input_path = &out_dir.join(target_name).join("release").join(crate_name);
	let orig =
		fs::read(input_path).unwrap_or_else(|e| panic!("Failed to read {:?} :{:?}", input_path, e));
	let linked = polkavm_linker::program_from_elf(config, orig.as_ref())
		.expect("Failed to link polkavm program:");

	// Write out a full `.polkavm` blob for debugging/inspection.
	let output_path_polkavm = &out_dir.join(format!("{}.polkavm", &crate_name));
	fs::write(output_path_polkavm, &linked).expect("Error writing resulting binary");

	let parts = polkavm_linker::ProgramParts::from_bytes(linked.into())
		.expect("failed to deserialize linked PolkaVM program");

	let mut ro_data = parts.ro_data.to_vec();
	ro_data.resize(parts.ro_data_size as usize, 0);

	let blob_jam = jam_types::ProgramBlob {
		ro_data: ro_data.into(),
		rw_data: (&parts.rw_data[..]).into(),
		code_blob: (&parts.code_and_jump_table[..]).into(),
		rw_data_padding: (parts.rw_data_size as usize - parts.rw_data.len()) as u32,
		stack_size: parts.stack_size,
	};

	fs::write(output_file, blob_jam.to_vec().expect("error serializing the .jam blob"))
		.expect("error writing the .jam blob");
}

#[macro_export]
macro_rules! pvm_binary {
	($name:literal) => {
		include_bytes!(concat!(env!("OUT_DIR"), "/", $name, ".jam"));
	};
}