mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 15:20:16 +00:00
feat(aquavm-air-cli): --near
execution mode [fixes VM-322] (#672)
Adding the NEAR execution mode that executes AIR NEAR smart contract, measuring its gas consumption.
This commit is contained in:
parent
7a8a460572
commit
0e80ee7908
6
.github/release-please/config.json
vendored
6
.github/release-please/config.json
vendored
@ -14,7 +14,8 @@
|
||||
"components": [
|
||||
"air",
|
||||
"air-interpreter",
|
||||
"avm-client"
|
||||
"avm-client",
|
||||
"avm-near-contract"
|
||||
]
|
||||
}
|
||||
],
|
||||
@ -56,5 +57,8 @@
|
||||
"tools/wasm/air-beautify-wasm": {
|
||||
"component": "air-beautify-wasm"
|
||||
}
|
||||
"tools/wasm/air-near-contract": {
|
||||
"component": "air-near-contract"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2463
Cargo.lock
generated
2463
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,7 @@ air-lambda-parser = { version = "0.1.0", path = "../crates/air-lib/lambda/parser
|
||||
air-trace-handler = { version = "0.4.0", path = "../crates/air-lib/trace-handler" }
|
||||
air-utils = { version = "0.1.1", path = "../crates/air-lib/utils" }
|
||||
polyplets = { version = "0.4.1", path = "../crates/air-lib/polyplets" }
|
||||
fluence-keypair = { version = "0.10.1" }
|
||||
fluence-keypair = { version = "0.10.1", default-features = false }
|
||||
|
||||
serde = { version = "1.0.164", features = [ "derive", "rc" ] }
|
||||
serde_json = "1.0.95"
|
||||
@ -49,7 +49,6 @@ tracing = "0.1.37"
|
||||
air-test-utils = { path = "../crates/air-lib/test-utils" }
|
||||
air-testing-framework = { path = "../crates/testing-framework" }
|
||||
fluence-app-service = "0.28.0"
|
||||
fluence-keypair = "0.10.1"
|
||||
marine-rs-sdk = { version = "0.8.1", features = ["logger"] }
|
||||
|
||||
# the feature just silence a warning in the criterion 0.3.x.
|
||||
|
@ -30,4 +30,4 @@ serde = "1.0.164"
|
||||
log = "0.4.17"
|
||||
parking_lot = "0.12.1"
|
||||
tracing = "0.1.37"
|
||||
fluence-keypair = "0.10.1"
|
||||
fluence-keypair = { version = "0.10.1", default-features = false }
|
||||
|
@ -12,7 +12,7 @@ categories = ["wasm"]
|
||||
|
||||
[dependencies]
|
||||
air-interpreter-cid = { version = "0.3.0", path = "../interpreter-cid" }
|
||||
fluence-keypair = "0.10.1"
|
||||
fluence-keypair = { version = "0.10.1", default-features = false }
|
||||
|
||||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
serde_json = "1.0.96"
|
||||
|
@ -30,9 +30,15 @@ fluence-keypair = "0.10.1"
|
||||
bs58 = "0.5.0"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
# near
|
||||
near-sdk = { version = "4.1.1", optional = true }
|
||||
tokio = { version = "1", features = ["rt"], optional = true }
|
||||
workspaces = { version = "0.7.0", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["wasm"]
|
||||
wasm = ["air-test-utils"]
|
||||
near = [ "dep:near-sdk", "dep:tokio", "dep:workspaces" ]
|
||||
|
||||
[[bin]]
|
||||
name = "air"
|
||||
|
@ -27,7 +27,9 @@ All common parameters are optional. Their position is always before the mode se
|
||||
+ `--runner-tracing-params` defines tracing logging level for the runner. By default, it is equal to `warn`.
|
||||
+ `--random-key` or `--ed25519-key keyfile` (required): use random signing key or load it from a file.
|
||||
|
||||
The important option is `--native`. It runs the AquaVM as the native code that can be profiled with any native profiler. As input data deserialization and serialization time can be comparable to particle execution time, and short execution times provides less reliable results, one can use `--repeat N` option to repeat particle execution several times. Execution result is not printed in this case, so you may run `--repeat 1` to suppress it.
|
||||
The important option is the execution mode `--native`. It runs the AquaVM as the native code that can be profiled with any native profiler. As input data deserialization and serialization time can be comparable to particle execution time, and short execution times provides less reliable results, one can use `--repeat N` option to repeat particle execution several times. Execution result is not printed in this case, so you may run `--repeat 1` to suppress it.
|
||||
|
||||
Another alternative execution mode is `--near`, enabled by with `near` feature flag. It runs a NEAR contract defined by the `--near-contract` option, measuring its gas consumption. The `workspaces` crate in sandbox mode is used for the most accurate NEAR gas measurement. The workspaces build script installs NEAR Sandbox automatically, but on the other machines you have to install it manually and set the NEAR_SANDBOX_BIN_PATH variable.
|
||||
|
||||
Run `air run --help` to see all common parameters.
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
mod data;
|
||||
mod native;
|
||||
#[cfg(feature = "near")]
|
||||
mod near;
|
||||
mod runner;
|
||||
#[cfg(feature = "wasm")]
|
||||
mod wasm;
|
||||
@ -56,6 +58,13 @@ pub(crate) struct Args {
|
||||
)]
|
||||
air_interpreter_path: PathBuf,
|
||||
|
||||
#[clap(
|
||||
long = "near-contract",
|
||||
env = "AIR_NEAR_CONTRACT_PATH",
|
||||
default_value = "tools/wasm/air-near-contract/target/wasm32-unknown-unknown/release/aqua_vm.wasm"
|
||||
)]
|
||||
air_near_contract_path: PathBuf,
|
||||
|
||||
#[clap(long, help = "Execute several times; great for native profiling")]
|
||||
repeat: Option<u32>,
|
||||
#[clap(long, help = "Output JSON tracing info")]
|
||||
@ -104,9 +113,14 @@ impl Keys {
|
||||
struct ModeArgs {
|
||||
#[arg(long)]
|
||||
native: bool,
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
#[arg(long)]
|
||||
wasm: bool,
|
||||
|
||||
#[cfg(feature = "near")]
|
||||
#[arg(long)]
|
||||
near: bool,
|
||||
}
|
||||
|
||||
impl From<ModeArgs> for Option<Mode> {
|
||||
@ -120,14 +134,23 @@ impl From<ModeArgs> for Option<Mode> {
|
||||
return Some(Mode::Wasm);
|
||||
}
|
||||
|
||||
#[cfg(feature = "near")]
|
||||
if value.near {
|
||||
return Some(Mode::Near);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
Native,
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
Wasm,
|
||||
|
||||
#[cfg(feature = "near")]
|
||||
Near,
|
||||
}
|
||||
|
||||
pub(crate) fn run(args: Args) -> anyhow::Result<()> {
|
||||
@ -146,6 +169,7 @@ pub(crate) fn run(args: Args) -> anyhow::Result<()> {
|
||||
let mut runner = get_runner(
|
||||
args.mode.into(),
|
||||
&args.air_interpreter_path,
|
||||
&args.air_near_contract_path,
|
||||
args.max_heap_size,
|
||||
)?;
|
||||
|
||||
@ -195,6 +219,7 @@ pub(crate) fn run(args: Args) -> anyhow::Result<()> {
|
||||
fn get_runner(
|
||||
mode: Option<Mode>,
|
||||
air_interpreter_wasm_path: &Path,
|
||||
_air_contract_wasm_path: &Path,
|
||||
max_heap_size: Option<u64>,
|
||||
) -> anyhow::Result<Box<dyn AirRunner>> {
|
||||
let mode = mode.unwrap_or(Mode::Wasm);
|
||||
@ -204,6 +229,9 @@ fn get_runner(
|
||||
}
|
||||
Mode::Wasm => self::wasm::create_wasm_avm_runner(air_interpreter_wasm_path, max_heap_size)
|
||||
.context("Failed to instantiate WASM AVM"),
|
||||
#[cfg(feature = "near")]
|
||||
Mode::Near => self::near::create_near_runner(_air_contract_wasm_path)
|
||||
.context("Failed to instantiate NEAR AVM"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,6 +239,7 @@ fn get_runner(
|
||||
fn get_runner(
|
||||
mode: Option<Mode>,
|
||||
_air_interpreter_wasm_path: &Path,
|
||||
_air_contract_wasm_path: &Path,
|
||||
_max_heap_size: Option<u64>,
|
||||
) -> anyhow::Result<Box<dyn AirRunner>> {
|
||||
let mode = mode.unwrap_or(Mode::Native);
|
||||
@ -218,6 +247,9 @@ fn get_runner(
|
||||
Mode::Native => {
|
||||
self::native::create_native_avm_runner().context("Failed to instantiate a native AVM")
|
||||
}
|
||||
#[cfg(feature = "near")]
|
||||
Mode::Near => self::near::create_near_runner(_air_contract_wasm_path)
|
||||
.context("Failed to instantiate NEAR AVM"),
|
||||
}
|
||||
}
|
||||
|
||||
|
122
tools/cli/air/src/trace/run/near.rs
Normal file
122
tools/cli/air/src/trace/run/near.rs
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2023 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::runner::AirRunner;
|
||||
|
||||
use air_interpreter_interface::RunParameters;
|
||||
use avm_interface::raw_outcome::RawAVMOutcome;
|
||||
use fluence_keypair::KeyPair;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub(crate) struct NearRunner {
|
||||
air_contract_wasm_path: PathBuf,
|
||||
}
|
||||
|
||||
impl AirRunner for NearRunner {
|
||||
fn call_tracing(
|
||||
&mut self,
|
||||
air: String,
|
||||
prev_data: Vec<u8>,
|
||||
current_data: Vec<u8>,
|
||||
init_peer_id: String,
|
||||
timestamp: u64,
|
||||
ttl: u32,
|
||||
current_peer_id: String,
|
||||
call_results: avm_interface::CallResults,
|
||||
_tracing_params: String,
|
||||
_tracing_output_mode: u8,
|
||||
keypair: &KeyPair,
|
||||
particle_id: String,
|
||||
) -> anyhow::Result<RawAVMOutcome> {
|
||||
let key_format = keypair.key_format().into();
|
||||
let secret_key_bytes = keypair.secret().expect("Failed to get secret key");
|
||||
|
||||
let run_parameters = RunParameters {
|
||||
init_peer_id,
|
||||
current_peer_id,
|
||||
timestamp,
|
||||
ttl,
|
||||
key_format,
|
||||
secret_key_bytes,
|
||||
particle_id,
|
||||
};
|
||||
|
||||
execute_on_near(
|
||||
&self.air_contract_wasm_path,
|
||||
air,
|
||||
prev_data,
|
||||
current_data,
|
||||
run_parameters,
|
||||
call_results,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_near_runner(
|
||||
air_contract_wasm_path: &Path,
|
||||
) -> anyhow::Result<Box<dyn AirRunner>> {
|
||||
let air_contract_wasm_path = air_contract_wasm_path.to_owned();
|
||||
|
||||
Ok(Box::new(NearRunner {
|
||||
air_contract_wasm_path,
|
||||
}))
|
||||
}
|
||||
|
||||
fn execute_on_near(
|
||||
path: &Path,
|
||||
air_script: String,
|
||||
prev_data: Vec<u8>,
|
||||
current_data: Vec<u8>,
|
||||
run_parameters: RunParameters,
|
||||
call_results: avm_interface::CallResults,
|
||||
) -> anyhow::Result<avm_interface::raw_outcome::RawAVMOutcome> {
|
||||
use avm_interface::into_raw_result;
|
||||
|
||||
let run_parameters = serde_json::to_string(&run_parameters)?;
|
||||
|
||||
// some inner parts transformations
|
||||
let raw_call_results = into_raw_result(call_results);
|
||||
let raw_call_results = serde_json::to_vec(&raw_call_results)?;
|
||||
|
||||
let wasm = std::fs::read(path)?;
|
||||
|
||||
let result = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()?
|
||||
.block_on(async move {
|
||||
let worker = workspaces::sandbox().await?;
|
||||
let contract = worker.dev_deploy(&wasm).await?;
|
||||
contract
|
||||
.call("execute_script")
|
||||
.max_gas()
|
||||
.args_borsh((
|
||||
air_script,
|
||||
prev_data,
|
||||
current_data,
|
||||
run_parameters,
|
||||
raw_call_results,
|
||||
))
|
||||
.transact()
|
||||
.await
|
||||
})?;
|
||||
|
||||
eprintln!("total gas: {:e}", result.total_gas_burnt);
|
||||
eprintln!("transaction gas: {:e}", result.outcome().gas_burnt);
|
||||
|
||||
let data: String = result.borsh()?;
|
||||
serde_json::from_str(&data).map_err(Into::into)
|
||||
}
|
2591
tools/wasm/air-near-contract/Cargo.lock
generated
Normal file
2591
tools/wasm/air-near-contract/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
tools/wasm/air-near-contract/Cargo.toml
Normal file
37
tools/wasm/air-near-contract/Cargo.toml
Normal file
@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "air-near-contract"
|
||||
version = "0.43.1"
|
||||
description = "AIR interpreter as a NEAR contract"
|
||||
authors = ["Fluence labs"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
keywords = ["fluence", "air", "webassembly", "programming-language", "near"]
|
||||
categories = ["wasm"]
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
aquavm-air = { path = "../../../air" }
|
||||
air-interpreter-interface = { path = "../../../crates/air-lib/interpreter-interface" }
|
||||
near-sdk = "4.1.1"
|
||||
serde = { version = "1.0.118", features = [ "derive", "rc" ] }
|
||||
serde_json = "1.0.61"
|
||||
hashbrown = { version = "0.14.0", default-features = false }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
debug = false
|
||||
panic = "abort"
|
||||
overflow-checks = true
|
||||
|
||||
# it is required to be build in own workspace, as it has special profile
|
||||
[workspace]
|
||||
members = []
|
||||
|
||||
[patch.crates-io]
|
||||
fluence-keypair = { git = "https://github.com/fluencelabs/trust-graph.git", branch = "lean-keypair" }
|
||||
libp2p-identity = { git = "https://github.com/fluencelabs/rust-libp2p.git", branch = "rand-feature" }
|
6
tools/wasm/air-near-contract/build.sh
Executable file
6
tools/wasm/air-near-contract/build.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo ">> Building contract"
|
||||
|
||||
rustup target add wasm32-unknown-unknown
|
||||
cargo build --target wasm32-unknown-unknown --release
|
2
tools/wasm/air-near-contract/rust-toolchain.toml
Normal file
2
tools/wasm/air-near-contract/rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "1.69"
|
63
tools/wasm/air-near-contract/src/lib.rs
Normal file
63
tools/wasm/air-near-contract/src/lib.rs
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2023 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air::execute_air;
|
||||
use air::RunParameters;
|
||||
use air_interpreter_interface::InterpreterOutcome;
|
||||
|
||||
use near_sdk::near_bindgen;
|
||||
use near_sdk::borsh as borsh;
|
||||
use borsh::BorshDeserialize;
|
||||
use borsh::BorshSerialize;
|
||||
|
||||
#[near_bindgen]
|
||||
#[derive(BorshDeserialize, BorshSerialize, Default)]
|
||||
pub struct Aqua {}
|
||||
|
||||
#[near_bindgen]
|
||||
impl Aqua {
|
||||
#[result_serializer(borsh)]
|
||||
pub fn execute_script(
|
||||
&self,
|
||||
#[serializer(borsh)] air_script: String,
|
||||
#[serializer(borsh)] prev_data: Vec<u8>,
|
||||
#[serializer(borsh)] current_data: Vec<u8>,
|
||||
#[serializer(borsh)] run_parameters: String,
|
||||
#[serializer(borsh)] call_results: String,
|
||||
) -> String {
|
||||
let outcome = Self::execute(
|
||||
air_script,
|
||||
prev_data,
|
||||
current_data,
|
||||
run_parameters.into(),
|
||||
call_results.into(),
|
||||
);
|
||||
serde_json::to_string(&outcome).unwrap()
|
||||
}
|
||||
|
||||
fn execute(
|
||||
air: String,
|
||||
prev_data: Vec<u8>,
|
||||
cur_data: Vec<u8>,
|
||||
params: Vec<u8>,
|
||||
call_results: Vec<u8>,
|
||||
) -> InterpreterOutcome {
|
||||
let params: RunParameters =
|
||||
serde_json::from_slice(¶ms).expect("cannot parse RunParameters");
|
||||
|
||||
execute_air(air, prev_data, cur_data, params, call_results)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user