Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] #2099: Add WASM integration test based on Orillion use-case #2122

Merged
merged 23 commits into from
Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
75eaca8
Add smartcontract
Arjentix Apr 14, 2022
7abd8a5
Add test wihtout wasm itself
Arjentix Apr 14, 2022
1f0f6d8
Glue together test and smartcontract
Arjentix Apr 15, 2022
cd2367a
Add success checking in build script
Arjentix Apr 18, 2022
663025d
Bring back workspace section in wasm package
Arjentix Apr 18, 2022
df78b99
Add dbg function when debug-wasm feature enabled
Arjentix Apr 18, 2022
b39050b
Fix decoding problem caused by roles
Arjentix Apr 19, 2022
ef29ce9
Add fmt running in build script
Arjentix Apr 19, 2022
e7a734e
Add `DebugUnwrapExt`
Arjentix Apr 19, 2022
dd4c861
Add wasm runtime config into wsv config
Arjentix Apr 19, 2022
d4837d9
Make test finally work
Arjentix Apr 19, 2022
9c7f39a
Fix nightly clippy warnings
Arjentix Apr 19, 2022
985fba3
Fix infinite recursion when running `cargo lints`
Arjentix Apr 19, 2022
8a97f34
Add nightly toolchain installing for ci image
Arjentix Apr 19, 2022
60d0c54
Revert "Add nightly toolchain installing for ci image"
Arjentix Apr 20, 2022
7f4fbfb
Fixed remarks from @mversic
Arjentix Apr 20, 2022
e1d7704
Fix another remarks from @mversic
Arjentix Apr 20, 2022
3018661
Add rustup show execution to debug ci/cd
Arjentix Apr 20, 2022
c9de621
Commented env clearing to debug ci/cd
Arjentix Apr 20, 2022
94ce061
Add env vars printing to debug ci/cd
Arjentix Apr 20, 2022
70daf55
Remove only RUST_RECURSION_COUNT env var
Arjentix Apr 20, 2022
5677183
Fix some ci/cd and clippy problems
Arjentix Apr 21, 2022
f7dd546
Remove roles feature usage
Arjentix Apr 22, 2022
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 .github/workflows/iroha2-dev-pr-static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ jobs:
- name: Format check
run: cargo +nightly-2022-04-20 fmt --all -- --check
- name: Static analysis without features
run: cargo lints clippy --workspace --benches --tests --examples --quiet --no-default-features
run: cargo +nightly-2022-04-20 lints clippy --workspace --benches --tests --examples --quiet --no-default-features
if: always()
- name: Static analysis with all features enabled
run: cargo lints clippy --workspace --benches --tests --examples --quiet --all-features
run: cargo +nightly-2022-04-20 lints clippy --workspace --benches --tests --examples --quiet --all-features
if: always()
- name: Verify iroha_data_model still supports no_std
run: cargo nono check --package iroha_data_model --no-default-features
Expand Down
1 change: 1 addition & 0 deletions Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ RUN git clone https://github.com/rui314/mold.git; \
RUN rustup component add llvm-tools-preview clippy; \
rustup target add wasm32-unknown-unknown; \
rustup install --profile default nightly-2022-04-20; \
rustup component add rust-src --toolchain nightly-2022-04-20-x86_64-unknown-linux-gnu; \
cargo install cargo-lints cargo-nono webassembly-test-runner grcov

RUN curl -fsSL https://get.docker.com -o get-docker.sh; \
Expand Down
9 changes: 9 additions & 0 deletions cli/src/samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use iroha_core::{
genesis::config::GenesisConfiguration,
kura::config::KuraConfiguration,
queue::Configuration as QueueConfiguration,
smartcontracts::wasm::config::Configuration as WasmConfiguration,
sumeragi::config::{SumeragiConfiguration, TrustedPeers},
wsv::config::Configuration as WsvConfiguration,
};
use iroha_crypto::{KeyPair, PublicKey};
use iroha_data_model::peer::Id as PeerId;
Expand Down Expand Up @@ -101,6 +103,13 @@ pub fn get_config(trusted_peers: HashSet<PeerId>, key_pair: Option<KeyPair>) ->
account_private_key: Some(private_key),
..GenesisConfiguration::default()
},
wsv: WsvConfiguration {
wasm_runtime_config: WasmConfiguration {
fuel_limit: 10_000_000,
..WasmConfiguration::default()
},
..WsvConfiguration::default()
},
..Configuration::default()
}
}
2 changes: 1 addition & 1 deletion cli/src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub trait Stream<R: DecodeVersioned>:

