Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
5d26731
Fix URIs
cmichi Nov 2, 2020
806929f
Make `generate-metadata` output consistent with `build`
cmichi Nov 2, 2020
7161a6d
Add `cargo contract pack`
cmichi Nov 2, 2020
5e3f520
Return error instead of panicking
cmichi Nov 3, 2020
e7fbaba
Use blake2_hash()
cmichi Nov 3, 2020
61f7261
Replace match with if
cmichi Nov 3, 2020
6cc0a67
Pass reference instead of ownership
cmichi Nov 3, 2020
d9eb49e
Generate metadata.json and <contract>.pack
cmichi Nov 3, 2020
4d1edfa
Output .wasm, .json, .pack
cmichi Nov 3, 2020
0eb1949
Return result object instead of tuple
cmichi Nov 3, 2020
3b3206e
Get it to run with '--features test-ci-only'
cmichi Nov 3, 2020
f5450d6
Rename .pack to .contract
cmichi Nov 4, 2020
c71639f
Apply suggestions from code review
Nov 4, 2020
2aa7c96
Introduce '--skip-packing' and '--skip-metadata'
cmichi Nov 4, 2020
9fc63a0
Merge branch 'master' into cmichi-implement-cargo-pack
cmichi Nov 5, 2020
b40a82e
Apply suggestions from code review
Nov 5, 2020
bd6477f
Short help message
cmichi Nov 5, 2020
294d206
Output deprecated error for 'generate-metadata'
cmichi Nov 5, 2020
bb6f5e5
Rename pack ➜ bundle
cmichi Nov 5, 2020
8b7f712
Add 'cargo contract check' command
cmichi Nov 5, 2020
e89b6df
Optimize resulting Wasm file, except on 'check'
cmichi Nov 5, 2020
0850779
Do not make unoptimized file easily mistake for optimized one
cmichi Nov 5, 2020
4dc41b7
Get it to run with
cmichi Nov 5, 2020
85efad8
Update readme
cmichi Nov 5, 2020
44d3a85
Make unoptimized wasm not show up in target folder
cmichi Nov 5, 2020
63a7131
Update comments
cmichi Nov 5, 2020
54a7105
Remove 'generate-metadata' variants
cmichi Nov 5, 2020
73d8e94
Move dispatch logic into metadata
cmichi Nov 6, 2020
134c07a
Update src/main.rs
Nov 6, 2020
1e7dabb
Move logic into build.rs
cmichi Nov 6, 2020
c7e2ffe
Improve progress output
cmichi Nov 6, 2020
8229246
Make clippy happy
cmichi Nov 6, 2020
3500946
Fix progress output
cmichi Nov 6, 2020
d2290c4
Make it work with `--features test-ci-only`
cmichi Nov 6, 2020
f8246d3
Apply cargo fmt
cmichi Nov 6, 2020
939a19b
Always use optimized Wasm for metadata hash
cmichi Nov 6, 2020
007bb65
Always use optimized Wasm for metadata hash
cmichi Nov 6, 2020
c99bd37
Make it work with `--features test-ci-only`
cmichi Nov 6, 2020
c74e3aa
Switch naming
cmichi Nov 6, 2020
bc965c7
Fix metadata/bundle output
cmichi Nov 6, 2020
da0c4ef
Use enum `BuildArtifacts` instead of boolean flags
cmichi Nov 6, 2020
c521d61
Improve misleading fn name
cmichi Nov 6, 2020
8b2b3d2
Make it work with `--features test-ci-only`
cmichi Nov 6, 2020
d08a6f1
Make output more concise
cmichi Nov 9, 2020
9ed109b
Print optimization result at the end
cmichi Nov 9, 2020
693f987
Improve output
cmichi Nov 9, 2020
160b992
Replace 5-tuple return value with struct
cmichi Nov 9, 2020
c699312
Include hash only for bundle in metadata
cmichi Nov 9, 2020
923cded
Make it work with `--features test-ci-only`
cmichi Nov 9, 2020
fcb66c6
Fix doc test
cmichi Nov 9, 2020
c5296ad
Remove comments
cmichi Nov 9, 2020
4cdc220
Introduce wrapper type CodeHash
cmichi Nov 9, 2020
930a309
Make it work with `--features test-ci-only`
cmichi Nov 9, 2020
4403bd7
Display important results bold
cmichi Nov 9, 2020
0c596f0
Include size diff for `code-only` as well
cmichi Nov 9, 2020
d9a0f10
Remove comment
cmichi Nov 9, 2020
8737b9b
Shorten code
cmichi Nov 9, 2020
234b45f
Clone metadata for correct UI output
cmichi Nov 9, 2020
8474209
Remove unnecessary return
cmichi Nov 9, 2020
b6b9cad
Fix return type
cmichi Nov 9, 2020
1c067d9
Print metadata generation in correct step
cmichi Nov 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ OPTIONS:

