Commit cbf505cc authored by Andrew Jones's avatar Andrew Jones

Merge branch 'master' into aj-runtime-get-storage

parents 77a323ef f5f2a210
......@@ -15,8 +15,9 @@ variables:
SCCACHE_DIR: "/ci-cache/${CI_PROJECT_NAME}/sccache"
CARGO_INCREMENTAL: 0
CI_SERVER_NAME: "GitLab CI"
RUSTFLAGS: "-C link-dead-code"
REGISTRY: registry.parity.io/parity/infrastructure/scripts
ALL_CRATES: "core alloc utils lang2 lang2/macro cli"
WASM_CRATES: "core alloc utils lang2 lang2/macro"
.collect-artifacts: &collect-artifacts
artifacts:
......@@ -33,6 +34,9 @@ variables:
- rustc -vV
- rustup show
- bash --version
- mkdir -p ${CARGO_HOME}; touch ${CARGO_HOME}/config
# global RUSTFLAGS overrides the linker args so this way is better to pass the flags
- printf '[build]\nrustflags = ["-C", "link-dead-code"]\n' | tee ${CARGO_HOME}/config
- sccache -s
only:
- master
......@@ -40,78 +44,132 @@ variables:
- schedules
- web
- /^[0-9]+$/ # PRs
retry:
max: 2
when:
- runner_system_failure
- unknown_failure
- api_failure
tags:
- linux-docker
#### stage: check-workspace
cargo-check-af:
check-std:
stage: check-workspace
<<: *docker-env
script:
- cargo check --verbose --all --all-features
- for crate in ${ALL_CRATES}; do
cargo check --verbose --all-features;
done
cargo-check-nf:
check-wasm:
stage: check-workspace
<<: *docker-env
script:
- cargo check --verbose --all --no-default-features
- for crate in ${WASM_CRATES}; do
cargo check --verbose --no-default-features --target wasm32-unknown-unknown --manifest-path ${crate}/Cargo.toml;
done
cargo-fmt:
build-std:
stage: check-workspace
<<: *docker-env
script:
- cargo fmt --verbose --all -- --check
- for crate in ${ALL_CRATES}; do
cargo build --verbose --all-features --release;
done
cargo-clippy-af:
build-wasm:
stage: check-workspace
<<: *docker-env
script:
- cargo clippy --verbose --all --all-features -- -D warnings
- for crate in ${WASM_CRATES}; do
cargo build --verbose --no-default-features --release --target wasm32-unknown-unknown --manifest-path ${crate}/Cargo.toml;
done
cargo-clippy-nf:
test-std:
stage: check-workspace
<<: *docker-env
script:
- cargo clippy --verbose --all --no-default-features -- -D warnings
- for crate in ${ALL_CRATES}; do
cargo test --verbose --all-features --release;
done
cargo-test-af:
clippy-std:
stage: check-workspace
<<: *docker-env
script:
- cargo test --verbose --all --all-features
- for crate in ${ALL_CRATES}; do
cargo clippy --verbose --all-features -- -D warnings;
done
cargo-build-nf:
clippy-wasm:
stage: check-workspace
<<: *docker-env
script:
- cargo build --verbose --all --no-default-features --release --target=wasm32-unknown-unknown
- for crate in ${WASM_CRATES}; do
cargo clippy --verbose --manifest-path ${crate}/Cargo.toml --no-default-features -- -D warnings;
done
fmt:
stage: check-workspace
<<: *docker-env
script:
- for crate in ${ALL_CRATES}; do
cargo fmt --verbose -- --check;
done
#### stage: examples
examples-wasm:
examples-test:
stage: examples
<<: *docker-env
script:
- for example in $(ls examples/lang); do
cargo build --release --no-default-features --target=wasm32-unknown-unknown --verbose --manifest-path examples/lang/$example/Cargo.toml;
- for DIR in examples/lang2/*; do
cargo test --verbose --manifest-path ${DIR}/Cargo.toml;
done
- sccache -s
examples-test:
examples-fmt:
stage: examples
<<: *docker-env
script:
- for example in $(ls examples/lang); do
cargo test --verbose --manifest-path examples/lang/$example/Cargo.toml;
- for example in examples/lang2/*; do
cargo fmt --verbose --manifest-path ${example}/Cargo.toml -- --check;
done
- sccache -s
examples-abi:
examples-clippy-std:
stage: examples
<<: *docker-env
script:
- for example in $(ls examples/lang); do
cargo run --package abi-gen --manifest-path examples/lang/$example/Cargo.toml;
- for example in examples/lang2/*; do
cargo clippy --verbose --manifest-path ${example}/Cargo.toml --all-features -- -D warnings;
done
examples-clippy-wasm:
stage: examples
<<: *docker-env
script:
- for example in examples/lang2/*; do
cargo clippy --verbose --manifest-path ${example}/Cargo.toml --no-default-features -- -D warnings;
done
examples-contract-build:
stage: examples
<<: *docker-env
script:
- for example in examples/lang2/*; do
pushd $example &&
cargo contract build &&
popd;
done
examples-generate-abi:
stage: examples
<<: *docker-env
script:
- for example in examples/lang2/*; do
pushd $example &&
cargo contract generate-abi &&
popd;
done
- sccache -s
......@@ -5,6 +5,7 @@ members = [
"core",
"model",
"lang",
"lang2",
"cli",
"abi",
]
......
This diff is collapsed.
This diff is collapsed.
......@@ -15,10 +15,13 @@ include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"]
[dependencies]
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
derive_more = { version = "0.15", default-features = false, features = ["no_std"] }
derive_more = { version = "0.99.1", default-features = false, features = ["from"] }
ink_abi_derive = { version = "0.1.0", path = "derive", default-features = false, optional = true }
type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"] }
[dev-dependencies]
serde_json = "1.0"
[features]
default = [
"std",
......
......@@ -31,9 +31,9 @@ use syn::{
};
pub fn generate(input: TokenStream2) -> TokenStream2 {
match generate_impl(input.into()) {
Ok(output) => output.into(),
Err(err) => err.to_compile_error().into(),
match generate_impl(input) {
Ok(output) => output,
Err(err) => err.to_compile_error(),
}
}
......@@ -61,7 +61,7 @@ pub fn generate_impl(input: TokenStream2) -> Result<TokenStream2> {
}
};
Ok(wrap(ident, "HAS_LAYOUT", has_layout_impl).into())
Ok(wrap(ident, "HAS_LAYOUT", has_layout_impl))
}
fn generate_fields_layout<'a>(
......
......@@ -35,7 +35,7 @@ pub fn wrap(
trait_name: &'static str,
impl_quote: TokenStream2,
) -> TokenStream2 {
let mut renamed = String::from(format!("_IMPL_{}_FOR_", trait_name));
let mut renamed = format!("_IMPL_{}_FOR_", trait_name);
renamed.push_str(ident.to_string().trim_start_matches("r#"));
let dummy_const = Ident::new(&renamed, Span::call_site());
......
......@@ -15,7 +15,11 @@
// along with ink!. If not, see <http://www.gnu.org/licenses/>.
use derive_more::From;
use serde::Serialize;
use serde::{
Serialize,
Serializer,
};
use std::fmt::Write;
use type_metadata::{
form::{
CompactForm,
......@@ -158,7 +162,7 @@ impl IntoCompact for LayoutField {
#[serde(bound = "F::TypeId: Serialize")]
pub struct LayoutRange<F: Form = MetaForm> {
/// The single key for cells or the starting key address for chunks.
#[serde(rename = "range.offset")]
#[serde(rename = "range.offset", serialize_with = "serialize_key")]
offset: LayoutKey,
/// The amount of associated key addresses starting from the offset key.
#[serde(rename = "range.len")]
......@@ -205,3 +209,52 @@ impl LayoutRange {
}
}
}
fn serialize_key<S>(key: &LayoutKey, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = key.0;
let mut hex = String::with_capacity(bytes.len() * 2 + 2);
write!(hex, "0x").expect("failed writing to string");
for byte in &bytes {
write!(hex, "{:02x}", byte).expect("failed writing to string");
}
serializer.serialize_str(&hex)
}
#[cfg(test)]
mod tests {
use super::*;
use type_metadata::{
form::{
Form,
MetaForm,
},
IntoCompact,
Registry,
};
#[test]
fn key_must_serialize_to_hex() {
// given
let type_id = <MetaForm as Form>::TypeId::new::<u32>();
let offset = LayoutKey([1; 32]);
let cs: LayoutRange<MetaForm> = LayoutRange {
offset,
len: 1337,
elem_ty: type_id,
};
let mut registry = Registry::new();
// when
let json = serde_json::to_string(&cs.into_compact(&mut registry)).unwrap();
// then
assert_eq!(
json,
"{\"range.offset\":\"0x0101010101010101010101010101010101010101010101010101010101010101\",\"range.len\":1337,\"range.elem_type\":1}"
);
}
}
......@@ -15,7 +15,10 @@
// along with ink!. If not, see <http://www.gnu.org/licenses/>.
use core::marker::PhantomData;
use serde::Serialize;
use serde::{
Serialize,
Serializer,
};
use type_metadata::{
form::{
CompactForm,
......@@ -190,7 +193,8 @@ pub struct ConstructorSpec<F: Form = MetaForm> {
/// The name of the message.
name: F::String,
/// The selector hash of the message.
selector: u32,
#[serde(serialize_with = "serialize_selector")]
selector: [u8; 4],
/// The parameters of the deploy handler.
args: Vec<MessageParamSpec<F>>,
/// The deploy handler documentation.
......@@ -234,7 +238,7 @@ impl ConstructorSpec {
ConstructorSpecBuilder {
spec: Self {
name,
selector: 0,
selector: [0u8; 4],
args: Vec::new(),
docs: Vec::new(),
},
......@@ -245,7 +249,7 @@ impl ConstructorSpec {
impl ConstructorSpecBuilder<Missing<state::Selector>> {
/// Sets the function selector of the message.
pub fn selector(self, selector: u32) -> ConstructorSpecBuilder<state::Selector> {
pub fn selector(self, selector: [u8; 4]) -> ConstructorSpecBuilder<state::Selector> {
ConstructorSpecBuilder {
spec: ConstructorSpec {
selector,
......@@ -294,7 +298,8 @@ pub struct MessageSpec<F: Form = MetaForm> {
/// The name of the message.
name: F::String,
/// The selector hash of the message.
selector: u32,
#[serde(serialize_with = "serialize_selector")]
selector: [u8; 4],
/// If the message is allowed to mutate the contract state.
mutates: bool,
/// The parameters of the message.
......@@ -333,7 +338,7 @@ impl MessageSpec {
MessageSpecBuilder {
spec: Self {
name,
selector: 0,
selector: [0u8; 4],
mutates: false,
args: Vec::new(),
return_type: ReturnTypeSpec::new(None),
......@@ -351,6 +356,7 @@ impl MessageSpec {
/// Some of the fields are guarded by a type-state pattern to
/// fail at compile-time instead of at run-time. This is useful
/// to better debug code-gen macros.
#[allow(clippy::type_complexity)]
pub struct MessageSpecBuilder<Selector, Mutates, Returns> {
spec: MessageSpec,
marker: PhantomData<fn() -> (Selector, Mutates, Returns)>,
......@@ -358,7 +364,10 @@ pub struct MessageSpecBuilder<Selector, Mutates, Returns> {
impl<M, R> MessageSpecBuilder<Missing<state::Selector>, M, R> {
/// Sets the function selector of the message.
pub fn selector(self, selector: u32) -> MessageSpecBuilder<state::Selector, M, R> {
pub fn selector(
self,
selector: [u8; 4],
) -> MessageSpecBuilder<state::Selector, M, R> {
MessageSpecBuilder {
spec: MessageSpec {
selector,
......@@ -644,6 +653,8 @@ pub struct EventParamSpec<F: Form = MetaForm> {
/// The type of the parameter.
#[serde(rename = "type")]
ty: TypeSpec<F>,
/// The documentation associated with the arguments.
docs: Vec<&'static str>,
}
impl IntoCompact for EventParamSpec {
......@@ -654,6 +665,7 @@ impl IntoCompact for EventParamSpec {
name: registry.register_string(self.name),
indexed: self.indexed,
ty: self.ty.into_compact(registry),
docs: self.docs,
}
}
}
......@@ -668,6 +680,8 @@ impl EventParamSpec {
indexed: false,
// We initialize every parameter type as `()`.
ty: TypeSpec::new::<()>(),
// We start with empty docs.
docs: vec![],
},
}
}
......@@ -694,6 +708,21 @@ impl EventParamSpecBuilder {
this
}
/// Sets the documentation of the event parameter.
pub fn docs<D>(self, docs: D) -> Self
where
D: IntoIterator<Item = &'static str>,
{
debug_assert!(self.spec.docs.is_empty());
Self {
spec: EventParamSpec {
docs: docs.into_iter().collect::<Vec<_>>(),
..self.spec
},
..self
}
}
/// Finishes constructing the event parameter spec.
pub fn done(self) -> EventParamSpec {
self.spec
......@@ -795,3 +824,41 @@ impl MessageParamSpecBuilder {
self.spec
}
}
fn serialize_selector<S>(s: &[u8; 4], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let hex = format!(
r#"["0x{:02X}","0x{:02X}","0x{:02X}","0x{:02X}"]"#,
s[0], s[1], s[2], s[3]
);
serializer.serialize_str(&hex)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn construct_selector_must_serialize_to_hex() {
// given
let name = <MetaForm as Form>::String::from("foo");
let cs: ConstructorSpec<MetaForm> = ConstructorSpec {
name,
selector: 123456789u32.to_be_bytes(),
args: Vec::new(),
docs: Vec::new(),
};
let mut registry = Registry::new();
// when
let json = serde_json::to_string(&cs.into_compact(&mut registry)).unwrap();
// then
assert_eq!(
json,
r#"{"name":1,"selector":"[\"0x07\",\"0x5B\",\"0xCD\",\"0x15\"]","args":[],"docs":[]}"#
);
}
}
[package]
name = "cargo-contract"
version = "0.1.1"
version = "0.1.2"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"
edition = "2018"
......@@ -16,7 +16,7 @@ include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"]
[dependencies]
env_logger = "0.6"
derive_more = "0.14.0"
derive_more = { version = "0.99.1", default-features = false, features = ["from", "display"] }
structopt = "0.2.15"
itertools = "0.8"
log = "0.4"
......@@ -28,7 +28,7 @@ pwasm-utils = "0.11.0"
parity-wasm = "0.40.2"
cargo_metadata = "0.8.2"
substrate-primitives = { git = "https://github.com/paritytech/substrate/", package = "substrate-primitives" }
subxt = { git = "https://github.com/paritytech/substrate-subxt/", package = "substrate-subxt" }
subxt = { git = "https://github.com/paritytech/substrate-subxt/", branch = "v0.2", package = "substrate-subxt" }
tokio = "0.1.21"
url = "1.7"
......
......@@ -25,14 +25,7 @@ use parity_wasm::elements::{
Module,
Section,
};
use std::{
io::{
self,
Write,
},
path::PathBuf,
process::Command,
};
use std::path::PathBuf;
/// This is the maximum number of pages available for a contract to allocate.
const MAX_MEMORY_PAGES: u32 = 16;
......@@ -51,8 +44,12 @@ impl CrateMetadata {
}
/// Parses the contract manifest and returns relevant metadata.
pub fn collect_crate_metadata() -> Result<CrateMetadata> {
let metadata = MetadataCommand::new().exec()?;
pub fn collect_crate_metadata(working_dir: Option<&PathBuf>) -> Result<CrateMetadata> {
let mut cmd = MetadataCommand::new();
if let Some(dir) = working_dir {
cmd.current_dir(dir);
}
let metadata = cmd.exec()?;
let root_package_id = metadata
.resolve
......@@ -88,30 +85,20 @@ pub fn collect_crate_metadata() -> Result<CrateMetadata> {
})
}
/// Invokes `cargo build` in the current directory.
/// Invokes `cargo build` in the specified directory, defaults to the current directory.
///
/// Currently it assumes that user wants to use `+nightly`.
fn build_cargo_project() -> Result<()> {
// We also assume that the user uses +nightly.
let output = Command::new("cargo")
.args(&[
"+nightly",
"build",
fn build_cargo_project(working_dir: Option<&PathBuf>) -> Result<()> {
super::exec_cargo(
"build",
&[
"--no-default-features",
"--release",
"--target=wasm32-unknown-unknown",
"--verbose",
])
.output()?;
if !output.status.success() {
// Dump the output streams produced by cargo into the stdout/stderr.
io::stdout().write_all(&output.stdout)?;
io::stderr().write_all(&output.stderr)?;
return Err(Error::BuildFailed)
}
Ok(())
],
working_dir,
)
}
/// Ensures the wasm memory import of a given module has the maximum number of pages.
......@@ -165,11 +152,13 @@ fn ensure_maximum_memory_pages(
///
/// Presently all custom sections are not required so they can be stripped safely.
fn strip_custom_sections(module: &mut Module) {
module.sections_mut().retain(|section| match section {
Section::Custom(_) => false,
Section::Name(_) => false,
Section::Reloc(_) => false,
_ => true,
module.sections_mut().retain(|section| {
match section {
Section::Custom(_) => false,
Section::Name(_) => false,
Section::Reloc(_) => false,
_ => true,
}
});
}
......@@ -193,11 +182,11 @@ fn post_process_wasm(crate_metadata: &CrateMetadata) -> Result<()> {
/// Executes build of the smart-contract which produces a wasm binary that is ready for deploying.
///
/// It does so by invoking build by cargo and then post processing the final binary.
pub(crate) fn execute_build() -> Result<String> {
pub(crate) fn execute_build(working_dir: Option<&PathBuf>) -> Result<String> {
println!(" [1/3] Collecting crate metadata");
let crate_metadata = collect_crate_metadata()?;
let crate_metadata = collect_crate_metadata(working_dir)?;
println!(" [2/3] Building cargo project");
build_cargo_project()?;
build_cargo_project(working_dir)?;
println!(" [3/3] Post processing wasm file");
post_process_wasm(&crate_metadata)?;
......@@ -209,32 +198,21 @@ pub(crate) fn execute_build() -> Result<String> {
#[cfg(test)]
mod tests {
use super::*;
use