impl StreamMessage for warp::ws::Message {
fn binary(source: Vec<u8>) -> Self {
Self::binary(source)
warp::ws::Message::binary(source)
}

fn as_bytes(&self) -> &[u8] {
Expand Down
2 changes: 1 addition & 1 deletion client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ base64 = "0.13.0"


[dev-dependencies]
iroha_core = { version = "=2.0.0-pre-rc.3", path = "../core", features = ["dev-telemetry", "telemetry"] }
iroha_core = { version = "=2.0.0-pre-rc.3", path = "../core", features = ["dev-telemetry", "telemetry"]}
iroha_permissions_validators = { version = "=2.0.0-pre-rc.3", path = "../permissions_validators" }
iroha = { path = "../cli", features = ["dev-telemetry", "telemetry"] }

Expand Down
49 changes: 49 additions & 0 deletions client/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! Build script which builds smartcontract for test
//!
//! Technically this script is used only for testing purposes, but current cargo implementation
//! doesn't allow to run build script only for tests or get info about current profile from it.
//! See [cargo issue #4001](https://github.com/rust-lang/cargo/issues/4001)

use std::{env, path::Path, process::Command};

#[allow(clippy::expect_used)]
fn main() {
let manifest_dir =
env::var("CARGO_MANIFEST_DIR").expect("Expected `CARGO_MANIFEST_DIR` environment variable");
let smartcontract_path =
Path::new(&manifest_dir).join("tests/integration/create_nft_for_every_user_smartcontract");
let out_dir = env::var_os("OUT_DIR").expect("Expected `OUT_DIR` environment variable");

println!("cargo:rerun-if-changed=..");

let fmt = Command::new("cargo")
// Removing environment variable to avoid
// `error: infinite recursion detected` when running `cargo lints`
.env_remove("RUST_RECURSION_COUNT")
.current_dir(smartcontract_path.clone())
.args(&["+nightly-2022-04-20", "fmt", "--all"])
.status()
.expect("Failed to run `cargo fmt` on smartcontract");
assert!(fmt.success(), "Can't format smartcontract");

let build = Command::new("cargo")
// Removing environment variable to avoid
// `error: infinite recursion detected` when running `cargo lints`
.env_remove("RUST_RECURSION_COUNT")
.env("CARGO_TARGET_DIR", out_dir)
.current_dir(smartcontract_path)
.args(&[
"+nightly-2022-04-20",
"build",
"--release",
"-Z",
"build-std",
"-Z",
"build-std-features=panic_immediate_abort",
"--target",
"wasm32-unknown-unknown",
])
.status()
.expect("Failed to run `cargo build` on smartcontract");
assert!(build.success(), "Can't build smartcontract")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "create_nft_for_every_user_smartcontract"
version = "2.0.0-pre-rc.3"
authors = ["Iroha 2 team <https://github.com/orgs/soramitsu/teams/iroha2>"]
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
# Smartcontract should be linked dynamically so that it may link to functions exported
# from the host environment. Also, host environment executes the smartcontract by
# calling the function which smartcontract exports(entry point of execution)
crate-type = ['cdylib']

# Empty workspace to fix "current package believes it's in a workspace when it's not"
[workspace]

[profile.release]
strip = "debuginfo" # Remove debugging info from the binary
panic = "abort" # Panics are transcribed to Traps when compiling for wasm anyways
lto = true # Link-time-optimization produces notable decrease in binary size
opt-level = "z" # Optimize for size vs speed with "s"/"z"(removes vectorization)
codegen-units = 1 # Further reduces binary size but increases compilation time

[dependencies]
iroha_wasm = { path = "../../../../wasm", features = ["debug"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! Smartcontract which creates new nft for every user
//!
//! This module isn't included in the build-tree,
//! but instead it is being built by a `client/build.rs`

#![no_std]
#![no_main]
#![allow(clippy::all)]

extern crate alloc;

use alloc::{format, string::ToString, vec::Vec};
use core::str::FromStr;

use iroha_wasm::{data_model::prelude::*, DebugUnwrapExt, Execute};

#[iroha_wasm::iroha_wasm]
fn smartcontract_entry_point(_account_id: AccountId) {
let query = QueryBox::FindAllAccounts(FindAllAccounts {});
let accounts: Vec<Account> = query.execute().try_into().dbg_unwrap();

let limits = MetadataLimits::new(256, 256);

for account in accounts {
let mut metadata = Metadata::new();
let name = format!(
"nft_for_{}_in_{}",
account.id().name,
account.id().domain_id
)
.parse()
.dbg_unwrap();
metadata
.insert_with_limits(name, true.into(), limits)
.dbg_unwrap();

let nft_id = generate_new_nft_id(account.id());
let nft_definition = AssetDefinition::store(nft_id.clone())
.mintable_once()
.with_metadata(metadata)
.build();
let account_nft_id = <Asset as Identifiable>::Id::new(nft_id, account.id().clone());

Instruction::Register(RegisterBox::new(nft_definition)).execute();
Instruction::SetKeyValue(SetKeyValueBox::new(
account_nft_id,
Name::from_str("has_this_nft").dbg_unwrap(),
Value::Bool(true),
))
.execute();
}
}

fn generate_new_nft_id(account_id: &<Account as Identifiable>::Id) -> AssetDefinitionId {
let query = QueryBox::FindAssetsByAccountId(FindAssetsByAccountId::new(account_id.clone()));
let assets: Vec<Asset> = query.execute().try_into().dbg_unwrap();

let new_number = assets
.into_iter()
.filter(|asset| asset.id().definition_id.to_string().starts_with("nft_"))
.count()
+ 1;

format!(
"nft_number_{}_for_{}#{}",
new_number, account_id.name, account_id.domain_id
)
.parse()
.dbg_unwrap()
}
101 changes: 96 additions & 5 deletions client/tests/integration/triggers/time_trigger.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#![allow(clippy::restriction)]

use std::{str::FromStr as _, time::Duration};
use std::{fs, str::FromStr as _, time::Duration};

use eyre::Result;
use eyre::{Context, Result};
use iroha_client::client::{self, Client};
use iroha_core::block::DEFAULT_CONSENSUS_ESTIMATION_MS;
use iroha_data_model::prelude::*;
use iroha_data_model::{prelude::*, transaction::WasmSmartContract};
use test_network::{Peer as TestPeer, *};

/// Macro to abort compilation, if `e` isn't `true`
Expand Down Expand Up @@ -52,7 +52,12 @@ fn time_trigger_execution_count_error_should_be_less_than_10_percent() -> Result
));
test_client.submit(register_trigger)?;

submit_sample_isi_on_every_block_commit(&mut test_client, &account_id, 3)?;
submit_sample_isi_on_every_block_commit(
&mut test_client,
&account_id,
Duration::from_secs(1),
3,
)?;
std::thread::sleep(Duration::from_millis(DEFAULT_CONSENSUS_ESTIMATION_MS));

let finish_time = current_time();
Expand Down Expand Up @@ -101,6 +106,7 @@ fn change_asset_metadata_after_1_sec() -> Result<()> {
submit_sample_isi_on_every_block_commit(
&mut test_client,
&account_id,
Duration::from_secs(1),
usize::try_from(PERIOD_MS / DEFAULT_CONSENSUS_ESTIMATION_MS + 1)?,
)?;

Expand Down Expand Up @@ -163,6 +169,90 @@ fn pre_commit_trigger_should_be_executed() -> Result<()> {
Ok(())
}

#[test]
fn mint_nft_for_every_user_every_1_sec() -> Result<()> {
const TRIGGER_PERIOD_MS: u64 = 1000;
const EXPECTED_COUNT: u64 = 4;

let (_rt, _peer, mut test_client) = <TestPeer>::start_test_with_runtime();
wait_for_genesis_committed(&vec![test_client.clone()], 0);

let alice_id = "alice@wonderland"
.parse::<<Account as Identifiable>::Id>()
.expect("Valid");

let accounts: Vec<AccountId> = vec![
alice_id.clone(),
"mad_hatter@wonderland".parse().expect("Valid"),
"cheshire_cat@wonderland".parse().expect("Valid"),
"caterpillar@wonderland".parse().expect("Valid"),
"white_rabbit@wonderland".parse().expect("Valid"),
];

// Registering accounts
let register_accounts = accounts
.iter()
.skip(1) // Alice has already been registered in genesis
.cloned()
.map(|account_id| RegisterBox::new(Account::new(account_id, [])).into())
.collect::<Vec<_>>();
test_client.submit_all_blocking(register_accounts)?;

// Reading wasm smartcontract
let wasm = fs::read(concat!(
env!("OUT_DIR"),
"/wasm32-unknown-unknown/release/create_nft_for_every_user_smartcontract.wasm"
))
.wrap_err("Can't read smartcontract")?;
println!("wasm size is {} bytes", wasm.len());

// Registering trigger
let start_time = current_time();
let schedule =
TimeSchedule::starting_at(start_time).with_period(Duration::from_millis(TRIGGER_PERIOD_MS));
let register_trigger = RegisterBox::new(Trigger::new(
"mint_nft_for_all".parse()?,
Action::new(
Executable::Wasm(WasmSmartContract { raw_data: wasm }),
Repeats::Indefinitely,
alice_id.clone(),
EventFilter::Time(TimeEventFilter(ExecutionTime::Schedule(schedule))),
),
));
test_client.submit(register_trigger)?;

// Time trigger will be executed on block commits, so we have to produce some transactions
submit_sample_isi_on_every_block_commit(
&mut test_client,
&alice_id,
Duration::from_millis(TRIGGER_PERIOD_MS),
usize::try_from(EXPECTED_COUNT)?,
)?;

// Checking results
for account_id in accounts {
let start_pattern = "nft_number_";
let end_pattern = format!("_for_{}#{}", account_id.name, account_id.domain_id);
let assets = test_client.request(client::asset::by_account_id(account_id.clone()))?;
let count: u64 = assets
.into_iter()
.filter(|asset| {
let s = asset.id().definition_id.to_string();
s.starts_with(&start_pattern) && s.ends_with(&end_pattern)
})
.count()
.try_into()
.expect("`usize` should always fit in `u64`");

assert!(
count >= EXPECTED_COUNT,
"{account_id} has {count} NFT, but at least {EXPECTED_COUNT} expected",
);
}

Ok(())
}

/// Get asset numeric value
fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Result<u32> {
let asset = client.request(client::asset::by_id(asset_id))?;
Expand All @@ -173,6 +263,7 @@ fn get_asset_value(client: &mut Client, asset_id: AssetId) -> Result<u32> {
fn submit_sample_isi_on_every_block_commit(
test_client: &mut Client,
account_id: &AccountId,
timeout: Duration,
times: usize,
) -> Result<()> {
let block_filter =
Expand All @@ -189,7 +280,7 @@ fn submit_sample_isi_on_every_block_commit(
})
.take(times)
{
std::thread::sleep(Duration::from_secs(1));
std::thread::sleep(timeout);
// ISI just to create a new block
let sample_isi = SetKeyValueBox::new(
account_id.clone(),
Expand Down
Loading