SUBCOMMANDS:
new Setup and create a new smart contract project
build Compiles the smart contract
generate-metadata Generate contract metadata artifacts
build Compiles the contract, generates metadata, bundles both together in a '.contract' file
check Check that the code builds as Wasm; does not output any build artifact to the top level `target/` directory
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should mention that this step also deploys (or will deploy) some custom checks that we implement on top besides just making sure that things compile for Wasm.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but would only add it once we actually have some checks on top.

test Test the smart contract off-chain
deploy Upload the smart contract code to the chain
instantiate Instantiate a deployed smart contract
Expand Down
96 changes: 78 additions & 18 deletions metadata/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@
//!
//! let language = SourceLanguage::new(Language::Ink, Version::new(2, 1, 0));
//! let compiler = SourceCompiler::new(Compiler::RustC, Version::parse("1.46.0-nightly").unwrap());
//! let source = Source::new([0u8; 32], language, compiler);
//! let wasm = SourceWasm::new(vec![0u8]);
//! let source = Source::new(Some(wasm), Some(CodeHash([0u8; 32])), language, compiler);
//! let contract = Contract::builder()
//! .name("incrementer".to_string())
//! .version(Version::new(2, 1, 0))
//! .authors(vec!["Parity Technologies <admin@parity.io>".to_string()])
//! .description("increment a value".to_string())
//! .documentation(Url::parse("http:docs.rs/").unwrap())
//! .repository(Url::parse("http:github.com/paritytech/ink/").unwrap())
//! .homepage(Url::parse("http:example.com/").unwrap())
//! .documentation(Url::parse("http://docs.rs/").unwrap())
//! .repository(Url::parse("http://github.com/paritytech/ink/").unwrap())
//! .homepage(Url::parse("http://example.com/").unwrap())
//! .license("Apache-2.0".to_string())
//! .build()
//! .unwrap();
Expand All @@ -60,7 +61,7 @@ use url::Url;
const METADATA_VERSION: &str = "0.1.0";

/// Smart contract metadata.
#[derive(Debug, Serialize)]
#[derive(Clone, Debug, Serialize)]
pub struct ContractMetadata {
#[serde(rename = "metadataVersion")]
metadata_version: semver::Version,
Expand Down Expand Up @@ -92,29 +93,86 @@ impl ContractMetadata {
abi,
}
}

pub fn remove_source_wasm_attribute(&mut self) {
self.source.wasm = None;
}
}

/// Representation of the Wasm code hash.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CodeHash(pub [u8; 32]);

impl Serialize for CodeHash {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_as_byte_str(&self.0[..], serializer)
}
}

#[derive(Debug, Serialize)]
#[derive(Clone, Debug, Serialize)]
pub struct Source {
#[serde(serialize_with = "serialize_as_byte_str")]
hash: [u8; 32],
#[serde(skip_serializing_if = "Option::is_none")]
hash: Option<CodeHash>,
Comment on lines +117 to +118
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this ever None? I guess it is always useful even if we provide the sources.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussion happened above, here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I read from the discussion is that we still always want the hash field to be available. And also I don't think that we need --metadata-only. Please convince me of the opposite by coming up with a useful use case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I read from the discussion is that we still always want the hash field to be available.

I understood the discussion in the way that we want to omit the hash:

It is not required, currently. It's just there as a nice to have. In the future the client tools could use that (if present) to verify the metadata matches the deployed code.

So I would say that if metadata-only is specified then that field should be omitted, even if the wasm is already there because it is not guaranteed to match.

language: SourceLanguage,
compiler: SourceCompiler,
#[serde(skip_serializing_if = "Option::is_none")]
wasm: Option<SourceWasm>,
}

