From 836ec93008f2f0ff6361e25796b94b9f2da0d8dd Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Sun, 30 Sep 2018 18:24:36 +0100 Subject: [PATCH] Introduce substrate contracts support. --- cli/build/main.rs | 15 +++++++++++- cli/pack/main.rs | 6 +++-- cli/prune/main.rs | 6 +++-- src/build.rs | 15 ++++++------ src/lib.rs | 28 ++++++++++++++++++---- src/pack.rs | 60 ++++++++++++++++++++++++++--------------------- 6 files changed, 87 insertions(+), 43 deletions(-) diff --git a/cli/build/main.rs b/cli/build/main.rs index fa471c8..d52d4b1 100644 --- a/cli/build/main.rs +++ b/cli/build/main.rs @@ -13,7 +13,7 @@ use std::path::PathBuf; use clap::{App, Arg}; use parity_wasm::elements; -use utils::{build, BuildError, SourceTarget}; +use utils::{build, BuildError, SourceTarget, TargetRuntime}; #[derive(Debug)] pub enum Error { @@ -77,6 +77,12 @@ fn do_main() -> Result<(), Error> { .index(2) .required(true) .help("Wasm binary name")) + .arg(Arg::with_name("target-runtime") + .help("What runtime we are compiling to") + .long("target-runtime") + .takes_value(true) + .default_value("pwasm") + .possible_values(&["substrate", "pwasm"])) .arg(Arg::with_name("skip_optimization") .help("Skip symbol optimization step producing final wasm") .long("skip-optimization")) @@ -158,6 +164,12 @@ fn do_main() -> Result<(), Error> { .map(|val| val.split(",").collect()) .unwrap_or(Vec::new()); + let target_runtime = match matches.value_of("target-runtime").expect("target-runtime has a default value; qed") { + "pwasm" => TargetRuntime::pwasm(), + "substrate" => TargetRuntime::substrate(), + _ => unreachable!("all possible values are enumerated in clap config; qed"), + }; + let (module, ctor_module) = build( module, source_input.target(), @@ -167,6 +179,7 @@ fn do_main() -> Result<(), Error> { matches.value_of("shrink_stack").unwrap_or_else(|| "49152").parse() .expect("New stack size is not valid u32"), matches.is_present("skip_optimization"), + &target_runtime, ).map_err(Error::Build)?; if let Some(save_raw_path) = matches.value_of("save_raw") { diff --git a/cli/pack/main.rs b/cli/pack/main.rs index 7206241..41703d7 100644 --- a/cli/pack/main.rs +++ b/cli/pack/main.rs @@ -8,6 +8,8 @@ use clap::{App, Arg}; fn main() { logger::init_log(); + let target_runtime = utils::TargetRuntime::pwasm(); + let matches = App::new("wasm-pack") .arg(Arg::with_name("input") .index(1) @@ -27,9 +29,9 @@ fn main() { let raw_module = parity_wasm::serialize(module).expect("Serialization failed"); // Invoke packer - let mut result_module = utils::pack_instance(raw_module, ctor_module).expect("Packing failed"); + let mut result_module = utils::pack_instance(raw_module, ctor_module, &utils::TargetRuntime::pwasm()).expect("Packing failed"); // Optimize constructor, since it does not need everything - utils::optimize(&mut result_module, vec![utils::CALL_SYMBOL]).expect("Optimization failed"); + utils::optimize(&mut result_module, vec![target_runtime.call_symbol]).expect("Optimization failed"); parity_wasm::serialize_to_file(&output, result_module).expect("Serialization failed"); } diff --git a/cli/prune/main.rs b/cli/prune/main.rs index 50d40d5..348f28e 100644 --- a/cli/prune/main.rs +++ b/cli/prune/main.rs @@ -8,6 +8,8 @@ use clap::{App, Arg}; fn main() { logger::init_log(); + let target_runtime = utils::TargetRuntime::pwasm(); + let matches = App::new("wasm-prune") .arg(Arg::with_name("input") .index(1) @@ -22,12 +24,12 @@ fn main() { .short("e") .takes_value(true) .value_name("functions") - .help(&format!("Comma-separated list of exported functions to keep. Default: '{}'", utils::CALL_SYMBOL))) + .help(&format!("Comma-separated list of exported functions to keep. Default: '{}'", target_runtime.call_symbol))) .get_matches(); let exports = matches .value_of("exports") - .unwrap_or(utils::CALL_SYMBOL) + .unwrap_or(target_runtime.call_symbol) .split(',') .collect(); diff --git a/src/build.rs b/src/build.rs index 0f4809e..bd55490 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,7 +1,5 @@ use std; use super::{ - CREATE_SYMBOL, - CALL_SYMBOL, optimize, pack_instance, ununderscore_funcs, @@ -10,6 +8,7 @@ use super::{ inject_runtime_type, PackingError, OptimizerError, + TargetRuntime, }; use parity_wasm; use parity_wasm::elements; @@ -50,9 +49,9 @@ impl std::fmt::Display for Error { } } -fn has_ctor(module: &elements::Module) -> bool { +fn has_ctor(module: &elements::Module, target_runtime: &TargetRuntime) -> bool { if let Some(ref section) = module.export_section() { - section.entries().iter().any(|e| CREATE_SYMBOL == e.field()) + section.entries().iter().any(|e| target_runtime.create_symbol == e.field()) } else { false } @@ -66,6 +65,7 @@ pub fn build( enforce_stack_adjustment: bool, stack_size: u32, skip_optimization: bool, + target_runtime: &TargetRuntime, ) -> Result<(elements::Module, Option), Error> { if let SourceTarget::Emscripten = source_target { @@ -94,7 +94,7 @@ pub fn build( let mut ctor_module = module.clone(); let mut public_api_entries = public_api_entries.to_vec(); - public_api_entries.push(CALL_SYMBOL); + public_api_entries.push(target_runtime.call_symbol); if !skip_optimization { optimize( &mut module, @@ -102,13 +102,14 @@ pub fn build( )?; } - if has_ctor(&ctor_module) { + if has_ctor(&ctor_module, target_runtime) { if !skip_optimization { - optimize(&mut ctor_module, vec![CREATE_SYMBOL])?; + optimize(&mut ctor_module, vec![target_runtime.create_symbol])?; } let ctor_module = pack_instance( parity_wasm::serialize(module.clone()).map_err(Error::Encoding)?, ctor_module.clone(), + target_runtime, )?; Ok((module, Some(ctor_module))) } else { diff --git a/src/lib.rs b/src/lib.rs index 6d47ff6..3d1c546 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,10 +9,6 @@ extern crate parity_wasm; extern crate byteorder; #[macro_use] extern crate log; -pub static CREATE_SYMBOL: &'static str = "deploy"; -pub static CALL_SYMBOL: &'static str = "call"; -pub static RET_SYMBOL: &'static str = "ret"; - pub mod rules; mod build; @@ -32,6 +28,30 @@ pub use ext::{externalize, externalize_mem, underscore_funcs, ununderscore_funcs pub use pack::{pack_instance, Error as PackingError}; pub use runtime_type::inject_runtime_type; +pub struct TargetRuntime { + pub create_symbol: &'static str, + pub call_symbol: &'static str, + pub return_symbol: &'static str, +} + +impl TargetRuntime { + pub fn substrate() -> TargetRuntime { + TargetRuntime { + create_symbol: "deploy", + call_symbol: "call", + return_symbol: "ext_return", + } + } + + pub fn pwasm() -> TargetRuntime { + TargetRuntime { + create_symbol: "deploy", + call_symbol: "call", + return_symbol: "ret", + } + } +} + #[cfg(not(feature = "std"))] mod std { pub use core::*; diff --git a/src/pack.rs b/src/pack.rs index fc52890..cdcc5c9 100644 --- a/src/pack.rs +++ b/src/pack.rs @@ -7,7 +7,7 @@ use parity_wasm::elements::{ ImportCountType, }; use parity_wasm::builder; -use super::{CREATE_SYMBOL, CALL_SYMBOL, RET_SYMBOL}; +use super::TargetRuntime; use super::gas::update_call_index; /// Pack error. @@ -20,9 +20,9 @@ pub enum Error { NoTypeSection, NoExportSection, NoCodeSection, - InvalidCreateSignature, - NoCreateSymbol, - InvalidCreateMember, + InvalidCreateSignature(&'static str), + NoCreateSymbol(&'static str), + InvalidCreateMember(&'static str), NoImportSection, } @@ -33,9 +33,9 @@ impl fmt::Display for Error { Error::NoTypeSection => write!(f, "No type section in the module"), Error::NoExportSection => write!(f, "No export section in the module"), Error::NoCodeSection => write!(f, "No code section inthe module"), - Error::InvalidCreateSignature => write!(f, "Exported symbol `{}` has invalid signature, should be () -> ()", CREATE_SYMBOL), - Error::InvalidCreateMember => write!(f, "Exported symbol `{}` should be a function", CREATE_SYMBOL), - Error::NoCreateSymbol => write!(f, "No exported `{}` symbol", CREATE_SYMBOL), + Error::InvalidCreateSignature(sym) => write!(f, "Exported symbol `{}` has invalid signature, should be () -> ()", sym), + Error::InvalidCreateMember(sym) => write!(f, "Exported symbol `{}` should be a function", sym), + Error::NoCreateSymbol(sym) => write!(f, "No exported `{}` symbol", sym), Error::NoImportSection => write!(f, "No import section in the module"), } } @@ -44,7 +44,7 @@ impl fmt::Display for Error { /// If module has an exported "CREATE_SYMBOL" function we want to pack it into "constructor". /// `raw_module` is the actual contract code /// `ctor_module` is the constructor which should return `raw_module` -pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module) -> Result { +pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, target: &TargetRuntime) -> Result { // Total number of constructor module import functions let ctor_import_functions = ctor_module.import_section().map(|x| x.functions()).unwrap_or(0); @@ -53,11 +53,11 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module) -> // in order to find it in the Code section of the module let mut create_func_id = { let found_entry = ctor_module.export_section().ok_or(Error::NoExportSection)?.entries().iter() - .find(|entry| CREATE_SYMBOL == entry.field()).ok_or(Error::NoCreateSymbol)?; + .find(|entry| target.create_symbol == entry.field()).ok_or_else(|| Error::NoCreateSymbol(target.create_symbol))?; let function_index: usize = match found_entry.internal() { &Internal::Function(index) => index as usize, - _ => { return Err(Error::InvalidCreateMember) }, + _ => { return Err(Error::InvalidCreateMember(target.create_symbol)) }, }; // Calculates a function index within module's function section @@ -73,10 +73,10 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module) -> // Deploy should have no arguments and also should return nothing if !func.params().is_empty() { - return Err(Error::InvalidCreateSignature); + return Err(Error::InvalidCreateSignature(target.create_symbol)); } if func.return_type().is_some() { - return Err(Error::InvalidCreateSignature); + return Err(Error::InvalidCreateSignature(target.create_symbol)); } function_internal_index @@ -87,7 +87,7 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module) -> let mut found = false; for entry in ctor_module.import_section().ok_or(Error::NoImportSection)?.entries().iter() { if let External::Function(_) = *entry.external() { - if entry.field() == RET_SYMBOL { found = true; break; } + if entry.field() == target.return_symbol { found = true; break; } else { id += 1; } } } @@ -102,7 +102,7 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module) -> mbuilder.push_import( builder::import() .module("env") - .field("ret") + .field(&target.return_symbol) .external().func(import_sig) .build() ); @@ -199,9 +199,9 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module) -> for section in new_module.sections_mut() { if let &mut Section::Export(ref mut export_section) = section { for entry in export_section.entries_mut().iter_mut() { - if CREATE_SYMBOL == entry.field() { - // change "CREATE_SYMBOL" export name into default "CALL_SYMBOL" - *entry.field_mut() = CALL_SYMBOL.to_owned(); + if target.create_symbol == entry.field() { + // change `create_symbol` export name into default `call_symbol`. + *entry.field_mut() = target.call_symbol.to_owned(); *entry.internal_mut() = elements::Internal::Function(last_function_index as u32); } } @@ -219,13 +219,13 @@ mod test { use super::*; use super::super::optimize; - fn test_packer(mut module: elements::Module) { + fn test_packer(mut module: elements::Module, target_runtime: &TargetRuntime) { let mut ctor_module = module.clone(); - optimize(&mut module, vec![CALL_SYMBOL]).expect("Optimizer to finish without errors"); - optimize(&mut ctor_module, vec![CREATE_SYMBOL]).expect("Optimizer to finish without errors"); + optimize(&mut module, vec![target_runtime.call_symbol]).expect("Optimizer to finish without errors"); + optimize(&mut ctor_module, vec![target_runtime.create_symbol]).expect("Optimizer to finish without errors"); let raw_module = parity_wasm::serialize(module).unwrap(); - let ctor_module = pack_instance(raw_module.clone(), ctor_module).expect("Packing failed"); + let ctor_module = pack_instance(raw_module.clone(), ctor_module, target_runtime).expect("Packing failed"); let data_section = ctor_module.data_section().expect("Packed module has to have a data section"); let data_segment = data_section.entries().iter().last().expect("Packed module has to have a data section with at least one entry"); @@ -234,6 +234,8 @@ mod test { #[test] fn no_data_section() { + let target_runtime = TargetRuntime::pwasm(); + test_packer(builder::module() .import() .module("env") @@ -267,19 +269,22 @@ mod test { .build() .build() .export() - .field(CALL_SYMBOL) + .field(target_runtime.call_symbol) .internal().func(1) .build() .export() - .field(CREATE_SYMBOL) + .field(target_runtime.create_symbol) .internal().func(2) .build() - .build() + .build(), + &target_runtime, ); } #[test] fn with_data_section() { + let target_runtime = TargetRuntime::pwasm(); + test_packer(builder::module() .import() .module("env") @@ -316,14 +321,15 @@ mod test { .build() .build() .export() - .field(CALL_SYMBOL) + .field(target_runtime.call_symbol) .internal().func(1) .build() .export() - .field(CREATE_SYMBOL) + .field(target_runtime.create_symbol) .internal().func(2) .build() - .build() + .build(), + &target_runtime, ); } }