mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 07:10:18 +00:00
feat(aquavm-air)!: signature checking (#607)
This commit is contained in:
parent
b6c4eaaddc
commit
8a07613027
6
.github/workflows/tests.yml
vendored
6
.github/workflows/tests.yml
vendored
@ -86,6 +86,12 @@ jobs:
|
||||
for san in address leak; do
|
||||
RUSTFLAGS="$RUSTFLAGS -Z sanitizer=$san" cargo test --features test_with_native_code --target x86_64-unknown-linux-gnu
|
||||
done
|
||||
- name: Run signature tests with test_with_native_code
|
||||
env:
|
||||
RUST_TEST_THREADS: 1
|
||||
run: |
|
||||
# Temporary solution until signatures works: it depends on other PRs
|
||||
cargo test --features test_with_native_code,gen_signatures,check_signatures --target x86_64-unknown-linux-gnu features::signatures
|
||||
|
||||
- name: Check native aquavm-air-cli
|
||||
run: cargo check --package aquavm-air-cli --no-default-features
|
||||
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[submodule "benches/performance_metering"]
|
||||
path = benches/performance_metering
|
||||
url = git@github.com:fluencelabs/aquavm-benchmark-data.git
|
||||
branch = feat/VM-221-signature-checking
|
93
Cargo.lock
generated
93
Cargo.lock
generated
@ -100,6 +100,7 @@ dependencies = [
|
||||
"multihash 0.18.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -110,6 +111,7 @@ dependencies = [
|
||||
"air-interpreter-signatures",
|
||||
"air-utils",
|
||||
"aquavm-air-parser",
|
||||
"fluence-keypair",
|
||||
"newtype_derive",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
@ -117,6 +119,7 @@ dependencies = [
|
||||
"semver 1.0.18",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@ -135,10 +138,11 @@ name = "air-interpreter-signatures"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"air-interpreter-cid",
|
||||
"borsh 0.10.3",
|
||||
"borsh-derive 0.10.3",
|
||||
"bs58 0.5.0",
|
||||
"fluence-keypair",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -194,6 +198,7 @@ dependencies = [
|
||||
name = "air-testing-framework"
|
||||
version = "0.6.1"
|
||||
dependencies = [
|
||||
"air-interpreter-signatures",
|
||||
"air-test-utils",
|
||||
"aquavm-air-parser",
|
||||
"itertools",
|
||||
@ -201,6 +206,7 @@ dependencies = [
|
||||
"nom",
|
||||
"nom_locate",
|
||||
"pretty_assertions 1.4.0",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"strum",
|
||||
]
|
||||
@ -333,9 +339,11 @@ dependencies = [
|
||||
"air-trace-handler",
|
||||
"air-utils",
|
||||
"aquavm-air-parser",
|
||||
"borsh 0.10.3",
|
||||
"concat-idents",
|
||||
"criterion 0.3.6",
|
||||
"csv",
|
||||
"ed25519-dalek",
|
||||
"env_logger 0.7.1",
|
||||
"fluence-app-service",
|
||||
"fluence-keypair",
|
||||
@ -806,7 +814,17 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa"
|
||||
dependencies = [
|
||||
"borsh-derive",
|
||||
"borsh-derive 0.9.3",
|
||||
"hashbrown 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
||||
dependencies = [
|
||||
"borsh-derive 0.10.3",
|
||||
"hashbrown 0.11.2",
|
||||
]
|
||||
|
||||
@ -816,8 +834,21 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775"
|
||||
dependencies = [
|
||||
"borsh-derive-internal",
|
||||
"borsh-schema-derive-internal",
|
||||
"borsh-derive-internal 0.9.3",
|
||||
"borsh-schema-derive-internal 0.9.3",
|
||||
"proc-macro-crate 0.1.5",
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7"
|
||||
dependencies = [
|
||||
"borsh-derive-internal 0.10.3",
|
||||
"borsh-schema-derive-internal 0.10.3",
|
||||
"proc-macro-crate 0.1.5",
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
@ -834,6 +865,17 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-derive-internal"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-schema-derive-internal"
|
||||
version = "0.9.3"
|
||||
@ -845,6 +887,17 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-schema-derive-internal"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.4.0"
|
||||
@ -3332,7 +3385,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "885db39b08518fa700b73fa2214e8adbbfba316ba82dd510f50519173eadaf73"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"schemars",
|
||||
"semver 1.0.18",
|
||||
"serde",
|
||||
@ -3344,7 +3397,7 @@ version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d258582a1878e6db67400b0504a5099db85718d22c2e07f747fe1706ae7150"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -3354,7 +3407,7 @@ version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d924011380de759c3dc6fdbcda37a19a5c061f56dab69d28a34ecee765e23e4"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -3385,7 +3438,7 @@ checksum = "1e75673d69fd7365508f3d32483669fe45b03bfb34e4d9363e90adae9dfb416c"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"blake2",
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"bs58 0.4.0",
|
||||
"c2-chacha",
|
||||
"curve25519-dalek",
|
||||
@ -3411,7 +3464,7 @@ checksum = "7754612b47737d277fb818e9fdbb1406e90f9e57151c55c3584d714421976cb6"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"blake2",
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"bs58 0.4.0",
|
||||
"c2-chacha",
|
||||
"curve25519-dalek",
|
||||
@ -3434,7 +3487,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1335ffce1476da6516dcd22b26cece1a495fc725c0e8fec1879073752ac068d"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"near-chain-configs",
|
||||
@ -3469,7 +3522,7 @@ version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ad1a9a1640539c81f065425c31bffcfbf6b31ef1aeaade59ce905f5df6ac860"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"byteorder",
|
||||
"bytesize",
|
||||
"chrono",
|
||||
@ -3498,7 +3551,7 @@ version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97670b302dce15f09bba50f24c67aa08130fd01528cc61d4415892401e88e974"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"byteorder",
|
||||
"bytesize",
|
||||
"cfg-if 1.0.0",
|
||||
@ -3529,7 +3582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91d508f0fc340f6461e4e256417685720d3c4c00bb5a939b105160e49137caba"
|
||||
dependencies = [
|
||||
"base64 0.11.0",
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"bs58 0.4.0",
|
||||
"derive_more",
|
||||
"near-account-id 0.14.0",
|
||||
@ -3546,7 +3599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7929e19d862221949734c4a0063a8f55e7069de3a2ebc2d4f4c13497a5e953cb"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"bs58 0.4.0",
|
||||
"derive_more",
|
||||
"near-account-id 0.15.0",
|
||||
@ -3623,7 +3676,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15eb3de2defe3626260cc209a6cdb985c6b27b0bd4619fad97dcfae002c3c5bd"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"bs58 0.4.0",
|
||||
"near-abi",
|
||||
"near-crypto 0.14.0",
|
||||
@ -3663,7 +3716,7 @@ version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0da466a30f0446639cbd788c30865086fac3e8dcb07a79e51d2b0775ed4261e"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"near-account-id 0.14.0",
|
||||
"near-rpc-error-macro 0.14.0",
|
||||
"serde",
|
||||
@ -3675,7 +3728,7 @@ version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5591c9c8afa83a040cb5c3f29bc52b2efae2c32d4bcaee1bba723738da1a5cf6"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"near-account-id 0.15.0",
|
||||
"near-rpc-error-macro 0.15.0",
|
||||
"serde",
|
||||
@ -3689,7 +3742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81b534828419bacbf1f7b11ef7b00420f248c548c485d3f0cfda8bb6931152f2"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"bs58 0.4.0",
|
||||
"byteorder",
|
||||
"near-account-id 0.14.0",
|
||||
@ -6370,7 +6423,7 @@ dependencies = [
|
||||
"async-process",
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"bs58 0.4.0",
|
||||
"chrono",
|
||||
"dirs 3.0.2",
|
||||
@ -6441,7 +6494,7 @@ version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e61de68ede9ffdd69c01664f65a178c5188b73f78faa21f0936016a888ff7c"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"borsh 0.9.3",
|
||||
"byteorder",
|
||||
"crunchy",
|
||||
"lazy_static",
|
||||
|
@ -37,3 +37,6 @@ tracing-subscriber = { version = "0.3.17", default-features = false, features =
|
||||
# indicates that this library should be compiled for the marine target,
|
||||
# otherwise it will be compiled for the wasm bindgen target
|
||||
marine = []
|
||||
gen_signatures = ["aquavm-air/gen_signatures"]
|
||||
check_signatures = ["aquavm-air/check_signatures"]
|
||||
|
||||
|
@ -51,14 +51,20 @@ air-testing-framework = { path = "../crates/testing-framework" }
|
||||
fluence-app-service = "0.29.0"
|
||||
marine-rs-sdk = { version = "0.10.0", features = ["logger"] }
|
||||
|
||||
borsh = "0.10.3"
|
||||
# the feature just silence a warning in the criterion 0.3.x.
|
||||
criterion = { version = "0.3.3", features = ["html_reports"] }
|
||||
csv = "1.1.5"
|
||||
once_cell = "1.4.1"
|
||||
ed25519-dalek = "1.0.1"
|
||||
env_logger = "0.7.1"
|
||||
once_cell = "1.4.1"
|
||||
pretty_assertions = "0.6.1"
|
||||
serde_json = "1.0.61"
|
||||
|
||||
[features]
|
||||
check_signatures = []
|
||||
gen_signatures = []
|
||||
|
||||
[[bench]]
|
||||
name = "call_benchmark"
|
||||
harness = false
|
||||
|
@ -117,6 +117,17 @@ pub enum UncatchableError {
|
||||
/// CanonStreamMap related errors.
|
||||
#[error(transparent)]
|
||||
CanonStreamMapError(#[from] CanonStreamMapError),
|
||||
|
||||
/// Argument hash or tetraplet mismatch in a call/canon merged from current_data with an evaluated value.
|
||||
#[error("{param} doesn't match expected parameters: expected {expected_value}, got {stored_value} ")]
|
||||
InstructionParametersMismatch {
|
||||
param: &'static str,
|
||||
expected_value: String,
|
||||
stored_value: String,
|
||||
},
|
||||
|
||||
#[error("failed to sign data: {0}")]
|
||||
SigningError(#[from] fluence_keypair::error::SigningError),
|
||||
}
|
||||
|
||||
impl ToErrorCode for UncatchableError {
|
||||
|
@ -147,15 +147,28 @@ impl ExecutionCidState {
|
||||
.ok_or_else(|| UncatchableError::ValueForCidNotFound("service result aggregate", cid.clone().into()))
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_service_value(
|
||||
pub(crate) fn resolve_service_info(
|
||||
&self,
|
||||
service_result_agg_cid: &CID<ServiceResultCidAggregate>,
|
||||
) -> Result<Rc<JValue>, UncatchableError> {
|
||||
) -> Result<ResolvedServiceInfo, UncatchableError> {
|
||||
let service_result_aggregate = self.get_service_result_agg_by_cid(service_result_agg_cid)?;
|
||||
self.get_value_by_cid(&service_result_aggregate.value_cid)
|
||||
let value = self.get_value_by_cid(&service_result_aggregate.value_cid)?;
|
||||
let tetraplet = self.get_tetraplet_by_cid(&service_result_aggregate.tetraplet_cid)?;
|
||||
|
||||
Ok(ResolvedServiceInfo {
|
||||
value,
|
||||
tetraplet,
|
||||
service_result_aggregate,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ResolvedServiceInfo {
|
||||
pub(crate) value: Rc<JValue>,
|
||||
pub(crate) tetraplet: RcSecurityTetraplet,
|
||||
pub(crate) service_result_aggregate: Rc<ServiceResultCidAggregate>,
|
||||
}
|
||||
|
||||
impl From<ExecutionCidState> for CidInfo {
|
||||
fn from(value: ExecutionCidState) -> Self {
|
||||
Self {
|
||||
|
@ -31,13 +31,12 @@ use air_interpreter_data::CanonResultCidAggregate;
|
||||
use air_interpreter_data::CidInfo;
|
||||
use air_interpreter_data::ServiceResultCidAggregate;
|
||||
use air_interpreter_interface::*;
|
||||
use air_interpreter_signatures::PeerCidTracker;
|
||||
use air_interpreter_signatures::SignatureStore;
|
||||
use air_interpreter_signatures::SignatureTracker;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Contains all necessary state needed to execute AIR script.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ExecutionCtx<'i> {
|
||||
/// Contains all scalars.
|
||||
pub(crate) scalars: Scalars<'i>,
|
||||
@ -89,11 +88,10 @@ pub(crate) struct ExecutionCtx<'i> {
|
||||
/// It contains peers' signatures for verification.
|
||||
pub(crate) signature_store: SignatureStore,
|
||||
|
||||
/// Local signatures tracker.
|
||||
/// Current peer's CID tracker.
|
||||
///
|
||||
/// It gathers peers' CIDs (call results and canon results) stored in the trace either for signing (current peer's
|
||||
/// CIDs) or sign verification (other peers).
|
||||
pub(crate) signature_tracker: SignatureTracker,
|
||||
/// It gathers current peer's CIDs (call results and canon results) for further signing.
|
||||
pub(crate) peer_cid_tracker: PeerCidTracker,
|
||||
}
|
||||
|
||||
impl<'i> ExecutionCtx<'i> {
|
||||
@ -101,15 +99,15 @@ impl<'i> ExecutionCtx<'i> {
|
||||
prev_ingredients: ExecCtxIngredients,
|
||||
current_ingredients: ExecCtxIngredients,
|
||||
call_results: CallResults,
|
||||
signature_store: SignatureStore,
|
||||
run_parameters: &RunParameters,
|
||||
) -> Self {
|
||||
let run_parameters = RcRunParameters::from_run_parameters(run_parameters);
|
||||
let streams = Streams::new();
|
||||
|
||||
let cid_state = ExecutionCidState::from_cid_info(prev_ingredients.cid_info, current_ingredients.cid_info);
|
||||
// TODO we might keep both stores and merge them only with signature info collected into SignatureTracker
|
||||
let signature_store =
|
||||
SignatureStore::merge(prev_ingredients.signature_store, current_ingredients.signature_store);
|
||||
|
||||
let peer_cid_tracker = PeerCidTracker::new(run_parameters.current_peer_id.clone());
|
||||
|
||||
Self {
|
||||
run_parameters,
|
||||
@ -117,9 +115,16 @@ impl<'i> ExecutionCtx<'i> {
|
||||
last_call_request_id: prev_ingredients.last_call_request_id,
|
||||
call_results,
|
||||
streams,
|
||||
stream_maps: <_>::default(),
|
||||
cid_state,
|
||||
signature_store,
|
||||
..<_>::default()
|
||||
peer_cid_tracker,
|
||||
scalars: <_>::default(),
|
||||
next_peer_pks: <_>::default(),
|
||||
last_error_descriptor: <_>::default(),
|
||||
error_descriptor: <_>::default(),
|
||||
tracker: <_>::default(),
|
||||
call_requests: <_>::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,12 +141,12 @@ impl<'i> ExecutionCtx<'i> {
|
||||
self.last_call_request_id
|
||||
}
|
||||
|
||||
pub(crate) fn record_call_cid(&mut self, peer_id: impl Into<Box<str>>, cid: &CID<ServiceResultCidAggregate>) {
|
||||
self.signature_tracker.register(peer_id, cid);
|
||||
pub(crate) fn record_call_cid(&mut self, peer_id: &str, cid: &CID<ServiceResultCidAggregate>) {
|
||||
self.peer_cid_tracker.register(peer_id, cid);
|
||||
}
|
||||
|
||||
pub(crate) fn record_canon_cid(&mut self, peer_id: impl Into<Box<str>>, cid: &CID<CanonResultCidAggregate>) {
|
||||
self.signature_tracker.register(peer_id, cid);
|
||||
pub(crate) fn record_canon_cid(&mut self, peer_id: &str, cid: &CID<CanonResultCidAggregate>) {
|
||||
self.peer_cid_tracker.register(peer_id, cid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +210,6 @@ impl ExecutionCtx<'_> {
|
||||
pub(crate) struct ExecCtxIngredients {
|
||||
pub(crate) last_call_request_id: u32,
|
||||
pub(crate) cid_info: CidInfo,
|
||||
pub(crate) signature_store: SignatureStore,
|
||||
}
|
||||
|
||||
use serde::Deserialize;
|
||||
@ -218,6 +222,8 @@ use std::fmt::Formatter;
|
||||
pub(crate) struct RcRunParameters {
|
||||
pub(crate) init_peer_id: Rc<str>,
|
||||
pub(crate) current_peer_id: Rc<String>,
|
||||
// TODO use [u8]
|
||||
pub(crate) salt: Rc<str>,
|
||||
pub(crate) timestamp: u64,
|
||||
pub(crate) ttl: u32,
|
||||
}
|
||||
@ -227,23 +233,13 @@ impl RcRunParameters {
|
||||
Self {
|
||||
init_peer_id: run_parameters.init_peer_id.as_str().into(),
|
||||
current_peer_id: Rc::new(run_parameters.current_peer_id.clone()),
|
||||
salt: run_parameters.particle_id.as_str().into(),
|
||||
timestamp: run_parameters.timestamp,
|
||||
ttl: run_parameters.ttl,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RcRunParameters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
init_peer_id: "".into(),
|
||||
current_peer_id: Default::default(),
|
||||
timestamp: Default::default(),
|
||||
ttl: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Display for ExecutionCtx<'i> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "scalars:")?;
|
||||
|
@ -24,6 +24,7 @@ mod streams_variables;
|
||||
pub use instruction_error::*;
|
||||
|
||||
pub use cid_state::ExecutionCidState;
|
||||
pub(crate) use cid_state::ResolvedServiceInfo;
|
||||
pub(crate) use context::*;
|
||||
pub(crate) use scalar_variables::*;
|
||||
pub(crate) use stream_maps_variables::*;
|
||||
|
@ -18,6 +18,7 @@ pub(crate) mod call_result_setter;
|
||||
mod prev_result_handler;
|
||||
mod resolved_call;
|
||||
pub(crate) mod triplet;
|
||||
mod verifier;
|
||||
|
||||
use resolved_call::ResolvedCall;
|
||||
|
||||
|
@ -38,7 +38,7 @@ pub(crate) fn populate_context_from_peer_service_result<'i>(
|
||||
) -> ExecutionResult<CallResult> {
|
||||
match output {
|
||||
CallOutputValue::Scalar(scalar) => {
|
||||
let peer_id: Box<str> = tetraplet.peer_pk.as_str().into();
|
||||
let peer_id = tetraplet.peer_pk.clone();
|
||||
let service_result_agg_cid =
|
||||
exec_ctx
|
||||
.cid_state
|
||||
@ -46,11 +46,11 @@ pub(crate) fn populate_context_from_peer_service_result<'i>(
|
||||
let executed_result = ValueAggregate::from_service_result(executed_result, service_result_agg_cid.clone());
|
||||
|
||||
exec_ctx.scalars.set_scalar_value(scalar.name, executed_result)?;
|
||||
exec_ctx.record_call_cid(peer_id, &service_result_agg_cid);
|
||||
exec_ctx.record_call_cid(&peer_id, &service_result_agg_cid);
|
||||
Ok(CallResult::executed_scalar(service_result_agg_cid))
|
||||
}
|
||||
CallOutputValue::Stream(stream) => {
|
||||
let peer_id: Box<str> = tetraplet.peer_pk.as_str().into();
|
||||
let peer_id = tetraplet.peer_pk.clone();
|
||||
let service_result_agg_cid =
|
||||
exec_ctx
|
||||
.cid_state
|
||||
@ -61,7 +61,7 @@ pub(crate) fn populate_context_from_peer_service_result<'i>(
|
||||
let value_descriptor =
|
||||
StreamValueDescriptor::new(executed_result, stream.name, Generation::New, stream.position);
|
||||
exec_ctx.streams.add_stream_value(value_descriptor)?;
|
||||
exec_ctx.record_call_cid(&*peer_id, &service_result_agg_cid);
|
||||
exec_ctx.record_call_cid(&peer_id, &service_result_agg_cid);
|
||||
Ok(CallResult::executed_stream_stub(service_result_agg_cid))
|
||||
}
|
||||
CallOutputValue::None => {
|
||||
@ -76,6 +76,7 @@ pub(crate) fn populate_context_from_peer_service_result<'i>(
|
||||
|
||||
pub(crate) fn populate_context_from_data<'i>(
|
||||
value: ValueRef,
|
||||
argument_hash: &str,
|
||||
tetraplet: RcSecurityTetraplet,
|
||||
trace_pos: TracePos,
|
||||
value_source: ValueSource,
|
||||
@ -84,13 +85,37 @@ pub(crate) fn populate_context_from_data<'i>(
|
||||
) -> ExecutionResult<()> {
|
||||
match (output, value) {
|
||||
(CallOutputValue::Scalar(scalar), ValueRef::Scalar(cid)) => {
|
||||
let value = exec_ctx.cid_state.resolve_service_value(&cid)?;
|
||||
let ResolvedServiceInfo {
|
||||
value,
|
||||
tetraplet: current_tetraplet,
|
||||
service_result_aggregate,
|
||||
} = exec_ctx.cid_state.resolve_service_info(&cid)?;
|
||||
|
||||
verifier::verify_call(
|
||||
argument_hash,
|
||||
&tetraplet,
|
||||
&service_result_aggregate.argument_hash,
|
||||
¤t_tetraplet,
|
||||
)?;
|
||||
|
||||
let result = ServiceResultAggregate::new(value, tetraplet, trace_pos);
|
||||
let result = ValueAggregate::from_service_result(result, cid);
|
||||
exec_ctx.scalars.set_scalar_value(scalar.name, result)?;
|
||||
}
|
||||
(CallOutputValue::Stream(stream), ValueRef::Stream { cid, generation }) => {
|
||||
let value = exec_ctx.cid_state.resolve_service_value(&cid)?;
|
||||
let ResolvedServiceInfo {
|
||||
value,
|
||||
tetraplet: current_tetraplet,
|
||||
service_result_aggregate,
|
||||
} = exec_ctx.cid_state.resolve_service_info(&cid)?;
|
||||
|
||||
verifier::verify_call(
|
||||
argument_hash,
|
||||
&tetraplet,
|
||||
&service_result_aggregate.argument_hash,
|
||||
¤t_tetraplet,
|
||||
)?;
|
||||
|
||||
let result = ServiceResultAggregate::new(value, tetraplet, trace_pos);
|
||||
let result = ValueAggregate::from_service_result(result, cid);
|
||||
let generation = Generation::from_data(value_source, generation);
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::execution_step::execution_context::ResolvedServiceInfo;
|
||||
use crate::execution_step::instructions::call::call_result_setter::populate_context_from_data;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::RcSecurityTetraplet;
|
||||
@ -50,15 +51,27 @@ pub(super) fn handle_prev_state<'i>(
|
||||
// this call was failed on one of the previous executions,
|
||||
// here it's needed to bubble this special error up
|
||||
Failed(ref failed_cid) => {
|
||||
let err_value = exec_ctx
|
||||
let ResolvedServiceInfo {
|
||||
value: err_value,
|
||||
tetraplet: current_tetraplet,
|
||||
service_result_aggregate,
|
||||
} = exec_ctx
|
||||
.cid_state
|
||||
.resolve_service_value(failed_cid)
|
||||
.resolve_service_info(failed_cid)
|
||||
.map_err(UncatchableError::from)?;
|
||||
|
||||
verifier::verify_call(
|
||||
argument_hash.as_ref().unwrap(),
|
||||
tetraplet,
|
||||
&service_result_aggregate.argument_hash,
|
||||
¤t_tetraplet,
|
||||
)?;
|
||||
|
||||
let call_service_failed: CallServiceFailed =
|
||||
serde_json::from_value((*err_value).clone()).map_err(UncatchableError::MalformedCallServiceFailed)?;
|
||||
|
||||
exec_ctx.make_subgraph_incomplete();
|
||||
exec_ctx.record_call_cid(&*tetraplet.peer_pk, failed_cid);
|
||||
exec_ctx.record_call_cid(&tetraplet.peer_pk, failed_cid);
|
||||
trace_ctx.meet_call_end(met_result.result);
|
||||
|
||||
let err_msg = call_service_failed.message;
|
||||
@ -103,6 +116,7 @@ pub(super) fn handle_prev_state<'i>(
|
||||
|
||||
populate_context_from_data(
|
||||
value.clone(),
|
||||
argument_hash.as_ref().unwrap(),
|
||||
tetraplet.clone(),
|
||||
met_result.trace_pos,
|
||||
met_result.source,
|
||||
@ -112,7 +126,7 @@ pub(super) fn handle_prev_state<'i>(
|
||||
|
||||
match &value {
|
||||
ValueRef::Scalar(ref cid) | ValueRef::Stream { ref cid, .. } => {
|
||||
exec_ctx.record_call_cid(&*tetraplet.peer_pk, cid);
|
||||
exec_ctx.record_call_cid(&tetraplet.peer_pk, cid);
|
||||
}
|
||||
ValueRef::Unused(_) => {}
|
||||
}
|
||||
@ -178,13 +192,13 @@ fn handle_service_error(
|
||||
|
||||
let failed_value = CallServiceFailed::new(service_result.ret_code, error_message).to_value();
|
||||
|
||||
let peer_id: Box<str> = tetraplet.peer_pk.as_str().into();
|
||||
let peer_id = tetraplet.peer_pk.clone();
|
||||
let service_result_agg_cid =
|
||||
exec_ctx
|
||||
.cid_state
|
||||
.track_service_result(failed_value.into(), tetraplet, argument_hash)?;
|
||||
|
||||
exec_ctx.record_call_cid(peer_id, &service_result_agg_cid);
|
||||
exec_ctx.record_call_cid(&peer_id, &service_result_agg_cid);
|
||||
trace_ctx.meet_call_end(Failed(service_result_agg_cid));
|
||||
|
||||
Err(error.into())
|
||||
|
43
air/src/execution_step/instructions/call/verifier.rs
Normal file
43
air/src/execution_step/instructions/call/verifier.rs
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 crate::UncatchableError;
|
||||
|
||||
use polyplets::SecurityTetraplet;
|
||||
|
||||
/// Check that computed call parameters match the parameters from current data.
|
||||
pub(crate) fn verify_call(
|
||||
expected_argument_hash: &str,
|
||||
expected_tetraplet: &SecurityTetraplet,
|
||||
stored_argument_hash: &str,
|
||||
stored_tetraplet: &SecurityTetraplet,
|
||||
) -> Result<(), UncatchableError> {
|
||||
if expected_argument_hash != stored_argument_hash {
|
||||
return Err(UncatchableError::InstructionParametersMismatch {
|
||||
param: "call argument_hash",
|
||||
expected_value: expected_argument_hash.to_owned(),
|
||||
stored_value: stored_argument_hash.to_owned(),
|
||||
});
|
||||
}
|
||||
if expected_tetraplet != stored_tetraplet {
|
||||
return Err(UncatchableError::InstructionParametersMismatch {
|
||||
param: "call tetraplet",
|
||||
expected_value: format!("{expected_tetraplet:?}"),
|
||||
stored_value: format!("{stored_tetraplet:?}"),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -47,6 +47,7 @@ impl<'i> super::ExecutableInstruction<'i> for ast::Canon<'i> {
|
||||
let create_canon_producer = create_canon_stream_producer(self.stream.name, self.stream.position);
|
||||
match canon_result {
|
||||
MergerCanonResult::CanonResult(canon_result) => handle_seen_canon(
|
||||
&self.peer_id,
|
||||
epilog,
|
||||
&create_canon_producer,
|
||||
canon_result,
|
||||
|
@ -47,6 +47,7 @@ impl<'i> super::ExecutableInstruction<'i> for ast::CanonMap<'i> {
|
||||
let create_canon_producer = create_canon_stream_producer(self.stream_map.name, self.stream_map.position);
|
||||
match canon_result {
|
||||
MergerCanonResult::CanonResult(canon_result) => handle_seen_canon(
|
||||
&self.peer_id,
|
||||
epilog,
|
||||
&create_canon_producer,
|
||||
canon_result,
|
||||
|
@ -50,6 +50,7 @@ impl<'i> super::ExecutableInstruction<'i> for ast::CanonStreamMapScalar<'i> {
|
||||
let create_canon_producer = create_canon_stream_producer(self.stream_map.name, self.stream_map.position);
|
||||
match canon_result {
|
||||
MergerCanonResult::CanonResult(canon_result) => handle_seen_canon(
|
||||
&self.peer_id,
|
||||
epilog,
|
||||
&create_canon_producer,
|
||||
canon_result,
|
||||
|
@ -25,6 +25,7 @@ use air_interpreter_cid::CID;
|
||||
use air_interpreter_data::CanonResult;
|
||||
use air_interpreter_data::CanonResultCidAggregate;
|
||||
use air_parser::ast::ResolvableToPeerIdVariable;
|
||||
use polyplets::SecurityTetraplet;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -34,6 +35,7 @@ pub(crate) type CanonEpilogClosure<'closure> = dyn Fn(CanonStream, Rc<CID<CanonR
|
||||
pub(crate) type CreateCanonStreamClosure<'closure> = dyn Fn(&mut ExecutionCtx<'_>, String) -> CanonStream + 'closure;
|
||||
|
||||
pub(crate) fn handle_seen_canon(
|
||||
peer_id_var: &ResolvableToPeerIdVariable<'_>,
|
||||
epilog: &CanonEpilogClosure<'_>,
|
||||
create_canon_stream: &CreateCanonStreamClosure<'_>,
|
||||
canon_result: CanonResult,
|
||||
@ -45,7 +47,9 @@ pub(crate) fn handle_seen_canon(
|
||||
CanonResult::RequestSentBy(..) => {
|
||||
handle_canon_request_sent_by(epilog, create_canon_stream, peer_id, canon_result, exec_ctx, trace_ctx)
|
||||
}
|
||||
CanonResult::Executed(canon_result_cid) => handle_canon_executed(epilog, canon_result_cid, exec_ctx, trace_ctx),
|
||||
CanonResult::Executed(canon_result_cid) => {
|
||||
handle_canon_executed(peer_id_var, epilog, canon_result_cid, exec_ctx, trace_ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,21 +76,29 @@ pub(crate) fn handle_canon_request_sent_by(
|
||||
}
|
||||
|
||||
pub(crate) fn handle_canon_executed(
|
||||
peer_id_var: &ResolvableToPeerIdVariable<'_>,
|
||||
epilog: &CanonEpilogClosure<'_>,
|
||||
canon_result_cid: Rc<CID<CanonResultCidAggregate>>,
|
||||
exec_ctx: &mut ExecutionCtx<'_>,
|
||||
trace_ctx: &mut TraceHandler,
|
||||
) -> ExecutionResult<()> {
|
||||
let canon_result_agg = exec_ctx.cid_state.get_canon_result_by_cid(&canon_result_cid)?;
|
||||
let tetraplet = exec_ctx.cid_state.get_tetraplet_by_cid(&canon_result_agg.tetraplet)?;
|
||||
let peer_id = crate::execution_step::instructions::resolve_peer_id_to_string(peer_id_var, exec_ctx)?;
|
||||
let expected_tetraplet = SecurityTetraplet::new(peer_id, "", "", "");
|
||||
|
||||
exec_ctx.record_canon_cid(&*tetraplet.peer_pk, &canon_result_cid);
|
||||
let canon_result_agg = exec_ctx.cid_state.get_canon_result_by_cid(&canon_result_cid)?;
|
||||
let tetraplet_cid = canon_result_agg.tetraplet.clone();
|
||||
let tetraplet = exec_ctx.cid_state.get_tetraplet_by_cid(&tetraplet_cid)?;
|
||||
|
||||
verify_canon(&expected_tetraplet, &tetraplet)?;
|
||||
|
||||
let value_cids = canon_result_agg.values.clone();
|
||||
let values = value_cids
|
||||
.iter()
|
||||
.map(|canon_value_cid| exec_ctx.cid_state.get_canon_value_by_cid(canon_value_cid))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
populate_seen_cid_context(exec_ctx, &tetraplet.peer_pk, &canon_result_cid);
|
||||
|
||||
let canon_stream = CanonStream::new(values, tetraplet);
|
||||
|
||||
epilog(canon_stream, canon_result_cid, exec_ctx, trace_ctx)
|
||||
@ -124,11 +136,19 @@ fn create_canon_stream_for_first_time(
|
||||
trace_ctx: &mut TraceHandler,
|
||||
) -> ExecutionResult<()> {
|
||||
let canon_stream = create_canon_stream(exec_ctx, peer_id);
|
||||
let canon_result_cid = populate_cid_context(exec_ctx, &canon_stream)?;
|
||||
let canon_result_cid = populate_unseen_cid_context(exec_ctx, &canon_stream)?;
|
||||
epilog(canon_stream, canon_result_cid, exec_ctx, trace_ctx)
|
||||
}
|
||||
|
||||
fn populate_cid_context(
|
||||
fn populate_seen_cid_context(
|
||||
exec_ctx: &mut ExecutionCtx<'_>,
|
||||
peer_id: &str,
|
||||
canon_result_cid: &Rc<CID<CanonResultCidAggregate>>,
|
||||
) {
|
||||
exec_ctx.record_canon_cid(peer_id, canon_result_cid)
|
||||
}
|
||||
|
||||
fn populate_unseen_cid_context(
|
||||
exec_ctx: &mut ExecutionCtx<'_>,
|
||||
canon_stream: &CanonStream,
|
||||
) -> ExecutionResult<Rc<CID<CanonResultCidAggregate>>> {
|
||||
@ -151,6 +171,20 @@ fn populate_cid_context(
|
||||
.track_value(canon_result)
|
||||
.map_err(UncatchableError::from)?;
|
||||
|
||||
exec_ctx.record_canon_cid(&*tetraplet.peer_pk, &canon_result_cid);
|
||||
exec_ctx.record_canon_cid(&tetraplet.peer_pk, &canon_result_cid);
|
||||
Ok(canon_result_cid)
|
||||
}
|
||||
|
||||
pub(crate) fn verify_canon(
|
||||
expected_tetraplet: &SecurityTetraplet,
|
||||
stored_tetraplet: &SecurityTetraplet,
|
||||
) -> Result<(), UncatchableError> {
|
||||
if expected_tetraplet != stored_tetraplet {
|
||||
return Err(UncatchableError::InstructionParametersMismatch {
|
||||
param: "canon tetraplet",
|
||||
expected_value: format!("{expected_tetraplet:?}"),
|
||||
stored_value: format!("{stored_tetraplet:?}"),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -133,8 +133,8 @@ fn compactify_streams(exec_ctx: &mut ExecutionCtx<'_>, trace_ctx: &mut TraceHand
|
||||
|
||||
fn sign_result(exec_ctx: &mut ExecutionCtx<'_>, keypair: &KeyPair) -> Result<(), InterpreterOutcome> {
|
||||
let current_signature = exec_ctx
|
||||
.signature_tracker
|
||||
.into_signature(&exec_ctx.run_parameters.current_peer_id, keypair)
|
||||
.peer_cid_tracker
|
||||
.gen_signature(&exec_ctx.run_parameters.salt, keypair)
|
||||
.map_err(signing_error_into_outcome)?;
|
||||
|
||||
let current_pubkey = keypair.public();
|
||||
|
@ -30,7 +30,9 @@ mod execution_step;
|
||||
mod farewell_step;
|
||||
mod preparation_step;
|
||||
mod runner;
|
||||
mod signing_step;
|
||||
mod utils;
|
||||
mod verification_step;
|
||||
|
||||
pub use air_interpreter_interface::InterpreterOutcome;
|
||||
pub use air_interpreter_interface::RunParameters;
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
use crate::ToErrorCode;
|
||||
use air_interpreter_data::data_version;
|
||||
use air_interpreter_data::verification::DataVerifierError;
|
||||
use air_interpreter_data::CidStoreVerificationError;
|
||||
use air_interpreter_data::Versions;
|
||||
|
||||
use serde_json::Error as SerdeJsonError;
|
||||
@ -84,6 +86,14 @@ pub enum PreparationError {
|
||||
#[from]
|
||||
error: fluence_keypair::error::DecodingError,
|
||||
},
|
||||
|
||||
/// Failed to verify CidStore contents of the current data.
|
||||
#[error(transparent)]
|
||||
CidStoreVerificationError(#[from] CidStoreVerificationError),
|
||||
|
||||
/// Failed to check peers' signatures.
|
||||
#[error(transparent)]
|
||||
DataSignatureCheckError(#[from] DataVerifierError),
|
||||
}
|
||||
|
||||
impl ToErrorCode for PreparationError {
|
||||
|
@ -22,5 +22,7 @@ pub use errors::PreparationError;
|
||||
pub use interpreter_versions::interpreter_version;
|
||||
pub use interpreter_versions::min_supported_version;
|
||||
|
||||
pub(crate) use preparation::parse_data;
|
||||
pub(crate) use preparation::prepare;
|
||||
pub(crate) use preparation::ParsedDataPair;
|
||||
pub(crate) use preparation::PreparationDescriptor;
|
||||
|
@ -21,6 +21,7 @@ use crate::execution_step::TraceHandler;
|
||||
|
||||
use air_interpreter_data::InterpreterData;
|
||||
use air_interpreter_interface::RunParameters;
|
||||
use air_interpreter_signatures::SignatureStore;
|
||||
use air_parser::ast::Instruction;
|
||||
use fluence_keypair::KeyFormat;
|
||||
use fluence_keypair::KeyPair;
|
||||
@ -37,35 +38,54 @@ pub(crate) struct PreparationDescriptor<'ctx, 'i> {
|
||||
pub(crate) keypair: KeyPair,
|
||||
}
|
||||
|
||||
/// Parse and prepare supplied data and AIR script.
|
||||
pub(crate) struct ParsedDataPair {
|
||||
pub(crate) prev_data: InterpreterData,
|
||||
pub(crate) current_data: InterpreterData,
|
||||
}
|
||||
|
||||
/// Parse data and check its version.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn prepare<'i>(
|
||||
prev_data: &[u8],
|
||||
current_data: &[u8],
|
||||
raw_air: &'i str,
|
||||
call_results: &[u8],
|
||||
run_parameters: RunParameters,
|
||||
) -> PreparationResult<PreparationDescriptor<'static, 'i>> {
|
||||
pub(crate) fn parse_data(prev_data: &[u8], current_data: &[u8]) -> PreparationResult<ParsedDataPair> {
|
||||
let prev_data = try_to_data(prev_data)?;
|
||||
let current_data = try_to_data(current_data)?;
|
||||
|
||||
check_version_compatibility(¤t_data)?;
|
||||
|
||||
Ok(ParsedDataPair {
|
||||
prev_data,
|
||||
current_data,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse and prepare supplied data and AIR script.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn prepare<'i>(
|
||||
prev_data: InterpreterData,
|
||||
current_data: InterpreterData,
|
||||
raw_air: &'i str,
|
||||
call_results: &[u8],
|
||||
run_parameters: RunParameters,
|
||||
signature_store: SignatureStore,
|
||||
) -> PreparationResult<PreparationDescriptor<'static, 'i>> {
|
||||
let air: Instruction<'i> = *air_parser::parse(raw_air).map_err(PreparationError::AIRParseError)?;
|
||||
|
||||
let prev_ingredients = ExecCtxIngredients {
|
||||
last_call_request_id: prev_data.last_call_request_id,
|
||||
cid_info: prev_data.cid_info,
|
||||
signature_store: prev_data.signatures,
|
||||
};
|
||||
|
||||
let current_ingredients = ExecCtxIngredients {
|
||||
last_call_request_id: current_data.last_call_request_id,
|
||||
cid_info: current_data.cid_info,
|
||||
signature_store: current_data.signatures,
|
||||
};
|
||||
|
||||
let exec_ctx = make_exec_ctx(prev_ingredients, current_ingredients, call_results, &run_parameters)?;
|
||||
let exec_ctx = make_exec_ctx(
|
||||
prev_ingredients,
|
||||
current_ingredients,
|
||||
call_results,
|
||||
signature_store,
|
||||
&run_parameters,
|
||||
)?;
|
||||
let trace_handler = TraceHandler::from_trace(prev_data.trace, current_data.trace);
|
||||
|
||||
let key_format = KeyFormat::try_from(run_parameters.key_format)?;
|
||||
@ -81,7 +101,7 @@ pub(crate) fn prepare<'i>(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn try_to_data(raw_data: &[u8]) -> PreparationResult<InterpreterData> {
|
||||
pub(crate) fn try_to_data(raw_data: &[u8]) -> PreparationResult<InterpreterData> {
|
||||
// treat empty slice as an empty data,
|
||||
// it allows abstracting from an internal format for an empty data
|
||||
if raw_data.is_empty() {
|
||||
@ -103,16 +123,23 @@ fn make_exec_ctx(
|
||||
prev_ingredients: ExecCtxIngredients,
|
||||
current_ingredients: ExecCtxIngredients,
|
||||
call_results: &[u8],
|
||||
signature_store: SignatureStore,
|
||||
run_parameters: &RunParameters,
|
||||
) -> PreparationResult<ExecutionCtx<'static>> {
|
||||
let call_results = serde_json::from_slice(call_results)
|
||||
.map_err(|e| PreparationError::call_results_de_failed(call_results.to_vec(), e))?;
|
||||
|
||||
let ctx = ExecutionCtx::new(prev_ingredients, current_ingredients, call_results, run_parameters);
|
||||
let ctx = ExecutionCtx::new(
|
||||
prev_ingredients,
|
||||
current_ingredients,
|
||||
call_results,
|
||||
signature_store,
|
||||
run_parameters,
|
||||
);
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
fn check_version_compatibility(data: &InterpreterData) -> PreparationResult<()> {
|
||||
pub(crate) fn check_version_compatibility(data: &InterpreterData) -> PreparationResult<()> {
|
||||
if &data.versions.interpreter_version < super::min_supported_version() {
|
||||
return Err(PreparationError::UnsupportedInterpreterVersion {
|
||||
actual_version: data.versions.interpreter_version.clone(),
|
||||
|
@ -16,8 +16,12 @@
|
||||
|
||||
use crate::execution_step::ExecutableInstruction;
|
||||
use crate::farewell_step as farewell;
|
||||
use crate::preparation_step::parse_data;
|
||||
use crate::preparation_step::prepare;
|
||||
use crate::preparation_step::ParsedDataPair;
|
||||
use crate::preparation_step::PreparationDescriptor;
|
||||
use crate::signing_step::sign_produced_cids;
|
||||
use crate::verification_step::verify;
|
||||
|
||||
use air_interpreter_interface::InterpreterOutcome;
|
||||
use air_interpreter_interface::RunParameters;
|
||||
@ -50,20 +54,45 @@ pub fn execute_air(
|
||||
#[allow(clippy::result_large_err)]
|
||||
fn execute_air_impl(
|
||||
air: String,
|
||||
prev_data: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
raw_prev_data: Vec<u8>,
|
||||
raw_current_data: Vec<u8>,
|
||||
params: RunParameters,
|
||||
call_results: Vec<u8>,
|
||||
) -> Result<InterpreterOutcome, InterpreterOutcome> {
|
||||
let ParsedDataPair {
|
||||
prev_data,
|
||||
current_data,
|
||||
} = match parse_data(&raw_prev_data, &raw_current_data) {
|
||||
Ok(parsed_datas) => parsed_datas,
|
||||
// return the prev data in case of errors
|
||||
Err(error) => return Err(farewell::from_uncatchable_error(raw_prev_data, error)),
|
||||
};
|
||||
|
||||
// TODO currently we use particle ID, but it should be changed to signature,
|
||||
// as partical ID can be equally replayed
|
||||
let salt = params.particle_id.clone();
|
||||
let signature_store = match verify(&prev_data, ¤t_data, &salt) {
|
||||
Ok(signature_store) => signature_store,
|
||||
// return the prev data in case of errors
|
||||
Err(error) => return Err(farewell::from_uncatchable_error(raw_prev_data, error)),
|
||||
};
|
||||
|
||||
let PreparationDescriptor {
|
||||
mut exec_ctx,
|
||||
mut trace_handler,
|
||||
air,
|
||||
keypair,
|
||||
} = match prepare(&prev_data, &data, air.as_str(), &call_results, params) {
|
||||
} = match prepare(
|
||||
prev_data,
|
||||
current_data,
|
||||
air.as_str(),
|
||||
&call_results,
|
||||
params,
|
||||
signature_store,
|
||||
) {
|
||||
Ok(descriptor) => descriptor,
|
||||
// return the prev data in case of errors
|
||||
Err(error) => return Err(farewell::from_uncatchable_error(prev_data, error)),
|
||||
Err(error) => return Err(farewell::from_uncatchable_error(raw_prev_data, error)),
|
||||
};
|
||||
|
||||
// match here is used instead of map_err, because the compiler can't determine that
|
||||
@ -73,6 +102,17 @@ fn execute_air_impl(
|
||||
tracing::Level::INFO,
|
||||
"execute",
|
||||
);
|
||||
|
||||
match sign_produced_cids(
|
||||
&mut exec_ctx.peer_cid_tracker,
|
||||
&mut exec_ctx.signature_store,
|
||||
&salt,
|
||||
&keypair,
|
||||
) {
|
||||
Ok(()) => {}
|
||||
Err(error) => return Err(farewell::from_uncatchable_error(raw_prev_data, error)),
|
||||
}
|
||||
|
||||
measure!(
|
||||
match exec_result {
|
||||
Ok(_) => farewell::from_success_result(exec_ctx, trace_handler, &keypair),
|
||||
@ -81,7 +121,7 @@ fn execute_air_impl(
|
||||
Err(farewell::from_execution_error(exec_ctx, trace_handler, error, &keypair))
|
||||
}
|
||||
// return the prev data in case of any trace errors
|
||||
Err(error) => Err(farewell::from_uncatchable_error(prev_data, error)),
|
||||
Err(error) => Err(farewell::from_uncatchable_error(raw_prev_data, error)),
|
||||
},
|
||||
tracing::Level::INFO,
|
||||
"farewell",
|
||||
|
48
air/src/signing_step.rs
Normal file
48
air/src/signing_step.rs
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 crate::ExecutionError;
|
||||
|
||||
use air_interpreter_signatures::{PeerCidTracker, SignatureStore};
|
||||
|
||||
#[cfg(feature = "gen_signatures")]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn sign_produced_cids(
|
||||
signature_tracker: &mut PeerCidTracker,
|
||||
signature_store: &mut SignatureStore,
|
||||
salt: &str,
|
||||
keypair: &fluence_keypair::KeyPair,
|
||||
) -> Result<(), ExecutionError> {
|
||||
use crate::UncatchableError;
|
||||
|
||||
let signature = signature_tracker
|
||||
.gen_signature(salt, keypair)
|
||||
.map_err(UncatchableError::SigningError)?;
|
||||
let public_key = keypair.public().into();
|
||||
signature_store.put(public_key, signature);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "gen_signatures"))]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn sign_produced_cids(
|
||||
_signature_tracker: &mut PeerCidTracker,
|
||||
_signature_store: &mut SignatureStore,
|
||||
_salt: &str,
|
||||
_keypair: &fluence_keypair::KeyPair,
|
||||
) -> Result<(), ExecutionError> {
|
||||
Ok(())
|
||||
}
|
51
air/src/verification_step.rs
Normal file
51
air/src/verification_step.rs
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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_interpreter_data::InterpreterData;
|
||||
use air_interpreter_signatures::SignatureStore;
|
||||
|
||||
// TODO replace with VerificationError
|
||||
use crate::PreparationError;
|
||||
|
||||
#[cfg(feature = "check_signatures")]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn verify(
|
||||
prev_data: &InterpreterData,
|
||||
current_data: &InterpreterData,
|
||||
salt: &str,
|
||||
) -> Result<SignatureStore, PreparationError> {
|
||||
use air_interpreter_data::verification;
|
||||
|
||||
current_data.cid_info.verify()?;
|
||||
|
||||
let prev_data_verifier = verification::DataVerifier::new(prev_data, salt)?;
|
||||
let current_data_verifier = verification::DataVerifier::new(current_data, salt)?;
|
||||
// prev_data is always correct, check only current_data
|
||||
current_data_verifier.verify()?;
|
||||
|
||||
let signature_store = prev_data_verifier.merge(current_data_verifier)?;
|
||||
Ok(signature_store)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "check_signatures"))]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn verify(
|
||||
_prev_data: &InterpreterData,
|
||||
_current_data: &InterpreterData,
|
||||
_salt: &str,
|
||||
) -> Result<SignatureStore, PreparationError> {
|
||||
Ok(<_>::default())
|
||||
}
|
@ -19,34 +19,37 @@ use air::UncatchableError::ValueForCidNotFound;
|
||||
use air_interpreter_cid::CID;
|
||||
use air_interpreter_data::{CidStore, CidTracker};
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::key_utils::at;
|
||||
use air_test_utils::prelude::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_canon_ok() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let init_peer_name = "init_peer_id";
|
||||
|
||||
let script = format!(
|
||||
r#"(seq
|
||||
(seq
|
||||
(ap 42 $stream)
|
||||
(call "{init_peer_id}" ("serv" "func") [] $stream)) ; ok = "to canon"
|
||||
(canon "{init_peer_id}" $stream #canon)
|
||||
(call "{init_peer_name}" ("serv" "func") [] $stream)) ; ok = "to canon"
|
||||
(canon "{init_peer_name}" $stream #canon)
|
||||
)"#
|
||||
);
|
||||
|
||||
let executor =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &script).unwrap();
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &script).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
let data = data_from_result(&result);
|
||||
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
|
||||
let init_peer_id = at(init_peer_name);
|
||||
|
||||
let stream_exec_state = stream_tracked!(
|
||||
"to canon",
|
||||
1,
|
||||
cid_state,
|
||||
peer = init_peer_id,
|
||||
peer_name = init_peer_name,
|
||||
service = "serv..0",
|
||||
function = "func"
|
||||
);
|
||||
@ -98,33 +101,36 @@ fn test_canon_ok() {
|
||||
|
||||
#[test]
|
||||
fn test_canon_ok_multi() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let other_peer_id = "other_peer_id";
|
||||
let init_peer_name = "init_peer_id";
|
||||
let other_peer_name = "other_peer_id";
|
||||
|
||||
let script = format!(
|
||||
r#"(seq
|
||||
(seq
|
||||
(call "{init_peer_id}" ("serv" "func") [] $stream) ; ok = "to canon"
|
||||
(call "{other_peer_id}" ("other_serv" "other_func") [] $stream) ; ok = "other"
|
||||
(call "{init_peer_name}" ("serv" "func") [] $stream) ; ok = "to canon"
|
||||
(call "{other_peer_name}" ("other_serv" "other_func") [] $stream) ; ok = "other"
|
||||
)
|
||||
(canon "{init_peer_id}" $stream #canon)
|
||||
(canon "{init_peer_name}" $stream #canon)
|
||||
)"#
|
||||
);
|
||||
|
||||
let executor =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &script).unwrap();
|
||||
let _result1 = executor.execute_one(init_peer_id).unwrap();
|
||||
let _result2 = executor.execute_one(other_peer_id).unwrap();
|
||||
let result3 = executor.execute_one(init_peer_id).unwrap();
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &script).unwrap();
|
||||
let _result1 = executor.execute_one(init_peer_name).unwrap();
|
||||
let _result2 = executor.execute_one(other_peer_name).unwrap();
|
||||
let result3 = executor.execute_one(init_peer_name).unwrap();
|
||||
let data = data_from_result(&result3);
|
||||
|
||||
let init_peer_id = at(init_peer_name);
|
||||
let other_peer_id = at(other_peer_name);
|
||||
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
|
||||
let stream_state_1 = stream_tracked!(
|
||||
"to canon",
|
||||
0,
|
||||
cid_state,
|
||||
peer = init_peer_id,
|
||||
peer_name = init_peer_name,
|
||||
service = "serv..0",
|
||||
function = "func"
|
||||
);
|
||||
@ -134,7 +140,7 @@ fn test_canon_ok_multi() {
|
||||
"other",
|
||||
1,
|
||||
cid_state,
|
||||
peer = other_peer_id,
|
||||
peer_name = other_peer_name,
|
||||
service = "other_serv..1",
|
||||
function = "other_func"
|
||||
);
|
||||
@ -310,7 +316,7 @@ fn test_canon_tetraplet_not_found() {
|
||||
"function_name": "func",
|
||||
"json_path": "",
|
||||
"peer_pk": "peer_1",
|
||||
"service_id": "serv",
|
||||
"service_id": "serv..0",
|
||||
},
|
||||
}]
|
||||
}),
|
||||
@ -318,7 +324,7 @@ fn test_canon_tetraplet_not_found() {
|
||||
),
|
||||
];
|
||||
|
||||
let missing_cid = "bagaaieracu6twiik6az3cosyzlplrscon3ek6rnu3lkjnflibphqkw6kcdiq";
|
||||
let missing_cid = "bagaaierawgvzxeomczgjfgaf7jhbap27kqihlzm4i4na42uoi36lgzfrzwdq";
|
||||
let tetraplet_store: CidStore<_> = cid_state.tetraplet_tracker.into();
|
||||
assert!(tetraplet_store.get(&CID::<_>::new(missing_cid)).is_some());
|
||||
|
||||
@ -332,7 +338,7 @@ fn test_canon_tetraplet_not_found() {
|
||||
let result = call_vm!(vm, <_>::default(), air_script, vec![], cur_data);
|
||||
|
||||
let expected_error = ValueForCidNotFound("tetraplet", String::from(missing_cid));
|
||||
assert!(check_error(&result, expected_error));
|
||||
assert!(check_error(&result, expected_error), "{}", result.error_message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -69,34 +69,36 @@ fn test_correct_cid() {
|
||||
|
||||
#[test]
|
||||
fn test_scalar_cid() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let vm_peer_name = "vm_peer_id";
|
||||
|
||||
let annotated_air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{vm_peer_id}" ("service" "call1") [] x) ; ok="hi"
|
||||
(call "{vm_peer_id}" ("service" "call2") [] y) ; ok="ipld"
|
||||
(call "{vm_peer_name}" ("service" "call1") [] x) ; ok="hi"
|
||||
(call "{vm_peer_name}" ("service" "call2") [] y) ; ok="ipld"
|
||||
)"#
|
||||
);
|
||||
let executor =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_peer_id), &annotated_air_script)
|
||||
.unwrap();
|
||||
let executor = AirScriptExecutor::from_annotated(
|
||||
TestRunParameters::from_init_peer_id(vm_peer_name),
|
||||
&annotated_air_script,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = executor.execute_one(vm_peer_id).unwrap();
|
||||
let result = executor.execute_one(vm_peer_name).unwrap();
|
||||
let data = data_from_result(&result);
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
let expected_trace = vec![
|
||||
scalar_tracked!(
|
||||
"hi",
|
||||
cid_state,
|
||||
peer = vm_peer_id,
|
||||
peer_name = vm_peer_name,
|
||||
service = "service..0",
|
||||
function = "call1"
|
||||
),
|
||||
scalar_tracked!(
|
||||
"ipld",
|
||||
cid_state,
|
||||
peer = vm_peer_id,
|
||||
peer_name = vm_peer_name,
|
||||
service = "service..1",
|
||||
function = "call2"
|
||||
),
|
||||
@ -114,20 +116,22 @@ fn test_scalar_cid() {
|
||||
|
||||
#[test]
|
||||
fn test_stream_cid() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let vm_peer_name = "vm_peer_id";
|
||||
|
||||
let annotated_air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{vm_peer_id}" ("service" "call1") [] $x) ; ok="hi"
|
||||
(call "{vm_peer_id}" ("service" "call2") [] $x) ; ok="ipld"
|
||||
(call "{vm_peer_name}" ("service" "call1") [] $x) ; ok="hi"
|
||||
(call "{vm_peer_name}" ("service" "call2") [] $x) ; ok="ipld"
|
||||
)"#
|
||||
);
|
||||
let executor =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_peer_id), &annotated_air_script)
|
||||
.unwrap();
|
||||
let executor = AirScriptExecutor::from_annotated(
|
||||
TestRunParameters::from_init_peer_id(vm_peer_name),
|
||||
&annotated_air_script,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = executor.execute_one(vm_peer_id).unwrap();
|
||||
let result = executor.execute_one(vm_peer_name).unwrap();
|
||||
let data = data_from_result(&result);
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
let expected_trace = vec![
|
||||
@ -135,7 +139,7 @@ fn test_stream_cid() {
|
||||
"hi",
|
||||
0,
|
||||
cid_state,
|
||||
peer = vm_peer_id,
|
||||
peer_name = vm_peer_name,
|
||||
service = "service..0",
|
||||
function = "call1"
|
||||
),
|
||||
@ -143,7 +147,7 @@ fn test_stream_cid() {
|
||||
"ipld",
|
||||
1,
|
||||
cid_state,
|
||||
peer = vm_peer_id,
|
||||
peer_name = vm_peer_name,
|
||||
service = "service..1",
|
||||
function = "call2"
|
||||
),
|
||||
|
@ -54,7 +54,7 @@ fn fail_with_rebubble_error() {
|
||||
scalar_tracked!(
|
||||
expected_error_json.clone(),
|
||||
cid_tracker,
|
||||
peer = peer_id,
|
||||
peer_name = peer_id,
|
||||
service = "m..0",
|
||||
function = "f1",
|
||||
args = [expected_error_json]
|
||||
@ -62,7 +62,7 @@ fn fail_with_rebubble_error() {
|
||||
scalar_tracked!(
|
||||
no_error_object(),
|
||||
cid_tracker,
|
||||
peer = peer_id,
|
||||
peer_name = peer_id,
|
||||
service = "m..1",
|
||||
function = "f2",
|
||||
args = [no_error_object()]
|
||||
@ -120,7 +120,7 @@ fn rebubble_error_from_xor_right_branch() {
|
||||
scalar_tracked!(
|
||||
inner_expected_error_json.clone(),
|
||||
cid_tracker,
|
||||
peer = peer_id,
|
||||
peer_name = peer_id,
|
||||
service = "m..0",
|
||||
function = "f1",
|
||||
args = [inner_expected_error_json]
|
||||
@ -128,7 +128,7 @@ fn rebubble_error_from_xor_right_branch() {
|
||||
scalar_tracked!(
|
||||
outer_expected_error_json.clone(),
|
||||
cid_tracker,
|
||||
peer = peer_id,
|
||||
peer_name = peer_id,
|
||||
service = "m..1",
|
||||
function = "f2",
|
||||
args = [outer_expected_error_json]
|
||||
@ -136,12 +136,14 @@ fn rebubble_error_from_xor_right_branch() {
|
||||
scalar_tracked!(
|
||||
no_error_object(),
|
||||
cid_tracker,
|
||||
peer = peer_id,
|
||||
peer_name = peer_id,
|
||||
service = "m..2",
|
||||
function = "f3",
|
||||
args = [no_error_object()]
|
||||
),
|
||||
];
|
||||
|
||||
assert_eq!(actual_trace, expected_trace,);
|
||||
print_trace(&result.last().unwrap(), "");
|
||||
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
@ -517,7 +517,7 @@ fn undefined_last_error_errcode() {
|
||||
let expected_trace = ExecutionTrace::from(vec![scalar_tracked!(
|
||||
errcode_lambda_output.clone(),
|
||||
cid_state,
|
||||
peer = local_peer_id,
|
||||
peer_name = local_peer_id,
|
||||
service = "test..0",
|
||||
function = "error_code",
|
||||
args = vec![errcode_lambda_output]
|
||||
@ -545,7 +545,7 @@ fn undefined_last_error_msg_errcode() {
|
||||
let expected_trace = ExecutionTrace::from(vec![scalar_tracked!(
|
||||
message_lambda_output.clone(),
|
||||
cid_state,
|
||||
peer = local_peer_id,
|
||||
peer_name = local_peer_id,
|
||||
service = "test..0",
|
||||
function = "message",
|
||||
args = vec![message_lambda_output]
|
||||
|
@ -30,16 +30,16 @@ fn length_functor_for_array_scalar() {
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), script)
|
||||
let init_peer_name = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let expected_trace = vec![
|
||||
scalar!(json!([1, 1, 1]), peer = init_peer_id, service = "..0"),
|
||||
unused!(3, peer = init_peer_id, args = vec![3], service = "..1"),
|
||||
scalar!(json!([1, 1, 1]), peer_name = init_peer_name, service = "..0"),
|
||||
unused!(3, peer_name = init_peer_name, args = vec![3], service = "..1"),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
@ -56,11 +56,11 @@ fn length_functor_for_non_array_scalar() {
|
||||
"#
|
||||
);
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &script)
|
||||
let init_peer_name = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
check_error(
|
||||
&result,
|
||||
CatchableError::LengthFunctorAppliedToNotArray(json!(result_jvalue)),
|
||||
@ -81,27 +81,29 @@ fn length_functor_for_stream() {
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), script)
|
||||
let init_peer_name = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let init_peer_id = executor.resolve_name(init_peer_name).to_string();
|
||||
|
||||
let expected_trace = vec![
|
||||
executed_state::ap(0),
|
||||
executed_state::ap(0),
|
||||
executed_state::canon(json!({
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "init_peer_id", "service_id": ""},
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": init_peer_id, "service_id": ""},
|
||||
"values": [
|
||||
{
|
||||
"result": 1,
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "init_peer_id", "service_id": ""},
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": init_peer_id, "service_id": ""},
|
||||
"trace_pos": 0,
|
||||
},
|
||||
{
|
||||
"result": 1,
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "init_peer_id", "service_id": ""},
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": init_peer_id, "service_id": ""},
|
||||
"trace_pos": 1,
|
||||
},
|
||||
]
|
||||
@ -122,16 +124,18 @@ fn length_functor_for_empty_stream() {
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), script)
|
||||
let init_peer_name = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let init_peer_id = executor.resolve_name(init_peer_name).to_string();
|
||||
|
||||
let expected_trace = vec![
|
||||
executed_state::canon(
|
||||
json!({"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "init_peer_id", "service_id": ""},
|
||||
json!({"tetraplet": {"function_name": "", "json_path": "", "peer_pk": init_peer_id, "service_id": ""},
|
||||
"values": []} ),
|
||||
),
|
||||
unused!(0, peer = init_peer_id, service = "..0", args = vec![0]),
|
||||
@ -153,20 +157,22 @@ fn length_functor_for_canon_stream() {
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), script)
|
||||
let init_peer_name = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let init_peer_id = executor.resolve_name(init_peer_name).to_string();
|
||||
|
||||
let expected_trace = vec![
|
||||
executed_state::ap(0),
|
||||
executed_state::ap(0),
|
||||
executed_state::canon(
|
||||
json!({"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "init_peer_id", "service_id": ""},
|
||||
"values": [{"result": 1, "tetraplet": {"function_name": "", "json_path": "", "peer_pk": "init_peer_id", "service_id": ""}, "trace_pos": 0},
|
||||
{"result": 1, "tetraplet": {"function_name": "", "json_path": "", "peer_pk": "init_peer_id", "service_id": ""}, "trace_pos": 1}
|
||||
json!({"tetraplet": {"function_name": "", "json_path": "", "peer_pk": init_peer_id, "service_id": ""},
|
||||
"values": [{"result": 1, "tetraplet": {"function_name": "", "json_path": "", "peer_pk": init_peer_id, "service_id": ""}, "trace_pos": 0},
|
||||
{"result": 1, "tetraplet": {"function_name": "", "json_path": "", "peer_pk": init_peer_id, "service_id": ""}, "trace_pos": 1}
|
||||
]} ),
|
||||
),
|
||||
unused!(2, peer = init_peer_id, service = "..0", args = vec![2]),
|
||||
@ -185,16 +191,18 @@ fn length_functor_for_empty_canon_stream() {
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), script)
|
||||
let init_peer_name = "init_peer_id";
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let init_peer_id = executor.resolve_name(init_peer_name).to_string();
|
||||
|
||||
let expected_trace = vec![
|
||||
executed_state::canon(
|
||||
json!({"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "init_peer_id", "service_id": ""}, "values": []} ),
|
||||
json!({"tetraplet": {"function_name": "", "json_path": "", "peer_pk": init_peer_id, "service_id": ""}, "values": []} ),
|
||||
),
|
||||
unused!(0, peer = init_peer_id, service = "..0", args = vec![0]),
|
||||
];
|
||||
|
@ -14,441 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air_interpreter_signatures::SignatureTracker;
|
||||
use air_test_framework::ephemeral::PeerId;
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::key_utils::derive_dummy_keypair;
|
||||
use air_test_utils::prelude::*;
|
||||
use air_test_utils::test_runner::TestRunParameters;
|
||||
#[cfg(feature = "check_signatures")]
|
||||
mod attacks;
|
||||
#[cfg(feature = "check_signatures")]
|
||||
mod corruption;
|
||||
|
||||
#[test]
|
||||
fn test_signature_empty() {
|
||||
let script = "(null)";
|
||||
let init_peer_id = "init_peer_id";
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
mod runtime;
|
||||
|
||||
let exec = <AirScriptExecutor>::new(
|
||||
TestRunParameters::from_init_peer_id(init_peer_id),
|
||||
vec![],
|
||||
vec![PeerId::from(init_peer_id)].into_iter(),
|
||||
script,
|
||||
)
|
||||
.unwrap();
|
||||
let res = exec.execute_one(init_peer_id).unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
|
||||
let expected_signature: air_interpreter_signatures::Signature = keypair.sign(b"[]").unwrap().into();
|
||||
|
||||
let data = data_from_result(&res);
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_var() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(call "{init_peer_id}" ("" "") [] var) ; ok = "ok"
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
|
||||
|
||||
let res = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
let data = data_from_result(&res);
|
||||
|
||||
let expected_call_state = scalar!("ok", peer = init_peer_id, service = "..0");
|
||||
let expected_cid = extract_service_result_cid(&expected_call_state);
|
||||
|
||||
let mut expected_tracker = SignatureTracker::new();
|
||||
expected_tracker.register(init_peer_id, &expected_cid);
|
||||
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_stream() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(call "{init_peer_id}" ("" "") [] $var) ; ok = "ok"
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
|
||||
|
||||
let res = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
let data = data_from_result(&res);
|
||||
|
||||
let expected_call_state = stream!("ok", 0, peer = init_peer_id, service = "..0");
|
||||
let expected_cid = extract_service_result_cid(&expected_call_state);
|
||||
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
|
||||
let mut expected_tracker = SignatureTracker::new();
|
||||
expected_tracker.register(init_peer_id, &expected_cid);
|
||||
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_ununsed() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(call "{init_peer_id}" ("" "") []) ; ok = "ok"
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
|
||||
|
||||
let res = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
let data = data_from_result(&res);
|
||||
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
|
||||
let mut expected_tracker = SignatureTracker::new();
|
||||
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_merged() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let other_peer_id = "other_peer_id";
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{init_peer_id}" ("" "") [] x) ; ok = "res0"
|
||||
(seq
|
||||
(call "{other_peer_id}" ("" "") [] y) ; ok = "res1"
|
||||
(call "{init_peer_id}" ("" "") [] z) ; ok = "res2"
|
||||
))
|
||||
"#
|
||||
);
|
||||
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
|
||||
let _ = exec.execute_one(init_peer_id).unwrap();
|
||||
let _ = exec.execute_one(other_peer_id).unwrap();
|
||||
let res2 = exec.execute_one(init_peer_id).unwrap();
|
||||
let data2 = data_from_result(&res2);
|
||||
|
||||
let expected_call_state0 = scalar!("res0", peer = init_peer_id, service = "..0");
|
||||
let expected_cid0 = extract_service_result_cid(&expected_call_state0);
|
||||
let expected_call_state2 = scalar!("res2", peer = init_peer_id, service = "..2");
|
||||
let expected_cid2 = extract_service_result_cid(&expected_call_state2);
|
||||
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
|
||||
let mut expected_tracker = SignatureTracker::new();
|
||||
expected_tracker.register(init_peer_id, &expected_cid0);
|
||||
expected_tracker.register(init_peer_id, &expected_cid2);
|
||||
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
let signature = data2.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data2.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_double() {
|
||||
// Test that if some CID appears twice in the call result, it is accounted twice.
|
||||
let init_peer_id = "init_peer_id";
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq (ap 1 $s) (ap 2 $s))
|
||||
(fold $s i
|
||||
(seq
|
||||
(call "{init_peer_id}" ("" "") [] var) ; ok = "ok"
|
||||
(next i))))
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
|
||||
|
||||
let res = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
let data = data_from_result(&res);
|
||||
|
||||
let expected_call_state = scalar!("ok", peer = init_peer_id, service = "..0");
|
||||
let expected_cid = extract_service_result_cid(&expected_call_state);
|
||||
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
|
||||
let mut unexpected_tracker = SignatureTracker::new();
|
||||
unexpected_tracker.register(init_peer_id, &expected_cid);
|
||||
let unexpected_signature = unexpected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
let mut expected_tracker = SignatureTracker::new();
|
||||
expected_tracker.register(init_peer_id, &expected_cid);
|
||||
expected_tracker.register(init_peer_id, &expected_cid);
|
||||
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
assert_ne!(expected_signature, unexpected_signature, "test is incorrect");
|
||||
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_canon_basic() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{init_peer_id}" ("serv" "func") [] items) ; ok = [1, 2, 3]
|
||||
(seq
|
||||
(fold items i
|
||||
(seq
|
||||
(ap i $stream)
|
||||
(next i)))
|
||||
(canon "{init_peer_id}" $stream #canon)))
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
|
||||
|
||||
let last_result = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
|
||||
let last_data = data_from_result(&last_result);
|
||||
|
||||
let expected_call_result = scalar!(
|
||||
json!([1, 2, 3]),
|
||||
peer = init_peer_id,
|
||||
service = "serv..0",
|
||||
function = "func"
|
||||
);
|
||||
let expected_call_result_cid = extract_service_result_cid(&expected_call_result);
|
||||
|
||||
let expected_canon_state = canon(json!({
|
||||
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"result": 1,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[0]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}, {
|
||||
"result": 2,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[1]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}, {
|
||||
"result": 3,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[2]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}]
|
||||
}));
|
||||
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
|
||||
|
||||
let mut expected_tracker = SignatureTracker::new();
|
||||
expected_tracker.register(init_peer_id, &expected_canon_cid);
|
||||
expected_tracker.register(init_peer_id.to_owned(), &expected_call_result_cid);
|
||||
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
let signature = last_data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_canon_merge() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let other_peer_id = "other_peer_id";
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{init_peer_id}" ("serv" "func") [] items) ; ok = [1, 2, 3]
|
||||
(seq
|
||||
(fold items i
|
||||
(seq
|
||||
(ap i $stream)
|
||||
(next i)))
|
||||
(canon "{init_peer_id}" $stream #canon)))
|
||||
(seq
|
||||
(call "{other_peer_id}" ("" "") []) ; ok = "ok"
|
||||
(call "{init_peer_id}" ("" "") []))) ; ok = "ok"
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
|
||||
|
||||
exec.execute_all(init_peer_id);
|
||||
exec.execute_one(other_peer_id);
|
||||
|
||||
let last_result = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
|
||||
let last_data = data_from_result(&last_result);
|
||||
|
||||
let expected_call_result = scalar!(
|
||||
json!([1, 2, 3]),
|
||||
peer = init_peer_id,
|
||||
service = "serv..0",
|
||||
function = "func"
|
||||
);
|
||||
let expected_call_result_cid = extract_service_result_cid(&expected_call_result);
|
||||
|
||||
let expected_canon_state = canon(json!({
|
||||
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"result": 1,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[0]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}, {
|
||||
"result": 2,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[1]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}, {
|
||||
"result": 3,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[2]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}]
|
||||
}));
|
||||
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
|
||||
|
||||
let mut expected_tracker = SignatureTracker::new();
|
||||
expected_tracker.register(init_peer_id, &expected_canon_cid);
|
||||
expected_tracker.register(init_peer_id, &expected_call_result_cid);
|
||||
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
let signature = last_data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_canon_result() {
|
||||
// this test checks that call result in canon doesn't lead to repeadted accounting of the call result
|
||||
let init_peer_id = "init_peer_id";
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{init_peer_id}" ("serv" "func") [] items) ; ok = [1, 2, 3]
|
||||
(fold items i
|
||||
(seq
|
||||
(ap i $stream)
|
||||
(next i))))
|
||||
(seq
|
||||
(call "{init_peer_id}" ("serv" "func2") [] $stream) ; ok = 42
|
||||
(canon "{init_peer_id}" $stream #canon)))
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
|
||||
|
||||
let last_result = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
|
||||
let last_data = data_from_result(&last_result);
|
||||
|
||||
let expected_call_result1 = scalar!(
|
||||
json!([1, 2, 3]),
|
||||
peer = init_peer_id,
|
||||
service = "serv..0",
|
||||
function = "func"
|
||||
);
|
||||
let expected_call_result_cid1 = extract_service_result_cid(&expected_call_result1);
|
||||
|
||||
let expected_call_result2 = stream!(
|
||||
json!(42),
|
||||
1,
|
||||
peer = init_peer_id,
|
||||
service = "serv..1",
|
||||
function = "func2"
|
||||
);
|
||||
let expected_call_result_cid2 = extract_service_result_cid(&expected_call_result2);
|
||||
|
||||
let expected_canon_state = canon(json!({
|
||||
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"result": 1,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[0]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
|
||||
}, {
|
||||
"result": 2,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[1]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
|
||||
}, {
|
||||
"result": 3,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[2]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
|
||||
}, {
|
||||
"result": 42,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..1",
|
||||
"function_name": "func2",
|
||||
"json_path": "",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid2.clone()),
|
||||
}]
|
||||
}));
|
||||
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
|
||||
|
||||
let mut expected_tracker = SignatureTracker::new();
|
||||
expected_tracker.register(init_peer_id, &expected_call_result_cid1);
|
||||
expected_tracker.register(init_peer_id, &expected_call_result_cid2);
|
||||
expected_tracker.register(init_peer_id, &expected_canon_cid);
|
||||
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
|
||||
|
||||
let signature = last_data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
|
||||
}
|
||||
#[cfg(feature = "gen_signatures")]
|
||||
mod signing;
|
||||
|
457
air/tests/test_module/features/signatures/attacks.rs
Normal file
457
air/tests/test_module/features/signatures/attacks.rs
Normal file
@ -0,0 +1,457 @@
|
||||
/*
|
||||
* 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::{ExecutionCidState, PreparationError};
|
||||
use air_interpreter_signatures::{PeerCidTracker, PublicKey, SignatureStore};
|
||||
use air_test_utils::key_utils::derive_dummy_keypair;
|
||||
use air_test_utils::prelude::*;
|
||||
use semver::Version;
|
||||
|
||||
/// This testing modules assert AquaVM resistance to various attacks.
|
||||
///
|
||||
/// CID store manipulations are checked in the `corruption` module.
|
||||
|
||||
#[test]
|
||||
fn test_attack_injection_current_peer_scalar() {
|
||||
// injecting a value that arrives to peer who does the next step
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair("alice_peer");
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair("mallory_peer");
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{alice_peer_id}" ("" "") [] z))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut alice_cid_state = ExecutionCidState::new();
|
||||
let mut alice_signature_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
let mut alice_signature_store = SignatureStore::new();
|
||||
|
||||
let alice_call_1 = scalar_tracked!("good result", &mut alice_cid_state, peer = &alice_peer_id);
|
||||
alice_signature_tracker.register(&*alice_peer_id, &extract_service_result_cid(&alice_call_1));
|
||||
let alice_trace = vec![alice_call_1.clone()];
|
||||
let alice_signature = alice_signature_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
alice_signature_store.put(alice_keypair.public().into(), alice_signature);
|
||||
|
||||
let mut mallory_cid_state = alice_cid_state.clone();
|
||||
let mut mallory_signature_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
let mut mallory_signature_store = alice_signature_store.clone();
|
||||
|
||||
let mallory_call_2 = scalar_tracked!("valid result", &mut mallory_cid_state, peer = &mallory_peer_id);
|
||||
let fake_call_3 = scalar_tracked!("fake result", &mut mallory_cid_state, peer = &alice_peer_id);
|
||||
mallory_signature_tracker.register(&*mallory_peer_id, &extract_service_result_cid(&mallory_call_2));
|
||||
let mallory_trace = vec![alice_call_1, mallory_call_2, fake_call_3];
|
||||
let mallory_signature = mallory_signature_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
mallory_signature_store.put(mallory_keypair.public().into(), mallory_signature);
|
||||
|
||||
let alice_data = InterpreterData::from_execution_result(
|
||||
alice_trace.into(),
|
||||
alice_cid_state.into(),
|
||||
alice_signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
mallory_cid_state.into(),
|
||||
mallory_signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair, unit_call_service());
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = serde_json::to_vec(&alice_data).unwrap();
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = alice_avm
|
||||
.call(&air_script, prev_data, cur_data, test_run_params)
|
||||
.unwrap();
|
||||
assert_ne!(res.ret_code, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_injection_current_peer_stream() {
|
||||
// injecting a value that arrives to peer who does the next step
|
||||
let alice_peer_id = "alice_peer";
|
||||
let mallory_peer_id = "mallory_peer";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{alice_peer_id}" ("" "") [] $z))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut alice_cid_state = ExecutionCidState::default();
|
||||
|
||||
let alice_call_1 = scalar_tracked!("good result", &mut alice_cid_state, peer = &alice_peer_id);
|
||||
|
||||
let mut alice_signature_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_signature_tracker.register(&*alice_peer_id, &extract_service_result_cid(&alice_call_1));
|
||||
let mut alice_signature_store = SignatureStore::new();
|
||||
let alice_signature = alice_signature_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
alice_signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let alice_trace = vec![alice_call_1.clone()];
|
||||
|
||||
let mut mallory_cid_state = alice_cid_state.clone();
|
||||
let mallory_call_2 = scalar_tracked!("valid result", &mut mallory_cid_state, peer = &mallory_peer_id);
|
||||
let fake_call_3 = stream_tracked!("fake result", 0, &mut mallory_cid_state, peer = &alice_peer_id);
|
||||
|
||||
let mut mallory_signature_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_signature_tracker.register(&*mallory_peer_id, &extract_service_result_cid(&mallory_call_2));
|
||||
let mut mallory_signature_store = SignatureStore::new();
|
||||
let mallory_signature = mallory_signature_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
mallory_signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_trace = vec![alice_call_1, mallory_call_2, fake_call_3];
|
||||
|
||||
let alice_data = InterpreterData::from_execution_result(
|
||||
alice_trace.into(),
|
||||
alice_cid_state.into(),
|
||||
alice_signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
mallory_cid_state.into(),
|
||||
mallory_signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair, unit_call_service());
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = serde_json::to_vec(&alice_data).unwrap();
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = alice_avm
|
||||
.call(&air_script, prev_data, cur_data, test_run_params)
|
||||
.unwrap();
|
||||
assert_ne!(res.ret_code, 0, "{}", res.error_message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_injection_current_injection_unused() {
|
||||
// injecting a value that arrives to peer who does the next step
|
||||
let alice_peer_id = "alice_peer";
|
||||
let mallory_peer_id = "mallory_peer";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{alice_peer_id}" ("" "") []))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut alice_cid_state = ExecutionCidState::default();
|
||||
let alice_call_1 = scalar_tracked!("good result", &mut alice_cid_state, peer = &alice_peer_id);
|
||||
let alice_trace = vec![alice_call_1.clone()];
|
||||
|
||||
let mut mallory_cid_state = alice_cid_state.clone();
|
||||
let mallory_call_2 = scalar_tracked!("valid result", &mut mallory_cid_state, peer = &mallory_peer_id);
|
||||
let fake_call_3 = unused!("fake result", peer = &alice_peer_id);
|
||||
let mallory_trace = vec![alice_call_1, mallory_call_2, fake_call_3];
|
||||
|
||||
let mut alice_signature_store = SignatureStore::new();
|
||||
|
||||
let mut alice_cid_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_cid_tracker.register(&alice_peer_id, &extract_service_result_cid(&mallory_trace[0]));
|
||||
let alice_signature = alice_cid_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
alice_signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mallory_signature_store = alice_signature_store.clone();
|
||||
let mut mallory_cid_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_cid_tracker.register(&mallory_peer_id, &extract_service_result_cid(&mallory_trace[1]));
|
||||
let mallory_signature = mallory_cid_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
alice_signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let alice_data = InterpreterData::from_execution_result(
|
||||
alice_trace.into(),
|
||||
alice_cid_state.into(),
|
||||
alice_signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
mallory_cid_state.into(),
|
||||
mallory_signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair, unit_call_service());
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = serde_json::to_vec(&alice_data).unwrap();
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = alice_avm
|
||||
.call(&air_script, prev_data, cur_data, test_run_params)
|
||||
.unwrap();
|
||||
|
||||
assert_ne!(res.ret_code, 0, "{}", res.error_message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_injection_other_peer_scalar() {
|
||||
// injecting a value that arrives to peer who does the next step
|
||||
let alice_peer_id = "alice_peer";
|
||||
let bob_peer_id = "bob_peer";
|
||||
let mallory_peer_id = "mallory_peer";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (bob_keypair, bob_peer_id) = derive_dummy_keypair(bob_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{bob_peer_id}" ("" "") [] z))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::default();
|
||||
|
||||
let alice_call_1 = scalar_tracked!("good result", &mut mallory_cid_state, peer = &alice_peer_id);
|
||||
let mallory_call_2 = scalar_tracked!("valid result", &mut mallory_cid_state, peer = &mallory_peer_id);
|
||||
let fake_call_3 = scalar_tracked!("fake result", &mut mallory_cid_state, peer = &alice_peer_id);
|
||||
|
||||
let mallory_trace = vec![alice_call_1, mallory_call_2, fake_call_3];
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
|
||||
let mut alice_cid_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_cid_tracker.register(&alice_peer_id, &extract_service_result_cid(&mallory_trace[0]));
|
||||
let alice_signature = alice_cid_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_cid_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_cid_tracker.register(&mallory_peer_id, &extract_service_result_cid(&mallory_trace[1]));
|
||||
let mallory_signature = mallory_cid_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
mallory_cid_state.into(),
|
||||
signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair, unit_call_service());
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
assert_ne!(res.ret_code, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_injection_other_peer_stream() {
|
||||
// injecting a value that arrives to peer who does the next step
|
||||
let alice_peer_id = "alice_peer";
|
||||
let bob_peer_id = "bob_peer";
|
||||
let mallory_peer_id = "mallory_peer";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (bob_keypair, bob_peer_id) = derive_dummy_keypair(bob_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{bob_peer_id}" ("" "") [] $z))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::default();
|
||||
|
||||
let alice_call_1 = scalar_tracked!("good result", &mut mallory_cid_state, peer = &alice_peer_id);
|
||||
let mallory_call_2 = scalar_tracked!("valid result", &mut mallory_cid_state, peer = &mallory_peer_id);
|
||||
let fake_call_3 = stream_tracked!("fake result", 0, &mut mallory_cid_state, peer = &alice_peer_id);
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
let mut alice_signature_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_signature_tracker.register(&*alice_peer_id, &extract_service_result_cid(&alice_call_1));
|
||||
let alice_signature = alice_signature_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_signature_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_signature_tracker.register(&*mallory_peer_id, &extract_service_result_cid(&mallory_call_2));
|
||||
let mallory_signature = mallory_signature_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_trace = vec![alice_call_1, mallory_call_2, fake_call_3];
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
mallory_cid_state.into(),
|
||||
signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair, unit_call_service());
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
assert_ne!(res.ret_code, 0, "{}", res.error_message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_injection_other_peer_unused() {
|
||||
// injecting a value that arrives to peer who does the next step
|
||||
let alice_peer_id = "alice_peer";
|
||||
let bob_peer_id = "bob_peer";
|
||||
let mallory_peer_id = "mallory_peer";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (bob_keypair, bob_peer_id) = derive_dummy_keypair(bob_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{bob_peer_id}" ("" "") []))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::default();
|
||||
|
||||
let alice_call_1 = scalar_tracked!("good result", &mut mallory_cid_state, peer = &alice_peer_id);
|
||||
let mallory_call_2 = scalar_tracked!("valid result", &mut mallory_cid_state, peer = &mallory_peer_id);
|
||||
let fake_call_3 = unused!("fake result", peer = &alice_peer_id);
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
let mut alice_signature_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_signature_tracker.register(&*alice_peer_id, &extract_service_result_cid(&alice_call_1));
|
||||
let alice_signature = alice_signature_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_signature_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_signature_tracker.register(&*mallory_peer_id, &extract_service_result_cid(&mallory_call_2));
|
||||
let mallory_signature = mallory_signature_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_trace = vec![alice_call_1, mallory_call_2, fake_call_3];
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
mallory_cid_state.into(),
|
||||
signature_store,
|
||||
2,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair, unit_call_service());
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
|
||||
// please not that such injection is not caught
|
||||
assert_eq!(res.ret_code, 0, "{}", res.error_message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_replay() {
|
||||
let alice_name = "alice_peer_id";
|
||||
let bob_name = "bob_peer_id";
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_name);
|
||||
let (bob_keypair, _) = derive_dummy_keypair(bob_name);
|
||||
|
||||
let air_script = format!(
|
||||
r#"(seq
|
||||
(call "{alice_peer_id}" ("" "") [] y)
|
||||
(call "bob" ("" "") [] z))"#
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair.clone(), unit_call_service());
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair.clone(), unit_call_service());
|
||||
|
||||
let run_params1 = TestRunParameters::from_init_peer_id(&alice_peer_id).with_particle_id("first_particle");
|
||||
let run_params2 = run_params1.clone();
|
||||
|
||||
let res1 = alice_avm.call(&air_script, "", "", run_params1.clone()).unwrap();
|
||||
let res2 = alice_avm.call(&air_script, "", "", run_params2).unwrap();
|
||||
|
||||
assert_eq!(res1.ret_code, 0, "test validity check failed: {}", res1.error_message);
|
||||
assert_eq!(res1, res2, "test validity check failed");
|
||||
|
||||
let res_bob = bob_avm.call(&air_script, "", res1.data.clone(), run_params1).unwrap();
|
||||
assert_eq!(
|
||||
res_bob.ret_code, 0,
|
||||
"test validity check failed: {}",
|
||||
res_bob.error_message
|
||||
);
|
||||
|
||||
let mallory_run_params = TestRunParameters::from_init_peer_id(&alice_peer_id).with_particle_id("second_particle");
|
||||
|
||||
let res_replay = bob_avm.call(&air_script, "", res1.data, mallory_run_params).unwrap();
|
||||
|
||||
let dalek_error = ed25519_dalek::ed25519::Error::from_source("Verification equation was not satisfied");
|
||||
let nested_error = fluence_keypair::error::VerificationError::Ed25519(
|
||||
dalek_error,
|
||||
// will break if signed data format changes
|
||||
"3eSuF5uvjQvmvSC6vu5Kmb8bJcswXhNUcqsSG9USEad1oNgnpAcBNm2maM4Tyk3BsLYnwdwNEj4KiJ4pqence7XF".to_owned(),
|
||||
"6m3zmtymxDL56KBpNgKqc7QiGRuWuxr82bG2q7dF5xCD".to_owned(),
|
||||
);
|
||||
let cids: Vec<Box<str>> = vec!["bagaaieraazcwm4lxybe4pwlisvcgpv4mii63nxouogvf4ihkmz762mnhea7a".into()];
|
||||
let expected = PreparationError::DataSignatureCheckError(verification::DataVerifierError::SignatureMismatch {
|
||||
error: nested_error.into(),
|
||||
cids,
|
||||
peer_id: alice_peer_id,
|
||||
});
|
||||
assert_error_eq!(&res_replay, expected);
|
||||
}
|
536
air/tests/test_module/features/signatures/corruption.rs
Normal file
536
air/tests/test_module/features/signatures/corruption.rs
Normal file
@ -0,0 +1,536 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/// This test module asserts CID store verification functionality:
|
||||
/// values forged in the CID stores.
|
||||
use air::ExecutionCidState;
|
||||
use air::PreparationError;
|
||||
use air_interpreter_signatures::PeerCidTracker;
|
||||
use air_interpreter_signatures::PublicKey;
|
||||
use air_interpreter_signatures::SignatureStore;
|
||||
use air_test_utils::key_utils::derive_dummy_keypair;
|
||||
use air_test_utils::prelude::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use semver::Version;
|
||||
|
||||
#[test]
|
||||
fn test_attack_replace_value() {
|
||||
// Bob gets a trace where call result value is edited by Mallory.
|
||||
let alice_peer_id = "alice";
|
||||
let bob_peer_id = "bob";
|
||||
let mallory_peer_id = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{bob_peer_id}" ("" "") [] z))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::new();
|
||||
let mallory_trace = vec![
|
||||
scalar_tracked!("alice", &mut mallory_cid_state, peer = &alice_peer_id),
|
||||
scalar_tracked!("mallory", &mut mallory_cid_state, peer = &mallory_peer_id),
|
||||
];
|
||||
|
||||
let mut mallory_cid_info = serde_json::to_value::<CidInfo>(mallory_cid_state.into()).unwrap();
|
||||
let mut cnt = 0;
|
||||
for (_cid, val) in mallory_cid_info["value_store"].as_object_mut().unwrap().iter_mut() {
|
||||
if *val == "alice" {
|
||||
*val = "evil".into();
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
assert_eq!(cnt, 1, "test validity failed");
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
|
||||
let mut alice_cid_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_cid_tracker.register(&alice_peer_id, &extract_service_result_cid(&mallory_trace[0]));
|
||||
let alice_signature = alice_cid_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_cid_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_cid_tracker.register(&mallory_peer_id, &extract_service_result_cid(&mallory_trace[1]));
|
||||
let mallory_signature = mallory_cid_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
serde_json::from_value(mallory_cid_info).unwrap(),
|
||||
signature_store,
|
||||
0,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm(unit_call_service(), bob_peer_id);
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
|
||||
assert_error_eq!(
|
||||
&res,
|
||||
PreparationError::CidStoreVerificationError(CidStoreVerificationError::MismatchError {
|
||||
type_name: "serde_json::value::Value",
|
||||
cid_repr: "bagaaierabjifaczkgq2745dsq57lelki2r5cfduunmfzsgvxiavi2ahwwmwq".to_owned(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_replace_tetraplet() {
|
||||
// Bob gets a trace where call result tetraplet is edited by Mallory.
|
||||
let alice_peer_id = "alice";
|
||||
let bob_peer_id = "bob";
|
||||
let mallory_peer_id = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{bob_peer_id}" ("" "") [] z))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::new();
|
||||
let mallory_trace = vec![
|
||||
scalar_tracked!("alice", &mut mallory_cid_state, peer = &alice_peer_id),
|
||||
scalar_tracked!("mallory", &mut mallory_cid_state, peer = &mallory_peer_id),
|
||||
];
|
||||
|
||||
let mut mallory_cid_info = serde_json::to_value::<CidInfo>(mallory_cid_state.into()).unwrap();
|
||||
let mut cnt = 0;
|
||||
for (_cid, tetraplet_val) in mallory_cid_info["tetraplet_store"].as_object_mut().unwrap().iter_mut() {
|
||||
if tetraplet_val["peer_pk"] == alice_peer_id {
|
||||
tetraplet_val["service_id"] = json!("evil");
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
assert_eq!(cnt, 1, "test validity failed");
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
|
||||
let mut alice_cid_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_cid_tracker.register(&alice_peer_id, &extract_service_result_cid(&mallory_trace[0]));
|
||||
let alice_signature = alice_cid_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_cid_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_cid_tracker.register(&mallory_peer_id, &extract_service_result_cid(&mallory_trace[1]));
|
||||
let mallory_signature = mallory_cid_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
serde_json::from_value(mallory_cid_info).unwrap(),
|
||||
signature_store,
|
||||
0,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm(unit_call_service(), bob_peer_id);
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
|
||||
assert_error_eq!(
|
||||
&res,
|
||||
PreparationError::CidStoreVerificationError(CidStoreVerificationError::MismatchError {
|
||||
type_name: "marine_call_parameters::SecurityTetraplet",
|
||||
cid_repr: "bagaaierapisclqfeq36psuo6uxiazvcash32pndayqlwxrqchii2ykxerfba".to_owned(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_replace_call_result() {
|
||||
// Bob gets a trace where call result is edited by Mallory.
|
||||
let alice_peer_id = "alice";
|
||||
let bob_peer_id = "bob";
|
||||
let mallory_peer_id = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("" "") [] x)
|
||||
(call "{mallory_peer_id}" ("" "") [] y))
|
||||
(call "{bob_peer_id}" ("" "") [] z))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::new();
|
||||
let alice_trace_1 = scalar_tracked!("alice", &mut mallory_cid_state, peer = &alice_peer_id);
|
||||
let alice_trace_1_cid = (*extract_service_result_cid(&alice_trace_1)).clone().into_inner();
|
||||
|
||||
let mallory_trace = vec![
|
||||
alice_trace_1,
|
||||
scalar_tracked!("mallory", &mut mallory_cid_state, peer = &mallory_peer_id),
|
||||
];
|
||||
|
||||
let mut mallory_cid_info = serde_json::to_value::<CidInfo>(mallory_cid_state.into()).unwrap();
|
||||
let mut cnt = 0;
|
||||
for (cid, service_cid_val) in mallory_cid_info["service_result_store"]
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.iter_mut()
|
||||
{
|
||||
if *cid == alice_trace_1_cid {
|
||||
service_cid_val["argument_hash"] = "42".into();
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
assert_eq!(cnt, 1, "test validity failed");
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
|
||||
let mut alice_cid_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_cid_tracker.register(&alice_peer_id, &extract_service_result_cid(&mallory_trace[0]));
|
||||
let alice_signature = alice_cid_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_cid_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_cid_tracker.register(&mallory_peer_id, &extract_service_result_cid(&mallory_trace[1]));
|
||||
let mallory_signature = mallory_cid_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
serde_json::from_value(mallory_cid_info).unwrap(),
|
||||
signature_store,
|
||||
0,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm(unit_call_service(), bob_peer_id);
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
|
||||
assert_error_eq!(
|
||||
&res,
|
||||
PreparationError::CidStoreVerificationError(CidStoreVerificationError::MismatchError {
|
||||
type_name: "air_interpreter_data::executed_state::ServiceResultCidAggregate",
|
||||
cid_repr: "bagaaierarbji6ebokx3pantdp6xg2l57bhdj7pmlydwe2wnbd6fdkatg7xka".to_owned(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_replace_canon_value() {
|
||||
// Bob gets a trace where canon value is edited by Mallory.
|
||||
let alice_peer_id = "alice";
|
||||
let bob_peer_id = "bob";
|
||||
let mallory_peer_id = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(ap 1 $s)
|
||||
(canon "{alice_peer_id}" $s #c))
|
||||
(seq
|
||||
(call "{mallory_peer_id}" ("" "") [] x)
|
||||
(call "{bob_peer_id}" ("" "") [])))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::new();
|
||||
let alice_canon_cid = canon_tracked(
|
||||
json!({
|
||||
"tetraplet": {"peer_pk": &alice_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"tetraplet": {"peer_pk": &alice_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"result": 1,
|
||||
"provenance": Provenance::literal(),
|
||||
}]
|
||||
}),
|
||||
&mut mallory_cid_state,
|
||||
);
|
||||
let mallory_call_result_state = scalar_tracked!("mallory", &mut mallory_cid_state, peer = &mallory_peer_id);
|
||||
let mallory_call_result_cid = extract_service_result_cid(&mallory_call_result_state);
|
||||
let mallory_trace = vec![ap(0), ap(0), alice_canon_cid, mallory_call_result_state];
|
||||
|
||||
let mut mallory_cid_info = serde_json::to_value::<CidInfo>(mallory_cid_state.into()).unwrap();
|
||||
let mut cnt = 0;
|
||||
for (_cid, canon_element) in mallory_cid_info["canon_element_store"]
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.iter_mut()
|
||||
{
|
||||
canon_element["provenance"] = json!(Provenance::service_result(mallory_call_result_cid.clone()));
|
||||
cnt += 1;
|
||||
}
|
||||
assert_eq!(cnt, 1, "test validity failed");
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
|
||||
let mut alice_cid_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_cid_tracker.register(&alice_peer_id, &extract_canon_result_cid(&mallory_trace[2]));
|
||||
let alice_signature = alice_cid_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_cid_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_cid_tracker.register(&mallory_peer_id, &extract_service_result_cid(&mallory_trace[3]));
|
||||
let mallory_signature = mallory_cid_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
serde_json::from_value(mallory_cid_info).unwrap(),
|
||||
signature_store,
|
||||
0,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm(unit_call_service(), bob_peer_id);
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
|
||||
assert_error_eq!(
|
||||
&res,
|
||||
PreparationError::CidStoreVerificationError(CidStoreVerificationError::MismatchError {
|
||||
type_name: "air_interpreter_data::executed_state::CanonCidAggregate",
|
||||
cid_repr: "bagaaierayrb7yu6tvdofr3d7tvuzx7fb3uve27rqty4ckzy7ox66oicuhjjq".to_owned(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_replace_canon_result_values() {
|
||||
// Bob gets a trace where canon result is edited by Mallory.
|
||||
let alice_peer_id = "alice";
|
||||
let bob_peer_id = "bob";
|
||||
let mallory_peer_id = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(ap 1 $s)
|
||||
(ap 2 $s))
|
||||
(canon "{alice_peer_id}" $s #c))
|
||||
(seq
|
||||
(call "{mallory_peer_id}" ("" "") [] x)
|
||||
(call "{bob_peer_id}" ("" "") [])))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::new();
|
||||
let alice_canon_cid = canon_tracked(
|
||||
json!({
|
||||
"tetraplet": {"peer_pk": alice_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"tetraplet": {"peer_pk": alice_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"result": 1,
|
||||
"provenance": Provenance::literal(),
|
||||
}, {
|
||||
"tetraplet": {"peer_pk": alice_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"result": 2,
|
||||
"provenance": Provenance::literal(),
|
||||
}]
|
||||
}),
|
||||
&mut mallory_cid_state,
|
||||
);
|
||||
let mallory_trace = vec![
|
||||
ap(0),
|
||||
ap(0),
|
||||
alice_canon_cid,
|
||||
scalar_tracked!("mallory", &mut mallory_cid_state, peer = &mallory_peer_id),
|
||||
];
|
||||
|
||||
let mut mallory_cid_info = serde_json::to_value::<CidInfo>(mallory_cid_state.into()).unwrap();
|
||||
let mut cnt = 0;
|
||||
for (_cid, canon_result) in mallory_cid_info["canon_result_store"]
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.iter_mut()
|
||||
{
|
||||
canon_result["values"].as_array_mut().unwrap().pop();
|
||||
cnt += 1;
|
||||
}
|
||||
assert_eq!(cnt, 1, "test validity failed");
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
|
||||
let mut alice_cid_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_cid_tracker.register(&alice_peer_id, &extract_canon_result_cid(&mallory_trace[2]));
|
||||
let alice_signature = alice_cid_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_cid_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_cid_tracker.register(&mallory_peer_id, &extract_service_result_cid(&mallory_trace[3]));
|
||||
let mallory_signature = mallory_cid_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
serde_json::from_value(mallory_cid_info).unwrap(),
|
||||
signature_store,
|
||||
0,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm(unit_call_service(), bob_peer_id);
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
|
||||
assert_error_eq!(
|
||||
&res,
|
||||
PreparationError::CidStoreVerificationError(CidStoreVerificationError::MismatchError {
|
||||
type_name: "air_interpreter_data::executed_state::CanonResultCidAggregate",
|
||||
cid_repr: "bagaaieratezrhuyz2eprlmiidxywv6ir2tmswlxycad37noykg3p5oxhs5tq".to_owned(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attack_replace_canon_result_tetraplet() {
|
||||
// Bob gets a trace where canon result is edited by Mallory.
|
||||
let alice_peer_id = "alice";
|
||||
let bob_peer_id = "bob";
|
||||
let mallory_peer_id = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_peer_id);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_peer_id);
|
||||
let alice_pk: PublicKey = alice_keypair.public().into();
|
||||
let mallory_pk: PublicKey = mallory_keypair.public().into();
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(ap 1 $s)
|
||||
(ap 2 $s))
|
||||
(canon "{alice_peer_id}" $s #c))
|
||||
(seq
|
||||
(call "{mallory_peer_id}" ("" "") [] x)
|
||||
(call "{bob_peer_id}" ("" "") [])))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut mallory_cid_state = ExecutionCidState::new();
|
||||
let alice_canon_cid = canon_tracked(
|
||||
json!({
|
||||
"tetraplet": {"peer_pk": alice_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"tetraplet": {"peer_pk": alice_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"result": 1,
|
||||
"provenance": Provenance::literal(),
|
||||
}, {
|
||||
"tetraplet": {"peer_pk": alice_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"result": 2,
|
||||
"provenance": Provenance::literal(),
|
||||
}]
|
||||
}),
|
||||
&mut mallory_cid_state,
|
||||
);
|
||||
let mallory_trace = vec![
|
||||
ap(0),
|
||||
ap(0),
|
||||
alice_canon_cid,
|
||||
scalar_tracked!("mallory", &mut mallory_cid_state, peer = &mallory_peer_id),
|
||||
];
|
||||
|
||||
let mut mallory_cid_info = serde_json::to_value::<CidInfo>(mallory_cid_state.into()).unwrap();
|
||||
let mut cnt = 0;
|
||||
|
||||
let mut fake_cid = None;
|
||||
for (tetraplet_cid, tetraplet) in mallory_cid_info["tetraplet_store"].as_object().unwrap() {
|
||||
if tetraplet["peer_pk"] == mallory_peer_id {
|
||||
fake_cid = Some(tetraplet_cid.clone());
|
||||
}
|
||||
}
|
||||
assert!(fake_cid.is_some(), "test is invalid");
|
||||
for (_cid, canon_result) in mallory_cid_info["canon_result_store"].as_object_mut().unwrap() {
|
||||
canon_result["tetraplet"] = json!(fake_cid.clone().unwrap());
|
||||
cnt += 1;
|
||||
}
|
||||
assert_eq!(cnt, 1, "test validity failed");
|
||||
|
||||
let mut signature_store = SignatureStore::new();
|
||||
|
||||
let mut alice_cid_tracker = PeerCidTracker::new(alice_peer_id.clone());
|
||||
alice_cid_tracker.register(&alice_peer_id, &extract_canon_result_cid(&mallory_trace[2]));
|
||||
let alice_signature = alice_cid_tracker.gen_signature("", &alice_keypair).unwrap();
|
||||
signature_store.put(alice_pk, alice_signature);
|
||||
|
||||
let mut mallory_cid_tracker = PeerCidTracker::new(mallory_peer_id.clone());
|
||||
mallory_cid_tracker.register(&mallory_peer_id, &extract_service_result_cid(&mallory_trace[3]));
|
||||
let mallory_signature = mallory_cid_tracker.gen_signature("", &mallory_keypair).unwrap();
|
||||
signature_store.put(mallory_pk, mallory_signature);
|
||||
|
||||
let mallory_data = InterpreterData::from_execution_result(
|
||||
mallory_trace.into(),
|
||||
serde_json::from_value(mallory_cid_info).unwrap(),
|
||||
signature_store,
|
||||
0,
|
||||
Version::new(1, 1, 1),
|
||||
);
|
||||
|
||||
let mut bob_avm = create_avm(unit_call_service(), bob_peer_id);
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(alice_peer_id);
|
||||
let prev_data = "";
|
||||
let cur_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
let res = bob_avm.call(&air_script, prev_data, cur_data, test_run_params).unwrap();
|
||||
|
||||
assert_error_eq!(
|
||||
&res,
|
||||
PreparationError::CidStoreVerificationError(CidStoreVerificationError::MismatchError {
|
||||
type_name: "air_interpreter_data::executed_state::CanonResultCidAggregate",
|
||||
cid_repr: "bagaaieratezrhuyz2eprlmiidxywv6ir2tmswlxycad37noykg3p5oxhs5tq".to_owned(),
|
||||
})
|
||||
);
|
||||
}
|
334
air/tests/test_module/features/signatures/runtime.rs
Normal file
334
air/tests/test_module/features/signatures/runtime.rs
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* 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::UncatchableError;
|
||||
use air_test_utils::key_utils::derive_dummy_keypair;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
/// This test module asserts various runtime safety checks, for example,
|
||||
/// that actual calls' tetraplets are compared to stored one.
|
||||
|
||||
#[test]
|
||||
fn test_runtime_executed_call_argument_hash() {
|
||||
// Mallory gets a trace where there are two calls that differ only by argument_hash.
|
||||
// Can it swap them successfully?
|
||||
let alice_name = "alice";
|
||||
let bob_name = "bob";
|
||||
let mallory_name = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_name);
|
||||
let (bob_keypair, bob_peer_id) = derive_dummy_keypair(bob_name);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_name);
|
||||
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(&alice_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("service" "func") [42] x)
|
||||
(call "{alice_peer_id}" ("service" "func") [43] y))
|
||||
(seq
|
||||
(call "{mallory_peer_id}" ("" "") [42] z)
|
||||
(call "{bob_peer_id}" ("service" "secret") [x y z] w)))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair, echo_call_service());
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair, echo_call_service());
|
||||
let mut mallory_avm = create_avm_with_key::<NativeAirRunner>(mallory_keypair, echo_call_service());
|
||||
|
||||
let alice_res = alice_avm.call(&air_script, "", "", test_run_params.clone()).unwrap();
|
||||
let mallory_res = mallory_avm
|
||||
.call(&air_script, "", alice_res.data, test_run_params.clone())
|
||||
.unwrap();
|
||||
let mut mallory_data = data_from_result(&mallory_res);
|
||||
let mut mallory_raw_trace: Vec<_> = mallory_data.trace.iter().cloned().collect();
|
||||
mallory_raw_trace.swap(0, 1);
|
||||
mallory_data.trace = ExecutionTrace::from(mallory_raw_trace);
|
||||
|
||||
let mallory_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
|
||||
let bob_res = bob_avm.call(air_script, "", mallory_data, test_run_params).unwrap();
|
||||
assert_error_eq!(
|
||||
&bob_res,
|
||||
UncatchableError::InstructionParametersMismatch {
|
||||
param: "call argument_hash",
|
||||
expected_value: "bagaaieraxbvr5ii3fajw7zjcjoor4maxw7x3ndkpvyfzbreubyga7cntsb5a".to_owned(),
|
||||
stored_value: "bagaaieralq23ubl3bxyggvynq44g6p5233fla7hrr2jrspeamml4zu2iapvq".to_owned(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_runtime_executed_call_tetraplet() {
|
||||
// Mallory gets a trace where there are two calls that differ only by argument_hash.
|
||||
// Can it swap them successfully?
|
||||
let alice_name = "alice";
|
||||
let bob_name = "bob";
|
||||
let mallory_name = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_name);
|
||||
let (bob_keypair, bob_peer_id) = derive_dummy_keypair(bob_name);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_name);
|
||||
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(&alice_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{alice_peer_id}" ("service1" "func") [42] x)
|
||||
(call "{alice_peer_id}" ("service2" "func") [42] y))
|
||||
(seq
|
||||
(call "{mallory_peer_id}" ("" "") [42] z)
|
||||
(call "{bob_peer_id}" ("service" "secret") [x y z] w)))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair, echo_call_service());
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair, echo_call_service());
|
||||
let mut mallory_avm = create_avm_with_key::<NativeAirRunner>(mallory_keypair, echo_call_service());
|
||||
|
||||
let alice_res = alice_avm.call(&air_script, "", "", test_run_params.clone()).unwrap();
|
||||
let mallory_res = mallory_avm
|
||||
.call(&air_script, "", alice_res.data, test_run_params.clone())
|
||||
.unwrap();
|
||||
let mut mallory_data = data_from_result(&mallory_res);
|
||||
let mut mallory_raw_trace: Vec<_> = mallory_data.trace.iter().cloned().collect();
|
||||
mallory_raw_trace.swap(0, 1);
|
||||
mallory_data.trace = ExecutionTrace::from(mallory_raw_trace);
|
||||
|
||||
let mallory_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
|
||||
let bob_res = bob_avm.call(air_script, "", mallory_data, test_run_params).unwrap();
|
||||
let expected_value = format!(
|
||||
concat!(
|
||||
r#"SecurityTetraplet {{ peer_pk: "{alice_peer_id}","#,
|
||||
r#" service_id: "service1", function_name: "func", json_path: "" }}"#
|
||||
),
|
||||
alice_peer_id = alice_peer_id,
|
||||
);
|
||||
let stored_value = format!(
|
||||
concat!(
|
||||
r#"SecurityTetraplet {{ peer_pk: "{alice_peer_id}","#,
|
||||
r#" service_id: "service2", function_name: "func", json_path: "" }}"#,
|
||||
),
|
||||
alice_peer_id = alice_peer_id,
|
||||
);
|
||||
assert_error_eq!(
|
||||
&bob_res,
|
||||
UncatchableError::InstructionParametersMismatch {
|
||||
param: "call tetraplet",
|
||||
// please note that order is important here: if values are swapped, then the error is
|
||||
// handled by Executed branch, not Failed branch
|
||||
expected_value,
|
||||
stored_value,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_runtime_executed_failed_argument_hash() {
|
||||
// Mallory gets a trace where there are two calls that differ only by argument_hash.
|
||||
// Can it swap them successfully?
|
||||
let alice_name = "alice";
|
||||
let bob_name = "bob";
|
||||
let mallory_name = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_name);
|
||||
let (bob_keypair, bob_peer_id) = derive_dummy_keypair(bob_name);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_name);
|
||||
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(&alice_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(xor
|
||||
(call "{alice_peer_id}" ("service" "func") [42] x)
|
||||
(null))
|
||||
(call "{alice_peer_id}" ("service" "func") [43] y))
|
||||
(seq
|
||||
(call "{mallory_peer_id}" ("" "") [42] z)
|
||||
(call "{bob_peer_id}" ("service" "secret") [x y z] w)))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair, fallible_call_service_by_arg(43));
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair, echo_call_service());
|
||||
let mut mallory_avm = create_avm_with_key::<NativeAirRunner>(mallory_keypair, echo_call_service());
|
||||
|
||||
let alice_res = alice_avm.call(&air_script, "", "", test_run_params.clone()).unwrap();
|
||||
let mallory_res = mallory_avm
|
||||
.call(&air_script, "", alice_res.data, test_run_params.clone())
|
||||
.unwrap();
|
||||
let mut mallory_data = data_from_result(&mallory_res);
|
||||
let mut mallory_raw_trace: Vec<_> = mallory_data.trace.iter().cloned().collect();
|
||||
mallory_raw_trace.swap(0, 1);
|
||||
mallory_data.trace = ExecutionTrace::from(mallory_raw_trace);
|
||||
|
||||
let mallory_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
|
||||
let bob_res = bob_avm.call(air_script, "", mallory_data, test_run_params).unwrap();
|
||||
assert_error_eq!(
|
||||
&bob_res,
|
||||
UncatchableError::InstructionParametersMismatch {
|
||||
param: "call argument_hash",
|
||||
// please note that order is important here: if values are swapped, then the error is
|
||||
// handled by Executed branch, not Failed branch
|
||||
expected_value: "bagaaieraxbvr5ii3fajw7zjcjoor4maxw7x3ndkpvyfzbreubyga7cntsb5a".to_owned(),
|
||||
stored_value: "bagaaieralq23ubl3bxyggvynq44g6p5233fla7hrr2jrspeamml4zu2iapvq".to_owned(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_runtime_failed_call_tetraplet() {
|
||||
// Mallory gets a trace where there are two calls that differ only by argument_hash.
|
||||
// Can it swap them successfully?
|
||||
let alice_name = "alice";
|
||||
let bob_name = "bob";
|
||||
let mallory_name = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_name);
|
||||
let (bob_keypair, bob_peer_id) = derive_dummy_keypair(bob_name);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_name);
|
||||
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(&alice_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(xor
|
||||
(call "{alice_peer_id}" ("service1" "func") [42] x)
|
||||
(null))
|
||||
(call "{alice_peer_id}" ("service2" "func") [42] y))
|
||||
(seq
|
||||
(call "{mallory_peer_id}" ("" "") [42] z)
|
||||
(call "{bob_peer_id}" ("service" "secret") [x y z] w)))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair, fallible_call_service("service1"));
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair, echo_call_service());
|
||||
let mut mallory_avm = create_avm_with_key::<NativeAirRunner>(mallory_keypair, echo_call_service());
|
||||
|
||||
let alice_res = alice_avm.call(&air_script, "", "", test_run_params.clone()).unwrap();
|
||||
let mallory_res = mallory_avm
|
||||
.call(&air_script, "", alice_res.data, test_run_params.clone())
|
||||
.unwrap();
|
||||
let mut mallory_data = data_from_result(&mallory_res);
|
||||
let mut mallory_raw_trace: Vec<_> = mallory_data.trace.iter().cloned().collect();
|
||||
mallory_raw_trace.swap(0, 1);
|
||||
mallory_data.trace = ExecutionTrace::from(mallory_raw_trace);
|
||||
|
||||
let mallory_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
|
||||
let bob_res = bob_avm.call(air_script, "", mallory_data, test_run_params).unwrap();
|
||||
let expected_value = format!(
|
||||
concat!(
|
||||
r#"SecurityTetraplet {{ peer_pk: "{alice_peer_id}","#,
|
||||
r#" service_id: "service1", function_name: "func", json_path: "" }}"#
|
||||
),
|
||||
alice_peer_id = alice_peer_id,
|
||||
);
|
||||
let stored_value = format!(
|
||||
concat!(
|
||||
r#"SecurityTetraplet {{ peer_pk: "{alice_peer_id}","#,
|
||||
r#" service_id: "service2", function_name: "func", json_path: "" }}"#,
|
||||
),
|
||||
alice_peer_id = alice_peer_id,
|
||||
);
|
||||
assert_error_eq!(
|
||||
&bob_res,
|
||||
UncatchableError::InstructionParametersMismatch {
|
||||
param: "call tetraplet",
|
||||
// please note that order is important here: if values are swapped, then the error is
|
||||
// handled by Executed branch, not Failed branch
|
||||
expected_value,
|
||||
stored_value,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_runtime_canon_tetraplet() {
|
||||
let alice_name = "alice";
|
||||
let bob_name = "bob";
|
||||
let mallory_name = "mallory";
|
||||
|
||||
let (alice_keypair, alice_peer_id) = derive_dummy_keypair(alice_name);
|
||||
let (bob_keypair, bob_peer_id) = derive_dummy_keypair(bob_name);
|
||||
let (mallory_keypair, mallory_peer_id) = derive_dummy_keypair(mallory_name);
|
||||
|
||||
let test_run_params = TestRunParameters::from_init_peer_id(&alice_peer_id);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(ap 42 $x)
|
||||
(ap 43 $x))
|
||||
(seq
|
||||
(seq
|
||||
(canon "{alice_peer_id}" $x #xa)
|
||||
(canon "{mallory_peer_id}" $x #xm))
|
||||
(call "{bob_peer_id}" ("" "") [#xa #xm] z)))
|
||||
"#
|
||||
);
|
||||
|
||||
let mut alice_avm = create_avm_with_key::<NativeAirRunner>(alice_keypair, fallible_call_service("service1"));
|
||||
let mut bob_avm = create_avm_with_key::<NativeAirRunner>(bob_keypair, echo_call_service());
|
||||
let mut mallory_avm = create_avm_with_key::<NativeAirRunner>(mallory_keypair, echo_call_service());
|
||||
|
||||
let alice_res = alice_avm.call(&air_script, "", "", test_run_params.clone()).unwrap();
|
||||
let mallory_res = mallory_avm
|
||||
.call(&air_script, "", alice_res.data, test_run_params.clone())
|
||||
.unwrap();
|
||||
let mut mallory_data = data_from_result(&mallory_res);
|
||||
let mut mallory_raw_trace: Vec<_> = mallory_data.trace.iter().cloned().collect();
|
||||
mallory_raw_trace.swap(2, 3);
|
||||
mallory_data.trace = ExecutionTrace::from(mallory_raw_trace);
|
||||
|
||||
let mallory_data = serde_json::to_vec(&mallory_data).unwrap();
|
||||
|
||||
let bob_res = bob_avm.call(air_script, "", mallory_data, test_run_params).unwrap();
|
||||
let expected_value = format!(
|
||||
concat!(
|
||||
r#"SecurityTetraplet {{ peer_pk: "{alice_peer_id}","#,
|
||||
r#" service_id: "", function_name: "", json_path: "" }}"#
|
||||
),
|
||||
alice_peer_id = alice_peer_id,
|
||||
);
|
||||
let stored_value = format!(
|
||||
concat!(
|
||||
r#"SecurityTetraplet {{ peer_pk: "{mallory_peer_id}","#,
|
||||
r#" service_id: "", function_name: "", json_path: "" }}"#,
|
||||
),
|
||||
mallory_peer_id = mallory_peer_id,
|
||||
);
|
||||
assert_error_eq!(
|
||||
&bob_res,
|
||||
UncatchableError::InstructionParametersMismatch {
|
||||
param: "canon tetraplet",
|
||||
expected_value,
|
||||
stored_value,
|
||||
}
|
||||
);
|
||||
}
|
455
air/tests/test_module/features/signatures/signing.rs
Normal file
455
air/tests/test_module/features/signatures/signing.rs
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* 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_interpreter_signatures::PeerCidTracker;
|
||||
use air_test_framework::{ephemeral::PeerId, AirScriptExecutor};
|
||||
use air_test_utils::key_utils::derive_dummy_keypair;
|
||||
use air_test_utils::prelude::*;
|
||||
use air_test_utils::test_runner::TestRunParameters;
|
||||
|
||||
#[test]
|
||||
fn test_signature_empty() {
|
||||
let script = "(null)";
|
||||
let init_peer_name = "init_peer_id";
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let exec = <AirScriptExecutor>::new(
|
||||
TestRunParameters::from_init_peer_id(init_peer_name),
|
||||
vec![],
|
||||
vec![PeerId::from(init_peer_name)].into_iter(),
|
||||
script,
|
||||
)
|
||||
.unwrap();
|
||||
let res = exec.execute_one(init_peer_name).unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
|
||||
let data = borsh::to_vec(&(vec![""; 0], "")).unwrap();
|
||||
let expected_signature: air_interpreter_signatures::Signature = keypair.sign(&data).unwrap().into();
|
||||
|
||||
let data = data_from_result(&res);
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_var() {
|
||||
let init_peer_name = "init_peer_id";
|
||||
let (keypair, init_peer_id) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(call "{init_peer_name}" ("" "") [] var) ; ok = "ok"
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &air_script).unwrap();
|
||||
|
||||
let res = exec.execution_iter(init_peer_id.as_str()).unwrap().last().unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
let data = data_from_result(&res);
|
||||
|
||||
let expected_call_state = scalar!("ok", peer = &init_peer_id, service = "..0");
|
||||
let expected_cid = extract_service_result_cid(&expected_call_state);
|
||||
|
||||
let mut expected_tracker = PeerCidTracker::new(init_peer_id.clone());
|
||||
expected_tracker.register(&init_peer_id, &expected_cid);
|
||||
let expected_signature = expected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_stream() {
|
||||
let init_peer_name = "init_peer_id";
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(call "{init_peer_name}" ("" "") [] $var) ; ok = "ok"
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &air_script).unwrap();
|
||||
|
||||
let res = exec.execution_iter(init_peer_name).unwrap().last().unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
let data = data_from_result(&res);
|
||||
|
||||
let (keypair, init_peer_id) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let expected_call_state = stream!("ok", 0, peer = &init_peer_id, service = "..0");
|
||||
let expected_cid = extract_service_result_cid(&expected_call_state);
|
||||
|
||||
let mut expected_tracker = PeerCidTracker::new(init_peer_id.clone());
|
||||
expected_tracker.register(&init_peer_id, &expected_cid);
|
||||
let expected_signature = expected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_unused() {
|
||||
let init_peer_name = "init_peer_id";
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(call "{init_peer_name}" ("" "") []) ; ok = "ok"
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &air_script).unwrap();
|
||||
|
||||
let res = exec.execution_iter(init_peer_name).unwrap().last().unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
let data = data_from_result(&res);
|
||||
|
||||
let (keypair, init_peer_id) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let expected_tracker = PeerCidTracker::new(init_peer_id.to_owned());
|
||||
let expected_signature = expected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_merged() {
|
||||
let init_peer_name = "init_peer_id";
|
||||
let other_peer_name = "other_peer_id";
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{init_peer_name}" ("" "") [] x) ; ok = "res0"
|
||||
(seq
|
||||
(call "{other_peer_name}" ("" "") [] y) ; ok = "res1"
|
||||
(call "{init_peer_name}" ("" "") [] z) ; ok = "res2"
|
||||
))
|
||||
"#
|
||||
);
|
||||
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &air_script).unwrap();
|
||||
let _ = exec.execute_one(init_peer_name).unwrap();
|
||||
let _ = exec.execute_one(other_peer_name).unwrap();
|
||||
let res2 = exec.execute_one(init_peer_name).unwrap();
|
||||
let data2 = data_from_result(&res2);
|
||||
|
||||
let expected_call_state0 = scalar!("res0", peer_name = init_peer_name, service = "..0");
|
||||
let expected_cid0 = extract_service_result_cid(&expected_call_state0);
|
||||
let expected_call_state2 = scalar!("res2", peer_name = init_peer_name, service = "..2");
|
||||
let expected_cid2 = extract_service_result_cid(&expected_call_state2);
|
||||
|
||||
let (keypair, _) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let mut expected_tracker = PeerCidTracker::new(init_peer_name.to_owned());
|
||||
expected_tracker.register(init_peer_name, &expected_cid0);
|
||||
expected_tracker.register(init_peer_name, &expected_cid2);
|
||||
let expected_signature = expected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
let signature = data2.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data2.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_call_twice() {
|
||||
// Test that if some CID appears twice in the call result, it is accounted twice.
|
||||
let init_peer_name = "init_peer_id";
|
||||
|
||||
let (keypair, init_peer_id) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq (ap 1 $s) (ap 2 $s))
|
||||
(fold $s i
|
||||
(seq
|
||||
(call "{init_peer_name}" ("" "") [] var) ; ok = "ok"
|
||||
(next i))))
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &air_script).unwrap();
|
||||
|
||||
let res = exec.execution_iter(init_peer_id.as_str()).unwrap().last().unwrap();
|
||||
assert_eq!(res.ret_code, 0, "{:?}", res);
|
||||
let data = data_from_result(&res);
|
||||
|
||||
let expected_call_state = scalar!("ok", peer = &init_peer_id, service = "..0");
|
||||
let expected_cid = extract_service_result_cid(&expected_call_state);
|
||||
|
||||
let mut unexpected_tracker = PeerCidTracker::new(init_peer_id.to_owned());
|
||||
unexpected_tracker.register(&init_peer_id, &expected_cid);
|
||||
let unexpected_signature = unexpected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
let mut expected_tracker = PeerCidTracker::new(init_peer_id.to_owned());
|
||||
expected_tracker.register(&init_peer_id, &expected_cid);
|
||||
expected_tracker.register(&init_peer_id, &expected_cid);
|
||||
let expected_signature = expected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
assert_ne!(expected_signature, unexpected_signature, "test is incorrect");
|
||||
|
||||
let signature = data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_canon_basic() {
|
||||
let init_peer_name = "init_peer_id";
|
||||
let (keypair, init_peer_id) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{init_peer_name}" ("serv" "func") [] items) ; ok = [1, 2, 3]
|
||||
(seq
|
||||
(fold items i
|
||||
(seq
|
||||
(ap i $stream)
|
||||
(next i)))
|
||||
(canon "{init_peer_name}" $stream #canon)))
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &air_script).unwrap();
|
||||
|
||||
let last_result = exec.execution_iter(init_peer_name).unwrap().last().unwrap();
|
||||
let last_data = data_from_result(&last_result);
|
||||
|
||||
let expected_call_result = scalar!(
|
||||
json!([1, 2, 3]),
|
||||
peer = &init_peer_id,
|
||||
service = "serv..0",
|
||||
function = "func"
|
||||
);
|
||||
let expected_call_result_cid = extract_service_result_cid(&expected_call_result);
|
||||
|
||||
let expected_canon_state = canon(json!({
|
||||
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"result": 1,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[0]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}, {
|
||||
"result": 2,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[1]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}, {
|
||||
"result": 3,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[2]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}]
|
||||
}));
|
||||
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
|
||||
|
||||
let mut expected_tracker = PeerCidTracker::new(init_peer_name.to_owned());
|
||||
expected_tracker.register(init_peer_name, &expected_canon_cid);
|
||||
expected_tracker.register(init_peer_name, &expected_call_result_cid);
|
||||
let expected_signature = expected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
let signature = last_data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_canon_merge() {
|
||||
let init_peer_name = "init_peer_id";
|
||||
let other_peer_name = "other_peer_id";
|
||||
let (keypair, init_peer_id) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{init_peer_name}" ("serv" "func") [] items) ; ok = [1, 2, 3]
|
||||
(seq
|
||||
(fold items i
|
||||
(seq
|
||||
(ap i $stream)
|
||||
(next i)))
|
||||
(canon "{init_peer_name}" $stream #canon)))
|
||||
(seq
|
||||
(call "{other_peer_name}" ("" "") []) ; ok = "ok"
|
||||
(call "{init_peer_name}" ("" "") []))) ; ok = "ok"
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &air_script).unwrap();
|
||||
|
||||
exec.execute_all(init_peer_name);
|
||||
exec.execute_one(other_peer_name);
|
||||
|
||||
let last_result = exec.execution_iter(init_peer_name).unwrap().last().unwrap();
|
||||
let last_data = data_from_result(&last_result);
|
||||
|
||||
let expected_call_result = scalar!(
|
||||
json!([1, 2, 3]),
|
||||
peer = &init_peer_id,
|
||||
service = "serv..0",
|
||||
function = "func"
|
||||
);
|
||||
let expected_call_result_cid = extract_service_result_cid(&expected_call_result);
|
||||
|
||||
let expected_canon_state = canon(json!({
|
||||
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"result": 1,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[0]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}, {
|
||||
"result": 2,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[1]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}, {
|
||||
"result": 3,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[2]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
|
||||
}]
|
||||
}));
|
||||
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
|
||||
|
||||
let mut expected_tracker = PeerCidTracker::new(init_peer_name.to_owned());
|
||||
expected_tracker.register(init_peer_name, &expected_canon_cid);
|
||||
expected_tracker.register(init_peer_name, &expected_call_result_cid);
|
||||
let expected_signature = expected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
let signature = last_data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_canon_result() {
|
||||
// this test checks that call result in canon doesn't lead to repeadted accounting of the call result
|
||||
let init_peer_name = "init_peer_id";
|
||||
let (keypair, init_peer_id) = derive_dummy_keypair(init_peer_name);
|
||||
|
||||
let air_script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{init_peer_name}" ("serv" "func") [] items) ; ok = [1, 2, 3]
|
||||
(fold items i
|
||||
(seq
|
||||
(ap i $stream)
|
||||
(next i))))
|
||||
(seq
|
||||
(call "{init_peer_name}" ("serv" "func2") [] $stream) ; ok = 42
|
||||
(canon "{init_peer_name}" $stream #canon)))
|
||||
"#
|
||||
);
|
||||
let exec =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), &air_script).unwrap();
|
||||
|
||||
let last_result = exec.execution_iter(init_peer_name).unwrap().last().unwrap();
|
||||
let last_data = data_from_result(&last_result);
|
||||
|
||||
let expected_call_result1 = scalar!(
|
||||
json!([1, 2, 3]),
|
||||
peer = &init_peer_id,
|
||||
service = "serv..0",
|
||||
function = "func"
|
||||
);
|
||||
let expected_call_result_cid1 = extract_service_result_cid(&expected_call_result1);
|
||||
|
||||
let expected_call_result2 = stream!(
|
||||
json!(42),
|
||||
1,
|
||||
peer = &init_peer_id,
|
||||
service = "serv..1",
|
||||
function = "func2"
|
||||
);
|
||||
let expected_call_result_cid2 = extract_service_result_cid(&expected_call_result2);
|
||||
|
||||
let expected_canon_state = canon(json!({
|
||||
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
|
||||
"values": [{
|
||||
"result": 1,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[0]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
|
||||
}, {
|
||||
"result": 2,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[1]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
|
||||
}, {
|
||||
"result": 3,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..0",
|
||||
"function_name": "func",
|
||||
"json_path": ".$.[2]",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
|
||||
}, {
|
||||
"result": 42,
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
"service_id": "serv..1",
|
||||
"function_name": "func2",
|
||||
"json_path": "",
|
||||
},
|
||||
"provenance": Provenance::service_result(expected_call_result_cid2.clone()),
|
||||
}]
|
||||
}));
|
||||
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
|
||||
|
||||
let mut expected_tracker = PeerCidTracker::new(init_peer_name.to_owned());
|
||||
expected_tracker.register(init_peer_name, &expected_call_result_cid1);
|
||||
expected_tracker.register(init_peer_name, &expected_call_result_cid2);
|
||||
expected_tracker.register(init_peer_name, &expected_canon_cid);
|
||||
let expected_signature = expected_tracker.gen_signature("", &keypair).unwrap();
|
||||
|
||||
let signature = last_data.signatures.get(&keypair.public().into());
|
||||
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
|
||||
}
|
@ -21,19 +21,19 @@ use air_test_utils::*;
|
||||
|
||||
#[test]
|
||||
fn global_streams_are_compactified() {
|
||||
let peer_id = "peer_id";
|
||||
let peer_name = "peer_id";
|
||||
let service_result = "service_result";
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(ap 1 $stream)
|
||||
(call "{peer_id}" ("" "") [] $stream) ; ok = "{service_result}"
|
||||
(call "{peer_name}" ("" "") [] $stream) ; ok = "{service_result}"
|
||||
)
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(peer_id), &script).unwrap();
|
||||
let result = executor.execute_all(peer_id).unwrap();
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(peer_name), &script).unwrap();
|
||||
let result = executor.execute_all(peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(result.last().unwrap());
|
||||
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
@ -43,7 +43,7 @@ fn global_streams_are_compactified() {
|
||||
service_result,
|
||||
1,
|
||||
cid_state,
|
||||
peer = peer_id,
|
||||
peer_name = peer_name,
|
||||
service = "..0",
|
||||
function = ""
|
||||
),
|
||||
@ -54,22 +54,22 @@ fn global_streams_are_compactified() {
|
||||
|
||||
#[test]
|
||||
fn global_stream_maps_are_compactified() {
|
||||
let peer_id = "peer_id";
|
||||
let peer_name = "peer_id";
|
||||
let service_result = "service_result";
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(ap (1 1) %stream_map)
|
||||
(seq
|
||||
(call "{peer_id}" ("" "") [] $stream) ; ok = "{service_result}"
|
||||
(call "{peer_name}" ("" "") [] $stream) ; ok = "{service_result}"
|
||||
(ap (1 1) %stream_map)
|
||||
)
|
||||
)
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(peer_id), &script).unwrap();
|
||||
let result = executor.execute_all(peer_id).unwrap();
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(peer_name), &script).unwrap();
|
||||
let result = executor.execute_all(peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(result.last().unwrap());
|
||||
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
@ -79,7 +79,7 @@ fn global_stream_maps_are_compactified() {
|
||||
service_result,
|
||||
0,
|
||||
cid_state,
|
||||
peer = peer_id,
|
||||
peer_name = peer_name,
|
||||
service = "..0",
|
||||
function = ""
|
||||
),
|
||||
@ -91,21 +91,21 @@ fn global_stream_maps_are_compactified() {
|
||||
|
||||
#[test]
|
||||
fn local_streams_are_compactified() {
|
||||
let peer_id = "peer_id";
|
||||
let peer_name = "peer_id";
|
||||
let service_result = "service_result";
|
||||
let script = format!(
|
||||
r#"
|
||||
(new $stream
|
||||
(seq
|
||||
(ap 1 $stream)
|
||||
(call "{peer_id}" ("" "") [] $stream) ; ok = "{service_result}"
|
||||
(call "{peer_name}" ("" "") [] $stream) ; ok = "{service_result}"
|
||||
)
|
||||
)
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(peer_id), &script).unwrap();
|
||||
let result = executor.execute_all(peer_id).unwrap();
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(peer_name), &script).unwrap();
|
||||
let result = executor.execute_all(peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(result.last().unwrap());
|
||||
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
@ -115,7 +115,7 @@ fn local_streams_are_compactified() {
|
||||
service_result,
|
||||
1,
|
||||
cid_state,
|
||||
peer = peer_id,
|
||||
peer_name = peer_name,
|
||||
service = "..0",
|
||||
function = ""
|
||||
),
|
||||
@ -126,7 +126,7 @@ fn local_streams_are_compactified() {
|
||||
|
||||
#[test]
|
||||
fn local_stream_maps_are_compactified() {
|
||||
let peer_id = "peer_id";
|
||||
let peer_name = "peer_id";
|
||||
let service_result = "service_result";
|
||||
let script = format!(
|
||||
r#"
|
||||
@ -134,7 +134,7 @@ fn local_stream_maps_are_compactified() {
|
||||
(seq
|
||||
(ap (1 1) %stream_map)
|
||||
(seq
|
||||
(call "{peer_id}" ("" "") [] $stream) ; ok = "{service_result}"
|
||||
(call "{peer_name}" ("" "") [] $stream) ; ok = "{service_result}"
|
||||
(ap (1 1) %stream_map)
|
||||
)
|
||||
)
|
||||
@ -142,8 +142,8 @@ fn local_stream_maps_are_compactified() {
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(peer_id), &script).unwrap();
|
||||
let result = executor.execute_all(peer_id).unwrap();
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(peer_name), &script).unwrap();
|
||||
let result = executor.execute_all(peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(result.last().unwrap());
|
||||
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
@ -153,7 +153,7 @@ fn local_stream_maps_are_compactified() {
|
||||
service_result,
|
||||
0,
|
||||
cid_state,
|
||||
peer = peer_id,
|
||||
peer_name = peer_name,
|
||||
service = "..0",
|
||||
function = ""
|
||||
),
|
||||
|
@ -15,14 +15,14 @@
|
||||
*/
|
||||
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::prelude::*;
|
||||
use air_test_utils::{key_utils::at, prelude::*};
|
||||
|
||||
#[test]
|
||||
fn merging_fold_iterations_extensively() {
|
||||
let script = r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "client" ("get" "data") [] permutations) ; ok = [["p1",[[["p1",1],["p2",2],["p3",3]],[["p1",4],["p3",5],["p2",6]]]],["p2",[[["p2",7],["p1",8],["p3",9]],[["p2",10],["p3",11],["p1",12]]]],["p3",[[["p3",13],["p1",14],["p2",15]],[["p3",16],["p2",17],["p1",18]]]]]
|
||||
(call "client" ("get" "data") [] permutations) ; ok = [[@"p1",[[[@"p1",1],[@"p2",2],[@"p3",3]],[[@"p1",4],[@"p3",5],[@"p2",6]]]],[@"p2",[[[@"p2",7],[@"p1",8],[@"p3",9]],[[@"p2",10],[@"p3",11],[@"p1",12]]]],[@"p3",[[[@"p3",13],[@"p1",14],[@"p2",15]],[[@"p3",16],[@"p2",17],[@"p1",18]]]]]
|
||||
(seq
|
||||
(fold permutations pair
|
||||
(seq
|
||||
@ -103,7 +103,7 @@ fn merging_fold_iterations_extensively() {
|
||||
queue.push_back(peer.clone());
|
||||
}
|
||||
|
||||
if peer == "relay" {
|
||||
if peer == at("relay") {
|
||||
relay_outcomes.push(outcome);
|
||||
}
|
||||
}
|
||||
@ -131,7 +131,7 @@ fn merging_fold_iterations_extensively_2() {
|
||||
let script = r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "client" ("get" "data") [] permutations) ; ok = [["p1",[[["p1",1],["p2",2],["p3",3]],[["p1",4],["p3",5],["p2",6]]]],["p2",[[["p2",7],["p1",8],["p3",9]],[["p2",10],["p3",11],["p1",12]]]],["p3",[[["p3",13],["p1",14],["p2",15]],[["p3",16],["p2",17],["p1",18]]]]]
|
||||
(call "client" ("get" "data") [] permutations) ; ok = [[@"p1",[[[@"p1",1],[@"p2",2],[@"p3",3]],[[@"p1",4],[@"p3",5],[@"p2",6]]]],[@"p2",[[[@"p2",7],[@"p1",8],[@"p3",9]],[[@"p2",10],[@"p3",11],[@"p1",12]]]],[@"p3",[[[@"p3",13],[@"p1",14],[@"p2",15]],[[@"p3",16],[@"p2",17],[@"p1",18]]]]]
|
||||
(seq
|
||||
(seq
|
||||
(fold permutations pair
|
||||
@ -243,7 +243,7 @@ fn merging_fold_iterations_extensively_2() {
|
||||
}
|
||||
}
|
||||
|
||||
if peer == "p1" {
|
||||
if peer == at("p1") {
|
||||
p1_outcomes.push(outcome);
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::prelude::*;
|
||||
use air_test_utils::{key_utils::at, prelude::*};
|
||||
|
||||
#[test]
|
||||
fn call_result() {
|
||||
let init_peer_id = "B";
|
||||
let init_peer_name = "B";
|
||||
|
||||
let air_script = r#"
|
||||
(seq
|
||||
@ -27,9 +27,9 @@ fn call_result() {
|
||||
(canon "B" $s #c))
|
||||
"#;
|
||||
let runner =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), air_script).unwrap();
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), air_script).unwrap();
|
||||
|
||||
let result = runner.execute_one(init_peer_id).unwrap();
|
||||
let result = runner.execute_one(init_peer_name).unwrap();
|
||||
assert_eq!(result.ret_code, 0, "{:?}", result.error_message);
|
||||
|
||||
let data = data_from_result(&result);
|
||||
@ -37,12 +37,14 @@ fn call_result() {
|
||||
|
||||
let val = scalar!(
|
||||
"some_data",
|
||||
peer = init_peer_id,
|
||||
peer_name = init_peer_name,
|
||||
service = "service..0",
|
||||
function = "func"
|
||||
);
|
||||
let val_cid = extract_service_result_cid(&val);
|
||||
|
||||
let init_peer_id = at(init_peer_name);
|
||||
|
||||
let expected_state = canon(json!({
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
@ -67,7 +69,7 @@ fn call_result() {
|
||||
|
||||
#[test]
|
||||
fn call_result_iteration() {
|
||||
let init_peer_id = "A";
|
||||
let init_peer_name = "A";
|
||||
|
||||
let air_script = r#"
|
||||
(seq
|
||||
@ -80,17 +82,19 @@ fn call_result_iteration() {
|
||||
(canon "A" $s #c))
|
||||
"#;
|
||||
let runner =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), air_script).unwrap();
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), air_script).unwrap();
|
||||
|
||||
let result = runner.execute_one(init_peer_id).unwrap();
|
||||
let result = runner.execute_one(init_peer_name).unwrap();
|
||||
assert_eq!(result.ret_code, 0, "{:?}", result.error_message);
|
||||
|
||||
let data = data_from_result(&result);
|
||||
let last_state = data.trace.last().unwrap();
|
||||
|
||||
let init_peer_id = at(init_peer_name);
|
||||
|
||||
let val = scalar!(
|
||||
json!([10, 11, 12]),
|
||||
peer = init_peer_id,
|
||||
peer_name = init_peer_name,
|
||||
service = "service..0",
|
||||
function = "func"
|
||||
);
|
||||
@ -138,7 +142,7 @@ fn call_result_iteration() {
|
||||
|
||||
#[test]
|
||||
fn literal() {
|
||||
let init_peer_id = "B";
|
||||
let init_peer_name = "B";
|
||||
|
||||
let air_script = r#"
|
||||
(seq
|
||||
@ -146,14 +150,16 @@ fn literal() {
|
||||
(canon "B" $s #c))
|
||||
"#;
|
||||
let runner =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), air_script).unwrap();
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), air_script).unwrap();
|
||||
|
||||
let result = runner.execute_one(init_peer_id).unwrap();
|
||||
let result = runner.execute_one(init_peer_name).unwrap();
|
||||
assert_eq!(result.ret_code, 0, "{:?}", result.error_message);
|
||||
|
||||
let data = data_from_result(&result);
|
||||
let last_state = data.trace.last().unwrap();
|
||||
|
||||
let init_peer_id = at(init_peer_name);
|
||||
|
||||
let expected_state = canon(json!({
|
||||
"tetraplet": {
|
||||
"peer_pk": init_peer_id,
|
||||
@ -178,7 +184,7 @@ fn literal() {
|
||||
|
||||
#[test]
|
||||
fn canon_in_canon() {
|
||||
let init_peer_id = "B";
|
||||
let init_peer_name = "B";
|
||||
|
||||
let air_script = r#"
|
||||
(seq
|
||||
@ -190,15 +196,17 @@ fn canon_in_canon() {
|
||||
(canon "B" $s #d)))
|
||||
"#;
|
||||
let runner =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), air_script).unwrap();
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), air_script).unwrap();
|
||||
|
||||
let result = runner.execute_one(init_peer_id).unwrap();
|
||||
let result = runner.execute_one(init_peer_name).unwrap();
|
||||
assert_eq!(result.ret_code, 0, "{:?}", result.error_message);
|
||||
|
||||
let trace = trace_from_result(&result);
|
||||
let last_state = trace.last().unwrap();
|
||||
|
||||
let val = scalar!(1, peer = init_peer_id, service = "service..0", function = "func");
|
||||
let init_peer_id = at(init_peer_name);
|
||||
|
||||
let val = scalar!(1, peer_name = init_peer_name, service = "service..0", function = "func");
|
||||
let val_cid = extract_service_result_cid(&val);
|
||||
let value_tetraplet = json!({
|
||||
"peer_pk": init_peer_id,
|
||||
@ -250,7 +258,7 @@ fn canon_in_canon() {
|
||||
|
||||
#[test]
|
||||
fn lambda_result_iteration() {
|
||||
let init_peer_id = "A";
|
||||
let init_peer_name = "A";
|
||||
|
||||
let air_script = r#"
|
||||
(seq
|
||||
@ -266,17 +274,19 @@ fn lambda_result_iteration() {
|
||||
(canon "A" $s #c))
|
||||
"#;
|
||||
let runner =
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_id), air_script).unwrap();
|
||||
AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(init_peer_name), air_script).unwrap();
|
||||
|
||||
let result = runner.execute_one(init_peer_id).unwrap();
|
||||
let result = runner.execute_one(init_peer_name).unwrap();
|
||||
assert_eq!(result.ret_code, 0, "{:?}", result.error_message);
|
||||
|
||||
let data = data_from_result(&result);
|
||||
let last_state = data.trace.last().unwrap();
|
||||
|
||||
let init_peer_id = at(init_peer_name);
|
||||
|
||||
let val = scalar!(
|
||||
json!([{"field": [10, 11, 12]}]),
|
||||
peer = init_peer_id,
|
||||
peer_name = init_peer_name,
|
||||
service = "service..0",
|
||||
function = "func"
|
||||
);
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use air_test_utils::key_utils::at;
|
||||
use air_test_utils::prelude::*;
|
||||
use polyplets::SecurityTetraplet;
|
||||
use pretty_assertions::assert_eq;
|
||||
@ -98,7 +99,7 @@ fn fold_with_inner_call() {
|
||||
|
||||
#[test]
|
||||
fn fold_stream_with_inner_call() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let init_peer_name = "init_peer_id";
|
||||
let air_script = r#"
|
||||
(seq
|
||||
(seq
|
||||
@ -112,20 +113,22 @@ fn fold_stream_with_inner_call() {
|
||||
(next i))))
|
||||
"#;
|
||||
let executor = air_test_framework::AirScriptExecutor::from_annotated(
|
||||
TestRunParameters::from_init_peer_id(init_peer_id),
|
||||
TestRunParameters::from_init_peer_id(init_peer_name),
|
||||
&air_script,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
assert_eq!(result.ret_code, 0, "{}", result.error_message);
|
||||
let data = data_from_result(&result);
|
||||
|
||||
let init_peer_id = at(init_peer_name);
|
||||
|
||||
let expected_trace = vec![
|
||||
stream!(
|
||||
json!([[{"peer_pk": init_peer_id, "service_id": "..0", "function_name": "", "json_path": ""}]]),
|
||||
0,
|
||||
peer = init_peer_id,
|
||||
peer = &init_peer_id,
|
||||
service = "..2",
|
||||
args = [42]
|
||||
),
|
||||
@ -142,7 +145,7 @@ fn fold_stream_with_inner_call() {
|
||||
|
||||
#[test]
|
||||
fn fold_canon_with_inner_call() {
|
||||
let init_peer_id = "init_peer_id";
|
||||
let init_peer_name = "init_peer_id";
|
||||
let air_script = r#"
|
||||
(seq
|
||||
(seq
|
||||
@ -158,20 +161,22 @@ fn fold_canon_with_inner_call() {
|
||||
(next x)))))
|
||||
"#;
|
||||
let executor = air_test_framework::AirScriptExecutor::from_annotated(
|
||||
TestRunParameters::from_init_peer_id(init_peer_id),
|
||||
TestRunParameters::from_init_peer_id(init_peer_name),
|
||||
&air_script,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let result = executor.execute_one(init_peer_name).unwrap();
|
||||
assert_eq!(result.ret_code, 0, "{}", result.error_message);
|
||||
let data = data_from_result(&result);
|
||||
|
||||
let init_peer_id = at(init_peer_name);
|
||||
|
||||
let expected_trace = vec![
|
||||
stream!(
|
||||
json!([[{"peer_pk": init_peer_id, "service_id": "..0", "function_name": "", "json_path": ""}]]),
|
||||
0,
|
||||
peer = init_peer_id,
|
||||
peer = &init_peer_id,
|
||||
service = "..2",
|
||||
args = [42]
|
||||
),
|
||||
|
@ -17,7 +17,9 @@
|
||||
use air::no_error_object;
|
||||
use air::ExecutionCidState;
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::key_utils::at;
|
||||
use air_test_utils::prelude::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
@ -643,25 +645,27 @@ fn ap_stream_map_with_undefined_last_error() {
|
||||
|
||||
#[test]
|
||||
fn ap_canon_stream_map_with_string_key_accessor_lambda() {
|
||||
let vm_1_peer_id = "vm_1_peer_id";
|
||||
let vm_1_peer_name = "vm_1_peer_id";
|
||||
let vm_1_peer_id = at(vm_1_peer_name);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(ap ("key" "value1") %map)
|
||||
(canon "{vm_1_peer_id}" %map #%canon_map)
|
||||
(canon "{vm_1_peer_name}" %map #%canon_map)
|
||||
)
|
||||
(seq
|
||||
(ap #%canon_map.$.key scalar)
|
||||
(call "{vm_1_peer_id}" ("m" "f") [scalar] scalar1) ; behaviour = echo
|
||||
(call "{vm_1_peer_name}" ("m" "f") [scalar] scalar1) ; behaviour = echo
|
||||
)
|
||||
)
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_id), &script)
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_name), &script)
|
||||
.expect("invalid test AIR script");
|
||||
let result = executor.execute_all(vm_1_peer_id).unwrap();
|
||||
let result = executor.execute_all(vm_1_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result.last().unwrap());
|
||||
|
||||
let mut cid_tracker: ExecutionCidState = ExecutionCidState::new();
|
||||
@ -692,30 +696,32 @@ fn ap_canon_stream_map_with_string_key_accessor_lambda() {
|
||||
),
|
||||
];
|
||||
|
||||
assert_eq!(actual_trace, expected_trace,);
|
||||
assert_eq!(&*actual_trace, expected_trace,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ap_canon_stream_map_with_numeric_key_accessor_lambda() {
|
||||
let vm_1_peer_id = "vm_1_peer_id";
|
||||
let vm_1_peer_name = "vm_1_peer_id";
|
||||
let vm_1_peer_id = at(vm_1_peer_name);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(ap (42 "value1") %map)
|
||||
(canon "{vm_1_peer_id}" %map #%canon_map)
|
||||
(canon "{vm_1_peer_name}" %map #%canon_map)
|
||||
)
|
||||
(seq
|
||||
(ap #%canon_map.$.[42] scalar)
|
||||
(call "{vm_1_peer_id}" ("m" "f") [scalar] scalar1) ; behaviour = echo
|
||||
(call "{vm_1_peer_name}" ("m" "f") [scalar] scalar1) ; behaviour = echo
|
||||
)
|
||||
)
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_id), &script)
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_name), &script)
|
||||
.expect("invalid test AIR script");
|
||||
let result = executor.execute_all(vm_1_peer_id).unwrap();
|
||||
let result = executor.execute_all(vm_1_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result.last().unwrap());
|
||||
|
||||
let mut cid_tracker: ExecutionCidState = ExecutionCidState::new();
|
||||
@ -745,7 +751,7 @@ fn ap_canon_stream_map_with_numeric_key_accessor_lambda() {
|
||||
args = [call_arg]
|
||||
),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace,);
|
||||
assert_eq!(&*actual_trace, expected_trace,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -17,7 +17,9 @@
|
||||
use air::ExecutionCidState;
|
||||
use air::UncatchableError;
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::key_utils::at;
|
||||
use air_test_utils::prelude::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
// Check that %init_peer_id% alias works correctly (by comparing result with it and explicit peer id).
|
||||
// Additionally, check that empty string for data does the same as empty call path.
|
||||
@ -199,7 +201,9 @@ fn string_parameters() {
|
||||
|
||||
#[test]
|
||||
fn call_canon_stream_map_arg() {
|
||||
let vm_1_peer_id = "vm_1_peer_id";
|
||||
let vm_1_peer_name = "vm_1_peer_id";
|
||||
let vm_1_peer_id = at(vm_1_peer_name);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
@ -208,16 +212,16 @@ fn call_canon_stream_map_arg() {
|
||||
(ap (-42 "value2") %map)
|
||||
)
|
||||
(seq
|
||||
(canon "{vm_1_peer_id}" %map #%canon_map)
|
||||
(call "{vm_1_peer_id}" ("m" "f") [#%canon_map] scalar) ; behaviour = echo
|
||||
(canon "{vm_1_peer_name}" %map #%canon_map)
|
||||
(call "{vm_1_peer_name}" ("m" "f") [#%canon_map] scalar) ; behaviour = echo
|
||||
)
|
||||
)
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_id), &script)
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_name), &script)
|
||||
.expect("invalid test AIR script");
|
||||
let result = executor.execute_all(vm_1_peer_id).unwrap();
|
||||
let result = executor.execute_all(vm_1_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result.last().unwrap());
|
||||
|
||||
let mut cid_tracker: ExecutionCidState = ExecutionCidState::new();
|
||||
@ -260,35 +264,32 @@ fn call_canon_stream_map_arg() {
|
||||
),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
actual_trace, expected_trace,
|
||||
"{:#?}\n {:#?}",
|
||||
actual_trace, expected_trace
|
||||
);
|
||||
assert_eq!(&*actual_trace, expected_trace,);
|
||||
}
|
||||
|
||||
// WIP add negative
|
||||
#[test]
|
||||
fn call_peer_id_from_canon_stream_map() {
|
||||
let vm_1_peer_id = "vm_1_peer_id";
|
||||
let vm_1_peer_name = "vm_1_peer_id";
|
||||
let vm_1_peer_id = at(vm_1_peer_name);
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(ap ("peerid" "{vm_1_peer_id}") %map)
|
||||
(ap ("peerid" @"{vm_1_peer_name}") %map)
|
||||
(ap (-42 "value2") %map)
|
||||
)
|
||||
(seq
|
||||
(canon "{vm_1_peer_id}" %map #%canon_map)
|
||||
(canon "{vm_1_peer_name}" %map #%canon_map)
|
||||
(call #%canon_map.$.peerid.[0] ("m" "f") [#%canon_map] scalar) ; behaviour = echo
|
||||
)
|
||||
)
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_id), &script)
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_name), &script)
|
||||
.expect("invalid test AIR script");
|
||||
let result = executor.execute_all(vm_1_peer_id).unwrap();
|
||||
let result = executor.execute_all(vm_1_peer_name).unwrap();
|
||||
let actual_trace = trace_from_result(&result.last().unwrap());
|
||||
|
||||
let mut cid_tracker: ExecutionCidState = ExecutionCidState::new();
|
||||
@ -330,11 +331,7 @@ fn call_peer_id_from_canon_stream_map() {
|
||||
),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
actual_trace, expected_trace,
|
||||
"{:#?}\n {:#?}",
|
||||
actual_trace, expected_trace
|
||||
);
|
||||
assert_eq!(&*actual_trace, expected_trace,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
use air::ExecutionCidState;
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::key_utils::at;
|
||||
use air_test_utils::prelude::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@ -695,29 +696,31 @@ fn test_merge_executed() {
|
||||
|
||||
#[test]
|
||||
fn canon_stream_map() {
|
||||
let vm_peer_id_1 = "vm_peer_id_1";
|
||||
let vm_peer_id_1_name = "vm_peer_id_1";
|
||||
let vm_peer_id_1_id = at(vm_peer_id_1_name);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(ap (42 "value2") %map)
|
||||
(seq
|
||||
(ap ("key" "value1") %map)
|
||||
(canon "{vm_peer_id_1}" %map #%canon_map)
|
||||
(canon "{vm_peer_id_1_name}" %map #%canon_map)
|
||||
)
|
||||
)
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_peer_id_1), &script)
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_peer_id_1_name), &script)
|
||||
.expect("invalid test AIR script");
|
||||
let result = executor.execute_all(vm_peer_id_1).unwrap();
|
||||
let result = executor.execute_all(vm_peer_id_1_name).unwrap();
|
||||
|
||||
let actual_trace = trace_from_result(&result.last().unwrap());
|
||||
let actual_data = data_from_result(&result.last().unwrap());
|
||||
|
||||
let mut cid_state: ExecutionCidState = ExecutionCidState::new();
|
||||
let map_value1 = json!({"key": 42, "value": "value2"});
|
||||
let map_value2 = json!({"key": "key", "value": "value1"});
|
||||
let tetraplet = json!({"function_name": "", "json_path": "", "peer_pk": "vm_peer_id_1", "service_id": ""});
|
||||
let tetraplet = json!({"function_name": "", "json_path": "", "peer_pk": vm_peer_id_1_id, "service_id": ""});
|
||||
|
||||
let states_vec = vec![
|
||||
executed_state::ap(0),
|
||||
@ -741,11 +744,7 @@ fn canon_stream_map() {
|
||||
|
||||
let expected_trace = ExecutionTrace::from(states_vec.clone());
|
||||
|
||||
assert_eq!(
|
||||
actual_trace, expected_trace,
|
||||
"left {:#?} \n right {:#?}",
|
||||
actual_trace, expected_trace
|
||||
);
|
||||
assert_eq!(actual_data.trace, expected_trace, "{:#?}", actual_data.cid_info,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1126,11 +1125,7 @@ fn canon_map_non_existing_index_tetraplet_check() {
|
||||
let expected_trace = ExecutionTrace::from(states_vec.clone());
|
||||
let expected_tetraplet = RefCell::new(vec![vec![SecurityTetraplet::new(vm_peer_id_1, "", "", ".$.key")]]);
|
||||
|
||||
assert_eq!(
|
||||
actual_trace, expected_trace,
|
||||
"left {:#?} \n right {:#?}",
|
||||
actual_trace, expected_trace
|
||||
);
|
||||
assert_eq!(actual_trace, expected_trace,);
|
||||
|
||||
assert_eq!(tetraplet_checker.as_ref(), &expected_tetraplet);
|
||||
}
|
||||
@ -1221,11 +1216,7 @@ fn canon_map_non_existing_index_and_element_tetraplet_check() {
|
||||
".$.[43].[2].some",
|
||||
)]]);
|
||||
|
||||
assert_eq!(
|
||||
actual_trace, expected_trace,
|
||||
"left {:#?} \n right {:#?}",
|
||||
actual_trace, expected_trace
|
||||
);
|
||||
assert_eq!(actual_trace, expected_trace,);
|
||||
|
||||
assert_eq!(tetraplet_checker.as_ref(), &expected_tetraplet);
|
||||
}
|
||||
@ -1264,13 +1255,13 @@ fn canon_map_2_scalar_tetraplet_check() {
|
||||
|
||||
let result = checked_call_vm!(vm_1, <_>::default(), &script, "", "");
|
||||
|
||||
let actual_trace = trace_from_result(&result);
|
||||
let actual_data = data_from_result(&result);
|
||||
|
||||
let mut cid_state: ExecutionCidState = ExecutionCidState::new();
|
||||
let map_value1 = json!({"42": "value1", "key": "value1"});
|
||||
let call_result = map_value1.clone();
|
||||
|
||||
let tetraplet = json!({"function_name": "", "json_path": "", "peer_pk": "vm_peer_id_1", "service_id": ""});
|
||||
let tetraplet = json!({"function_name": "", "json_path": "", "peer_pk": vm_peer_id_1, "service_id": ""});
|
||||
|
||||
let states_vec = vec![
|
||||
executed_state::ap(0),
|
||||
@ -1302,11 +1293,7 @@ fn canon_map_2_scalar_tetraplet_check() {
|
||||
|
||||
assert_eq!(tetraplet_checker.as_ref(), &expected_tetraplet);
|
||||
|
||||
assert_eq!(
|
||||
actual_trace, expected_trace,
|
||||
"left {:#?} \n right {:#?}",
|
||||
actual_trace, expected_trace
|
||||
);
|
||||
assert_eq!(actual_data.trace, expected_trace, "{:#?}", actual_data.cid_info,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1343,13 +1330,13 @@ fn canon_map_2_scalar_with_lens_tetraplet_check() {
|
||||
);
|
||||
|
||||
let result = checked_call_vm!(vm_1, <_>::default(), &script, "", "");
|
||||
let actual_trace = trace_from_result(&result);
|
||||
let actual_data = data_from_result(&result);
|
||||
|
||||
let mut cid_state: ExecutionCidState = ExecutionCidState::new();
|
||||
let map_value1 = json!({"42": "value1", "key": "value1"});
|
||||
let call_result = json!("value1");
|
||||
|
||||
let tetraplet = json!({"function_name": "", "json_path": "", "peer_pk": "vm_peer_id_1", "service_id": ""});
|
||||
let tetraplet = json!({"function_name": "", "json_path": "", "peer_pk": vm_peer_id_1, "service_id": ""});
|
||||
|
||||
let states_vec = vec![
|
||||
executed_state::ap(0),
|
||||
@ -1381,11 +1368,7 @@ fn canon_map_2_scalar_with_lens_tetraplet_check() {
|
||||
|
||||
assert_eq!(tetraplet_checker.as_ref(), &expected_tetraplet);
|
||||
|
||||
assert_eq!(
|
||||
actual_trace, expected_trace,
|
||||
"left {:#?} \n right {:#?}",
|
||||
actual_trace, expected_trace
|
||||
);
|
||||
assert_eq!(actual_data.trace, expected_trace, "{:#?}", actual_data.cid_info,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -19,6 +19,7 @@ use air::PreparationError;
|
||||
use air::ToErrorCode;
|
||||
use air_interpreter_data::ExecutionTrace;
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::key_utils::at;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
@ -936,7 +937,9 @@ fn fold_stream_map() {
|
||||
|
||||
#[test]
|
||||
fn fold_canon_stream_map() {
|
||||
let vm_1_peer_id = "vm_1_peer_id";
|
||||
let vm_1_peer_name = "vm_1_peer_id";
|
||||
let vm_1_peer_id = at(vm_1_peer_name);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
@ -945,10 +948,10 @@ fn fold_canon_stream_map() {
|
||||
(ap (-42 "value2") %map)
|
||||
)
|
||||
(seq
|
||||
(canon "{vm_1_peer_id}" %map #%canon_map)
|
||||
(canon "{vm_1_peer_name}" %map #%canon_map)
|
||||
(fold #%canon_map iter
|
||||
(seq
|
||||
(call "{vm_1_peer_id}" ("m" "f") [iter] scalar) ; behaviour = echo
|
||||
(call "{vm_1_peer_name}" ("m" "f") [iter] scalar) ; behaviour = echo
|
||||
(next iter)
|
||||
)
|
||||
)
|
||||
@ -957,9 +960,9 @@ fn fold_canon_stream_map() {
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_id), &script)
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_name), &script)
|
||||
.expect("invalid test AIR script");
|
||||
let result = executor.execute_all(vm_1_peer_id).unwrap();
|
||||
let result = executor.execute_all(vm_1_peer_name).unwrap();
|
||||
|
||||
let actual_trace = trace_from_result(&result.last().unwrap());
|
||||
|
||||
@ -991,7 +994,7 @@ fn fold_canon_stream_map() {
|
||||
scalar_tracked!(
|
||||
map_value_1.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..0",
|
||||
function = "f",
|
||||
args = [map_value_1]
|
||||
@ -1006,7 +1009,7 @@ fn fold_canon_stream_map() {
|
||||
),
|
||||
];
|
||||
|
||||
assert_eq!(actual_trace, expected_trace,);
|
||||
assert_eq!(&*actual_trace, expected_trace,);
|
||||
}
|
||||
|
||||
/// This test checks that fold over map and fold over canon map both produce
|
||||
@ -1015,7 +1018,8 @@ fn fold_canon_stream_map() {
|
||||
/// increments service name index for each call used.
|
||||
#[test]
|
||||
fn fold_map_and_canon_map_orders_are_same() {
|
||||
let vm_1_peer_id = "vm_1_peer_id";
|
||||
let vm_1_peer_name = "vm_1_peer_id";
|
||||
let vm_1_peer_id = at(vm_1_peer_name);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
@ -1032,17 +1036,17 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(canon "{vm_1_peer_id}" %map #%canon_map)
|
||||
(canon "{vm_1_peer_name}" %map #%canon_map)
|
||||
(fold #%canon_map iter
|
||||
(seq
|
||||
(call "{vm_1_peer_id}" ("m" "f") [iter] scalar) ; behaviour = echo
|
||||
(call "{vm_1_peer_name}" ("m" "f") [iter] scalar) ; behaviour = echo
|
||||
(next iter)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fold %map iter
|
||||
(seq
|
||||
(call "{vm_1_peer_id}" ("m" "f") [iter] scalar1) ; behaviour = echo
|
||||
(call "{vm_1_peer_name}" ("m" "f") [iter] scalar1) ; behaviour = echo
|
||||
(next iter)
|
||||
)
|
||||
)
|
||||
@ -1051,9 +1055,9 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
"#
|
||||
);
|
||||
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_id), &script)
|
||||
let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(vm_1_peer_name), &script)
|
||||
.expect("invalid test AIR script");
|
||||
let result = executor.execute_all(vm_1_peer_id).unwrap();
|
||||
let result = executor.execute_all(vm_1_peer_name).unwrap();
|
||||
|
||||
let actual_trace = trace_from_result(&result.last().unwrap());
|
||||
|
||||
@ -1099,7 +1103,7 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
scalar_tracked!(
|
||||
map_value_1.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..0",
|
||||
function = "f",
|
||||
args = [map_value_1.clone()]
|
||||
@ -1107,7 +1111,7 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
scalar_tracked!(
|
||||
map_value_2.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..0",
|
||||
function = "f",
|
||||
args = [map_value_2.clone()]
|
||||
@ -1115,7 +1119,7 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
scalar_tracked!(
|
||||
map_value_3.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..0",
|
||||
function = "f",
|
||||
args = [map_value_3.clone()]
|
||||
@ -1123,7 +1127,7 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
scalar_tracked!(
|
||||
map_value_4.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..0",
|
||||
function = "f",
|
||||
args = [map_value_4.clone()]
|
||||
@ -1137,7 +1141,7 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
scalar_tracked!(
|
||||
map_value_1.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..1",
|
||||
function = "f",
|
||||
args = [map_value_1]
|
||||
@ -1145,7 +1149,7 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
scalar_tracked!(
|
||||
map_value_2.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..1",
|
||||
function = "f",
|
||||
args = [map_value_2]
|
||||
@ -1153,7 +1157,7 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
scalar_tracked!(
|
||||
map_value_3.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..1",
|
||||
function = "f",
|
||||
args = [map_value_3]
|
||||
@ -1161,12 +1165,12 @@ fn fold_map_and_canon_map_orders_are_same() {
|
||||
scalar_tracked!(
|
||||
map_value_4.clone(),
|
||||
cid_tracker,
|
||||
peer = vm_1_peer_id,
|
||||
peer = &vm_1_peer_id,
|
||||
service = "m..1",
|
||||
function = "f",
|
||||
args = [map_value_4]
|
||||
),
|
||||
];
|
||||
|
||||
assert_eq!(actual_trace, expected_trace,);
|
||||
assert_eq!(&*actual_trace, expected_trace);
|
||||
}
|
||||
|
@ -413,7 +413,7 @@ fn match_with_undefined_last_error_errcode() {
|
||||
let expected_trace = ExecutionTrace::from(vec![scalar_tracked!(
|
||||
errcode_lambda_output.clone(),
|
||||
cid_state,
|
||||
peer = local_peer_id,
|
||||
peer_name = local_peer_id,
|
||||
service = "test..0",
|
||||
function = "error_code",
|
||||
args = vec![errcode_lambda_output]
|
||||
@ -444,7 +444,7 @@ fn match_with_undefined_last_error_message() {
|
||||
let expected_trace = ExecutionTrace::from(vec![scalar_tracked!(
|
||||
message_lambda_output.clone(),
|
||||
cid_state,
|
||||
peer = local_peer_id,
|
||||
peer_name = local_peer_id,
|
||||
service = "test..0",
|
||||
function = "message",
|
||||
args = vec![message_lambda_output]
|
||||
|
@ -19,23 +19,23 @@ use air_test_utils::prelude::*;
|
||||
#[test]
|
||||
// https://github.com/fluencelabs/aquavm/issues/178
|
||||
fn par_ap_behaviour() {
|
||||
let client_id = "client_id";
|
||||
let relay_id = "relay_id";
|
||||
let variable_setter_id = "variable_setter_id";
|
||||
let client_name = "client_id";
|
||||
let relay_name = "relay_id";
|
||||
let variable_setter_name = "variable_setter_id";
|
||||
|
||||
// ap doesn't affect the subgraph_complete flag
|
||||
let script = format!(
|
||||
r#"
|
||||
(par
|
||||
(call "{variable_setter_id}" ("peer" "timeout") [] join_it) ; behaviour=unit
|
||||
(call "{variable_setter_name}" ("peer" "timeout") [] join_it) ; behaviour=unit
|
||||
(seq
|
||||
(par
|
||||
(call "{relay_id}" ("peer" "timeout") [join_it] $result) ; behaviour=unit
|
||||
(call "{relay_name}" ("peer" "timeout") [join_it] $result) ; behaviour=unit
|
||||
(ap "fast_result" $result)
|
||||
)
|
||||
(seq
|
||||
(canon "{client_id}" $result #result)
|
||||
(call "{client_id}" ("op" "return") [#result.$[0]]) ; behaviour=unit
|
||||
(canon "{client_name}" $result #result)
|
||||
(call "{client_name}" ("op" "return") [#result.$[0]]) ; behaviour=unit
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -43,15 +43,21 @@ fn par_ap_behaviour() {
|
||||
);
|
||||
|
||||
let engine =
|
||||
air_test_framework::AirScriptExecutor::from_annotated(TestRunParameters::new("client_id", 0, 1, ""), &script)
|
||||
air_test_framework::AirScriptExecutor::from_annotated(TestRunParameters::new(client_name, 0, 1, ""), &script)
|
||||
.expect("invalid test executor config");
|
||||
|
||||
let client_result_1 = engine.execute_one(client_id).unwrap();
|
||||
assert_next_pks!(&client_result_1.next_peer_pks, [relay_id, variable_setter_id]);
|
||||
let relay_id = engine.resolve_name(relay_name).to_string();
|
||||
let variable_setter_id = engine.resolve_name(variable_setter_name).to_string();
|
||||
|
||||
let setter_result = engine.execute_one(variable_setter_id).unwrap();
|
||||
let client_result_1 = engine.execute_one(client_name).unwrap();
|
||||
assert_next_pks!(
|
||||
&client_result_1.next_peer_pks,
|
||||
[relay_id.as_str(), variable_setter_id.as_str()]
|
||||
);
|
||||
|
||||
let setter_result = engine.execute_one(variable_setter_name).unwrap();
|
||||
assert!(setter_result.next_peer_pks.is_empty());
|
||||
|
||||
let relay_result = engine.execute_one(relay_id).unwrap();
|
||||
let relay_result = engine.execute_one(relay_name).unwrap();
|
||||
assert!(relay_result.next_peer_pks.is_empty());
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ use pretty_assertions::assert_eq;
|
||||
// test for github.com/fluencelabs/aquavm/issues/211
|
||||
// On the versions < 0.20.1 it just crashes
|
||||
fn issue_211() {
|
||||
let peer_1_id = "peer_1_id";
|
||||
let peer_1_name = "peer_1_id";
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
@ -68,23 +68,25 @@ fn issue_211() {
|
||||
"#
|
||||
);
|
||||
|
||||
let run_params = TestRunParameters::from_init_peer_id(peer_1_id);
|
||||
let run_params = TestRunParameters::from_init_peer_id(peer_1_name);
|
||||
|
||||
let engine = air_test_framework::AirScriptExecutor::from_annotated(run_params, &script)
|
||||
.expect("invalid test executor config");
|
||||
let peer_1_id = engine.resolve_name(peer_1_name).to_string();
|
||||
let peer_1_id = peer_1_id.as_str();
|
||||
|
||||
let result = engine.execute_one(peer_1_id).unwrap();
|
||||
let result = engine.execute_one(peer_1_name).unwrap();
|
||||
|
||||
let scalar_2 = scalar!(
|
||||
json!([1, 2, 3]),
|
||||
peer = peer_1_id,
|
||||
peer_name = peer_1_name,
|
||||
service = "getdatasrv..1",
|
||||
function = "nodes"
|
||||
);
|
||||
let cid_2 = extract_service_result_cid(&scalar_2);
|
||||
|
||||
let expected_trace = ExecutionTrace::from(vec![
|
||||
scalar!(2, peer = peer_1_id, service = "getdatasrv..0", function = "idx"),
|
||||
scalar!(2, peer_name = peer_1_name, service = "getdatasrv..0", function = "idx"),
|
||||
scalar_2,
|
||||
executed_state::par(6, 0),
|
||||
executed_state::par(1, 4),
|
||||
@ -94,55 +96,55 @@ fn issue_211() {
|
||||
executed_state::par(1, 0),
|
||||
executed_state::ap(0),
|
||||
executed_state::canon(json!({
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "peer_1_id", "service_id": ""},
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": peer_1_id, "service_id": ""},
|
||||
"values": [
|
||||
{
|
||||
"result": 1,
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[0]", "peer_pk": "peer_1_id", "service_id": "getdatasrv..1"},
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[0]", "peer_pk": peer_1_id, "service_id": "getdatasrv..1"},
|
||||
"provenance": Provenance::service_result(cid_2.clone()),
|
||||
},
|
||||
{
|
||||
"result": 2,
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[1]", "peer_pk": "peer_1_id", "service_id": "getdatasrv..1"},
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[1]", "peer_pk": peer_1_id, "service_id": "getdatasrv..1"},
|
||||
"provenance": Provenance::service_result(cid_2.clone()),
|
||||
},
|
||||
{
|
||||
"result": 3,
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[2]", "peer_pk": "peer_1_id", "service_id": "getdatasrv..1"},
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[2]", "peer_pk": peer_1_id, "service_id": "getdatasrv..1"},
|
||||
"provenance": Provenance::service_result(cid_2.clone()),
|
||||
},
|
||||
]
|
||||
})),
|
||||
unused!(
|
||||
"expected result",
|
||||
peer = peer_1_id,
|
||||
peer_name = peer_1_name,
|
||||
service = "op..2",
|
||||
function = "noop",
|
||||
args = vec![json!(3), json!([1, 2, 3])]
|
||||
),
|
||||
executed_state::canon(json!({
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": "peer_1_id", "service_id": ""},
|
||||
"tetraplet": {"function_name": "", "json_path": "", "peer_pk": peer_1_id, "service_id": ""},
|
||||
"values": [
|
||||
{
|
||||
"result": 1,
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[0]", "peer_pk": "peer_1_id", "service_id": "getdatasrv..1"},
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[0]", "peer_pk": peer_1_id, "service_id": "getdatasrv..1"},
|
||||
"provenance": Provenance::service_result(cid_2.clone()),
|
||||
},
|
||||
{
|
||||
"result": 2,
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[1]", "peer_pk": "peer_1_id", "service_id": "getdatasrv..1"},
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[1]", "peer_pk": peer_1_id, "service_id": "getdatasrv..1"},
|
||||
"provenance": Provenance::service_result(cid_2.clone()),
|
||||
},
|
||||
{
|
||||
"result": 3,
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[2]", "peer_pk": "peer_1_id", "service_id": "getdatasrv..1"},
|
||||
"tetraplet": {"function_name": "nodes", "json_path": ".$.[2]", "peer_pk": peer_1_id, "service_id": "getdatasrv..1"},
|
||||
"provenance": Provenance::service_result(cid_2),
|
||||
},
|
||||
]
|
||||
})),
|
||||
scalar!(
|
||||
"expected result",
|
||||
peer = peer_1_id,
|
||||
peer_name = peer_1_name,
|
||||
service = "op..3",
|
||||
function = "identity",
|
||||
args = vec![json!([1, 2, 3])]
|
||||
|
@ -16,17 +16,18 @@
|
||||
|
||||
use air_interpreter_data::ExecutionTrace;
|
||||
use air_test_framework::AirScriptExecutor;
|
||||
use air_test_utils::key_utils::at;
|
||||
use air_test_utils::prelude::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
// test for github.com/fluencelabs/aquavm/issues/221
|
||||
fn issue_221() {
|
||||
let peer_1_id = "peer_1_id";
|
||||
let peer_2_id = "peer_2_id";
|
||||
let join_1_id = "join_1_id";
|
||||
let join_2_id = "join_2_id";
|
||||
let set_variable_id = "set_variable_id";
|
||||
let peer_1_name = "peer_1_id";
|
||||
let peer_2_name = "peer_2_id";
|
||||
let join_1_name = "join_1_id";
|
||||
let join_2_name = "join_2_id";
|
||||
let set_variable_name = "set_variable_id";
|
||||
|
||||
let peer_1_value = "peer_1_value";
|
||||
let peer_2_value = "peer_2_value";
|
||||
@ -37,11 +38,11 @@ fn issue_221() {
|
||||
(seq
|
||||
(seq
|
||||
;; let's peers be an array of two values [peer_1_id, peer_2_id]
|
||||
(call "{set_variable_id}" ("" "") [] peers) ; ok = ["{peer_1_id}", "{peer_2_id}"]
|
||||
(call "{set_variable_name}" ("" "") [] peers) ; ok = [@"{peer_1_name}", @"{peer_2_name}"]
|
||||
(fold peers peer
|
||||
(par
|
||||
(seq
|
||||
(call peer ("" "") [peer] value) ; map = {{"{peer_1_id}": "{peer_1_value}", "{peer_2_id}": "{peer_2_value}"}}
|
||||
(call peer ("" "") [peer] value) ; map = {{@"{peer_1_name}": "{peer_1_value}", @"{peer_2_name}": "{peer_2_value}"}}
|
||||
;; it's crucial to reproduce this bug to add value to stream
|
||||
;; with help of ap instruction
|
||||
(ap value $stream)
|
||||
@ -58,14 +59,14 @@ fn issue_221() {
|
||||
;; appropriate way and state for (1) is returned
|
||||
(par
|
||||
(par
|
||||
(call "{join_1_id}" ("" "") [iterator]) ; behaviour = echo
|
||||
(call "{join_2_id}" ("" "") [iterator]) ; behaviour = echo
|
||||
(call "{join_1_name}" ("" "") [iterator]) ; behaviour = echo
|
||||
(call "{join_2_name}" ("" "") [iterator]) ; behaviour = echo
|
||||
)
|
||||
(next iterator)
|
||||
)
|
||||
)
|
||||
)
|
||||
(call "some_peer_id" ("" "") []) ;; (1)
|
||||
(call "some_peer_name" ("" "") []) ;; (1)
|
||||
)
|
||||
"#
|
||||
);
|
||||
@ -73,25 +74,43 @@ fn issue_221() {
|
||||
let executor = <AirScriptExecutor>::new(
|
||||
TestRunParameters::from_init_peer_id("set_variable_id"),
|
||||
vec![],
|
||||
vec![peer_1_id, peer_2_id].into_iter().map(Into::into),
|
||||
vec![peer_1_name, peer_2_name].into_iter().map(Into::into),
|
||||
&script,
|
||||
)
|
||||
.expect("Invalid annotated AIR script");
|
||||
|
||||
let _result = executor.execute_one(set_variable_id).unwrap();
|
||||
let _peer_1_result = executor.execute_one(peer_1_id).unwrap();
|
||||
let _peer_2_result = executor.execute_one(peer_2_id).unwrap();
|
||||
let peer_1_id = at(peer_1_name);
|
||||
let peer_2_id = at(peer_2_name);
|
||||
let join_1_id = at(join_1_name);
|
||||
|
||||
let _join_1_result = executor.execute_one(join_1_id).unwrap();
|
||||
let join_1_result = executor.execute_one(join_1_id).unwrap(); // before 0.20.9 it fails here
|
||||
let _result = executor.execute_one(set_variable_name).unwrap();
|
||||
let _peer_1_result = executor.execute_one(peer_1_name).unwrap();
|
||||
let _peer_2_result = executor.execute_one(peer_2_name).unwrap();
|
||||
|
||||
let _join_1_result = executor.execute_one(join_1_name).unwrap();
|
||||
let join_1_result = executor.execute_one(join_1_name).unwrap(); // before 0.20.9 it fails here
|
||||
let actual_trace = trace_from_result(&join_1_result);
|
||||
let expected_trace = ExecutionTrace::from(vec![
|
||||
scalar!(json!([peer_1_id, peer_2_id]), peer = set_variable_id, service = "..0"),
|
||||
scalar!(
|
||||
json!([peer_1_id, peer_2_id]),
|
||||
peer_name = set_variable_name,
|
||||
service = "..0"
|
||||
),
|
||||
executed_state::par(2, 3),
|
||||
scalar!(peer_1_value, peer = peer_1_id, service = "..1", args = vec![peer_1_id]),
|
||||
scalar!(
|
||||
peer_1_value,
|
||||
peer_name = peer_1_name,
|
||||
service = "..1",
|
||||
args = vec![peer_1_id.as_str()]
|
||||
),
|
||||
executed_state::ap(0),
|
||||
executed_state::par(2, 0),
|
||||
scalar!(peer_2_value, peer = peer_2_id, service = "..1", args = vec![peer_2_id]),
|
||||
scalar!(
|
||||
peer_2_value,
|
||||
peer_name = peer_2_name,
|
||||
service = "..1",
|
||||
args = vec![peer_2_id.as_str()]
|
||||
),
|
||||
executed_state::ap(1),
|
||||
executed_state::fold(vec![
|
||||
executed_state::subtrace_lore(3, SubTraceDesc::new(8.into(), 4), SubTraceDesc::new(12.into(), 0)),
|
||||
@ -101,7 +120,7 @@ fn issue_221() {
|
||||
executed_state::par(1, 1),
|
||||
unused!(
|
||||
peer_1_value,
|
||||
peer = join_1_id,
|
||||
peer_name = join_1_name,
|
||||
service = "..2",
|
||||
args = vec![peer_1_value]
|
||||
),
|
||||
@ -110,7 +129,7 @@ fn issue_221() {
|
||||
executed_state::par(1, 1),
|
||||
unused!(
|
||||
peer_2_value,
|
||||
peer = join_1_id,
|
||||
peer_name = join_1_name,
|
||||
service = "..2",
|
||||
args = vec![peer_2_value]
|
||||
),
|
||||
|
@ -22,9 +22,9 @@ fn issue_356() {
|
||||
let script = r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "relay" ("kad" "neighborhood") ["relay"] neighs_top) ; ok = ["p1"]
|
||||
(call "relay" ("kad" "neighborhood") ["relay"] neighs_top) ; ok = [@"p1"]
|
||||
(seq
|
||||
(call "p1" ("kad" "neighborhood") ["p1"] neighs_inner) ; ok =["p1"]
|
||||
(call "p1" ("kad" "neighborhood") ["p1"] neighs_inner) ; ok =[@"p1"]
|
||||
(par
|
||||
(call "relay" ("peer" "identify") ["relay"] $external_addresses) ; behaviour = echo
|
||||
(call "p1" ("peer" "identify") ["p1"] $external_addresses) ; behaviour = echo
|
||||
|
@ -145,7 +145,7 @@ fn set_subtrace_len_and_pos_failed() {
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
let trace = vec![
|
||||
executed_state::par(1, 2),
|
||||
stream_tracked!(json!([42, 43]), 0, cid_state),
|
||||
stream_tracked!(json!([42, 43]), 0, cid_state, peer = vm_peer_id_1),
|
||||
executed_state::fold(vec![executed_state::subtrace_lore(
|
||||
1,
|
||||
subtrace_desc(5, 1),
|
||||
@ -184,7 +184,7 @@ fn no_element_at_position() {
|
||||
let mut cid_state = ExecutionCidState::new();
|
||||
let trace = vec![
|
||||
executed_state::par(1, 2),
|
||||
stream_tracked!(json!([42, 43]), 0, cid_state),
|
||||
stream_tracked!(json!([42, 43]), 0, cid_state, peer = vm_peer_id_1),
|
||||
executed_state::fold(vec![executed_state::subtrace_lore(
|
||||
42,
|
||||
subtrace_desc(3, 1),
|
||||
@ -224,7 +224,7 @@ fn no_stream_state() {
|
||||
let wrong_state = request_sent_by("vm_peer_id_1");
|
||||
let trace = vec![
|
||||
executed_state::par(1, 2),
|
||||
stream_tracked!(json!([42, 43]), 0, &mut tracker),
|
||||
stream_tracked!(json!([42, 43]), 0, &mut tracker, peer = vm_peer_id_1),
|
||||
executed_state::fold(vec![executed_state::subtrace_lore(
|
||||
3,
|
||||
subtrace_desc(3, 1), // try to change the number of elems to 3
|
||||
@ -444,7 +444,7 @@ fn several_records_with_same_pos() {
|
||||
let value_pos = 1;
|
||||
let trace = vec![
|
||||
executed_state::par(1, 2),
|
||||
stream_tracked!(json!([42, 43]), 0, cid_state),
|
||||
stream_tracked!(json!([42, 43]), 0, &mut cid_state, peer = vm_peer_id_1),
|
||||
fold(vec![
|
||||
subtrace_lore(value_pos, subtrace_desc(3, 1), subtrace_desc(4, 0)),
|
||||
subtrace_lore(value_pos, subtrace_desc(3, 1), subtrace_desc(4, 0)),
|
||||
@ -543,7 +543,7 @@ fn fold_pos_overflow() {
|
||||
let wrong_after_subtrace_len = TraceLen::MAX - 1;
|
||||
let trace = vec![
|
||||
executed_state::par(1, 2),
|
||||
stream_tracked!(json!([42, 43]), 0, cid_state),
|
||||
stream_tracked!(json!([42, 43]), 0, cid_state, peer = vm_peer_id_1),
|
||||
fold(vec![subtrace_lore(
|
||||
value_pos,
|
||||
subtrace_desc(before_subtrace_pos, 1),
|
||||
|
@ -143,7 +143,7 @@ fn malformed_call_service_failed() {
|
||||
let tetraplet_cid = cid_state.tetraplet_tracker.track_value(tetraplet).unwrap();
|
||||
let service_result_agg = ServiceResultCidAggregate {
|
||||
value_cid,
|
||||
argument_hash: "0000000000000".into(),
|
||||
argument_hash: "bagaaieraj5j43immfovaya2uxnpzupwl4xwrfk2nryi3vbz4f4irmeqcxfcq".into(),
|
||||
tetraplet_cid,
|
||||
};
|
||||
let service_result_agg_cid = cid_state
|
||||
@ -159,7 +159,7 @@ fn malformed_call_service_failed() {
|
||||
let result = vm.call(&air, vec![], data, TestRunParameters::default()).unwrap();
|
||||
let expected_serde_error = serde_json::from_value::<CallServiceFailed>(value).unwrap_err();
|
||||
let expected_error = MalformedCallServiceFailed(expected_serde_error);
|
||||
assert!(check_error(&result, expected_error));
|
||||
assert_error_eq!(&result, expected_error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -409,6 +409,469 @@ Machine c1f3ea5950db0a10b44da931c25774d64ab25084f47d504f72f311e694550ff1:
|
||||
execute: 29.00µs
|
||||
new: 38.00µs
|
||||
Machine d77ebe8481884bc3b2778c8083f1bf459e548e929edd87041beb14f6b868d35f:
|
||||
<<<<<<< HEAD
|
||||
Platform: macOS-13.4.1-arm64-arm-64bit
|
||||
Timestamp: 2023-07-23 15:42:03.477661+00:00
|
||||
AquaVM version: 0.42.0
|
||||
Benches:
|
||||
Features: check_signatures,gen_signatures
|
||||
big_values_data (9.29ms; 59.062 MiB, 59.062 MiB): Loading a trace with huge values
|
||||
air::runner::execute_air: 9.29ms
|
||||
preparation_step::preparation::parse_data: 4.90ms
|
||||
from_slice: 4.85ms
|
||||
preparation_step::preparation::prepare: 176.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 19.00µs
|
||||
air_parser::parser::air_parser::parse: 32.00µs
|
||||
runner::execute: 12.00µs
|
||||
runner::farewell: 3.86ms
|
||||
from_success_result: 3.84ms
|
||||
populate_outcome_from_contexts: 3.80ms
|
||||
signing_step::sign_produced_cids: 110.00µs
|
||||
verification_step::verify: 83.00µs
|
||||
verify: 10.00µs
|
||||
dashboard (6.71ms; 52.750 MiB, 52.750 MiB): big dashboard test
|
||||
air::runner::execute_air: 6.71ms
|
||||
preparation_step::preparation::parse_data: 1.00ms
|
||||
from_slice: 947.00µs
|
||||
preparation_step::preparation::prepare: 327.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 36.00µs
|
||||
air_parser::parser::air_parser::parse: 170.00µs
|
||||
runner::execute: 1.70ms
|
||||
to_string(tetraplets): 369.00µs
|
||||
runner::farewell: 485.00µs
|
||||
from_success_result: 465.00µs
|
||||
populate_outcome_from_contexts: 417.00µs
|
||||
signing_step::sign_produced_cids: 105.00µs
|
||||
verification_step::verify: 2.93ms
|
||||
verify: 197.00µs
|
||||
long_data (3.88ms; 54.000 MiB, 54.000 MiB): Long data trace
|
||||
air::runner::execute_air: 3.88ms
|
||||
preparation_step::preparation::parse_data: 1.76ms
|
||||
from_slice: 1.72ms
|
||||
preparation_step::preparation::prepare: 175.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 19.00µs
|
||||
air_parser::parser::air_parser::parse: 30.00µs
|
||||
runner::execute: 11.00µs
|
||||
runner::farewell: 1.06ms
|
||||
from_success_result: 1.04ms
|
||||
populate_outcome_from_contexts: 1.00ms
|
||||
signing_step::sign_produced_cids: 108.00µs
|
||||
verification_step::verify: 612.00µs
|
||||
verify: 10.00µs
|
||||
multiple-cids10 (2.63ms; 52.625 MiB, 52.625 MiB): verifying multiple CIDs for single peer
|
||||
air::runner::execute_air: 2.63ms
|
||||
preparation_step::preparation::parse_data: 418.00µs
|
||||
from_slice: 364.00µs
|
||||
preparation_step::preparation::prepare: 216.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 33.00µs
|
||||
air_parser::parser::air_parser::parse: 63.00µs
|
||||
runner::execute: 452.00µs
|
||||
to_string(tetraplets): 10.00µs
|
||||
runner::farewell: 342.00µs
|
||||
from_success_result: 322.00µs
|
||||
populate_outcome_from_contexts: 285.00µs
|
||||
signing_step::sign_produced_cids: 103.00µs
|
||||
verification_step::verify: 944.00µs
|
||||
verify: 227.00µs
|
||||
multiple-cids50 (24.68ms; 59.375 MiB, 59.375 MiB): verifying multiple CIDs for single peer
|
||||
air::runner::execute_air: 24.68ms
|
||||
preparation_step::preparation::parse_data: 4.87ms
|
||||
from_slice: 4.81ms
|
||||
preparation_step::preparation::prepare: 444.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 255.00µs
|
||||
air_parser::parser::air_parser::parse: 67.00µs
|
||||
runner::execute: 8.86ms
|
||||
to_string(tetraplets): 11.00µs
|
||||
runner::farewell: 3.40ms
|
||||
from_success_result: 3.38ms
|
||||
populate_outcome_from_contexts: 3.34ms
|
||||
signing_step::sign_produced_cids: 104.00µs
|
||||
verification_step::verify: 6.86ms
|
||||
verify: 4.68ms
|
||||
multiple-peers14 (36.77ms; 59.688 MiB, 59.688 MiB): verifying many CIDs for many peers
|
||||
air::runner::execute_air: 36.77ms
|
||||
preparation_step::preparation::parse_data: 5.62ms
|
||||
from_slice: 5.56ms
|
||||
preparation_step::preparation::prepare: 462.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 273.00µs
|
||||
air_parser::parser::air_parser::parse: 70.00µs
|
||||
runner::execute: 15.29ms
|
||||
to_string(tetraplets): 11.00µs
|
||||
runner::farewell: 3.80ms
|
||||
from_success_result: 3.77ms
|
||||
populate_outcome_from_contexts: 3.74ms
|
||||
signing_step::sign_produced_cids: 105.00µs
|
||||
verification_step::verify: 11.32ms
|
||||
verify: 5.12ms
|
||||
multiple-peers25 (188.40ms; 86.000 MiB, 86.000 MiB): verifying many CIDs for many peers
|
||||
air::runner::execute_air: 188.40ms
|
||||
preparation_step::preparation::parse_data: 30.22ms
|
||||
from_slice: 30.15ms
|
||||
preparation_step::preparation::prepare: 2.17ms
|
||||
air::preparation_step::preparation::make_exec_ctx: 1.97ms
|
||||
air_parser::parser::air_parser::parse: 79.00µs
|
||||
runner::execute: 87.63ms
|
||||
to_string(tetraplets): 22.00µs
|
||||
runner::farewell: 20.97ms
|
||||
from_success_result: 20.94ms
|
||||
populate_outcome_from_contexts: 20.88ms
|
||||
signing_step::sign_produced_cids: 115.00µs
|
||||
verification_step::verify: 46.95ms
|
||||
verify: 29.99ms
|
||||
multiple-peers5 (17.65ms; 52.625 MiB, 52.625 MiB): verifying many CIDs for many peers
|
||||
air::runner::execute_air: 17.65ms
|
||||
preparation_step::preparation::parse_data: 590.00µs
|
||||
from_slice: 540.00µs
|
||||
preparation_step::preparation::prepare: 229.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 35.00µs
|
||||
air_parser::parser::air_parser::parse: 67.00µs
|
||||
runner::execute: 13.61ms
|
||||
execute: 10.57ms
|
||||
execute: 2.47ms
|
||||
prepare_request_params: 44.00µs
|
||||
to_string(tetraplets): 13.00µs
|
||||
new: 1.83ms
|
||||
runner::farewell: 483.00µs
|
||||
from_success_result: 463.00µs
|
||||
populate_outcome_from_contexts: 429.00µs
|
||||
to_vec(call_results): 12.00µs
|
||||
to_vec(data): 209.00µs
|
||||
signing_step::sign_produced_cids: 109.00µs
|
||||
verification_step::verify: 2.48ms
|
||||
verify: 289.00µs
|
||||
multiple-sigs10 (13.63ms; 52.875 MiB, 52.875 MiB): signing multiple CIDs
|
||||
air::runner::execute_air: 13.63ms
|
||||
preparation_step::preparation::parse_data: 653.00µs
|
||||
from_slice: 602.00µs
|
||||
preparation_step::preparation::prepare: 224.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 37.00µs
|
||||
air_parser::parser::air_parser::parse: 65.00µs
|
||||
runner::execute: 10.85ms
|
||||
call::execute: 7.94ms
|
||||
execute: 1.77ms
|
||||
new: 1.35ms
|
||||
canon::execute: 496.00µs
|
||||
runner::farewell: 564.00µs
|
||||
from_success_result: 544.00µs
|
||||
populate_outcome_from_contexts: 485.00µs
|
||||
to_vec(call_results): 12.00µs
|
||||
to_vec(data): 227.00µs
|
||||
signing_step::sign_produced_cids: 155.00µs
|
||||
verification_step::verify: 1.02ms
|
||||
verify: 238.00µs
|
||||
multiple-sigs200 (906.10ms; 211.625 MiB, 211.625 MiB): signing multiple CIDs
|
||||
air::runner::execute_air: 906.10ms
|
||||
preparation_step::preparation::parse_data: 166.70ms
|
||||
from_slice: 166.67ms
|
||||
preparation_step::preparation::prepare: 16.93ms
|
||||
air::preparation_step::preparation::make_exec_ctx: 16.71ms
|
||||
air_parser::parser::air_parser::parse: 80.00µs
|
||||
runner::execute: 381.00ms
|
||||
runner::farewell: 165.40ms
|
||||
from_success_result: 165.40ms
|
||||
populate_outcome_from_contexts: 153.70ms
|
||||
signing_step::sign_produced_cids: 22.05ms
|
||||
verification_step::verify: 153.40ms
|
||||
verify: 84.07ms
|
||||
multiple-sigs50 (49.64ms; 62.625 MiB, 62.625 MiB): signing multiple CIDs
|
||||
air::runner::execute_air: 49.64ms
|
||||
preparation_step::preparation::parse_data: 10.52ms
|
||||
from_slice: 10.46ms
|
||||
preparation_step::preparation::prepare: 615.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 428.00µs
|
||||
air_parser::parser::air_parser::parse: 68.00µs
|
||||
runner::execute: 21.67ms
|
||||
runner::farewell: 6.97ms
|
||||
from_success_result: 6.95ms
|
||||
populate_outcome_from_contexts: 6.57ms
|
||||
signing_step::sign_produced_cids: 1.23ms
|
||||
verification_step::verify: 8.51ms
|
||||
verify: 4.56ms
|
||||
network-explore (3.12ms; 52.500 MiB, 52.500 MiB): 5 peers of network are discovered
|
||||
air::runner::execute_air: 3.12ms
|
||||
preparation_step::preparation::parse_data: 478.00µs
|
||||
from_slice: 423.00µs
|
||||
preparation_step::preparation::prepare: 233.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 27.00µs
|
||||
air_parser::parser::air_parser::parse: 86.00µs
|
||||
runner::execute: 174.00µs
|
||||
to_string(tetraplets): 10.00µs
|
||||
runner::farewell: 265.00µs
|
||||
from_success_result: 245.00µs
|
||||
populate_outcome_from_contexts: 208.00µs
|
||||
signing_step::sign_produced_cids: 105.00µs
|
||||
verification_step::verify: 1.71ms
|
||||
verify: 72.00µs
|
||||
network_explore (5.23ms; 52.500 MiB, 52.500 MiB): 5 peers of network are discovered
|
||||
air::runner::execute_air: 5.23ms
|
||||
preparation_step::preparation::parse_data: 480.00µs
|
||||
from_slice: 429.00µs
|
||||
preparation_step::preparation::prepare: 236.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 25.00µs
|
||||
air_parser::parser::air_parser::parse: 84.00µs
|
||||
runner::execute: 2.19ms
|
||||
execute: 1.67ms
|
||||
execute: 408.00µs
|
||||
prepare_request_params: 48.00µs
|
||||
to_string(tetraplets): 17.00µs
|
||||
new: 300.00µs
|
||||
runner::farewell: 326.00µs
|
||||
from_success_result: 306.00µs
|
||||
populate_outcome_from_contexts: 270.00µs
|
||||
to_vec(call_results): 12.00µs
|
||||
to_vec(data): 97.00µs
|
||||
signing_step::sign_produced_cids: 112.00µs
|
||||
verification_step::verify: 1.73ms
|
||||
verify: 76.00µs
|
||||
null (725.00µs; 52.500 MiB, 52.500 MiB): Empty data and null script
|
||||
air::runner::execute_air: 725.00µs
|
||||
preparation_step::preparation::parse_data: 20.00µs
|
||||
preparation_step::preparation::prepare: 193.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 24.00µs
|
||||
air_parser::parser::air_parser::parse: 39.00µs
|
||||
runner::execute: 11.00µs
|
||||
runner::farewell: 189.00µs
|
||||
from_success_result: 170.00µs
|
||||
populate_outcome_from_contexts: 133.00µs
|
||||
signing_step::sign_produced_cids: 112.00µs
|
||||
verification_step::verify: 38.00µs
|
||||
verify: 12.00µs
|
||||
parser_10000_100 (22.72ms; 57.812 MiB, 57.812 MiB): Running very long AIR script with lot of variables and assignments
|
||||
air::runner::execute_air: 22.72ms
|
||||
preparation_step::preparation::parse_data: 20.00µs
|
||||
preparation_step::preparation::prepare: 21.25ms
|
||||
air::preparation_step::preparation::make_exec_ctx: 23.00µs
|
||||
air_parser::parser::air_parser::parse: 21.11ms
|
||||
runner::execute: 47.00µs
|
||||
runner::farewell: 192.00µs
|
||||
from_success_result: 173.00µs
|
||||
populate_outcome_from_contexts: 137.00µs
|
||||
signing_step::sign_produced_cids: 110.00µs
|
||||
verification_step::verify: 39.00µs
|
||||
verify: 11.00µs
|
||||
Machine e536f8eaae8c978493a773ba566ae3393e2e6240d6ea8e05b5ca1b8f77e9c441:
|
||||
Platform: Linux-5.15.0-76-generic-x86_64-with-glibc2.29
|
||||
Timestamp: 2023-07-14 15:53:40.260043+00:00
|
||||
AquaVM version: 0.41.0
|
||||
Benches:
|
||||
Features: check_signatures,gen_signatures
|
||||
big_values_data (13.28ms; 59.062 MiB, 59.062 MiB): Loading a trace with huge values
|
||||
air::runner::execute_air: 13.28ms
|
||||
preparation_step::preparation::parse_data: 7.70ms
|
||||
from_slice: 7.64ms
|
||||
preparation_step::preparation::prepare: 226.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 16.00µs
|
||||
air_parser::parser::air_parser::parse: 21.00µs
|
||||
runner::execute: 10.00µs
|
||||
runner::farewell: 4.97ms
|
||||
from_success_result: 4.94ms
|
||||
populate_outcome_from_contexts: 4.90ms
|
||||
to_vec(call_results): 15.00µs
|
||||
to_vec(data): 3.93ms
|
||||
signing_step::sign_produced_cids: 154.00µs
|
||||
verification_step::verify: 90.00µs
|
||||
verify: 12.00µs
|
||||
dashboard (36.27ms; 52.750 MiB, 52.750 MiB): big dashboard test
|
||||
air::runner::execute_air: 36.27ms
|
||||
preparation_step::preparation::parse_data: 1.34ms
|
||||
from_slice: 1.28ms
|
||||
preparation_step::preparation::prepare: 350.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 42.00µs
|
||||
air_parser::parser::air_parser::parse: 130.00µs
|
||||
runner::execute: 29.00ms
|
||||
execute: 23.27ms
|
||||
execute: 6.94ms
|
||||
prepare_request_params: 1.86ms
|
||||
to_string(tetraplets): 573.00µs
|
||||
new: 3.79ms
|
||||
runner::farewell: 746.00µs
|
||||
from_success_result: 723.00µs
|
||||
populate_outcome_from_contexts: 678.00µs
|
||||
to_vec(call_results): 31.00µs
|
||||
to_vec(data): 379.00µs
|
||||
signing_step::sign_produced_cids: 159.00µs
|
||||
verification_step::verify: 4.55ms
|
||||
verify: 273.00µs
|
||||
long_data (5.23ms; 54.000 MiB, 54.000 MiB): Long data trace
|
||||
air::runner::execute_air: 5.23ms
|
||||
preparation_step::preparation::parse_data: 2.57ms
|
||||
from_slice: 2.53ms
|
||||
preparation_step::preparation::prepare: 223.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 16.00µs
|
||||
air_parser::parser::air_parser::parse: 20.00µs
|
||||
runner::execute: 10.00µs
|
||||
runner::farewell: 1.27ms
|
||||
from_success_result: 1.25ms
|
||||
populate_outcome_from_contexts: 1.21ms
|
||||
to_vec(call_results): 14.00µs
|
||||
to_vec(data): 658.00µs
|
||||
signing_step::sign_produced_cids: 156.00µs
|
||||
verification_step::verify: 858.00µs
|
||||
verify: 12.00µs
|
||||
multiple-cids10 (15.18ms; 52.625 MiB, 52.625 MiB): verifying multiple CIDs for single peer
|
||||
air::runner::execute_air: 15.18ms
|
||||
preparation_step::preparation::parse_data: 486.00µs
|
||||
from_slice: 432.00µs
|
||||
preparation_step::preparation::prepare: 256.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 34.00µs
|
||||
air_parser::parser::air_parser::parse: 46.00µs
|
||||
runner::execute: 12.19ms
|
||||
execute: 9.42ms
|
||||
execute: 2.23ms
|
||||
prepare_request_params: 48.00µs
|
||||
to_string(tetraplets): 14.00µs
|
||||
new: 1.49ms
|
||||
runner::farewell: 546.00µs
|
||||
from_success_result: 524.00µs
|
||||
populate_outcome_from_contexts: 492.00µs
|
||||
to_vec(call_results): 14.00µs
|
||||
to_vec(data): 229.00µs
|
||||
signing_step::sign_produced_cids: 157.00µs
|
||||
verification_step::verify: 1.41ms
|
||||
verify: 314.00µs
|
||||
multiple-cids50 (323.00ms; 59.375 MiB, 59.375 MiB): verifying multiple CIDs for single peer
|
||||
air::runner::execute_air: 323.00ms
|
||||
preparation_step::preparation::parse_data: 6.79ms
|
||||
from_slice: 6.72ms
|
||||
preparation_step::preparation::prepare: 595.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 362.00µs
|
||||
air_parser::parser::air_parser::parse: 49.00µs
|
||||
runner::execute: 299.20ms
|
||||
execute: 232.19ms
|
||||
execute: 53.08ms
|
||||
prepare_request_params: 54.00µs
|
||||
to_string(tetraplets): 16.00µs
|
||||
new: 36.44ms
|
||||
runner::farewell: 6.07ms
|
||||
from_success_result: 6.05ms
|
||||
populate_outcome_from_contexts: 6.01ms
|
||||
to_vec(call_results): 16.00µs
|
||||
to_vec(data): 4.77ms
|
||||
signing_step::sign_produced_cids: 180.00µs
|
||||
verification_step::verify: 10.09ms
|
||||
verify: 6.97ms
|
||||
multiple-peers14 (383.60ms; 59.750 MiB, 59.812 MiB): verifying many CIDs for many peers
|
||||
air::runner::execute_air: 383.60ms
|
||||
preparation_step::preparation::parse_data: 7.75ms
|
||||
from_slice: 7.68ms
|
||||
preparation_step::preparation::prepare: 607.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 374.00µs
|
||||
air_parser::parser::air_parser::parse: 52.00µs
|
||||
runner::execute: 351.40ms
|
||||
execute: 274.07ms
|
||||
execute: 65.03ms
|
||||
prepare_request_params: 52.00µs
|
||||
to_string(tetraplets): 15.00µs
|
||||
new: 46.23ms
|
||||
runner::farewell: 6.55ms
|
||||
from_success_result: 6.53ms
|
||||
populate_outcome_from_contexts: 6.49ms
|
||||
to_vec(call_results): 16.00µs
|
||||
to_vec(data): 5.13ms
|
||||
signing_step::sign_produced_cids: 170.00µs
|
||||
verification_step::verify: 17.10ms
|
||||
verify: 7.55ms
|
||||
multiple-peers25 (2.15s; 85.750 MiB, 86.562 MiB): verifying many CIDs for many peers
|
||||
air::runner::execute_air: 2.15s
|
||||
preparation_step::preparation::parse_data: 40.88ms
|
||||
from_slice: 40.76ms
|
||||
preparation_step::preparation::prepare: 3.70ms
|
||||
air::preparation_step::preparation::make_exec_ctx: 3.40ms
|
||||
air_parser::parser::air_parser::parse: 58.00µs
|
||||
runner::execute: 2.00s
|
||||
execute: 1.56s
|
||||
execute: 378.34ms
|
||||
prepare_request_params: 73.00µs
|
||||
to_string(tetraplets): 16.00µs
|
||||
new: 259.16ms
|
||||
runner::farewell: 34.76ms
|
||||
from_success_result: 34.72ms
|
||||
populate_outcome_from_contexts: 34.63ms
|
||||
to_vec(call_results): 18.00µs
|
||||
to_vec(data): 27.11ms
|
||||
signing_step::sign_produced_cids: 190.00µs
|
||||
verification_step::verify: 70.22ms
|
||||
verify: 44.09ms
|
||||
multiple-sigs200 (6.04s; 214.375 MiB, 214.375 MiB): signing multiple CIDs
|
||||
air::runner::execute_air: 6.04s
|
||||
preparation_step::preparation::parse_data: 219.80ms
|
||||
from_slice: 219.70ms
|
||||
preparation_step::preparation::prepare: 25.42ms
|
||||
air::preparation_step::preparation::make_exec_ctx: 25.09ms
|
||||
air_parser::parser::air_parser::parse: 58.00µs
|
||||
runner::execute: 5.30s
|
||||
call::execute: 3.83s
|
||||
execute: 941.01ms
|
||||
new: 604.96ms
|
||||
canon::execute: 308.30ms
|
||||
runner::farewell: 253.30ms
|
||||
from_success_result: 253.30ms
|
||||
populate_outcome_from_contexts: 233.40ms
|
||||
to_vec(call_results): 16.00µs
|
||||
to_vec(data): 145.70ms
|
||||
signing_step::sign_produced_cids: 30.84ms
|
||||
verification_step::verify: 214.00ms
|
||||
verify: 119.20ms
|
||||
multiple-sigs50 (369.10ms; 62.562 MiB, 62.562 MiB): signing multiple CIDs
|
||||
air::runner::execute_air: 369.10ms
|
||||
preparation_step::preparation::parse_data: 13.94ms
|
||||
from_slice: 13.87ms
|
||||
preparation_step::preparation::prepare: 914.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 678.00µs
|
||||
air_parser::parser::air_parser::parse: 52.00µs
|
||||
runner::execute: 328.70ms
|
||||
call::execute: 238.69ms
|
||||
execute: 56.21ms
|
||||
new: 38.04ms
|
||||
canon::execute: 18.24ms
|
||||
runner::farewell: 11.01ms
|
||||
from_success_result: 11.01ms
|
||||
populate_outcome_from_contexts: 10.57ms
|
||||
to_vec(call_results): 14.00µs
|
||||
to_vec(data): 7.77ms
|
||||
signing_step::sign_produced_cids: 1.77ms
|
||||
verification_step::verify: 12.49ms
|
||||
verify: 6.96ms
|
||||
network-explore (6.83ms; 52.500 MiB, 52.500 MiB): 5 peers of network are discovered
|
||||
air::runner::execute_air: 6.83ms
|
||||
preparation_step::preparation::parse_data: 603.00µs
|
||||
from_slice: 545.00µs
|
||||
preparation_step::preparation::prepare: 274.00µs
|
||||
air::preparation_step::preparation::make_exec_ctx: 26.00µs
|
||||
air_parser::parser::air_parser::parse: 68.00µs
|
||||
runner::execute: 2.55ms
|
||||
execute: 1.98ms
|
||||
execute: 490.00µs
|
||||
prepare_request_params: 52.00µs
|
||||
to_string(tetraplets): 16.00µs
|
||||
new: 339.00µs
|
||||
runner::farewell: 429.00µs
|
||||
from_success_result: 406.00µs
|
||||
populate_outcome_from_contexts: 372.00µs
|
||||
to_vec(call_results): 14.00µs
|
||||
to_vec(data): 140.00µs
|
||||
signing_step::sign_produced_cids: 157.00µs
|
||||
verification_step::verify: 2.67ms
|
||||
verify: 81.00µs
|
||||
parser_10000_100 (30.02ms; 57.812 MiB, 57.812 MiB): Running very long AIR script with lot of variables and assignments
|
||||
air::runner::execute_air: 30.02ms
|
||||
preparation_step::preparation::parse_data: 19.00µs
|
||||
preparation_step::preparation::prepare: 28.02ms
|
||||
air::preparation_step::preparation::make_exec_ctx: 18.00µs
|
||||
air_parser::parser::air_parser::parse: 27.78ms
|
||||
runner::execute: 163.00µs
|
||||
execute: 115.00µs
|
||||
execute: 29.00µs
|
||||
new: 21.00µs
|
||||
runner::farewell: 328.00µs
|
||||
from_success_result: 304.00µs
|
||||
populate_outcome_from_contexts: 270.00µs
|
||||
to_vec(call_results): 15.00µs
|
||||
to_vec(data): 44.00µs
|
||||
signing_step::sign_produced_cids: 161.00µs
|
||||
verification_step::verify: 40.00µs
|
||||
verify: 13.00µs
|
||||
=======
|
||||
Platform: macOS-14.0-arm64-arm-64bit
|
||||
Timestamp: 2023-10-04 14:50:18.122679+00:00
|
||||
AquaVM version: 0.48.0
|
||||
|
@ -1,25 +0,0 @@
|
||||
# Preparing data for the performance_metering
|
||||
|
||||
One may need to regenerate data on changing data format. Here is the instruction for data regeneration.
|
||||
|
||||
## `parser_10000_100`
|
||||
|
||||
Data is empty, no regeneration required.
|
||||
|
||||
## `dashboard` and `network_explore`
|
||||
|
||||
Run `junk/gen_test_data`. No WASM binary required. It will generate prev and current data as JSON files
|
||||
with prefixes `dashboard` and `explore`.
|
||||
|
||||
## `big_data` and `long_data`
|
||||
|
||||
In the `junk/cidify` directory, run
|
||||
|
||||
``` sh
|
||||
cargo run -- ./anomaly_big.json simple-calls-info.json > ../../air/benches/data/anomaly_big.json
|
||||
cargo run -- ./anomaly_long.json simple-calls-info.json > ../../air/benches/data/anomaly_long.json
|
||||
cp ../../air/benches/data/anomaly_big.json ../../benches/performance_metering/big_values_data/prev_data.json
|
||||
cp ../../air/benches/data/anomaly_long.json ../../benches/performance_metering/long_data/cur_data.json
|
||||
```
|
||||
|
||||
You may need update the `cidify` tool if you change data format again.
|
@ -1 +0,0 @@
|
||||
4nWRt5MyWTEjBJEYaKokky9SS7T5Vms9v2VfiFwAwNaW1E9FVWAFV8sKxMy7yLCkm5zKhkV6cRKwWAGTcVMvvEac
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"comment": "Loading a trace with huge values"
|
||||
}
|
@ -1 +0,0 @@
|
||||
(null)
|
@ -1 +0,0 @@
|
||||
4nWRt5MyWTEjBJEYaKokky9SS7T5Vms9v2VfiFwAwNaW1E9FVWAFV8sKxMy7yLCkm5zKhkV6cRKwWAGTcVMvvEac
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"comment": "big dashboard test"
|
||||
}
|
@ -1 +0,0 @@
|
||||
4nWRt5MyWTEjBJEYaKokky9SS7T5Vms9v2VfiFwAwNaW1E9FVWAFV8sKxMy7yLCkm5zKhkV6cRKwWAGTcVMvvEac
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"comment": "Long data trace"
|
||||
}
|
@ -1 +0,0 @@
|
||||
(null)
|
@ -1 +0,0 @@
|
||||
36gwouRjGormaTzTsyzx7cbuLbsTMk31VoZZ7LA2DaLNHQBqWND4ThNfJUUCnbBhMvRLCJrfr9zW68QURik7zPnT
|
@ -1 +0,0 @@
|
||||
{"comment":"verifying multiple CIDs for single peer"}
|
@ -1 +0,0 @@
|
||||
(seq (seq (call "12D3KooWPeUMWXdtEveZonecW8hhFdt4wfT4AyYpp4yrFGti7w5m" ("..0" "init") [] data) (fold data x (seq (fold data y (seq (call "12D3KooWHNgtEsSgAQukzihG36ErkNc9f58mjUeB8JcCRKNr1jQu" ("serv..1" "args") [x y] $stream) (next y))) (next x)))) (call "12D3KooWPeUMWXdtEveZonecW8hhFdt4wfT4AyYpp4yrFGti7w5m" ("" "complete") []))
|
@ -1 +0,0 @@
|
||||
36gwouRjGormaTzTsyzx7cbuLbsTMk31VoZZ7LA2DaLNHQBqWND4ThNfJUUCnbBhMvRLCJrfr9zW68QURik7zPnT
|
@ -1 +0,0 @@
|
||||
{"comment":"verifying many CIDs for many peers"}
|
@ -1 +0,0 @@
|
||||
(seq (seq (call "12D3KooWPeUMWXdtEveZonecW8hhFdt4wfT4AyYpp4yrFGti7w5m" ("..0" "init") [] data) (fold data p (seq (fold data x (seq (fold data y (seq (call p ("serv..1" "args") [p x y] $stream) (next y))) (next x))) (next p)))) (seq (call "12D3KooWHNgtEsSgAQukzihG36ErkNc9f58mjUeB8JcCRKNr1jQu" ("..2" "collect") []) (call "12D3KooWPeUMWXdtEveZonecW8hhFdt4wfT4AyYpp4yrFGti7w5m" ("" "complete") [])))
|
@ -1 +0,0 @@
|
||||
4nWRt5MyWTEjBJEYaKokky9SS7T5Vms9v2VfiFwAwNaW1E9FVWAFV8sKxMy7yLCkm5zKhkV6cRKwWAGTcVMvvEac
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"comment": "5 peers of network are discovered"
|
||||
}
|
@ -1 +0,0 @@
|
||||
4nWRt5MyWTEjBJEYaKokky9SS7T5Vms9v2VfiFwAwNaW1E9FVWAFV8sKxMy7yLCkm5zKhkV6cRKwWAGTcVMvvEac
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"comment": "Running very long AIR script with lot of variables and assignments"
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -15,3 +15,4 @@ cid = { version = "0.10.1", default-features = false, features = ["std"] }
|
||||
multihash = { version = "0.18.1", default-features = false, features = ["multihash-impl", "std", "sha2"] }
|
||||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
serde_json = "1.0.95"
|
||||
sha2 = "0.10.7"
|
||||
|
@ -30,6 +30,7 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use std::fmt;
|
||||
use std::io::BufWriter;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -51,6 +52,7 @@ impl<T: ?Sized> Clone for CID<T> {
|
||||
Self(self.0.clone(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> fmt::Debug for CID<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("CID").field(&self.0).finish()
|
||||
@ -121,7 +123,26 @@ impl std::error::Error for CidCalculationError {
|
||||
}
|
||||
|
||||
/// Calculate a CID of JSON-serialized value.
|
||||
pub fn value_to_json_cid<Val: Serialize>(value: &Val) -> Result<CID<Val>, CidCalculationError> {
|
||||
let data = serde_json::to_vec(value)?;
|
||||
Ok(json_data_cid(&data))
|
||||
// TODO we might refactor this to `SerializationFormat` trait
|
||||
// that both transform data to binary/text form (be it JSON, CBOR or something else)
|
||||
// and produces CID too
|
||||
pub fn value_to_json_cid<Val: Serialize + ?Sized>(
|
||||
value: &Val,
|
||||
) -> Result<CID<Val>, CidCalculationError> {
|
||||
use cid::Cid;
|
||||
use multihash::{Code, MultihashDigest};
|
||||
use sha2::Digest;
|
||||
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
serde_json::to_writer(BufWriter::with_capacity(8 * 1024, &mut hasher), value)?;
|
||||
let hash = hasher.finalize();
|
||||
|
||||
let digest = Code::Sha2_256
|
||||
.wrap(&hash)
|
||||
.expect("can't happend: incorrect hash length");
|
||||
// seems to be better than RAW_CODEC = 0x55
|
||||
const JSON_CODEC: u64 = 0x0200;
|
||||
|
||||
let cid = Cid::new_v1(JSON_CODEC, digest);
|
||||
Ok(CID::new(cid.to_string()))
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ air-interpreter-cid = { version = "0.3.0", path = "../interpreter-cid" }
|
||||
air-interpreter-signatures = { version = "0.1.1", path = "../interpreter-signatures" }
|
||||
polyplets = { version = "0.5.1", path = "../polyplets" }
|
||||
|
||||
fluence-keypair = { version = "0.10.1", default-features = false }
|
||||
serde = {version = "1.0.164", features = ["derive", "rc"]}
|
||||
serde_json = "1.0.95"
|
||||
semver = { version = "1.0.17", features = ["serde"] }
|
||||
@ -28,3 +29,4 @@ once_cell = "1.17.1"
|
||||
tracing = "0.1.37"
|
||||
newtype_derive = "0.1.6"
|
||||
num-traits = "0.2.15"
|
||||
thiserror = "1.0.40"
|
||||
|
111
crates/air-lib/interpreter-data/src/cid_info.rs
Normal file
111
crates/air-lib/interpreter-data/src/cid_info.rs
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 crate::CidStore;
|
||||
use crate::CidStoreVerificationError;
|
||||
|
||||
use crate::CanonCidAggregate;
|
||||
use crate::CanonResultCidAggregate;
|
||||
use crate::JValue;
|
||||
use crate::ServiceResultCidAggregate;
|
||||
|
||||
use polyplets::SecurityTetraplet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CidInfo {
|
||||
/// Map CID to value.
|
||||
pub value_store: CidStore<JValue>,
|
||||
|
||||
/// Map CID to a tetraplet.
|
||||
pub tetraplet_store: CidStore<SecurityTetraplet>,
|
||||
|
||||
/// Map CID to a canon element value.
|
||||
pub canon_element_store: CidStore<CanonCidAggregate>,
|
||||
|
||||
/// Map CID to a canon result.
|
||||
pub canon_result_store: CidStore<CanonResultCidAggregate>,
|
||||
|
||||
/// Map CID to a service result aggregate.
|
||||
pub service_result_store: CidStore<ServiceResultCidAggregate>,
|
||||
}
|
||||
|
||||
impl CidInfo {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn verify(&self) -> Result<(), CidStoreVerificationError> {
|
||||
self.verify_value_store()?;
|
||||
self.verify_tetraplet_store()?;
|
||||
|
||||
self.verify_canon_result_store()?;
|
||||
self.verify_service_result_store()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_value_store(&self) -> Result<(), CidStoreVerificationError> {
|
||||
self.value_store.verify()
|
||||
}
|
||||
|
||||
fn verify_tetraplet_store(&self) -> Result<(), CidStoreVerificationError> {
|
||||
self.tetraplet_store.verify()
|
||||
}
|
||||
|
||||
fn verify_service_result_store(&self) -> Result<(), CidStoreVerificationError> {
|
||||
self.service_result_store.verify()?;
|
||||
|
||||
for (serv_cid, serv_result) in self.service_result_store.iter() {
|
||||
self.tetraplet_store
|
||||
.check_reference(serv_cid, &serv_result.tetraplet_cid)?;
|
||||
self.value_store
|
||||
.check_reference(serv_cid, &serv_result.value_cid)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_canon_result_store(&self) -> Result<(), CidStoreVerificationError> {
|
||||
self.canon_element_store.verify()?;
|
||||
self.canon_result_store.verify()?;
|
||||
|
||||
for (canon_cid, canon_result) in self.canon_result_store.iter() {
|
||||
for val in &canon_result.values {
|
||||
self.canon_element_store.check_reference(canon_cid, val)?;
|
||||
}
|
||||
self.tetraplet_store
|
||||
.check_reference(canon_cid, &canon_result.tetraplet)?;
|
||||
}
|
||||
|
||||
for (element_cid, canon_element) in self.canon_element_store.iter() {
|
||||
self.tetraplet_store
|
||||
.check_reference(element_cid, &canon_element.tetraplet)?;
|
||||
self.value_store
|
||||
.check_reference(element_cid, &canon_element.value)?;
|
||||
|
||||
match &canon_element.provenance {
|
||||
crate::Provenance::Literal => {}
|
||||
crate::Provenance::ServiceResult { cid } => {
|
||||
self.service_result_store
|
||||
.check_reference(element_cid, cid)?;
|
||||
}
|
||||
crate::Provenance::Canon { cid } => {
|
||||
self.canon_result_store.check_reference(element_cid, cid)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ use air_interpreter_cid::CidCalculationError;
|
||||
use air_interpreter_cid::CID;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
@ -45,6 +46,60 @@ impl<Val> CidStore<Val> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Rc<CID<Val>>, &Rc<Val>)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
pub fn check_reference<Src>(
|
||||
&self,
|
||||
_source_cid: &CID<Src>,
|
||||
target_cid: &Rc<CID<Val>>,
|
||||
) -> Result<(), CidStoreVerificationError> {
|
||||
self.0
|
||||
.get(target_cid)
|
||||
.ok_or_else(|| CidStoreVerificationError::MissingReference {
|
||||
source_type_name: std::any::type_name::<Src>(),
|
||||
target_type_name: std::any::type_name::<Val>(),
|
||||
target_cid_repr: (**target_cid).clone().into_inner(),
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Val: Serialize> CidStore<Val> {
|
||||
pub fn verify(&self) -> Result<(), CidStoreVerificationError> {
|
||||
for (cid, value) in &self.0 {
|
||||
let expected_cid = value_to_json_cid::<Val>(value)?;
|
||||
if expected_cid != **cid {
|
||||
return Err(CidStoreVerificationError::MismatchError {
|
||||
type_name: std::any::type_name::<Val>(),
|
||||
cid_repr: (**cid).clone().into_inner(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ThisError, Debug)]
|
||||
pub enum CidStoreVerificationError {
|
||||
#[error("Failed to recalculate CID during the verification: {0}")]
|
||||
CidCalculationError(#[from] CidCalculationError),
|
||||
|
||||
#[error("Value mismatch in the {type_name:?} store for CID {cid_repr:?}")]
|
||||
MismatchError {
|
||||
// nb: type_name is std::any::type_name() result that may be inconsistent between the Rust compiler versions
|
||||
type_name: &'static str,
|
||||
cid_repr: String,
|
||||
},
|
||||
|
||||
#[error("Reference CID {target_cid_repr:?} from type {source_type_name:?} to {target_type_name:?} was not found")]
|
||||
MissingReference {
|
||||
source_type_name: &'static str,
|
||||
target_type_name: &'static str,
|
||||
target_cid_repr: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl<Val> Default for CidStore<Val> {
|
||||
|
@ -61,15 +61,11 @@ impl CallResult {
|
||||
CallResult::Failed(service_result_agg_cid)
|
||||
}
|
||||
|
||||
pub fn get_cid(&self) -> Option<Rc<CID<ServiceResultCidAggregate>>> {
|
||||
pub fn get_cid(&self) -> Option<&Rc<CID<ServiceResultCidAggregate>>> {
|
||||
match self {
|
||||
CallResult::RequestSentBy(_) => None,
|
||||
CallResult::Executed(executed) => match executed {
|
||||
ValueRef::Scalar(cid) => Some(cid.clone()),
|
||||
ValueRef::Stream { cid, .. } => Some(cid.clone()),
|
||||
ValueRef::Unused(_) => None,
|
||||
},
|
||||
CallResult::Failed(cid) => Some(cid.clone()),
|
||||
CallResult::Executed(executed) => executed.get_cid(),
|
||||
CallResult::Failed(cid) => Some(cid),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -214,6 +210,16 @@ impl std::fmt::Display for ExecutedState {
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueRef {
|
||||
pub(crate) fn get_cid(&self) -> Option<&Rc<CID<ServiceResultCidAggregate>>> {
|
||||
match self {
|
||||
ValueRef::Scalar(cid) => Some(cid),
|
||||
ValueRef::Stream { cid, .. } => Some(cid),
|
||||
ValueRef::Unused(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ValueRef {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
@ -14,16 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::cid_store::CidStore;
|
||||
use crate::CanonCidAggregate;
|
||||
use crate::CanonResultCidAggregate;
|
||||
pub(crate) mod errors;
|
||||
pub mod verification;
|
||||
|
||||
use crate::CidInfo;
|
||||
use crate::ExecutionTrace;
|
||||
use crate::JValue;
|
||||
use crate::ServiceResultCidAggregate;
|
||||
|
||||
use air_interpreter_signatures::SignatureStore;
|
||||
use air_utils::measure;
|
||||
use polyplets::SecurityTetraplet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@ -121,21 +119,3 @@ impl Versions {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CidInfo {
|
||||
/// Map CID to value.
|
||||
pub value_store: CidStore<JValue>,
|
||||
|
||||
/// Map CID to a tetraplet.
|
||||
pub tetraplet_store: CidStore<SecurityTetraplet>,
|
||||
|
||||
/// Map CID to a canon element value.
|
||||
pub canon_element_store: CidStore<CanonCidAggregate>,
|
||||
|
||||
/// Map CID to a canon result.
|
||||
pub canon_result_store: CidStore<CanonResultCidAggregate>,
|
||||
|
||||
/// Map CID to a service result aggregate.
|
||||
pub service_result_store: CidStore<ServiceResultCidAggregate>,
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 thiserror::Error as ThisError;
|
||||
#[derive(Debug, ThisError)]
|
||||
pub enum DataVerifierError {
|
||||
#[error(transparent)]
|
||||
MalformedKey(fluence_keypair::error::DecodingError),
|
||||
|
||||
#[error(transparent)]
|
||||
MalformedSignature(fluence_keypair::error::DecodingError),
|
||||
|
||||
#[error("peer_id doens't match any available public key: {0:?}")]
|
||||
PeerIdNotFound(String),
|
||||
|
||||
#[error("signature mismatch for {peer_id:?}: {error:?}, values: CIDS: {cids:?}")]
|
||||
SignatureMismatch {
|
||||
error: Box<fluence_keypair::error::VerificationError>,
|
||||
cids: Vec<Box<str>>,
|
||||
peer_id: String,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"inconsistent CID multisets on merge for peer {peer_id:?}, prev: {larger_cids:?}, current: {smaller_cids:?}"
|
||||
)]
|
||||
MergeMismatch {
|
||||
peer_id: String,
|
||||
larger_cids: Vec<Box<str>>,
|
||||
smaller_cids: Vec<Box<str>>,
|
||||
},
|
||||
}
|
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
pub use super::errors::DataVerifierError;
|
||||
use crate::CanonResult;
|
||||
use crate::CidInfo;
|
||||
use crate::ExecutedState;
|
||||
use crate::ExecutionTrace;
|
||||
use crate::InterpreterData;
|
||||
|
||||
use air_interpreter_cid::CID;
|
||||
use air_interpreter_signatures::PublicKey;
|
||||
use air_interpreter_signatures::Signature;
|
||||
use air_interpreter_signatures::SignatureStore;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
const CANNOT_HAPPEN_IN_VERIFIED_CID_STORE: &str = "cannot happen in a checked CID store";
|
||||
|
||||
/// An util for verificating particular data's signatures.
|
||||
pub struct DataVerifier<'data> {
|
||||
// a map from peer_id to peer's info (public key, signature, CIDS)
|
||||
grouped_cids: HashMap<Box<str>, PeerInfo<'data>>,
|
||||
salt: &'data str,
|
||||
}
|
||||
|
||||
impl<'data> DataVerifier<'data> {
|
||||
// it can be further optimized if only required parts are passed
|
||||
// SignatureStore is not used elsewhere
|
||||
pub fn new(data: &'data InterpreterData, salt: &'data str) -> Result<Self, DataVerifierError> {
|
||||
// it contains signature too; if we try to add a value to a peer w/o signature, it is an immediate error
|
||||
let mut grouped_cids: HashMap<Box<str>, PeerInfo<'data>> = data
|
||||
.signatures
|
||||
.iter()
|
||||
.map(|(public_key, signature)| {
|
||||
(
|
||||
public_key.to_peer_id().to_string().into(),
|
||||
PeerInfo::new(public_key, signature),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// fill PeerInfo's `cids` field, checking for peer IDs without a key
|
||||
collect_peers_cids_from_trace(&data.trace, &data.cid_info, &mut grouped_cids)?;
|
||||
|
||||
// sort cids for canonicalization
|
||||
for peer_info in grouped_cids.values_mut() {
|
||||
peer_info.cids.sort_unstable();
|
||||
}
|
||||
|
||||
Ok(Self { grouped_cids, salt })
|
||||
}
|
||||
|
||||
/// Verify each peers' signatures.
|
||||
pub fn verify(&self) -> Result<(), DataVerifierError> {
|
||||
for peer_info in self.grouped_cids.values() {
|
||||
peer_info
|
||||
.public_key
|
||||
.verify(&peer_info.cids, self.salt, peer_info.signature)
|
||||
.map_err(|error| DataVerifierError::SignatureMismatch {
|
||||
error: error.into(),
|
||||
cids: peer_info.cids.clone(),
|
||||
peer_id: peer_info.public_key.to_peer_id().to_string(),
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// For each peer, merge previous and current CID multisets by determining the largest set.
|
||||
///
|
||||
/// This code uses an invariant: peer's multiset of produced CIDs is always a superset of
|
||||
/// previous invocation's multiset:
|
||||
///
|
||||
/// A_0 ⊆ A_1 ⊆ ... ⊆ A_n.
|
||||
///
|
||||
/// So, the largest multiset is selected as the result of merging, the invariant is checked,
|
||||
/// and a error is returned if it is violated.
|
||||
///
|
||||
/// If the multisets are of same size, they have to be equal.
|
||||
// TODO enforce merging only verified sets
|
||||
// The result is same regardless argument order, so "prevous/current" terminology
|
||||
// is not used deliberately.
|
||||
pub fn merge(mut self, other: Self) -> Result<SignatureStore, DataVerifierError> {
|
||||
use std::collections::hash_map::Entry::*;
|
||||
|
||||
for (other_peer_pk, mut other_info) in other.grouped_cids {
|
||||
let our_info = self.grouped_cids.entry(other_peer_pk);
|
||||
match our_info {
|
||||
Occupied(mut our_info_ent) => {
|
||||
debug_assert_eq!(other_info.public_key, our_info_ent.get().public_key);
|
||||
|
||||
if our_info_ent.get().cids.len() < other_info.cids.len() {
|
||||
// the merged map contains the largest set for each peer_id
|
||||
//
|
||||
// this code assumes that a peer only adds CIDs to its set, so CID multisets
|
||||
// are growing-only; but it is additionally checked below
|
||||
// so, we get a largest set as merged one
|
||||
std::mem::swap(our_info_ent.get_mut(), &mut other_info);
|
||||
}
|
||||
// nb: if length are equal, sets should be equal, and any of them
|
||||
// should be used; if they are not equal, check_cid_multiset_consistency
|
||||
// will detect it.
|
||||
|
||||
let larger_info = our_info_ent.get();
|
||||
let smaller_info = &other_info;
|
||||
check_cid_multiset_invariant(larger_info, smaller_info)?;
|
||||
}
|
||||
Vacant(ent) => {
|
||||
ent.insert(other_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut store = SignatureStore::new();
|
||||
for peer_info in self.grouped_cids.into_values() {
|
||||
store.put(peer_info.public_key.clone(), peer_info.signature.clone())
|
||||
}
|
||||
Ok(store)
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_peers_cids_from_trace<'data>(
|
||||
trace: &'data ExecutionTrace,
|
||||
cid_info: &'data CidInfo,
|
||||
grouped_cids: &mut HashMap<Box<str>, PeerInfo<'data>>,
|
||||
) -> Result<(), DataVerifierError> {
|
||||
for elt in trace {
|
||||
match elt {
|
||||
ExecutedState::Call(ref call) => {
|
||||
let cid = call.get_cid();
|
||||
if let Some(cid) = cid {
|
||||
// TODO refactor
|
||||
let service_result = cid_info
|
||||
.service_result_store
|
||||
.get(cid)
|
||||
.expect(CANNOT_HAPPEN_IN_VERIFIED_CID_STORE);
|
||||
let tetraplet = cid_info
|
||||
.tetraplet_store
|
||||
.get(&service_result.tetraplet_cid)
|
||||
.expect(CANNOT_HAPPEN_IN_VERIFIED_CID_STORE);
|
||||
|
||||
let peer_pk = tetraplet.peer_pk.as_str();
|
||||
try_push_cid(grouped_cids, peer_pk, cid)?;
|
||||
}
|
||||
}
|
||||
ExecutedState::Canon(CanonResult::Executed(ref cid)) => {
|
||||
// TODO refactor
|
||||
let canon_result = cid_info
|
||||
.canon_result_store
|
||||
.get(cid)
|
||||
.expect(CANNOT_HAPPEN_IN_VERIFIED_CID_STORE);
|
||||
let tetraplet = cid_info
|
||||
.tetraplet_store
|
||||
.get(&canon_result.tetraplet)
|
||||
.expect(CANNOT_HAPPEN_IN_VERIFIED_CID_STORE);
|
||||
|
||||
let peer_pk = tetraplet.peer_pk.as_str();
|
||||
try_push_cid(grouped_cids, peer_pk, cid)?;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_push_cid<T>(
|
||||
grouped_cids: &mut HashMap<Box<str>, PeerInfo<'_>>,
|
||||
peer_pk: &str,
|
||||
cid: &Rc<CID<T>>,
|
||||
) -> Result<(), DataVerifierError> {
|
||||
match grouped_cids.get_mut(peer_pk) {
|
||||
Some(peer_info) => {
|
||||
peer_info.cids.push((**cid).clone().into_inner().into());
|
||||
Ok(())
|
||||
}
|
||||
None => Err(DataVerifierError::PeerIdNotFound(peer_pk.into())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety check for malicious peer that returns inconsistent CID multisets,
|
||||
/// i.e. non-increasing multisets.
|
||||
fn check_cid_multiset_invariant(
|
||||
larger_pair: &PeerInfo<'_>,
|
||||
smaller_pair: &PeerInfo<'_>,
|
||||
) -> Result<(), DataVerifierError> {
|
||||
let larger_cids = &larger_pair.cids;
|
||||
let smaller_cids = &smaller_pair.cids;
|
||||
|
||||
let larger_count_map = to_count_map(larger_cids);
|
||||
let smaller_count_map = to_count_map(smaller_cids);
|
||||
|
||||
if is_multisubset(larger_count_map, smaller_count_map) {
|
||||
Ok(())
|
||||
} else {
|
||||
let peer_id = smaller_pair.public_key.to_peer_id().to_string();
|
||||
Err(DataVerifierError::MergeMismatch {
|
||||
peer_id,
|
||||
larger_cids: larger_cids.clone(),
|
||||
smaller_cids: smaller_cids.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn to_count_map(cids: &Vec<Box<str>>) -> HashMap<&str, usize> {
|
||||
let mut count_map = HashMap::<_, usize>::new();
|
||||
for cid in cids {
|
||||
// the counter can't overflow, the memory will overflow first
|
||||
*count_map.entry(&**cid).or_default() += 1;
|
||||
}
|
||||
count_map
|
||||
}
|
||||
|
||||
fn is_multisubset(
|
||||
larger_count_set: HashMap<&str, usize>,
|
||||
smaller_count_set: HashMap<&str, usize>,
|
||||
) -> bool {
|
||||
for (cid, &smaller_count) in &smaller_count_set {
|
||||
debug_assert!(smaller_count > 0);
|
||||
|
||||
let larger_count = larger_count_set.get(cid).cloned().unwrap_or_default();
|
||||
if larger_count < smaller_count {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
struct PeerInfo<'data> {
|
||||
/// A peer's public key.
|
||||
public_key: &'data PublicKey,
|
||||
/// A peer's signature.
|
||||
signature: &'data Signature,
|
||||
/// Sorted vector of CIDs that belong to the peer.
|
||||
cids: Vec<Box<str>>,
|
||||
}
|
||||
|
||||
impl<'data> PeerInfo<'data> {
|
||||
fn new(public_key: &'data PublicKey, signature: &'data Signature) -> Self {
|
||||
Self {
|
||||
public_key,
|
||||
signature,
|
||||
cids: vec![],
|
||||
}
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@
|
||||
unreachable_patterns
|
||||
)]
|
||||
|
||||
mod cid_info;
|
||||
mod cid_store;
|
||||
mod executed_state;
|
||||
mod generation_idx;
|
||||
@ -33,6 +34,7 @@ mod interpreter_data;
|
||||
mod trace;
|
||||
mod trace_pos;
|
||||
|
||||
pub use cid_info::*;
|
||||
pub use cid_store::*;
|
||||
pub use executed_state::*;
|
||||
pub use generation_idx::*;
|
||||
|
@ -88,3 +88,13 @@ impl PartialEq<Vec<ExecutedState>> for ExecutionTrace {
|
||||
&self.0 == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'trace> IntoIterator for &'trace ExecutionTrace {
|
||||
type Item = &'trace ExecutedState;
|
||||
|
||||
type IntoIter = <&'trace Vec<ExecutedState> as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ categories = ["wasm"]
|
||||
air-interpreter-cid = { version = "0.3.0", path = "../interpreter-cid" }
|
||||
fluence-keypair = { version = "0.10.1", default-features = false }
|
||||
|
||||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
serde_json = "1.0.96"
|
||||
|
||||
bs58 = "0.5.0"
|
||||
borsh = "0.10.3"
|
||||
borsh-derive = "0.10.3"
|
||||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
|
@ -27,14 +27,17 @@
|
||||
)]
|
||||
|
||||
mod sede;
|
||||
mod stores;
|
||||
mod trackers;
|
||||
|
||||
use air_interpreter_cid::CID;
|
||||
use fluence_keypair::error::SigningError;
|
||||
use fluence_keypair::KeyPair;
|
||||
pub use crate::stores::*;
|
||||
pub use crate::trackers::*;
|
||||
|
||||
pub use fluence_keypair::KeyPair;
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::ops::Deref;
|
||||
|
||||
@ -52,16 +55,15 @@ pub struct PublicKey(
|
||||
);
|
||||
|
||||
impl PublicKey {
|
||||
pub fn verify<T: Serialize + ?Sized>(
|
||||
pub fn verify<T: BorshSerialize + ?Sized>(
|
||||
&self,
|
||||
value: &T,
|
||||
salt: &str,
|
||||
signature: &fluence_keypair::Signature,
|
||||
) -> Result<(), fluence_keypair::error::VerificationError> {
|
||||
let pk = &**self;
|
||||
|
||||
let serialized_value =
|
||||
serde_json::to_vec(value).expect("default serialization shouldn't fail");
|
||||
|
||||
let serialized_value = SaltedData::new(&value, salt).serialize();
|
||||
pk.verify(&serialized_value, signature)
|
||||
}
|
||||
}
|
||||
@ -126,75 +128,18 @@ impl From<Signature> for fluence_keypair::Signature {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SignatureTracker {
|
||||
// from peer id to CID strings
|
||||
peer_to_cids: HashMap<Box<str>, Vec<Box<str>>>,
|
||||
}
|
||||
#[derive(borsh_derive::BorshSerialize)]
|
||||
pub(crate) struct SaltedData<'ctx, Data: BorshSerialize>(&'ctx Data, &'ctx str);
|
||||
|
||||
impl SignatureTracker {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
impl<'ctx, Data: BorshSerialize> SaltedData<'ctx, Data> {
|
||||
pub(crate) fn new(data: &'ctx Data, salt: &'ctx str) -> Self {
|
||||
Self(data, salt)
|
||||
}
|
||||
|
||||
pub fn register<T>(&mut self, peer_id: impl Into<Box<str>>, cid: &CID<T>) {
|
||||
self.peer_to_cids
|
||||
.entry(peer_id.into())
|
||||
.or_default()
|
||||
.push(cid.clone().into_inner().into());
|
||||
}
|
||||
|
||||
pub fn into_signature(
|
||||
&mut self,
|
||||
peer_id: &str,
|
||||
signer: &KeyPair,
|
||||
) -> Result<Signature, SigningError> {
|
||||
let mut cids = self.peer_to_cids.get(peer_id).cloned().unwrap_or_default();
|
||||
cids.sort_unstable();
|
||||
|
||||
pub(crate) fn serialize(&self) -> Vec<u8> {
|
||||
// TODO make pluggable serialization
|
||||
// TODO it will be useful for CID too
|
||||
// TODO please note that using serde::Serializer is not enough
|
||||
let serialized_cids =
|
||||
serde_json::to_string(&cids).expect("default serialization shouldn't fail");
|
||||
|
||||
signer.sign(serialized_cids.as_bytes()).map(Signature::new)
|
||||
}
|
||||
}
|
||||
|
||||
/// A dictionary-like structure that stores peer public keys and their particle data signatures.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SignatureStore<Key: Hash + Eq = PublicKey, Sign = Signature>(HashMap<Key, Sign>);
|
||||
|
||||
impl<Key: Hash + Eq, Sign> SignatureStore<Key, Sign> {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn get<Q>(&self, peer_pk: &Q) -> Option<&Sign>
|
||||
where
|
||||
Key: Borrow<Q>,
|
||||
Q: Hash + Eq + ?Sized,
|
||||
{
|
||||
self.0.get(peer_pk)
|
||||
}
|
||||
|
||||
pub fn put(&mut self, peer_pk: Key, signature: Sign) {
|
||||
self.0.insert(peer_pk, signature);
|
||||
}
|
||||
|
||||
pub fn merge(prev: Self, _current: Self) -> Self {
|
||||
// TODO STUB
|
||||
prev
|
||||
}
|
||||
}
|
||||
|
||||
impl<Key: Hash + Eq, Sign> Default for SignatureStore<Key, Sign> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
borsh::to_vec(&self).expect("borsh serializer shouldn't fail")
|
||||
}
|
||||
}
|
||||
|
60
crates/air-lib/interpreter-signatures/src/stores.rs
Normal file
60
crates/air-lib/interpreter-signatures/src/stores.rs
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 crate::PublicKey;
|
||||
use crate::Signature;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
|
||||
/// A dictionary-like structure that stores peer public keys and their particle data signatures.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SignatureStore<Key: Hash + Eq = PublicKey, Sign = Signature>(HashMap<Key, Sign>);
|
||||
|
||||
impl<Key: Hash + Eq, Sign> SignatureStore<Key, Sign> {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn get<Q>(&self, peer_pk: &Q) -> Option<&Sign>
|
||||
where
|
||||
Key: Borrow<Q>,
|
||||
Q: Hash + Eq + ?Sized,
|
||||
{
|
||||
self.0.get(peer_pk)
|
||||
}
|
||||
|
||||
pub fn put(&mut self, peer_pk: Key, signature: Sign) {
|
||||
self.0.insert(peer_pk, signature);
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> <&HashMap<Key, Sign> as IntoIterator>::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Key: Hash + Eq, Sign> Default for SignatureStore<Key, Sign> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
64
crates/air-lib/interpreter-signatures/src/trackers.rs
Normal file
64
crates/air-lib/interpreter-signatures/src/trackers.rs
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 crate::SaltedData;
|
||||
|
||||
use air_interpreter_cid::CID;
|
||||
use fluence_keypair::error::SigningError;
|
||||
use fluence_keypair::KeyPair;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// The tracker that collect current peer's CIDs only.
|
||||
#[derive(Debug)]
|
||||
pub struct PeerCidTracker {
|
||||
current_peer_id: Rc<String>,
|
||||
cids: Vec<Box<str>>,
|
||||
}
|
||||
|
||||
impl PeerCidTracker {
|
||||
pub fn new(current_peer_id: impl Into<Rc<String>>) -> Self {
|
||||
Self {
|
||||
current_peer_id: current_peer_id.into(),
|
||||
cids: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register<T>(&mut self, peer: &str, cid: &CID<T>) {
|
||||
if peer == *self.current_peer_id {
|
||||
self.cids.push(cid.clone().into_inner().into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_signature(
|
||||
&self,
|
||||
salt: &str,
|
||||
keypair: &KeyPair,
|
||||
) -> Result<crate::Signature, SigningError> {
|
||||
sign_cids(self.cids.clone(), salt, keypair)
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_cids(
|
||||
mut cids: Vec<Box<str>>,
|
||||
salt: &str,
|
||||
keypair: &KeyPair,
|
||||
) -> Result<crate::Signature, SigningError> {
|
||||
cids.sort_unstable();
|
||||
|
||||
let serialized_cids = SaltedData::new(&cids, salt).serialize();
|
||||
keypair.sign(&serialized_cids).map(crate::Signature::new)
|
||||
}
|
@ -89,6 +89,20 @@ pub fn fallible_call_service(fallible_service_id: impl Into<String>) -> CallServ
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fallible_call_service_by_arg(arg: impl Into<JValue>) -> CallServiceClosure {
|
||||
let arg = arg.into();
|
||||
|
||||
Box::new(move |params| -> CallServiceResult {
|
||||
// return a error for service with specific arg
|
||||
if params.arguments.get(0) == Some(&arg) {
|
||||
CallServiceResult::err(1, json!("failed result from fallible_call_service_by_arg"))
|
||||
} else {
|
||||
// return success for services with other arg
|
||||
CallServiceResult::ok(json!("success result from fallible_call_service_by_arg"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub type ArgTetraplets = Vec<Vec<SecurityTetraplet>>;
|
||||
|
||||
pub fn tetraplet_host_function(
|
||||
|
@ -24,6 +24,7 @@ use super::ParResult;
|
||||
use super::Sender;
|
||||
use super::TracePos;
|
||||
use super::ValueRef;
|
||||
use crate::key_utils::at;
|
||||
use crate::FoldLore;
|
||||
use crate::FoldResult;
|
||||
use crate::FoldSubTraceLore;
|
||||
@ -311,6 +312,11 @@ impl ExecutedCallBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn peer_name(mut self, peer_name: impl AsRef<str>) -> Self {
|
||||
self.tetraplet.peer_pk = at(peer_name.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn service(mut self, service_id: impl Into<String>) -> Self {
|
||||
self.tetraplet.service_id = service_id.into();
|
||||
self
|
||||
|
@ -38,3 +38,7 @@ pub fn derive_dummy_keypair(seed: &str) -> (KeyPair, String) {
|
||||
let peer_id = keypair.public().to_peer_id().to_string();
|
||||
(keypair, peer_id)
|
||||
}
|
||||
|
||||
pub fn at(peer_name: &str) -> String {
|
||||
derive_dummy_keypair(peer_name).1
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ fn print_canon_values(data: &InterpreterData, canon_result: &CanonResult) {
|
||||
#[macro_export]
|
||||
macro_rules! rc {
|
||||
($expr:expr) => {
|
||||
std::rc::Rc::new($expr)
|
||||
::std::rc::Rc::new($expr)
|
||||
};
|
||||
}
|
||||
|
||||
@ -215,3 +215,24 @@ pub fn is_interpreter_succeded(result: &RawAVMOutcome) -> bool {
|
||||
pub fn check_error(result: &RawAVMOutcome, error: impl ToErrorCode + ToString) -> bool {
|
||||
result.ret_code == error.to_error_code() && result.error_message == error.to_string()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_error_eq {
|
||||
($result:expr, $error:expr $(,)?) => {{
|
||||
let result: &::air_test_utils::RawAVMOutcome = $result;
|
||||
let error = $error;
|
||||
::std::assert_eq!(
|
||||
(result.ret_code, &result.error_message),
|
||||
(::air::ToErrorCode::to_error_code(&error), &::std::string::ToString::to_string(&error))
|
||||
);
|
||||
}};
|
||||
($result:expr, $error:expr, $($arg:tt)+) => {{
|
||||
let result: &::air_test_utils::RawAVMOutcome = $result;
|
||||
let error = $error;
|
||||
::std::assert_eq!(
|
||||
(result.ret_code, &result.error_message),
|
||||
(::air::ToErrorCode::to_error_code(&error), &::std::string::ToString::to_string(&error)),
|
||||
$($arg)+
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
@ -73,4 +73,8 @@ impl AirRunner for NativeAirRunner {
|
||||
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
fn get_current_peer_id(&self) -> &str {
|
||||
&self.current_peer_id
|
||||
}
|
||||
}
|
||||
|
@ -49,12 +49,14 @@ pub trait AirRunner {
|
||||
key_pair: &KeyPair,
|
||||
particle_id: String,
|
||||
) -> Result<RawAVMOutcome, Box<dyn std::error::Error>>;
|
||||
|
||||
fn get_current_peer_id(&self) -> &str;
|
||||
}
|
||||
|
||||
pub struct TestRunner<R = DefaultAirRunner> {
|
||||
pub runner: R,
|
||||
call_service: CallServiceClosure,
|
||||
keypair: KeyPair,
|
||||
pub keypair: KeyPair,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
@ -179,6 +181,20 @@ pub fn create_custom_avm<R: AirRunner>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_avm_with_key<R: AirRunner>(
|
||||
keypair: KeyPair,
|
||||
call_service: CallServiceClosure,
|
||||
) -> TestRunner<R> {
|
||||
let current_peer_id = keypair.public().to_peer_id().to_string();
|
||||
let runner = R::new(current_peer_id);
|
||||
|
||||
TestRunner {
|
||||
runner,
|
||||
call_service,
|
||||
keypair,
|
||||
}
|
||||
}
|
||||
|
||||
impl TestRunParameters {
|
||||
pub fn new(
|
||||
init_peer_id: impl Into<String>,
|
||||
@ -215,6 +231,11 @@ impl TestRunParameters {
|
||||
..<_>::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_particle_id(mut self, particle_id: impl Into<String>) -> Self {
|
||||
self.particle_id = particle_id.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -92,6 +92,10 @@ impl AirRunner for WasmAirRunner {
|
||||
particle_id,
|
||||
)?)
|
||||
}
|
||||
|
||||
fn get_current_peer_id(&self) -> &str {
|
||||
&self.current_peer_id
|
||||
}
|
||||
}
|
||||
|
||||
/// WASM runner that runs release build form benchmarking.
|
||||
@ -147,4 +151,8 @@ impl AirRunner for ReleaseWasmAirRunner {
|
||||
particle_id,
|
||||
)?)
|
||||
}
|
||||
|
||||
fn get_current_peer_id(&self) -> &str {
|
||||
&self.current_peer_id
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ strum = { version="0.24.1", features=["derive"] }
|
||||
nom = "7.1.3"
|
||||
nom_locate = "4.1.0"
|
||||
serde_json = "1.0.95"
|
||||
regex = "1.8.4"
|
||||
air-interpreter-signatures = { version = "0.1.1", path = "../air-lib/interpreter-signatures" }
|
||||
|
||||
[dev-dependencies]
|
||||
maplit = "1.0.2"
|
||||
|
@ -22,20 +22,29 @@ use crate::{
|
||||
services::{services_to_call_service_closure, MarineServiceHandle, NetworkServices},
|
||||
};
|
||||
|
||||
use air_interpreter_signatures::KeyPair;
|
||||
use air_test_utils::{
|
||||
test_runner::{create_custom_avm, AirRunner, DefaultAirRunner, TestRunParameters, TestRunner},
|
||||
key_utils::derive_dummy_keypair,
|
||||
test_runner::{
|
||||
create_avm_with_key, AirRunner, DefaultAirRunner, TestRunParameters, TestRunner,
|
||||
},
|
||||
RawAVMOutcome,
|
||||
};
|
||||
|
||||
use std::{borrow::Borrow, cell::RefCell, collections::HashMap, hash::Hash, rc::Rc};
|
||||
use std::{borrow::Borrow, cell::RefCell, collections::HashMap, hash::Hash, ops::Deref, rc::Rc};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct PeerId(Rc<str>);
|
||||
|
||||
impl PeerId {
|
||||
pub fn new<'any>(peer_id: impl Into<&'any str>) -> Self {
|
||||
Self(peer_id.into().into())
|
||||
}
|
||||
|
||||
pub fn from_keypair(keypair: &KeyPair) -> Self {
|
||||
Self::new(keypair.public().to_peer_id().to_string().as_str())
|
||||
}
|
||||
}
|
||||
impl From<String> for PeerId {
|
||||
fn from(source: String) -> Self {
|
||||
@ -49,12 +58,26 @@ impl From<&str> for PeerId {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PeerId> for PeerId {
|
||||
fn from(value: &PeerId) -> Self {
|
||||
value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for PeerId {
|
||||
fn borrow(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PeerId {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub type Data = Vec<u8>;
|
||||
|
||||
pub struct Peer<R> {
|
||||
@ -63,14 +86,19 @@ pub struct Peer<R> {
|
||||
}
|
||||
|
||||
impl<R: AirRunner> Peer<R> {
|
||||
pub fn new(peer_id: impl Into<PeerId>, services: Rc<[MarineServiceHandle]>) -> Self {
|
||||
let peer_id = Into::into(peer_id);
|
||||
pub fn new(keypair: KeyPair, services: Rc<[MarineServiceHandle]>) -> Self {
|
||||
let call_service = services_to_call_service_closure(services);
|
||||
let runner = create_custom_avm(call_service, &*peer_id.0);
|
||||
|
||||
let runner = create_avm_with_key::<R>(keypair, call_service);
|
||||
let peer_id = runner.runner.get_current_peer_id().into();
|
||||
|
||||
Self { peer_id, runner }
|
||||
}
|
||||
|
||||
pub fn get_peer_id(&self) -> &PeerId {
|
||||
&self.peer_id
|
||||
}
|
||||
|
||||
pub(crate) fn invoke(
|
||||
&mut self,
|
||||
air: impl Into<String>,
|
||||
@ -85,6 +113,10 @@ impl<R: AirRunner> Peer<R> {
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn get_keypair(&self) -> &KeyPair {
|
||||
&self.runner.keypair
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AirRunner> std::fmt::Debug for Peer<R> {
|
||||
@ -98,7 +130,12 @@ impl<R: AirRunner> std::fmt::Debug for Peer<R> {
|
||||
|
||||
pub struct Network<R = DefaultAirRunner> {
|
||||
peers: RefCell<HashMap<PeerId, Rc<RefCell<PeerEnv<R>>>>>,
|
||||
|
||||
// Default peer services.
|
||||
services: Rc<NetworkServices>,
|
||||
|
||||
// Resolves human-readable peer names to real peer IDs.
|
||||
resolver: RefCell<HashMap<PeerId, PeerId>>,
|
||||
}
|
||||
|
||||
// it is implemented only for the default runner for compatibility reasons
|
||||
@ -112,15 +149,16 @@ impl Network<DefaultAirRunner> {
|
||||
|
||||
impl<R: AirRunner> Network<R> {
|
||||
pub fn new(
|
||||
peers: impl Iterator<Item = impl Into<PeerId>>,
|
||||
named_peers: impl Iterator<Item = impl Into<PeerId>>,
|
||||
common_services: Vec<MarineServiceHandle>,
|
||||
) -> Rc<Self> {
|
||||
let network = Rc::new(Self {
|
||||
peers: Default::default(),
|
||||
services: NetworkServices::new(common_services).into(),
|
||||
resolver: Default::default(),
|
||||
});
|
||||
for peer_id in peers {
|
||||
network.ensure_peer(peer_id);
|
||||
for peer_name in named_peers {
|
||||
network.ensure_named_peer(peer_name);
|
||||
}
|
||||
network
|
||||
}
|
||||
@ -145,15 +183,23 @@ impl<R: AirRunner> Network<R> {
|
||||
self.insert_peer_env_entry(peer_id, peer_env);
|
||||
}
|
||||
|
||||
pub fn ensure_peer(self: &Rc<Self>, peer_id: impl Into<PeerId>) {
|
||||
let peer_id = peer_id.into();
|
||||
let exists = {
|
||||
let peers_ref = self.peers.borrow();
|
||||
peers_ref.contains_key(&peer_id)
|
||||
};
|
||||
if !exists {
|
||||
let peer = Peer::new(peer_id, self.services.get_services());
|
||||
self.add_peer(peer);
|
||||
pub fn ensure_named_peer(self: &Rc<Self>, name: impl Into<PeerId>) -> PeerId {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let name = name.into();
|
||||
|
||||
match self.resolver.borrow_mut().entry(name.clone()) {
|
||||
Entry::Occupied(entry) => entry.get().clone(),
|
||||
Entry::Vacant(empty) => {
|
||||
let (keypair, _) = derive_dummy_keypair(&name);
|
||||
let peer = Peer::new(keypair, self.services.get_services());
|
||||
let peer_id = peer.get_peer_id().clone();
|
||||
self.add_peer(peer);
|
||||
|
||||
empty.insert(peer_id.clone());
|
||||
|
||||
peer_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,77 +211,100 @@ impl<R: AirRunner> Network<R> {
|
||||
}
|
||||
|
||||
fn insert_peer_env_entry(&self, peer_id: PeerId, peer_env: PeerEnv<R>) {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let mut peers_ref = self.peers.borrow_mut();
|
||||
let peer_env = Rc::new(peer_env.into());
|
||||
// It will be simplified with entry_insert stabilization
|
||||
// https://github.com/rust-lang/rust/issues/65225
|
||||
match peers_ref.entry(peer_id) {
|
||||
std::collections::hash_map::Entry::Occupied(ent) => {
|
||||
Entry::Occupied(ent) => {
|
||||
let cell = ent.into_mut();
|
||||
*cell = peer_env;
|
||||
cell
|
||||
}
|
||||
std::collections::hash_map::Entry::Vacant(ent) => ent.insert(peer_env),
|
||||
Entry::Vacant(ent) => ent.insert(peer_env),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_peer_failed<Id>(&mut self, peer_id: &Id, failed: bool)
|
||||
// TODO named peer
|
||||
pub fn set_peer_failed<Id>(&mut self, name: &Id, failed: bool)
|
||||
where
|
||||
PeerId: Borrow<Id>,
|
||||
PeerId: Borrow<Id> + for<'a> From<&'a Id>,
|
||||
Id: Hash + Eq + ?Sized,
|
||||
{
|
||||
let peer_id = self.resolve_peer(name);
|
||||
let mut peers_ref = self.peers.borrow_mut();
|
||||
peers_ref
|
||||
.get_mut(peer_id)
|
||||
.get_mut::<PeerId>(&peer_id)
|
||||
.expect("unknown peer")
|
||||
.as_ref()
|
||||
.borrow_mut()
|
||||
.set_failed(failed);
|
||||
}
|
||||
|
||||
pub fn fail_peer_for<Id>(&mut self, source_peer_id: &Id, target_peer_id: impl Into<PeerId>)
|
||||
// TODO named peer
|
||||
pub fn fail_peer_for<Id1, Id2>(&mut self, source_peer_name: &Id1, target_peer_name: &Id2)
|
||||
where
|
||||
PeerId: Borrow<Id>,
|
||||
Id: Hash + Eq + ?Sized,
|
||||
{
|
||||
let mut peers_ref = self.peers.borrow_mut();
|
||||
peers_ref
|
||||
.get_mut(source_peer_id)
|
||||
.expect("unknown peer")
|
||||
.as_ref()
|
||||
.borrow_mut()
|
||||
.get_neighborhood_mut()
|
||||
.set_target_unreachable(target_peer_id);
|
||||
}
|
||||
|
||||
pub fn unfail_peer_for<Id1, Id2>(&mut self, source_peer_id: &Id1, target_peer_id: &Id2)
|
||||
where
|
||||
PeerId: Borrow<Id1>,
|
||||
PeerId: Borrow<Id1> + for<'a> From<&'a Id1>,
|
||||
Id1: Hash + Eq + ?Sized,
|
||||
PeerId: Borrow<Id2>,
|
||||
PeerId: Borrow<Id2> + for<'a> From<&'a Id2>,
|
||||
Id2: Hash + Eq + ?Sized,
|
||||
{
|
||||
let source_peer_id = self.resolve_peer(source_peer_name);
|
||||
let target_peer_id = self.resolve_peer(target_peer_name);
|
||||
|
||||
let mut peers_ref = self.peers.borrow_mut();
|
||||
peers_ref
|
||||
.get_mut(source_peer_id)
|
||||
.get_mut::<PeerId>(&source_peer_id)
|
||||
.expect("unknown peer")
|
||||
.as_ref()
|
||||
.borrow_mut()
|
||||
.get_neighborhood_mut()
|
||||
.unset_target_unreachable(target_peer_id);
|
||||
.set_target_unreachable(&target_peer_id);
|
||||
}
|
||||
|
||||
// TODO named peer
|
||||
pub fn unfail_peer_for<Id1, Id2>(&mut self, source_peer_name: &Id1, target_peer_name: &Id2)
|
||||
where
|
||||
PeerId: Borrow<Id1> + for<'a> From<&'a Id1>,
|
||||
Id1: Hash + Eq + ?Sized,
|
||||
PeerId: Borrow<Id2> + for<'a> From<&'a Id2>,
|
||||
Id2: Hash + Eq + ?Sized,
|
||||
{
|
||||
let source_peer_id = self.resolve_peer(source_peer_name);
|
||||
let target_peer_id = self.resolve_peer(target_peer_name);
|
||||
let mut peers_ref = self.peers.borrow_mut();
|
||||
peers_ref
|
||||
.get_mut(&source_peer_id)
|
||||
.expect("unknown peer")
|
||||
.as_ref()
|
||||
.borrow_mut()
|
||||
.get_neighborhood_mut()
|
||||
.unset_target_unreachable(&target_peer_id);
|
||||
}
|
||||
|
||||
// TODO there is some kind of unsymmetry between these methods and the fail/unfail:
|
||||
// the latters panic on unknown peer; perhaps, it's OK
|
||||
// TODO named peer
|
||||
pub fn get_peer_env<Id>(&self, peer_id: &Id) -> Option<Rc<RefCell<PeerEnv<R>>>>
|
||||
where
|
||||
PeerId: Borrow<Id>,
|
||||
PeerId: Borrow<Id> + for<'a> From<&'a Id>,
|
||||
Id: Hash + Eq + ?Sized,
|
||||
{
|
||||
let peers_ref = self.peers.borrow();
|
||||
peers_ref.get(peer_id).cloned()
|
||||
}
|
||||
|
||||
pub fn get_named_peer_env<Id>(&self, peer_name: &Id) -> Option<Rc<RefCell<PeerEnv<R>>>>
|
||||
where
|
||||
PeerId: Borrow<Id> + for<'a> From<&'a Id>,
|
||||
Id: Hash + Eq + ?Sized,
|
||||
{
|
||||
let peer_id: PeerId = self.resolve_peer(peer_name);
|
||||
self.get_peer_env::<PeerId>(&peer_id)
|
||||
}
|
||||
|
||||
pub(crate) fn get_services(&self) -> Rc<NetworkServices> {
|
||||
self.services.clone()
|
||||
}
|
||||
@ -244,4 +313,16 @@ impl<R: AirRunner> Network<R> {
|
||||
let peers_ref = self.peers.borrow();
|
||||
peers_ref.keys().cloned().collect::<Vec<_>>().into_iter()
|
||||
}
|
||||
|
||||
// TODO it sounds cool, but actually, name and PeerId should be
|
||||
// distinct and have distinct API for working with a peer by name: &str
|
||||
// and by PeerId
|
||||
pub fn resolve_peer<Id>(&self, name: &Id) -> PeerId
|
||||
where
|
||||
PeerId: Borrow<Id> + for<'a> From<&'a Id>,
|
||||
Id: Hash + Eq + ?Sized,
|
||||
{
|
||||
let resolver = self.resolver.borrow();
|
||||
resolver.get(name).cloned().unwrap_or_else(|| (name).into())
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user