impl Source {
/// Constructs a new InkProjectSource.
pub fn new(hash: [u8; 32], language: SourceLanguage, compiler: SourceCompiler) -> Self {
pub fn new(
wasm: Option<SourceWasm>,
hash: Option<CodeHash>,
language: SourceLanguage,
compiler: SourceCompiler,
) -> Self {
Source {
hash,
language,
compiler,
wasm,
}
}
}

/// The bytes of the compiled Wasm smart contract.
#[derive(Clone, Debug)]
pub struct SourceWasm {
wasm: Vec<u8>,
}

impl SourceWasm {
/// Constructs a new `SourceWasm`.
pub fn new(wasm: Vec<u8>) -> Self {
SourceWasm { wasm }
}
}

impl Serialize for SourceWasm {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_as_byte_str(&self.wasm[..], serializer)
}
}

impl Display for SourceWasm {
fn fmt(&self, f: &mut Formatter<'_>) -> DisplayResult {
write!(f, "0x").expect("failed writing to string");
for byte in &self.wasm {
write!(f, "{:02x}", byte).expect("failed writing to string");
}
write!(f, "")
}
}

/// The language and version in which a smart contract is written.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct SourceLanguage {
language: Language,
version: Version,
Expand Down Expand Up @@ -143,7 +201,7 @@ impl Display for SourceLanguage {
}

/// The language in which the smart contract is written.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub enum Language {
Ink,
Solidity,
Expand All @@ -161,7 +219,7 @@ impl Display for Language {
}

/// A compiler used to compile a smart contract.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct SourceCompiler {
compiler: Compiler,
version: Version,
Expand Down Expand Up @@ -189,7 +247,7 @@ impl SourceCompiler {
}

/// Compilers used to compile a smart contract.
#[derive(Debug, Serialize)]
#[derive(Clone, Debug, Serialize)]
pub enum Compiler {
RustC,
Solang,
Expand All @@ -205,7 +263,7 @@ impl Display for Compiler {
}

/// Metadata about a smart contract.
#[derive(Debug, Serialize)]
#[derive(Clone, Debug, Serialize)]
pub struct Contract {
name: String,
version: Version,
Expand All @@ -229,7 +287,7 @@ impl Contract {
}

/// Additional user defined metadata, can be any valid json.
#[derive(Debug, Serialize)]
#[derive(Clone, Debug, Serialize)]
pub struct User {
#[serde(flatten)]
json: Map<String, Value>,
Expand Down Expand Up @@ -463,7 +521,8 @@ mod tests {
let language = SourceLanguage::new(Language::Ink, Version::new(2, 1, 0));
let compiler =
SourceCompiler::new(Compiler::RustC, Version::parse("1.46.0-nightly").unwrap());
let source = Source::new([0u8; 32], language, compiler);
let wasm = SourceWasm::new(vec![0u8, 1u8, 2u8]);
let source = Source::new(Some(wasm), Some(CodeHash([0u8; 32])), language, compiler);
let contract = Contract::builder()
.name("incrementer".to_string())
.version(Version::new(2, 1, 0))
Expand Down Expand Up @@ -507,7 +566,8 @@ mod tests {
"source": {
"hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"language": "ink! 2.1.0",
"compiler": "rustc 1.46.0-nightly"
"compiler": "rustc 1.46.0-nightly",
"wasm": "0x000102"
},
"contract": {
"name": "incrementer",
Expand Down Expand Up @@ -544,7 +604,7 @@ mod tests {
let language = SourceLanguage::new(Language::Ink, Version::new(2, 1, 0));
let compiler =
SourceCompiler::new(Compiler::RustC, Version::parse("1.46.0-nightly").unwrap());
let source = Source::new([0u8; 32], language, compiler);
let source = Source::new(None, Some(CodeHash([0u8; 32])), language, compiler);
let contract = Contract::builder()
.name("incrementer".to_string())
.version(Version::new(2, 1, 0))
Expand Down
76 changes: 57 additions & 19 deletions src/cmd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
crate_metadata::CrateMetadata,
util,
workspace::{ManifestPath, Profile, Workspace},
UnstableFlags, Verbosity,
GenerateArtifacts, GenerationResult, OptimizationResult, UnstableFlags, Verbosity,
};
use anyhow::{Context, Result};
use colored::Colorize;
Expand Down Expand Up @@ -192,7 +192,7 @@ fn post_process_wasm(crate_metadata: &CrateMetadata) -> Result<()> {
///
/// The intention is to reduce the size of bloated wasm binaries as a result of missing
/// optimizations (or bugs?) between Rust and Wasm.
fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<()> {
fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<OptimizationResult> {
let mut optimized = crate_metadata.dest_wasm.clone();
optimized.set_file_name(format!("{}-opt.wasm", crate_metadata.package_name));

Expand All @@ -219,14 +219,13 @@ fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<()> {

let original_size = metadata(&crate_metadata.dest_wasm)?.len() as f64 / 1000.0;
let optimized_size = metadata(&optimized)?.len() as f64 / 1000.0;
println!(
" Original wasm size: {:.1}K, Optimized: {:.1}K",
original_size, optimized_size
);

// overwrite existing destination wasm file with the optimised version
std::fs::rename(&optimized, &crate_metadata.dest_wasm)?;
Ok(())
Ok(OptimizationResult {
original_size,
optimized_size,
})
}

/// Executes build of the smart-contract which produces a wasm binary that is ready for deploying.
Expand All @@ -240,57 +239,96 @@ fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<()> {
pub(crate) fn execute(
manifest_path: &ManifestPath,
verbosity: Option<Verbosity>,
optimize_contract: bool,
build_artifact: GenerateArtifacts,
unstable_options: UnstableFlags,
) -> Result<PathBuf> {
) -> Result<GenerationResult> {
let crate_metadata = CrateMetadata::collect(manifest_path)?;
execute_with_metadata(&crate_metadata, verbosity, unstable_options)
if build_artifact == GenerateArtifacts::CodeOnly {
let (maybe_dest_wasm, maybe_optimization_result) = execute_with_crate_metadata(
&crate_metadata,
verbosity,
optimize_contract,
build_artifact,
unstable_options,
)?;
let res = GenerationResult {
dest_wasm: maybe_dest_wasm,
dest_metadata: None,
dest_bundle: None,
target_directory: crate_metadata.cargo_meta.target_directory,
optimization_result: maybe_optimization_result,
};
return Ok(res);
}

let res =
super::metadata::execute(&manifest_path, verbosity, build_artifact, unstable_options)?;
Ok(res)
}

/// Executes build of the smart-contract which produces a wasm binary that is ready for deploying.
/// Executes build of the smart-contract which produces a Wasm binary that is ready for deploying.
///
/// It does so by invoking `cargo build` and then post processing the final binary.
///
/// # Note
///
/// Uses the supplied `CrateMetadata`. If an instance is not available use [`execute_build`]
pub(crate) fn execute_with_metadata(
///
/// Returns a tuple of `(maybe_optimized_wasm_path, maybe_optimization_result)`.
pub(crate) fn execute_with_crate_metadata(
crate_metadata: &CrateMetadata,
verbosity: Option<Verbosity>,
optimize_contract: bool,
build_artifact: GenerateArtifacts,
unstable_options: UnstableFlags,
) -> Result<PathBuf> {
) -> Result<(Option<PathBuf>, Option<OptimizationResult>)> {
println!(
" {} {}",
"[1/3]".bold(),
format!("[1/{}]", build_artifact.steps()).bold(),
"Building cargo project".bright_green().bold()
);
build_cargo_project(&crate_metadata, verbosity, unstable_options)?;
println!(
" {} {}",
"[2/3]".bold(),
format!("[2/{}]", build_artifact.steps()).bold(),
"Post processing wasm file".bright_green().bold()
);
post_process_wasm(&crate_metadata)?;
if !optimize_contract {
return Ok((None, None));
}
println!(
" {} {}",
"[3/3]".bold(),
format!("[3/{}]", build_artifact.steps()).bold(),
"Optimizing wasm file".bright_green().bold()
);
optimize_wasm(&crate_metadata)?;
Ok(crate_metadata.dest_wasm.clone())
let optimization_result = optimize_wasm(&crate_metadata)?;
Ok((
Some(crate_metadata.dest_wasm.clone()),
Some(optimization_result),
))
}

#[cfg(feature = "test-ci-only")]
#[cfg(test)]
mod tests {
use crate::{cmd, util::tests::with_tmp_dir, ManifestPath, UnstableFlags};
use crate::{cmd, util::tests::with_tmp_dir, GenerateArtifacts, ManifestPath, UnstableFlags};

#[test]
fn build_template() {
with_tmp_dir(|path| {
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let manifest_path =
ManifestPath::new(&path.join("new_project").join("Cargo.toml")).unwrap();
super::execute(&manifest_path, None, UnstableFlags::default()).expect("build failed");
super::execute(
&manifest_path,
None,
true,
GenerateArtifacts::All,
UnstableFlags::default(),
)
.expect("build failed");
Ok(())
})
}
Expand Down
Loading