From ad891509d56dbb359713217ab5d02c85fb9d976d Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Fri, 29 Jul 2022 12:12:53 +0400 Subject: [PATCH 1/7] feat(dan/wasm): implement basic wasm module engine calls (#4350) Description --- - Implements OP_CREATE_COMPONENT call which calls into a mocked RuntimeInterface, returning the result to a test - adds `tari_template_lib` (TODO: exciting name) - Adds debug call to help with basic debugging output for WASM code - DRY up some memory reading code for loading template def from memory - Move WASM template definition loading from package to wasm module. - add permitted functions wasm ABI validations - efficient copy from wasm memory Motivation and Context --- Hooks up basic WASM interactions with an implementation of RuntimeInterface backend How Has This Been Tested? --- Existing tests that compile and run WASM modules. --- Cargo.lock | 2 + dan_layer/common_types/Cargo.toml | 5 +- dan_layer/common_types/src/hash.rs | 70 ++++++ dan_layer/common_types/src/lib.rs | 2 + dan_layer/engine/Cargo.toml | 2 +- dan_layer/engine/src/crypto.rs | 27 +- dan_layer/engine/src/instruction/error.rs | 2 +- dan_layer/engine/src/instruction/mod.rs | 8 +- dan_layer/engine/src/instruction/processor.rs | 46 +++- dan_layer/engine/src/lib.rs | 5 +- dan_layer/engine/src/models/component.rs | 46 ++++ dan_layer/engine/src/models/mod.rs | 7 +- .../engine/src/{env.rs => models/vault.rs} | 23 +- dan_layer/engine/src/package.rs | 216 ---------------- dan_layer/engine/src/packager/error.rs | 29 +++ dan_layer/engine/src/packager/mod.rs | 30 +++ .../engine/src/packager/module_loader.rs | 30 +++ dan_layer/engine/src/packager/package.rs | 92 +++++++ dan_layer/engine/src/runtime.rs | 76 ++++++ dan_layer/engine/src/{ => wasm}/compile.rs | 7 +- dan_layer/engine/src/wasm/environment.rs | 234 ++++++++++++++++++ dan_layer/engine/src/wasm/error.rs | 44 +++- dan_layer/engine/src/wasm/mod.rs | 14 +- dan_layer/engine/src/wasm/module.rs | 98 ++++++++ dan_layer/engine/src/wasm/process.rs | 102 ++++++-- dan_layer/engine/src/wasm/vm.rs | 153 ------------ dan_layer/engine/tests/common/Cargo.toml | 20 -- dan_layer/engine/tests/hello_world/Cargo.lock | 16 +- dan_layer/engine/tests/hello_world/Cargo.toml | 2 +- dan_layer/engine/tests/hello_world/src/lib.rs | 2 +- .../engine/tests/mock_runtime_interface.rs | 64 +++++ dan_layer/engine/tests/state/Cargo.lock | 16 +- dan_layer/engine/tests/state/Cargo.toml | 2 +- dan_layer/engine/tests/state/src/lib.rs | 147 ++++++----- dan_layer/engine/tests/test.rs | 51 ++-- dan_layer/template_abi/src/encoding.rs | 22 +- dan_layer/template_abi/src/lib.rs | 44 +++- dan_layer/template_abi/src/ops.rs | 24 ++ .../tests/common => template_lib}/Cargo.lock | 0 dan_layer/template_lib/Cargo.toml | 20 ++ .../tests/common => template_lib}/src/lib.rs | 56 ++++- .../template_lib/src/models/component.rs | 23 ++ dan_layer/template_lib/src/models/mod.rs | 24 ++ 43 files changed, 1315 insertions(+), 588 deletions(-) create mode 100644 dan_layer/common_types/src/hash.rs create mode 100644 dan_layer/engine/src/models/component.rs rename dan_layer/engine/src/{env.rs => models/vault.rs} (77%) mode change 100644 => 100755 delete mode 100644 dan_layer/engine/src/package.rs create mode 100644 dan_layer/engine/src/packager/error.rs create mode 100644 dan_layer/engine/src/packager/mod.rs create mode 100644 dan_layer/engine/src/packager/module_loader.rs create mode 100644 dan_layer/engine/src/packager/package.rs create mode 100644 dan_layer/engine/src/runtime.rs rename dan_layer/engine/src/{ => wasm}/compile.rs (93%) create mode 100644 dan_layer/engine/src/wasm/environment.rs delete mode 100644 dan_layer/engine/src/wasm/vm.rs delete mode 100644 dan_layer/engine/tests/common/Cargo.toml create mode 100644 dan_layer/engine/tests/mock_runtime_interface.rs create mode 100644 dan_layer/template_abi/src/ops.rs rename dan_layer/{engine/tests/common => template_lib}/Cargo.lock (100%) create mode 100644 dan_layer/template_lib/Cargo.toml rename dan_layer/{engine/tests/common => template_lib}/src/lib.rs (63%) create mode 100644 dan_layer/template_lib/src/models/component.rs create mode 100644 dan_layer/template_lib/src/models/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6330ec167c..3531e3ee17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7166,9 +7166,11 @@ dependencies = [ name = "tari_dan_common_types" version = "0.1.0" dependencies = [ + "borsh", "prost", "prost-types", "tari_common", + "tari_common_types", ] [[package]] diff --git a/dan_layer/common_types/Cargo.toml b/dan_layer/common_types/Cargo.toml index bf822c0abc..fc12b0ae16 100644 --- a/dan_layer/common_types/Cargo.toml +++ b/dan_layer/common_types/Cargo.toml @@ -7,9 +7,12 @@ license = "BSD-3-Clause" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +tari_common = { path = "../../common", features = ["build"] } +tari_common_types = { path = "../../base_layer/common_types" } + +borsh = "0.9.3" prost = "0.9" prost-types = "0.9" -tari_common = { path = "../../common", features = ["build"] } [build-dependencies] tari_common = { path = "../../common", features = ["build"] } diff --git a/dan_layer/common_types/src/hash.rs b/dan_layer/common_types/src/hash.rs new file mode 100644 index 0000000000..e6d7897832 --- /dev/null +++ b/dan_layer/common_types/src/hash.rs @@ -0,0 +1,70 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{io, io::Write}; + +use borsh::{BorshDeserialize, BorshSerialize}; +use tari_common_types::types::FixedHash; + +// This is to avoid adding borsh as a dependency in common types (and therefore every application). +// TODO: Either this becomes the standard Hash type for the dan layer, or add borsh support to FixedHash. +#[derive(Debug, Clone, PartialEq, Default)] +pub struct Hash(FixedHash); + +impl Hash { + pub fn into_inner(self) -> FixedHash { + self.0 + } +} + +impl From for Hash { + fn from(hash: FixedHash) -> Self { + Self(hash) + } +} + +impl BorshSerialize for Hash { + fn serialize(&self, writer: &mut W) -> io::Result<()> { + (*self.0).serialize(writer) + } +} + +impl BorshDeserialize for Hash { + fn deserialize(buf: &mut &[u8]) -> io::Result { + let hash = <[u8; 32] as BorshDeserialize>::deserialize(buf)?; + Ok(Hash(hash.into())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serialize_deserialize() { + let hash = Hash::default(); + let mut buf = Vec::new(); + hash.serialize(&mut buf).unwrap(); + let hash2 = Hash::deserialize(&mut &buf[..]).unwrap(); + assert_eq!(hash, hash2); + } +} diff --git a/dan_layer/common_types/src/lib.rs b/dan_layer/common_types/src/lib.rs index 6a2bf5022a..f738209b6a 100644 --- a/dan_layer/common_types/src/lib.rs +++ b/dan_layer/common_types/src/lib.rs @@ -4,8 +4,10 @@ pub mod proto; pub mod storage; +mod hash; mod template_id; +pub use hash::Hash; use tari_common::hashing_domain::HashingDomain; pub use template_id::TemplateId; diff --git a/dan_layer/engine/Cargo.toml b/dan_layer/engine/Cargo.toml index a9db59e36a..fe5d89b82d 100644 --- a/dan_layer/engine/Cargo.toml +++ b/dan_layer/engine/Cargo.toml @@ -24,4 +24,4 @@ rand = "0.8.1" serde = "1.0.126" serde_json = "1.0.81" thiserror = "^1.0.20" -wasmer = "2.2.1" +wasmer = "2.3.0" diff --git a/dan_layer/engine/src/crypto.rs b/dan_layer/engine/src/crypto.rs index 9413c91fbd..37f855003f 100644 --- a/dan_layer/engine/src/crypto.rs +++ b/dan_layer/engine/src/crypto.rs @@ -20,30 +20,19 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use digest::Digest; use rand::rngs::OsRng; use tari_common_types::types::{PrivateKey, PublicKey}; -use tari_crypto::{ - hash::blake2::Blake256, - hashing::{DomainSeparatedHasher, DomainSeparation}, - keys::PublicKey as PublicKeyT, -}; +use tari_crypto::{hash::blake2::Blake256, hash_domain, hashing::DomainSeparatedHasher, keys::PublicKey as PublicKeyT}; -pub fn create_key_pair() -> (PrivateKey, PublicKey) { - PublicKey::random_keypair(&mut OsRng) -} +hash_domain!(TariEngineHashDomain, "tari.dan.engine", 0); -pub struct TariEngineDomainSeparation; +pub type TariEngineHasher = DomainSeparatedHasher; -impl DomainSeparation for TariEngineDomainSeparation { - fn version() -> u8 { - 0 - } - - fn domain() -> &'static str { - "tari.dan.engine" - } +pub fn hasher(label: &'static str) -> impl Digest { + TariEngineHasher::new(label) } -pub fn domain_separated_hasher(label: &'static str) -> DomainSeparatedHasher { - DomainSeparatedHasher::new(label) +pub fn create_key_pair() -> (PrivateKey, PublicKey) { + PublicKey::random_keypair(&mut OsRng) } diff --git a/dan_layer/engine/src/instruction/error.rs b/dan_layer/engine/src/instruction/error.rs index 6f1cf4eed4..35926900a7 100644 --- a/dan_layer/engine/src/instruction/error.rs +++ b/dan_layer/engine/src/instruction/error.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{package::PackageId, wasm::WasmExecutionError}; +use crate::{packager::PackageId, wasm::WasmExecutionError}; #[derive(Debug, thiserror::Error)] pub enum InstructionError { diff --git a/dan_layer/engine/src/instruction/mod.rs b/dan_layer/engine/src/instruction/mod.rs index 0d76d569f6..286e4b5b40 100644 --- a/dan_layer/engine/src/instruction/mod.rs +++ b/dan_layer/engine/src/instruction/mod.rs @@ -30,7 +30,7 @@ pub use processor::InstructionProcessor; mod signature; -use crate::{instruction::signature::InstructionSignature, package::PackageId}; +use crate::{instruction::signature::InstructionSignature, packager::PackageId}; #[derive(Debug, Clone)] pub enum Instruction { @@ -40,6 +40,12 @@ pub enum Instruction { function: String, args: Vec>, }, + CallMethod { + package_id: PackageId, + component_id: String, + method: String, + args: Vec>, + }, } #[derive(Debug, Clone)] diff --git a/dan_layer/engine/src/instruction/processor.rs b/dan_layer/engine/src/instruction/processor.rs index e2a71db139..0aa9f529b8 100644 --- a/dan_layer/engine/src/instruction/processor.rs +++ b/dan_layer/engine/src/instruction/processor.rs @@ -20,24 +20,29 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; use crate::{ instruction::{error::InstructionError, Instruction, InstructionSet}, - package::{Package, PackageId}, + packager::{Package, PackageId}, + runtime::{Runtime, RuntimeInterface}, traits::Invokable, - wasm::{ExecutionResult, Process, VmInstance}, + wasm::{ExecutionResult, Process}, }; #[derive(Debug, Clone, Default)] -pub struct InstructionProcessor { +pub struct InstructionProcessor { packages: HashMap, + runtime_interface: TRuntimeInterface, } -impl InstructionProcessor { - pub fn new() -> Self { +impl InstructionProcessor +where TRuntimeInterface: RuntimeInterface + Clone + 'static +{ + pub fn new(runtime_interface: TRuntimeInterface) -> Self { Self { packages: HashMap::new(), + runtime_interface, } } @@ -47,7 +52,10 @@ impl InstructionProcessor { } pub fn execute(&self, instruction_set: InstructionSet) -> Result, InstructionError> { - let mut results = vec![]; + let mut results = Vec::with_capacity(instruction_set.instructions.len()); + + // TODO: implement engine + let state = Runtime::new(Arc::new(self.runtime_interface.clone())); for instruction in instruction_set.instructions { match instruction { Instruction::CallFunction { @@ -65,11 +73,31 @@ impl InstructionProcessor { .ok_or(InstructionError::TemplateNameNotFound { name: template })?; // TODO: implement intelligent instance caching - let vm = VmInstance::instantiate(module.wasm_module())?; - let process = Process::new(module.clone(), vm); + let process = Process::start(module.clone(), state.clone())?; let result = process.invoke_by_name(&function, args)?; results.push(result); }, + Instruction::CallMethod { + package_id, + component_id, + method, + args, + } => { + let package = self + .packages + .get(&package_id) + .ok_or(InstructionError::PackageNotFound { package_id })?; + // TODO: load component, not module - component_id is currently hard-coded as the template name in + // tests + let module = package + .get_module_by_name(&component_id) + .ok_or(InstructionError::TemplateNameNotFound { name: component_id })?; + + // TODO: implement intelligent instance caching + let process = Process::start(module.clone(), state.clone())?; + let result = process.invoke_by_name(&method, args)?; + results.push(result); + }, } } diff --git a/dan_layer/engine/src/lib.rs b/dan_layer/engine/src/lib.rs index 1d4ec36f67..c2d7b34f9d 100644 --- a/dan_layer/engine/src/lib.rs +++ b/dan_layer/engine/src/lib.rs @@ -10,11 +10,10 @@ pub mod models; pub mod state; pub mod wasm; -pub mod compile; pub mod crypto; -pub mod env; pub mod instruction; -pub mod package; +pub mod packager; +pub mod runtime; pub mod traits; /// The DAN layer engine domain separated hashing domain diff --git a/dan_layer/engine/src/models/component.rs b/dan_layer/engine/src/models/component.rs new file mode 100644 index 0000000000..f99e4fe432 --- /dev/null +++ b/dan_layer/engine/src/models/component.rs @@ -0,0 +1,46 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::collections::HashMap; + +use tari_dan_common_types::Hash; +use tari_template_abi::CreateComponentArg; + +pub type ComponentId = (Hash, u32); + +pub struct Component { + pub name: String, + pub quantity: u64, + pub metadata: HashMap, Vec>, + pub state: Vec, +} + +impl From for Component { + fn from(arg: CreateComponentArg) -> Self { + Self { + name: arg.name, + quantity: arg.quantity, + metadata: arg.metadata, + state: arg.state, + } + } +} diff --git a/dan_layer/engine/src/models/mod.rs b/dan_layer/engine/src/models/mod.rs index 020c508d6d..bfc989b36d 100644 --- a/dan_layer/engine/src/models/mod.rs +++ b/dan_layer/engine/src/models/mod.rs @@ -2,5 +2,10 @@ // SPDX-License-Identifier: BSD-3-Clause mod bucket; - pub use bucket::Bucket; + +mod component; +pub use component::{Component, ComponentId}; + +mod vault; +pub use vault::{Vault, VaultId}; diff --git a/dan_layer/engine/src/env.rs b/dan_layer/engine/src/models/vault.rs old mode 100644 new mode 100755 similarity index 77% rename from dan_layer/engine/src/env.rs rename to dan_layer/engine/src/models/vault.rs index 09d6be4509..dfa72ee415 --- a/dan_layer/engine/src/env.rs +++ b/dan_layer/engine/src/models/vault.rs @@ -19,23 +19,16 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use borsh::{BorshDeserialize, BorshSerialize}; +use tari_common_types::types::FixedHash; -use std::sync::{Arc, Mutex}; +pub type VaultId = FixedHash; -pub fn tari_engine(env: &EngineEnvironment, op: i32, _args_ptr: i32, args_len: i32) -> i32 { - println!("tari_engine CALLED: op: {}, args: {}", op, args_len); - // TODO: - env.inc_counter(); - 0 -} - -#[derive(wasmer::WasmerEnv, Clone, Default)] -pub struct EngineEnvironment { - counter: Arc>, -} +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub struct Vault {} -impl EngineEnvironment { - pub fn inc_counter(&self) { - *self.counter.lock().unwrap() += 1; +impl Vault { + pub fn empty() -> Self { + Self {} } } diff --git a/dan_layer/engine/src/package.rs b/dan_layer/engine/src/package.rs deleted file mode 100644 index f6227088fa..0000000000 --- a/dan_layer/engine/src/package.rs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::{cell::Cell, collections::HashMap, convert::TryInto}; - -use rand::{rngs::OsRng, RngCore}; -use tari_common_types::types::FixedHash; -use tari_template_abi::TemplateDef; -use wasmer::{imports, Extern, Function, Instance, Memory, Module, Store, Val}; - -use crate::{crypto, wasm::LoadedWasmModule}; - -#[derive(Debug, Clone, Default)] -pub struct PackageBuilder { - wasm_code: Vec>, -} - -impl PackageBuilder { - pub fn new() -> Self { - Self { wasm_code: Vec::new() } - } - - pub fn add_wasm_template(&mut self, wasm_code: Vec) -> &mut Self { - self.wasm_code.push(wasm_code); - self - } - - pub fn build(&self) -> Result { - let mut wasm_modules = HashMap::with_capacity(self.wasm_code.len()); - let store = Store::default(); - let id = new_package_id(); - for code in &self.wasm_code { - let module = load_wasm_module(&store, code)?; - wasm_modules.insert(module.template_name().to_string(), module); - } - - Ok(Package { - id, - wasm_modules, - _store: store, - }) - } -} - -fn new_package_id() -> PackageId { - let v = OsRng.next_u32(); - crypto::domain_separated_hasher("package") - // TODO: Proper package id - .chain(&v.to_le_bytes()) - .finalize() - .as_ref() - .try_into() - .unwrap() -} - -fn load_wasm_module(store: &Store, code: &[u8]) -> Result { - let module = Module::new(store, code)?; - - fn stub(_op: i32, _args_ptr: i32, _args_len: i32) -> i32 { - 0 - } - - let imports = imports! { - "env" => { - "tari_engine" => Function::new_native(store, stub), - } - }; - let instance = Instance::new(&module, &imports)?; - validate_instance(&instance)?; - - let template = initialize_and_load_template_abi(&instance)?; - Ok(LoadedWasmModule::new(template, module)) -} - -fn initialize_and_load_template_abi(instance: &Instance) -> Result { - let abi_func = instance - .exports - .iter() - .find_map(|(name, export)| match export { - Extern::Function(f) if name.ends_with("_abi") => Some(f), - _ => None, - }) - .ok_or(PackageError::NoAbiDefinition)?; - - // Initialize ABI memory - let ret = abi_func.call(&[])?; - let ptr = match ret.get(0) { - Some(Val::I32(ptr)) => *ptr as u32, - Some(_) | None => return Err(PackageError::InvalidReturnTypeFromAbiFunc), - }; - - // Load ABI from memory - let memory = instance.exports.get_memory("memory")?; - let data = copy_abi_data_from_memory_checked(memory, ptr)?; - let decoded = tari_template_abi::decode(&data).map_err(|_| PackageError::AbiDecodeError)?; - Ok(decoded) -} - -fn copy_abi_data_from_memory_checked(memory: &Memory, ptr: u32) -> Result, PackageError> { - // Check memory bounds - if memory.data_size() < u64::from(ptr) { - return Err(PackageError::AbiPointerOutOfBounds); - } - - let view = memory.uint8view().subarray(ptr, memory.data_size() as u32 - 1); - let data = &*view; - if data.len() < 4 { - return Err(PackageError::MemoryUnderflow { - required: 4, - remaining: data.len(), - }); - } - - fn copy_from_cell_slice(src: &[Cell], dest: &mut [u8], len: usize) { - for i in 0..len { - dest[i] = src[i].get(); - } - } - - let mut buf = [0u8; 4]; - copy_from_cell_slice(data, &mut buf, 4); - let len = u32::from_le_bytes(buf) as usize; - const MAX_ABI_DATA_LEN: usize = 1024 * 1024; - if len > MAX_ABI_DATA_LEN { - return Err(PackageError::AbiDataTooLarge { - max: MAX_ABI_DATA_LEN, - size: len, - }); - } - if data.len() < 4 + len { - return Err(PackageError::MemoryUnderflow { - required: 4 + len, - remaining: data.len(), - }); - } - - let mut data = vec![0u8; len]; - let src = view.subarray(4, 4 + len as u32); - copy_from_cell_slice(&*src, &mut data, len); - Ok(data) -} - -pub fn validate_instance(instance: &Instance) -> Result<(), PackageError> { - if let Ok(mem) = instance.exports.get_memory("memory") { - if mem.size().bytes().0 > 2 * 1024 * 1024 { - return Err(PackageError::MaxMemorySizeExceeded); - } - } - // TODO other package validations - - Ok(()) -} - -pub type PackageId = FixedHash; - -#[derive(Debug, Clone)] -pub struct Package { - id: PackageId, - wasm_modules: HashMap, - _store: Store, -} - -impl Package { - pub fn get_module_by_name(&self, name: &str) -> Option<&LoadedWasmModule> { - self.wasm_modules.get(name) - } - - pub fn id(&self) -> PackageId { - self.id - } -} - -#[derive(Debug, thiserror::Error)] -pub enum PackageError { - #[error(transparent)] - CompileError(#[from] wasmer::CompileError), - #[error(transparent)] - InstantiationError(#[from] wasmer::InstantiationError), - #[error(transparent)] - RuntimeError(#[from] wasmer::RuntimeError), - #[error(transparent)] - ExportError(#[from] wasmer::ExportError), - #[error("Failed to decode ABI")] - AbiDecodeError, - #[error("maximum module memory size exceeded")] - MaxMemorySizeExceeded, - #[error("package did not contain an ABI definition")] - NoAbiDefinition, - #[error("package ABI function returned an invalid type")] - InvalidReturnTypeFromAbiFunc, - #[error("package ABI function returned an out of bounds pointer")] - AbiPointerOutOfBounds, - #[error("memory underflow: {required} bytes required but {remaining} remaining")] - MemoryUnderflow { required: usize, remaining: usize }, - #[error("ABI data is too large: a maximum of {max} bytes allowed but size is {size}")] - AbiDataTooLarge { max: usize, size: usize }, -} diff --git a/dan_layer/engine/src/packager/error.rs b/dan_layer/engine/src/packager/error.rs new file mode 100644 index 0000000000..bac307265c --- /dev/null +++ b/dan_layer/engine/src/packager/error.rs @@ -0,0 +1,29 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::wasm::WasmExecutionError; + +#[derive(Debug, thiserror::Error)] +pub enum PackageError { + #[error(transparent)] + WasmModuleError(#[from] WasmExecutionError), +} diff --git a/dan_layer/engine/src/packager/mod.rs b/dan_layer/engine/src/packager/mod.rs new file mode 100644 index 0000000000..edc894fbf3 --- /dev/null +++ b/dan_layer/engine/src/packager/mod.rs @@ -0,0 +1,30 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod error; +pub use error::PackageError; + +mod package; +pub use package::{Package, PackageBuilder, PackageId}; + +mod module_loader; +pub use module_loader::PackageModuleLoader; diff --git a/dan_layer/engine/src/packager/module_loader.rs b/dan_layer/engine/src/packager/module_loader.rs new file mode 100644 index 0000000000..871d9558b6 --- /dev/null +++ b/dan_layer/engine/src/packager/module_loader.rs @@ -0,0 +1,30 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::packager::PackageError; + +pub trait PackageModuleLoader { + type Loaded; + type Error: Into; + + fn load_module(&self) -> Result; +} diff --git a/dan_layer/engine/src/packager/package.rs b/dan_layer/engine/src/packager/package.rs new file mode 100644 index 0000000000..f78128e0c0 --- /dev/null +++ b/dan_layer/engine/src/packager/package.rs @@ -0,0 +1,92 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::collections::HashMap; + +use digest::Digest; +use rand::{rngs::OsRng, RngCore}; +use tari_common_types::types::FixedHash; + +use crate::{ + crypto, + packager::{error::PackageError, PackageModuleLoader}, + wasm::{LoadedWasmModule, WasmModule}, +}; + +pub type PackageId = FixedHash; + +#[derive(Debug, Clone)] +pub struct Package { + id: PackageId, + wasm_modules: HashMap, +} + +impl Package { + pub fn builder() -> PackageBuilder { + PackageBuilder::new() + } + + pub fn get_module_by_name(&self, name: &str) -> Option<&LoadedWasmModule> { + self.wasm_modules.get(name) + } + + pub fn id(&self) -> PackageId { + self.id + } +} + +#[derive(Debug, Clone, Default)] +pub struct PackageBuilder { + wasm_modules: Vec, +} + +impl PackageBuilder { + pub fn new() -> Self { + Self { + wasm_modules: Vec::new(), + } + } + + pub fn add_wasm_module(&mut self, wasm_module: WasmModule) -> &mut Self { + self.wasm_modules.push(wasm_module); + self + } + + pub fn build(&self) -> Result { + let mut wasm_modules = HashMap::with_capacity(self.wasm_modules.len()); + let id = new_package_id(); + for wasm in &self.wasm_modules { + let loaded = wasm.load_module()?; + wasm_modules.insert(loaded.template_name().to_string(), loaded); + } + + Ok(Package { id, wasm_modules }) + } +} + +fn new_package_id() -> PackageId { + let v = OsRng.next_u32(); + crypto::hasher("package") + // TODO: Proper package id + .chain(&v.to_le_bytes()) + .finalize().into() +} diff --git a/dan_layer/engine/src/runtime.rs b/dan_layer/engine/src/runtime.rs new file mode 100644 index 0000000000..80fa392cf8 --- /dev/null +++ b/dan_layer/engine/src/runtime.rs @@ -0,0 +1,76 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ + collections::HashMap, + fmt::Debug, + sync::{Arc, RwLock}, +}; + +use tari_common_types::types::FixedHash; +use tari_template_abi::LogLevel; + +use crate::models::{Bucket, Component, ComponentId}; + +#[derive(Clone)] +pub struct Runtime { + tracker: Arc>, + interface: Arc, +} + +impl Runtime { + pub fn new(engine: Arc) -> Self { + Self { + tracker: Arc::new(RwLock::new(ChangeTracker::default())), + interface: engine, + } + } + + pub fn interface(&self) -> &dyn RuntimeInterface { + &*self.interface + } +} + +impl Debug for Runtime { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Runtime") + .field("tracker", &self.tracker) + .field("engine", &"dyn RuntimeEngine") + .finish() + } +} + +#[derive(Debug, Clone, Default)] +pub struct ChangeTracker { + pub buckets: HashMap, +} + +#[derive(Debug, thiserror::Error)] +pub enum RuntimeError { + #[error("todo")] + Todo, +} + +pub trait RuntimeInterface: Send + Sync { + fn emit_log(&self, level: LogLevel, message: &str); + fn create_component(&self, component: Component) -> Result; +} diff --git a/dan_layer/engine/src/compile.rs b/dan_layer/engine/src/wasm/compile.rs similarity index 93% rename from dan_layer/engine/src/compile.rs rename to dan_layer/engine/src/wasm/compile.rs index a22dc6f3d6..61b50e3fcc 100644 --- a/dan_layer/engine/src/compile.rs +++ b/dan_layer/engine/src/wasm/compile.rs @@ -24,7 +24,9 @@ use std::{fs, io, io::ErrorKind, path::Path, process::Command}; use cargo_toml::{Manifest, Product}; -pub fn compile_template>(package_dir: P) -> io::Result> { +use super::module::WasmModule; + +pub fn build_wasm_module_from_source>(package_dir: P) -> io::Result { let status = Command::new("cargo") .current_dir(package_dir.as_ref()) .args(["build", "--target", "wasm32-unknown-unknown", "--release"]) @@ -65,5 +67,6 @@ pub fn compile_template>(package_dir: P) -> io::Result> { path.set_extension("wasm"); // return - fs::read(path) + let code = fs::read(path)?; + Ok(WasmModule::from_code(code)) } diff --git a/dan_layer/engine/src/wasm/environment.rs b/dan_layer/engine/src/wasm/environment.rs new file mode 100644 index 0000000000..91d5454030 --- /dev/null +++ b/dan_layer/engine/src/wasm/environment.rs @@ -0,0 +1,234 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ + cell::Cell, + fmt::{Debug, Formatter}, +}; + +use wasmer::{ + imports, + Function, + HostEnvInitError, + Instance, + LazyInit, + Memory, + NativeFunc, + Pages, + Resolver, + Store, + WasmerEnv, +}; + +use crate::wasm::WasmExecutionError; + +#[derive(Clone)] +pub struct WasmEnv { + memory: LazyInit, + mem_alloc: LazyInit>, + mem_free: LazyInit>, + state: T, +} + +impl WasmEnv { + pub fn new(state: T) -> Self { + Self { + state, + memory: LazyInit::new(), + mem_alloc: LazyInit::new(), + mem_free: LazyInit::new(), + } + } + + pub(super) fn alloc(&self, len: u32) -> Result { + let ptr = self.get_mem_alloc_func()?.call(len as i32)?; + if ptr == 0 { + return Err(WasmExecutionError::MemoryAllocationFailed); + } + + Ok(AllocPtr(ptr as u32, len)) + } + + pub(super) fn free(&self, ptr: AllocPtr) -> Result<(), WasmExecutionError> { + self.get_mem_free_func()?.call(ptr.as_i32())?; + Ok(()) + } + + pub(super) fn write_to_memory(&self, ptr: &AllocPtr, data: &[u8]) -> Result<(), WasmExecutionError> { + if data.len() != ptr.len() as usize { + return Err(WasmExecutionError::InvalidWriteLength { + allocated: ptr.len(), + requested: data.len() as u32, + }); + } + // SAFETY: The pointer has been allocated by alloc above and the runtime is single-threaded so data + // races are not possible. + unsafe { + self.get_memory()? + .uint8view() + .subarray(ptr.get(), ptr.end()) + .copy_from(data); + } + Ok(()) + } + + pub(super) fn read_memory_with_embedded_len(&self, ptr: u32) -> Result, WasmExecutionError> { + let memory = self.get_memory()?; + let view = memory.uint8view().subarray(ptr, memory.data_size() as u32 - 1); + let view_bytes = &*view; + if view_bytes.len() < 4 { + return Err(WasmExecutionError::MemoryUnderflow { + required: 4, + remaining: view_bytes.len(), + }); + } + + let mut buf = [0u8; 4]; + copy_from_cell_slice(view_bytes, &mut buf); + let len = u32::from_le_bytes(buf); + let data = self.read_from_memory(ptr + 4, len)?; + + Ok(data) + } + + pub(super) fn read_from_memory(&self, ptr: u32, len: u32) -> Result, WasmExecutionError> { + let memory = self.get_memory()?; + let size = memory.data_size(); + if u64::from(ptr) >= size || u64::from(ptr + len) >= memory.data_size() { + return Err(WasmExecutionError::MemoryPointerOutOfRange { + size: memory.data_size(), + pointer: u64::from(ptr), + len: u64::from(len), + }); + } + let view = memory.uint8view().subarray(ptr, ptr + len); + let mut data = vec![0u8; len as usize]; + copy_from_cell_slice(&*view, &mut data); + Ok(data) + } + + pub fn state(&self) -> &T { + &self.state + } + + fn get_mem_alloc_func(&self) -> Result<&NativeFunc, WasmExecutionError> { + self.mem_alloc + .get_ref() + .ok_or_else(|| WasmExecutionError::MissingAbiFunction { + function: "tari_alloc".into(), + }) + } + + fn get_mem_free_func(&self) -> Result<&NativeFunc, WasmExecutionError> { + self.mem_free + .get_ref() + .ok_or_else(|| WasmExecutionError::MissingAbiFunction { + function: "tari_free".into(), + }) + } + + fn get_memory(&self) -> Result<&Memory, WasmExecutionError> { + self.memory.get_ref().ok_or(WasmExecutionError::MemoryNotInitialized) + } + + pub fn mem_size(&self) -> Pages { + self.memory.get_ref().map(|mem| mem.size()).unwrap_or(Pages(0)) + } + + pub fn create_resolver(&self, store: &Store, tari_engine: Function) -> impl Resolver { + imports! { + "env" => { + "tari_engine" => tari_engine, + "debug" => Function::new_native_with_env(store, self.clone(), Self::debug_handler), + } + } + } + + fn debug_handler(env: &Self, arg_ptr: i32, arg_len: i32) { + const WASM_DEBUG_LOG_TARGET: &str = "tari::dan::wasm"; + match env.read_from_memory(arg_ptr as u32, arg_len as u32) { + Ok(arg) => { + eprintln!("DEBUG: {}", String::from_utf8_lossy(&arg)); + }, + Err(err) => { + log::error!(target: WASM_DEBUG_LOG_TARGET, "Failed to read from memory: {}", err); + }, + } + } +} + +impl WasmerEnv for WasmEnv { + fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { + self.memory + .initialize(instance.exports.get_with_generics_weak("memory")?); + self.mem_alloc + .initialize(instance.exports.get_with_generics_weak("tari_alloc")?); + self.mem_free + .initialize(instance.exports.get_with_generics_weak("tari_free")?); + Ok(()) + } +} + +impl Debug for WasmEnv { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WasmEnv") + .field("memory", &"LazyInit") + .field("tari_alloc", &" LazyInit") + .field("tari_free", &"LazyInit>") + .field("State", &self.state) + .finish() + } +} + +/// Efficiently copy read-only memory into a mutable buffer. +/// Panics if the length of `dest` is more than the length of `src`. +fn copy_from_cell_slice(src: &[Cell], dest: &mut [u8]) { + assert!(dest.len() <= src.len()); + let len = dest.len(); + // SAFETY: size_of::() is equal to size_of::(), we assert this below just in case. + let (head, body, tail) = unsafe { src[..len].align_to() }; + assert_eq!(head.len(), 0); + assert_eq!(tail.len(), 0); + dest.copy_from_slice(body); +} + +#[derive(Debug)] +pub struct AllocPtr(u32, u32); + +impl AllocPtr { + pub fn get(&self) -> u32 { + self.0 + } + + pub fn len(&self) -> u32 { + self.1 + } + + pub fn end(&self) -> u32 { + self.get() + self.len() + } + + pub fn as_i32(&self) -> i32 { + // We want the 'u32 as i32' conversion to wrap + self.get() as i32 + } +} diff --git a/dan_layer/engine/src/wasm/error.rs b/dan_layer/engine/src/wasm/error.rs index 9e2ba062bc..b632fd41bc 100644 --- a/dan_layer/engine/src/wasm/error.rs +++ b/dan_layer/engine/src/wasm/error.rs @@ -1,8 +1,12 @@ // Copyright 2022 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +use std::io; + use thiserror::Error; -use wasmer::{ExportError, InstantiationError, RuntimeError}; +use wasmer::{ExportError, HostEnvInitError, InstantiationError}; + +use crate::runtime::RuntimeError; #[derive(Debug, Error)] pub enum WasmError { @@ -12,6 +16,16 @@ pub enum WasmError { #[derive(Debug, thiserror::Error)] pub enum WasmExecutionError { + #[error(transparent)] + InstantiationError(#[from] InstantiationError), + #[error(transparent)] + ExportError(#[from] ExportError), + #[error(transparent)] + WasmRuntimeError(#[from] wasmer::RuntimeError), + #[error(transparent)] + HostEnvInitError(#[from] HostEnvInitError), + #[error(transparent)] + CompileError(#[from] wasmer::CompileError), #[error("Function {name} not found")] FunctionNotFound { name: String }, #[error("Expected function {function} to return a pointer")] @@ -20,10 +34,28 @@ pub enum WasmExecutionError { InvalidWriteLength { allocated: u32, requested: u32 }, #[error("memory underflow: {required} bytes required but {remaining} remaining")] MemoryUnderflow { required: usize, remaining: usize }, - #[error(transparent)] - InstantiationError(#[from] InstantiationError), - #[error(transparent)] - ExportError(#[from] ExportError), - #[error(transparent)] + #[error("memory pointer out of range: memory size of {size} but pointer is {pointer}")] + MemoryPointerOutOfRange { size: u64, pointer: u64, len: u64 }, + #[error("Memory allocation failed")] + MemoryAllocationFailed, + #[error("Memory not initialized")] + MemoryNotInitialized, + #[error("Invalid operation {op}")] + InvalidOperation { op: i32 }, + #[error("Missing function {function}")] + MissingAbiFunction { function: String }, + #[error("Runtime error: {0}")] RuntimeError(#[from] RuntimeError), + #[error("Failed to decode argument for engine call: {0}")] + EngineArgDecodeFailed(io::Error), + #[error("maximum module memory size exceeded")] + MaxMemorySizeExceeded, + #[error("Failed to decode ABI")] + AbiDecodeError, + #[error("package ABI function returned an invalid type")] + InvalidReturnTypeFromAbiFunc, + #[error("package did not contain an ABI definition")] + NoAbiDefinition, + #[error("Unexpected ABI function {name}")] + UnexpectedAbiFunction { name: String }, } diff --git a/dan_layer/engine/src/wasm/mod.rs b/dan_layer/engine/src/wasm/mod.rs index 0612136de0..39f0d76272 100644 --- a/dan_layer/engine/src/wasm/mod.rs +++ b/dan_layer/engine/src/wasm/mod.rs @@ -1,19 +1,21 @@ // Copyright 2022 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -mod error; mod wasm_module_definition; mod wasm_module_factory; -pub use error::{WasmError, WasmExecutionError}; pub use wasm_module_definition::WasmModuleDefinition; pub use wasm_module_factory::WasmModuleFactory; +pub mod compile; + +mod error; +pub use error::{WasmError, WasmExecutionError}; + +mod environment; + mod module; -pub use module::LoadedWasmModule; +pub use module::{LoadedWasmModule, WasmModule}; mod process; pub use process::{ExecutionResult, Process}; - -mod vm; -pub use vm::VmInstance; diff --git a/dan_layer/engine/src/wasm/module.rs b/dan_layer/engine/src/wasm/module.rs index 44ec5deee1..904e148797 100644 --- a/dan_layer/engine/src/wasm/module.rs +++ b/dan_layer/engine/src/wasm/module.rs @@ -21,6 +21,75 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use tari_template_abi::{FunctionDef, TemplateDef}; +use wasmer::{Extern, Function, Instance, Module, Store, Val, WasmerEnv}; + +use crate::{ + packager::PackageModuleLoader, + wasm::{environment::WasmEnv, WasmExecutionError}, +}; + +#[derive(Debug, Clone)] +pub struct WasmModule { + code: Vec, +} + +impl WasmModule { + pub fn from_code(code: Vec) -> Self { + Self { code } + } + + pub fn code(&self) -> &[u8] { + &self.code + } +} + +impl PackageModuleLoader for WasmModule { + type Error = WasmExecutionError; + type Loaded = LoadedWasmModule; + + fn load_module(&self) -> Result { + let store = Store::default(); + let module = Module::new(&store, &self.code)?; + let mut env = WasmEnv::new(()); + + fn stub(_env: &WasmEnv<()>, _op: i32, _arg_ptr: i32, _arg_len: i32) -> i32 { + panic!("WASM module called engine while loading ABI") + } + + let stub = Function::new_native_with_env(&store, env.clone(), stub); + let imports = env.create_resolver(&store, stub); + let instance = Instance::new(&module, &imports)?; + env.init_with_instance(&instance)?; + validate_instance(&instance)?; + validate_environment(&env)?; + + let template = initialize_and_load_template_abi(&instance, &env)?; + Ok(LoadedWasmModule::new(template, module)) + } +} + +fn initialize_and_load_template_abi(instance: &Instance, env: &WasmEnv<()>) -> Result { + let abi_func = instance + .exports + .iter() + .find_map(|(name, export)| match export { + Extern::Function(f) if name.ends_with("_abi") => Some(f), + _ => None, + }) + .ok_or(WasmExecutionError::NoAbiDefinition)?; + + // Initialize ABI memory + let ret = abi_func.call(&[])?; + let ptr = match ret.get(0) { + Some(Val::I32(ptr)) => *ptr as u32, + Some(_) | None => return Err(WasmExecutionError::InvalidReturnTypeFromAbiFunc), + }; + + // Load ABI from memory + let data = env.read_memory_with_embedded_len(ptr)?; + let decoded = tari_template_abi::decode(&data).map_err(|_| WasmExecutionError::AbiDecodeError)?; + Ok(decoded) +} #[derive(Debug, Clone)] pub struct LoadedWasmModule { @@ -45,3 +114,32 @@ impl LoadedWasmModule { self.template.functions.iter().find(|f| f.name == *function_name) } } + +fn validate_environment(env: &WasmEnv<()>) -> Result<(), WasmExecutionError> { + const MAX_MEM_SIZE: usize = 2 * 1024 * 1024; + let mem_size = env.mem_size(); + if mem_size.bytes().0 > MAX_MEM_SIZE { + return Err(WasmExecutionError::MaxMemorySizeExceeded); + } + // TODO other package validations + + Ok(()) +} + +fn validate_instance(instance: &Instance) -> Result<(), WasmExecutionError> { + // Enforce that only permitted functions are allowed + let unexpected_abi_func = instance + .exports + .iter() + .functions() + .find(|(name, _)| !is_func_permitted(name)); + if let Some((name, _)) = unexpected_abi_func { + return Err(WasmExecutionError::UnexpectedAbiFunction { name: name.to_string() }); + } + + Ok(()) +} + +fn is_func_permitted(name: &str) -> bool { + name.ends_with("_abi") || name.ends_with("_main") || name == "tari_alloc" || name == "tari_free" +} diff --git a/dan_layer/engine/src/wasm/process.rs b/dan_layer/engine/src/wasm/process.rs index 1fc2524053..f9f901ffd4 100644 --- a/dan_layer/engine/src/wasm/process.rs +++ b/dan_layer/engine/src/wasm/process.rs @@ -23,45 +23,44 @@ use std::io; use borsh::{BorshDeserialize, BorshSerialize}; -use tari_template_abi::{encode_into, CallInfo}; -use wasmer::{Module, Val}; +use tari_template_abi::{decode, encode_into, encode_with_len, ops, CallInfo, CreateComponentArg, EmitLogArg, Type}; +use wasmer::{Function, Instance, Module, Store, Val, WasmerEnv}; use crate::{ + runtime::Runtime, traits::Invokable, wasm::{ + environment::{AllocPtr, WasmEnv}, error::WasmExecutionError, - vm::{AllocPtr, VmInstance}, LoadedWasmModule, }, }; +const LOG_TARGET: &str = "tari::dan::wasm::process"; + #[derive(Debug)] pub struct Process { module: LoadedWasmModule, - vm: VmInstance, -} - -pub struct ExecutionResult { - pub value: wasmer::Value, - pub raw: Vec, -} - -impl ExecutionResult { - pub fn decode(&self) -> io::Result { - tari_template_abi::decode(&self.raw) - } + env: WasmEnv, + instance: Instance, } impl Process { - pub fn new(module: LoadedWasmModule, vm: VmInstance) -> Self { - Self { module, vm } + pub fn start(module: LoadedWasmModule, state: Runtime) -> Result { + let store = Store::default(); + let mut env = WasmEnv::new(state); + let tari_engine = Function::new_native_with_env(&store, env.clone(), Self::tari_engine_entrypoint); + let resolver = env.create_resolver(&store, tari_engine); + let instance = Instance::new(module.wasm_module(), &resolver)?; + env.init_with_instance(&instance)?; + Ok(Self { module, env, instance }) } fn alloc_and_write(&self, val: &T) -> Result { let mut buf = Vec::with_capacity(512); encode_into(val, &mut buf).unwrap(); - let ptr = self.vm.alloc(buf.len() as u32)?; - self.vm.write_to_memory(&ptr, &buf)?; + let ptr = self.env.alloc(buf.len() as u32)?; + self.env.write_to_memory(&ptr, &buf)?; Ok(ptr) } @@ -69,6 +68,50 @@ impl Process { pub fn wasm_module(&self) -> &Module { self.module.wasm_module() } + + fn tari_engine_entrypoint(env: &WasmEnv, op: i32, arg_ptr: i32, arg_len: i32) -> i32 { + let arg = match env.read_from_memory(arg_ptr as u32, arg_len as u32) { + Ok(arg) => arg, + Err(err) => { + log::error!(target: LOG_TARGET, "Failed to read from memory: {}", err); + return 0; + }, + }; + let result = match op { + ops::OP_EMIT_LOG => Self::handle(env, arg, |env, arg: EmitLogArg| { + env.state().interface().emit_log(arg.level, &arg.message); + Result::<_, WasmExecutionError>::Ok(()) + }), + ops::OP_CREATE_COMPONENT => Self::handle(env, arg, |env, arg: CreateComponentArg| { + env.state().interface().create_component(arg.into()) + }), + _ => Err(WasmExecutionError::InvalidOperation { op }), + }; + result.unwrap_or_else(|err| { + log::error!(target: LOG_TARGET, "{}", err); + 0 + }) + } + + pub fn handle( + env: &WasmEnv, + args: Vec, + f: fn(&WasmEnv, T) -> Result, + ) -> Result + where + T: BorshDeserialize, + U: BorshSerialize, + WasmExecutionError: From, + { + let decoded = decode(&args).map_err(WasmExecutionError::EngineArgDecodeFailed)?; + let resp = f(env, decoded)?; + let encoded = encode_with_len(&resp); + let ptr = env.alloc(encoded.len() as u32)?; + env.write_to_memory(&ptr, &encoded)?; + // TODO: It's not clear how/if this memory is freed. When I drop it on the WASM side I get an + // out-of-bounds access error. + Ok(ptr.as_i32()) + } } impl Invokable for Process { @@ -86,23 +129,36 @@ impl Invokable for Process { }; let main_name = format!("{}_main", self.module.template_name()); - let func = self.vm.get_function(&main_name)?; + let func = self.instance.exports.get_function(&main_name)?; let call_info_ptr = self.alloc_and_write(&call_info)?; - let res = func.call(&[call_info_ptr.as_val_i32(), Val::I32(call_info_ptr.len() as i32)])?; - self.vm.free(call_info_ptr)?; + let res = func.call(&[call_info_ptr.as_i32().into(), Val::I32(call_info_ptr.len() as i32)])?; + self.env.free(call_info_ptr)?; let ptr = res .get(0) .and_then(|v| v.i32()) .ok_or(WasmExecutionError::ExpectedPointerReturn { function: main_name })?; // Read response from memory - let raw = self.vm.read_from_memory(ptr as u32)?; + let raw = self.env.read_memory_with_embedded_len(ptr as u32)?; // TODO: decode raw as per function def Ok(ExecutionResult { value: wasmer::Value::I32(ptr), raw, + return_type: func_def.output.clone(), }) } } + +pub struct ExecutionResult { + pub value: wasmer::Value, + pub raw: Vec, + pub return_type: Type, +} + +impl ExecutionResult { + pub fn decode(&self) -> io::Result { + tari_template_abi::decode(&self.raw) + } +} diff --git a/dan_layer/engine/src/wasm/vm.rs b/dan_layer/engine/src/wasm/vm.rs deleted file mode 100644 index dfbc94d353..0000000000 --- a/dan_layer/engine/src/wasm/vm.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::cell::Cell; - -use wasmer::{imports, Function, Instance, Memory, Module, Store, Val}; - -use crate::{ - env::{tari_engine, EngineEnvironment}, - wasm::error::WasmExecutionError, -}; - -#[derive(Debug)] -pub struct VmInstance { - memory: Memory, - instance: Instance, - _store: Store, -} - -impl VmInstance { - pub fn instantiate(module: &Module) -> Result { - let store = Store::default(); - // TODO: proper environment - let env = EngineEnvironment::default(); - let imports = imports! { - "env" => { - "tari_engine" => Function::new_native_with_env(&store, env, tari_engine), - } - }; - let instance = Instance::new(module, &imports)?; - let memory = instance.exports.get_memory("memory")?; - Ok(Self { - memory: memory.clone(), - _store: store, - instance, - }) - } - - pub(super) fn alloc(&self, len: u32) -> Result { - let alloc = self.instance.exports.get_function("tari_alloc")?; - let ret = alloc.call(&[Val::I32(len as i32)])?; - match ret.get(0) { - Some(Val::I32(ptr)) => Ok(AllocPtr(*ptr as u32, len)), - _ => Err(WasmExecutionError::ExpectedPointerReturn { - function: "tari_alloc".into(), - }), - } - } - - pub(super) fn free(&self, ptr: AllocPtr) -> Result<(), WasmExecutionError> { - let alloc = self.instance.exports.get_function("tari_free")?; - alloc.call(&[ptr.as_val_i32()])?; - Ok(()) - } - - pub(super) fn write_to_memory(&self, ptr: &AllocPtr, data: &[u8]) -> Result<(), WasmExecutionError> { - if data.len() != ptr.len() as usize { - return Err(WasmExecutionError::InvalidWriteLength { - allocated: ptr.len(), - requested: data.len() as u32, - }); - } - // SAFETY: The VM owns the only memory instance, and the pointer has been allocated by alloc above so data races - // are not possible. - unsafe { - self.memory.uint8view().subarray(ptr.get(), ptr.end()).copy_from(data); - } - Ok(()) - } - - pub(super) fn read_from_memory(&self, ptr: u32) -> Result, WasmExecutionError> { - // TODO: DRY this up - let view = self - .memory - .uint8view() - .subarray(ptr, self.memory.data_size() as u32 - 1); - let view_bytes = &*view; - if view_bytes.len() < 4 { - return Err(WasmExecutionError::MemoryUnderflow { - required: 4, - remaining: view_bytes.len(), - }); - } - - fn copy_from_cell_slice(src: &[Cell], dest: &mut [u8], len: usize) { - // TODO: Is there a more efficient way to do this? - for i in 0..len { - dest[i] = src[i].get(); - } - } - - let mut buf = [0u8; 4]; - copy_from_cell_slice(view_bytes, &mut buf, 4); - let len = u32::from_le_bytes(buf) as usize; - if view_bytes.len() < 4 + len { - return Err(WasmExecutionError::MemoryUnderflow { - required: 4 + len, - remaining: view_bytes.len(), - }); - } - - let mut data = vec![0u8; len]; - let src = view.subarray(4, 4 + len as u32); - copy_from_cell_slice(&*src, &mut data, len); - Ok(data) - } - - pub fn get_function(&self, name: &str) -> Result<&Function, WasmExecutionError> { - let func = self.instance.exports.get_function(name)?; - Ok(func) - } -} - -#[derive(Debug)] -pub struct AllocPtr(u32, u32); - -impl AllocPtr { - pub fn get(&self) -> u32 { - self.0 - } - - pub fn len(&self) -> u32 { - self.1 - } - - pub fn end(&self) -> u32 { - self.get() + self.len() - } - - pub fn as_val_i32(&self) -> Val { - // We want the 'u32 as i32' conversion to wrap - Val::I32(self.get() as i32) - } -} diff --git a/dan_layer/engine/tests/common/Cargo.toml b/dan_layer/engine/tests/common/Cargo.toml deleted file mode 100644 index 29698acac4..0000000000 --- a/dan_layer/engine/tests/common/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[workspace] -[package] -name = "common" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tari_template_abi = { path = "../../../template_abi" } - -[profile.release] -opt-level = 's' # Optimize for size. -lto = true # Enable Link Time Optimization. -codegen-units = 1 # Reduce number of codegen units to increase optimizations. -panic = 'abort' # Abort on panic. -strip = "debuginfo" # Strip debug info. - -[lib] -crate-type = ["cdylib", "lib"] \ No newline at end of file diff --git a/dan_layer/engine/tests/hello_world/Cargo.lock b/dan_layer/engine/tests/hello_world/Cargo.lock index 57f0926882..b09f1bec3b 100644 --- a/dan_layer/engine/tests/hello_world/Cargo.lock +++ b/dan_layer/engine/tests/hello_world/Cargo.lock @@ -64,13 +64,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "common" -version = "0.1.0" -dependencies = [ - "tari_template_abi", -] - [[package]] name = "getrandom" version = "0.2.7" @@ -95,8 +88,8 @@ dependencies = [ name = "hello_world" version = "0.1.0" dependencies = [ - "common", "tari_template_abi", + "tari_template_lib", "tari_template_macros", ] @@ -163,6 +156,13 @@ dependencies = [ "borsh", ] +[[package]] +name = "tari_template_lib" +version = "0.1.0" +dependencies = [ + "tari_template_abi", +] + [[package]] name = "tari_template_macros" version = "0.1.0" diff --git a/dan_layer/engine/tests/hello_world/Cargo.toml b/dan_layer/engine/tests/hello_world/Cargo.toml index ac1981236e..3dd529e4df 100644 --- a/dan_layer/engine/tests/hello_world/Cargo.toml +++ b/dan_layer/engine/tests/hello_world/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] tari_template_abi = { path = "../../../template_abi" } +tari_template_lib = { path = "../../../template_lib" } tari_template_macros = { path = "../macros" } -common = { path = "../common" } [profile.release] opt-level = 's' # Optimize for size. diff --git a/dan_layer/engine/tests/hello_world/src/lib.rs b/dan_layer/engine/tests/hello_world/src/lib.rs index 1c452dcf28..b4425cd5cc 100644 --- a/dan_layer/engine/tests/hello_world/src/lib.rs +++ b/dan_layer/engine/tests/hello_world/src/lib.rs @@ -25,7 +25,7 @@ use tari_template_macros::template; template! { struct HelloWorld {} - impl HelloWorld { + impl HelloWorld { pub fn greet() -> String { "Hello World!".to_string() } diff --git a/dan_layer/engine/tests/mock_runtime_interface.rs b/dan_layer/engine/tests/mock_runtime_interface.rs new file mode 100644 index 0000000000..c81591814b --- /dev/null +++ b/dan_layer/engine/tests/mock_runtime_interface.rs @@ -0,0 +1,64 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::sync::{atomic::AtomicU32, Arc}; + +use tari_dan_common_types::Hash; +use tari_dan_engine::{ + models::{Component, ComponentId}, + runtime::{RuntimeError, RuntimeInterface}, +}; +use tari_template_abi::LogLevel; + +#[derive(Debug, Clone, Default)] +pub struct MockRuntimeInterface { + ids: Arc, +} + +impl MockRuntimeInterface { + pub fn new() -> Self { + Self { + ids: Arc::new(AtomicU32::new(0)), + } + } + + pub fn next_id(&self) -> u32 { + self.ids.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + } +} + +impl RuntimeInterface for MockRuntimeInterface { + fn emit_log(&self, level: LogLevel, message: &str) { + let level = match level { + LogLevel::Error => log::Level::Error, + LogLevel::Warn => log::Level::Warn, + LogLevel::Info => log::Level::Info, + LogLevel::Debug => log::Level::Debug, + }; + eprintln!("[{:?}] {}", level, message); + log::log!(target: "tari::dan::engine::runtime", level, "{}", message); + } + + fn create_component(&self, _new_component: Component) -> Result { + Ok((Hash::default(), self.next_id())) + } +} diff --git a/dan_layer/engine/tests/state/Cargo.lock b/dan_layer/engine/tests/state/Cargo.lock index 3f75811d93..9964c09d41 100644 --- a/dan_layer/engine/tests/state/Cargo.lock +++ b/dan_layer/engine/tests/state/Cargo.lock @@ -64,13 +64,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "common" -version = "0.1.0" -dependencies = [ - "tari_template_abi", -] - [[package]] name = "getrandom" version = "0.2.7" @@ -140,8 +133,8 @@ checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" name = "state" version = "0.1.0" dependencies = [ - "common", "tari_template_abi", + "tari_template_lib", ] [[package]] @@ -162,6 +155,13 @@ dependencies = [ "borsh", ] +[[package]] +name = "tari_template_lib" +version = "0.1.0" +dependencies = [ + "tari_template_abi", +] + [[package]] name = "toml" version = "0.5.9" diff --git a/dan_layer/engine/tests/state/Cargo.toml b/dan_layer/engine/tests/state/Cargo.toml index 008ec555f7..19b00846d8 100644 --- a/dan_layer/engine/tests/state/Cargo.toml +++ b/dan_layer/engine/tests/state/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] tari_template_abi = { path = "../../../template_abi" } -common = { path = "../common" } +tari_template_lib = { path = "../../../template_lib" } [profile.release] opt-level = 's' # Optimize for size. diff --git a/dan_layer/engine/tests/state/src/lib.rs b/dan_layer/engine/tests/state/src/lib.rs index cdc61c488e..0514d3bd6c 100644 --- a/dan_layer/engine/tests/state/src/lib.rs +++ b/dan_layer/engine/tests/state/src/lib.rs @@ -20,31 +20,31 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// TODO: we should only use stdlib if the template dev needs to include it e.g. use core::mem when stdlib is not -// available - -use common::{generate_abi, TemplateImpl, generate_main}; -use tari_template_abi::{FunctionDef, Type, encode_with_len, decode}; +use tari_template_abi::{decode, encode_with_len, FunctionDef, Type}; +use tari_template_lib::{call_engine, generate_abi, generate_main, TemplateImpl}; // that's what the example should look like from the user's perspective #[allow(dead_code)] -mod rust { +mod state_template { + use tari_template_abi::{borsh, Decode, Encode}; + // #[tari::template] + #[derive(Encode, Decode)] pub struct State { value: u32, } - + // #[tari::impl] impl State { // #[tari::constructor] pub fn new() -> Self { Self { value: 0 } } - + pub fn set(&mut self, value: u32) { self.value = value; } - + pub fn get(&self) -> u32 { self.value } @@ -56,19 +56,23 @@ mod rust { extern "C" fn State_abi() -> *mut u8 { let template_name = "State".to_string(); - let functions = vec![FunctionDef { - name: "new".to_string(), - arguments: vec![], - output: Type::U32, // the component_id - }, FunctionDef { - name: "set".to_string(), - arguments: vec![Type::U32, Type::U32], // the component_id and the new value - output: Type::Unit, // does not return anything - }, FunctionDef { - name: "get".to_string(), - arguments: vec![Type::U32], // the component_id - output: Type::U32, // the stored value - }]; + let functions = vec![ + FunctionDef { + name: "new".to_string(), + arguments: vec![], + output: Type::U32, // the component_id + }, + FunctionDef { + name: "set".to_string(), + arguments: vec![Type::U32, Type::U32], // the component_id and the new value + output: Type::Unit, // does not return anything + }, + FunctionDef { + name: "get".to_string(), + arguments: vec![Type::U32], // the component_id + output: Type::U32, // the stored value + }, + ]; generate_abi(template_name, functions) } @@ -76,48 +80,67 @@ extern "C" fn State_abi() -> *mut u8 { #[no_mangle] extern "C" fn State_main(call_info: *mut u8, call_info_len: usize) -> *mut u8 { let mut template_impl = TemplateImpl::new(); + use tari_template_abi::{ops::*, CreateComponentArg, EmitLogArg, LogLevel}; + use tari_template_lib::models::ComponentId; + + tari_template_lib::call_engine::<_, ()>(OP_EMIT_LOG, &EmitLogArg { + message: "This is a log message from State_main!".to_string(), + level: LogLevel::Info, + }); // constructor - template_impl.add_function("new".to_string(), Box::new(|_| { - // Call the engine to create a new component - // TODO: use a real op code (not "123") when they are implemented - let _component_id = unsafe { tari_engine(123, std::ptr::null(), 0) }; - - // TODO: decode the returning value into a real component id - let component_id = 1_u32; - encode_with_len(&component_id) - })); - - template_impl.add_function("set".to_string(), Box::new(|args| { - // read the function paramenters - let _component_id: u32 = decode(&args[0]).unwrap(); - let _new_value: u32 = decode(&args[1]).unwrap(); - - // update the component value - // TODO: use a real op code (not "123") when they are implemented - unsafe { tari_engine(123, std::ptr::null(), 0) }; - - // the function does not return any value - // TODO: implement "Unit" type empty responses. Right now this fails: wrap_ptr(vec![]) - encode_with_len(&0) - })); - - template_impl.add_function("get".to_string(), Box::new(|args| { - // read the function paramenters - let _component_id: u32 = decode(&args[0]).unwrap(); - - // get the component state - // TODO: use a real op code (not "123") when they are implemented - let _state = unsafe { tari_engine(123, std::ptr::null(), 0) }; - - // return the value - let value = 1_u32; // TODO: read from the component state - encode_with_len(&value) - })); + template_impl.add_function( + "new".to_string(), + Box::new(|_| { + let ret = state_template::State::new(); + let encoded = encode_with_len(&ret); + // Call the engine to create a new component + // TODO: proper component id + // The macro will know to generate this call because of the #[tari(constructor)] attribute + // TODO: what happens if the user wants to return multiple components/types? + let component_id = call_engine::<_, ComponentId>(OP_CREATE_COMPONENT, &CreateComponentArg { + name: "State".to_string(), + quantity: 1, + metadata: Default::default(), + state: encoded, + }); + let component_id = component_id.expect("no asset id returned"); + encode_with_len(&component_id) + }), + ); + + template_impl.add_function( + "set".to_string(), + Box::new(|args| { + // read the function paramenters + let _component_id: u32 = decode(&args[0]).unwrap(); + let _new_value: u32 = decode(&args[1]).unwrap(); + + // update the component value + // TODO: use a real op code (not "123") when they are implemented + call_engine::<_, ()>(123, &()); + + // the function does not return any value + // TODO: implement "Unit" type empty responses. Right now this fails: wrap_ptr(vec![]) + encode_with_len(&0) + }), + ); + + template_impl.add_function( + "get".to_string(), + Box::new(|args| { + // read the function paramenters + let _component_id: u32 = decode(&args[0]).unwrap(); + + // get the component state + // TODO: use a real op code (not "123") when they are implemented + let _state = call_engine::<_, ()>(123, &()); + + // return the value + let value = 1_u32; // TODO: read from the component state + encode_with_len(&value) + }), + ); generate_main(call_info, call_info_len, template_impl) } - -extern "C" { - pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; -} diff --git a/dan_layer/engine/tests/test.rs b/dan_layer/engine/tests/test.rs index cefcf564b6..65194806b9 100644 --- a/dan_layer/engine/tests/test.rs +++ b/dan_layer/engine/tests/test.rs @@ -20,21 +20,25 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod mock_runtime_interface; + use borsh::BorshDeserialize; +use mock_runtime_interface::MockRuntimeInterface; use tari_common_types::types::FixedHash; use tari_crypto::ristretto::RistrettoSecretKey; use tari_dan_engine::{ - compile::compile_template, crypto::create_key_pair, instruction::{Instruction, InstructionBuilder, InstructionProcessor}, - package::PackageBuilder, + models::ComponentId, + packager::Package, + wasm::compile::build_wasm_module_from_source, }; use tari_template_abi::encode_with_len; #[test] fn test_hello_world() { let template_test = TemplateTest::new("HelloWorld".to_string(), "tests/hello_world".to_string()); - let result: String = template_test.run_instruction("greet".to_string(), vec![]); + let result: String = template_test.call_function("greet".to_string(), vec![]); // FIXME: without the "encode_with_len" calls, the strings are different because of added padding characters assert_eq!(encode_with_len(&result), encode_with_len(&"Hello World!")); @@ -45,35 +49,38 @@ fn test_state() { let template_test = TemplateTest::new("State".to_string(), "tests/state".to_string()); // constructor - let component_id: u32 = template_test.run_instruction("new".to_string(), vec![]); + let component: ComponentId = template_test.call_function("new".to_string(), vec![]); + assert_eq!(component.1, 0); + let component: ComponentId = template_test.call_function("new".to_string(), vec![]); + assert_eq!(component.1, 1); // call the "set" method to update the instance value let new_value = 20_u32; - // TODO: implement "Unit" type empty responses - let _: u32 = template_test.run_instruction("set".to_string(), vec![ - encode_with_len(&component_id), + template_test.call_method::<()>("State".to_string(), "set".to_string(), vec![ + encode_with_len(&component), encode_with_len(&new_value), ]); - // call the "get" method to get the current value - let value: u32 = template_test.run_instruction("get".to_string(), vec![encode_with_len(&component_id)]); + let value: u32 = template_test.call_method("State".to_string(), "get".to_string(), vec![encode_with_len( + &component, + )]); assert_eq!(value, 1); } struct TemplateTest { template_name: String, package_id: FixedHash, - processor: InstructionProcessor, + processor: InstructionProcessor, secret_key: RistrettoSecretKey, } impl TemplateTest { pub fn new(template_name: String, template_path: String) -> Self { - let mut processor = InstructionProcessor::new(); + let mut processor = InstructionProcessor::new(MockRuntimeInterface::new()); let (secret_key, _pk) = create_key_pair(); - let wasm = compile_template(template_path).unwrap(); - let package = PackageBuilder::new().add_wasm_template(wasm).build().unwrap(); + let wasm = build_wasm_module_from_source(template_path).unwrap(); + let package = Package::builder().add_wasm_module(wasm).build().unwrap(); let package_id = package.id(); processor.load(package); @@ -85,7 +92,7 @@ impl TemplateTest { } } - pub fn run_instruction(&self, func_name: String, args: Vec>) -> T + pub fn call_function(&self, func_name: String, args: Vec>) -> T where T: BorshDeserialize { let instruction = InstructionBuilder::new() .add_instruction(Instruction::CallFunction { @@ -100,4 +107,20 @@ impl TemplateTest { result[0].decode::().unwrap() } + + pub fn call_method(&self, component_id: String, method_name: String, args: Vec>) -> T + where T: BorshDeserialize { + let instruction = InstructionBuilder::new() + .add_instruction(Instruction::CallMethod { + package_id: self.package_id, + component_id, + method: method_name, + args, + }) + .sign(&self.secret_key) + .build(); + let result = self.processor.execute(instruction).unwrap(); + + result[0].decode::().unwrap() + } } diff --git a/dan_layer/template_abi/src/encoding.rs b/dan_layer/template_abi/src/encoding.rs index b64ce07917..d5723270ee 100644 --- a/dan_layer/template_abi/src/encoding.rs +++ b/dan_layer/template_abi/src/encoding.rs @@ -24,9 +24,9 @@ use std::io; -use borsh::{BorshDeserialize, BorshSerialize}; +use crate::{Decode, Encode}; -pub fn encode_with_len(val: &T) -> Vec { +pub fn encode_with_len(val: &T) -> Vec { let mut buf = Vec::with_capacity(512); buf.extend([0u8; 4]); @@ -38,10 +38,24 @@ pub fn encode_with_len(val: &T) -> Vec { buf } -pub fn encode_into(val: &T, buf: &mut Vec) -> io::Result<()> { +pub fn encode_into(val: &T, buf: &mut Vec) -> io::Result<()> { val.serialize(buf) } -pub fn decode(mut input: &[u8]) -> io::Result { +pub fn decode(mut input: &[u8]) -> io::Result { T::deserialize(&mut input) } + +pub fn decode_len(input: &[u8]) -> io::Result { + if input.len() < 4 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Not enough bytes to decode length", + )); + } + + let mut buf = [0u8; 4]; + buf.copy_from_slice(&input[..4]); + let len = u32::from_le_bytes(buf); + Ok(len as usize) +} diff --git a/dan_layer/template_abi/src/lib.rs b/dan_layer/template_abi/src/lib.rs index db7b1d48f5..3037149407 100644 --- a/dan_layer/template_abi/src/lib.rs +++ b/dan_layer/template_abi/src/lib.rs @@ -20,12 +20,20 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//! # Tari WASM module ABI (application binary interface) +//! +//! This library provides types and encoding that allow low-level communication between the Tari WASM runtime and the +//! WASM modules. + mod encoding; +pub mod ops; + +use std::collections::HashMap; -use borsh::{BorshDeserialize, BorshSerialize}; -pub use encoding::{decode, encode_into, encode_with_len}; +pub use borsh::{self, BorshDeserialize as Decode, BorshSerialize as Encode}; +pub use encoding::{decode, decode_len, encode_into, encode_with_len}; -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone, Encode, Decode)] pub struct TemplateDef { pub template_name: String, pub functions: Vec, @@ -37,14 +45,14 @@ impl TemplateDef { } } -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone, Encode, Decode)] pub struct FunctionDef { pub name: String, pub arguments: Vec, pub output: Type, } -#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] pub enum Type { Unit, Bool, @@ -61,8 +69,32 @@ pub enum Type { String, } -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone, Encode, Decode)] pub struct CallInfo { pub func_name: String, pub args: Vec>, } + +#[derive(Debug, Clone, Encode, Decode)] +pub struct EmitLogArg { + pub message: String, + pub level: LogLevel, +} + +#[derive(Debug, Clone, Encode, Decode)] +pub enum LogLevel { + Error, + Warn, + Info, + Debug, +} + +#[derive(Debug, Clone, Encode, Decode)] +pub struct CreateComponentArg { + // asset/component metadata + pub name: String, + pub quantity: u64, + pub metadata: HashMap, Vec>, + // encoded asset/component state + pub state: Vec, +} diff --git a/dan_layer/template_abi/src/ops.rs b/dan_layer/template_abi/src/ops.rs new file mode 100644 index 0000000000..0ce2f3e287 --- /dev/null +++ b/dan_layer/template_abi/src/ops.rs @@ -0,0 +1,24 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub const OP_EMIT_LOG: i32 = 0x00; +pub const OP_CREATE_COMPONENT: i32 = 0x01; diff --git a/dan_layer/engine/tests/common/Cargo.lock b/dan_layer/template_lib/Cargo.lock similarity index 100% rename from dan_layer/engine/tests/common/Cargo.lock rename to dan_layer/template_lib/Cargo.lock diff --git a/dan_layer/template_lib/Cargo.toml b/dan_layer/template_lib/Cargo.toml new file mode 100644 index 0000000000..b986932b1c --- /dev/null +++ b/dan_layer/template_lib/Cargo.toml @@ -0,0 +1,20 @@ +[workspace] +[package] +name = "tari_template_lib" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tari_template_abi = { path = "../template_abi" } +# +#[profile.release] +#opt-level = 's' # Optimize for size. +#lto = true # Enable Link Time Optimization. +#codegen-units = 1 # Reduce number of codegen units to increase optimizations. +#panic = 'abort' # Abort on panic. +#strip = "debuginfo" # Strip debug info. +# +#[lib] +#crate-type = ["cdylib", "lib"] \ No newline at end of file diff --git a/dan_layer/engine/tests/common/src/lib.rs b/dan_layer/template_lib/src/lib.rs similarity index 63% rename from dan_layer/engine/tests/common/src/lib.rs rename to dan_layer/template_lib/src/lib.rs index 6fad8278ff..cdb700bf4c 100644 --- a/dan_layer/engine/tests/common/src/lib.rs +++ b/dan_layer/template_lib/src/lib.rs @@ -20,9 +20,22 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{collections::HashMap, mem, intrinsics::copy}; +//! # Tari WASM module library +//! +//! This library provides primitives and functionality that allows Tari WASM modules to interact with the Tari engine. +//! It is intended to be used by WASM modules that are written in Rust and compiled into WASM. +//! +//! The tari engine itself should never depend on this crate. +//! +//! TODO: no_std support -use tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, CallInfo, decode}; +pub mod models; + +// TODO: we should only use stdlib if the template dev needs to include it e.g. use core::mem when stdlib is not +// available +use std::{collections::HashMap, mem, ptr::copy, slice}; + +use tari_template_abi::{encode_with_len, Decode, Encode, FunctionDef, TemplateDef}; pub fn generate_abi(template_name: String, functions: Vec) -> *mut u8 { let template = TemplateDef { @@ -49,16 +62,17 @@ impl TemplateImpl { } pub fn generate_main(call_info: *mut u8, call_info_len: usize, template_impl: TemplateImpl) -> *mut u8 { + use tari_template_abi::{decode, CallInfo}; if call_info.is_null() { panic!("call_info is null"); } - let call_data = unsafe { Vec::from_raw_parts(call_info, call_info_len, call_info_len) }; - let call_info: CallInfo = decode(&call_data).unwrap(); + let call_data = unsafe { slice::from_raw_parts(call_info, call_info_len) }; + let call_info: CallInfo = decode(call_data).unwrap(); // get the function let function = match template_impl.0.get(&call_info.func_name) { - Some(f) => f.clone(), + Some(f) => f, None => panic!("invalid function name"), }; @@ -69,12 +83,42 @@ pub fn generate_main(call_info: *mut u8, call_info_len: usize, template_impl: Te wrap_ptr(result) } +pub fn call_engine(op: i32, input: &T) -> Option { + use tari_template_abi::{decode, decode_len, encode_into}; + + let mut encoded = Vec::with_capacity(512); + encode_into(input, &mut encoded).unwrap(); + let len = encoded.len(); + let input_ptr = wrap_ptr(encoded) as *const _; + let ptr = unsafe { tari_engine(op, input_ptr, len) }; + if ptr.is_null() { + return None; + } + + let slice = unsafe { slice::from_raw_parts(ptr as *const _, 4) }; + let len = decode_len(&slice).unwrap(); + let slice = unsafe { slice::from_raw_parts(ptr.offset(4), len) }; + let ret = decode(&slice).unwrap(); + Some(ret) +} + pub fn wrap_ptr(mut v: Vec) -> *mut u8 { let ptr = v.as_mut_ptr(); mem::forget(v); ptr } +extern "C" { + fn tari_engine(op: i32, input_ptr: *const u8, input_len: usize) -> *mut u8; + fn debug(input_ptr: *const u8, input_len: usize); +} + +pub fn call_debug>(data: T) { + let ptr = data.as_ref().as_ptr(); + let len = data.as_ref().len(); + unsafe { debug(ptr, len) } +} + #[no_mangle] pub unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 { let cap = (len + 4) as usize; @@ -92,4 +136,4 @@ pub unsafe extern "C" fn tari_free(ptr: *mut u8) { let cap = (u32::from_le_bytes(len) + 4) as usize; let _ = Vec::::from_raw_parts(ptr, cap, cap); -} \ No newline at end of file +} diff --git a/dan_layer/template_lib/src/models/component.rs b/dan_layer/template_lib/src/models/component.rs new file mode 100644 index 0000000000..3b27286bbc --- /dev/null +++ b/dan_layer/template_lib/src/models/component.rs @@ -0,0 +1,23 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub type ComponentId = ([u8; 32], u32); diff --git a/dan_layer/template_lib/src/models/mod.rs b/dan_layer/template_lib/src/models/mod.rs new file mode 100644 index 0000000000..ef04fea78d --- /dev/null +++ b/dan_layer/template_lib/src/models/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod component; +pub use component::ComponentId; From d012823d91a425afbd55a1db17af1c5c2271781f Mon Sep 17 00:00:00 2001 From: Miguel Naveira <47919901+mrnaveira@users.noreply.github.com> Date: Sun, 31 Jul 2022 07:36:30 +0100 Subject: [PATCH 2/7] refactor(dan): template macro as an attribute macro (#4361) Description --- * Refactored the existing declarative macro (`template!`) into an attribute one (`#[template]`) * Moved the macro project to the root of the `dan_layer` crate * Improved unit testing of the generated ABI section of the wasm, to assert different function signatures. * Used the `indoc!` macro in tests to make the code more readable Motivation and Context --- The existing template definition macro was created as a declarative macro, but code completion on some editors breaks when wrapped such types of macros. The main goal of this PR is to transform the template macro into an attribute macro, to be used like: ``` use tari_template_macros::template; #[template] mod hello_world { struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { "Hello World!".to_string() } } } ``` How Has This Been Tested? --- * Existing tests pass --- dan_layer/engine/tests/hello_world/Cargo.toml | 2 +- dan_layer/engine/tests/hello_world/src/lib.rs | 3 +- .../macros => template_macros}/Cargo.lock | 7 +++ .../macros => template_macros}/Cargo.toml | 7 ++- .../macros => template_macros}/src/ast.rs | 30 ++++++++++++- .../macros => template_macros}/src/lib.rs | 6 +-- .../src/template/abi.rs | 44 ++++++++++++++----- .../src/template/definition.rs | 0 .../src/template/dependencies.rs | 0 .../src/template/dispatcher.rs | 14 ++++-- .../src/template/mod.rs | 0 11 files changed, 91 insertions(+), 22 deletions(-) rename dan_layer/{engine/tests/macros => template_macros}/Cargo.lock (95%) rename dan_layer/{engine/tests/macros => template_macros}/Cargo.toml (74%) rename dan_layer/{engine/tests/macros => template_macros}/src/ast.rs (79%) rename dan_layer/{engine/tests/macros => template_macros}/src/lib.rs (94%) rename dan_layer/{engine/tests/macros => template_macros}/src/template/abi.rs (76%) rename dan_layer/{engine/tests/macros => template_macros}/src/template/definition.rs (100%) rename dan_layer/{engine/tests/macros => template_macros}/src/template/dependencies.rs (100%) rename dan_layer/{engine/tests/macros => template_macros}/src/template/dispatcher.rs (93%) rename dan_layer/{engine/tests/macros => template_macros}/src/template/mod.rs (100%) diff --git a/dan_layer/engine/tests/hello_world/Cargo.toml b/dan_layer/engine/tests/hello_world/Cargo.toml index 3dd529e4df..e4fdadcce0 100644 --- a/dan_layer/engine/tests/hello_world/Cargo.toml +++ b/dan_layer/engine/tests/hello_world/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] tari_template_abi = { path = "../../../template_abi" } tari_template_lib = { path = "../../../template_lib" } -tari_template_macros = { path = "../macros" } +tari_template_macros = { path = "../../../template_macros" } [profile.release] opt-level = 's' # Optimize for size. diff --git a/dan_layer/engine/tests/hello_world/src/lib.rs b/dan_layer/engine/tests/hello_world/src/lib.rs index b4425cd5cc..4d96b755de 100644 --- a/dan_layer/engine/tests/hello_world/src/lib.rs +++ b/dan_layer/engine/tests/hello_world/src/lib.rs @@ -22,7 +22,8 @@ use tari_template_macros::template; -template! { +#[template] +mod hello_world { struct HelloWorld {} impl HelloWorld { diff --git a/dan_layer/engine/tests/macros/Cargo.lock b/dan_layer/template_macros/Cargo.lock similarity index 95% rename from dan_layer/engine/tests/macros/Cargo.lock rename to dan_layer/template_macros/Cargo.lock index ef3fb33195..72bd32a405 100644 --- a/dan_layer/engine/tests/macros/Cargo.lock +++ b/dan_layer/template_macros/Cargo.lock @@ -84,6 +84,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "indoc" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" + [[package]] name = "libc" version = "0.2.126" @@ -151,6 +157,7 @@ dependencies = [ name = "tari_template_macros" version = "0.1.0" dependencies = [ + "indoc", "proc-macro2", "quote", "syn", diff --git a/dan_layer/engine/tests/macros/Cargo.toml b/dan_layer/template_macros/Cargo.toml similarity index 74% rename from dan_layer/engine/tests/macros/Cargo.toml rename to dan_layer/template_macros/Cargo.toml index 725b337894..98666fc2f1 100644 --- a/dan_layer/engine/tests/macros/Cargo.toml +++ b/dan_layer/template_macros/Cargo.toml @@ -10,7 +10,10 @@ edition = "2021" proc-macro = true [dependencies] -tari_template_abi = { path = "../../../template_abi" } +tari_template_abi = { path = "../template_abi" } syn = { version = "1.0.98", features = ["full"] } proc-macro2 = "1.0.42" -quote = "1.0.20" \ No newline at end of file +quote = "1.0.20" + +[dev-dependencies] +indoc = "1.0.6" \ No newline at end of file diff --git a/dan_layer/engine/tests/macros/src/ast.rs b/dan_layer/template_macros/src/ast.rs similarity index 79% rename from dan_layer/engine/tests/macros/src/ast.rs rename to dan_layer/template_macros/src/ast.rs index bb64a9d30d..fd6f458297 100644 --- a/dan_layer/engine/tests/macros/src/ast.rs +++ b/dan_layer/template_macros/src/ast.rs @@ -24,11 +24,13 @@ use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, token::Comma, + Error, FnArg, Ident, ImplItem, ImplItemMethod, ItemImpl, + ItemMod, ItemStruct, Result, ReturnType, @@ -44,8 +46,32 @@ pub struct TemplateAst { impl Parse for TemplateAst { fn parse(input: ParseStream) -> Result { - let struct_section: ItemStruct = input.parse()?; - let impl_section = input.parse()?; + // parse the "mod" block + let module: ItemMod = input.parse()?; + + // get the contents of the "mod" block + let items = match module.content { + Some((_, items)) => items, + None => return Err(Error::new(module.ident.span(), "empty module")), + }; + + // there should be two items: the "struct" and the "impl" blocks + if items.len() != 2 { + return Err(Error::new(module.ident.span(), "invalid number of module sections")); + } + + // get the "struct" block + let struct_section = match &items[0] { + syn::Item::Struct(struct_item) => struct_item.clone(), + _ => return Err(Error::new(module.ident.span(), "the first section is not a 'struct'")), + }; + + // get the "impl" block + let impl_section = match &items[1] { + syn::Item::Impl(impl_item) => impl_item.clone(), + _ => return Err(Error::new(module.ident.span(), "the second section is not an 'impl'")), + }; + let template_name = struct_section.ident.clone(); Ok(Self { diff --git a/dan_layer/engine/tests/macros/src/lib.rs b/dan_layer/template_macros/src/lib.rs similarity index 94% rename from dan_layer/engine/tests/macros/src/lib.rs rename to dan_layer/template_macros/src/lib.rs index 82fe705cec..689ff88c05 100644 --- a/dan_layer/engine/tests/macros/src/lib.rs +++ b/dan_layer/template_macros/src/lib.rs @@ -25,9 +25,9 @@ mod template; use proc_macro::TokenStream; -#[proc_macro] -pub fn template(input: TokenStream) -> TokenStream { - template::generate_template(proc_macro2::TokenStream::from(input)) +#[proc_macro_attribute] +pub fn template(_attr: TokenStream, item: TokenStream) -> TokenStream { + template::generate_template(proc_macro2::TokenStream::from(item)) .unwrap_or_else(|err| err.to_compile_error()) .into() } diff --git a/dan_layer/engine/tests/macros/src/template/abi.rs b/dan_layer/template_macros/src/template/abi.rs similarity index 76% rename from dan_layer/engine/tests/macros/src/template/abi.rs rename to dan_layer/template_macros/src/template/abi.rs index e1d3fc204f..e1386b3198 100644 --- a/dan_layer/engine/tests/macros/src/template/abi.rs +++ b/dan_layer/template_macros/src/template/abi.rs @@ -92,6 +92,7 @@ fn generate_abi_type(rust_type: &str) -> Expr { mod tests { use std::str::FromStr; + use indoc::indoc; use proc_macro2::TokenStream; use quote::quote; use syn::parse2; @@ -101,9 +102,20 @@ mod tests { #[test] fn test_hello_world() { - let input = TokenStream::from_str( - "struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }", - ) + let input = TokenStream::from_str(indoc! {" + mod foo { + struct Foo {} + impl Foo { + pub fn no_args_function() -> String { + \"Hello World!\".to_string() + } + pub fn some_args_function(a: i8, b: String) -> u32 { + 1_u32 + } + pub fn no_return_function() {} + } + } + "}) .unwrap(); let ast = parse2::(input).unwrap(); @@ -112,16 +124,28 @@ mod tests { assert_code_eq(output, quote! { #[no_mangle] - pub extern "C" fn HelloWorld_abi() -> *mut u8 { + pub extern "C" fn Foo_abi() -> *mut u8 { use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; let template = TemplateDef { - template_name: "HelloWorld".to_string(), - functions: vec![ FunctionDef { - name: "greet".to_string(), - arguments: vec![], - output: Type::String, - }], + template_name: "Foo".to_string(), + functions: vec![ + FunctionDef { + name: "no_args_function".to_string(), + arguments: vec![], + output: Type::String, + }, + FunctionDef { + name: "some_args_function".to_string(), + arguments: vec![Type::I8, Type::String], + output: Type::U32, + }, + FunctionDef { + name: "no_return_function".to_string(), + arguments: vec![], + output: Type::Unit, + } + ], }; let buf = encode_with_len(&template); diff --git a/dan_layer/engine/tests/macros/src/template/definition.rs b/dan_layer/template_macros/src/template/definition.rs similarity index 100% rename from dan_layer/engine/tests/macros/src/template/definition.rs rename to dan_layer/template_macros/src/template/definition.rs diff --git a/dan_layer/engine/tests/macros/src/template/dependencies.rs b/dan_layer/template_macros/src/template/dependencies.rs similarity index 100% rename from dan_layer/engine/tests/macros/src/template/dependencies.rs rename to dan_layer/template_macros/src/template/dependencies.rs diff --git a/dan_layer/engine/tests/macros/src/template/dispatcher.rs b/dan_layer/template_macros/src/template/dispatcher.rs similarity index 93% rename from dan_layer/engine/tests/macros/src/template/dispatcher.rs rename to dan_layer/template_macros/src/template/dispatcher.rs index 53d42fcd27..12ebede5f3 100644 --- a/dan_layer/engine/tests/macros/src/template/dispatcher.rs +++ b/dan_layer/template_macros/src/template/dispatcher.rs @@ -83,6 +83,7 @@ pub fn get_function_blocks(ast: &TemplateAst) -> Vec { mod tests { use std::str::FromStr; + use indoc::indoc; use proc_macro2::TokenStream; use quote::quote; use syn::parse2; @@ -91,9 +92,16 @@ mod tests { #[test] fn test_hello_world() { - let input = TokenStream::from_str( - "struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }", - ) + let input = TokenStream::from_str(indoc! {" + mod hello_world { + struct HelloWorld {} + impl HelloWorld { + pub fn greet() -> String { + \"Hello World!\".to_string() + } + } + } + "}) .unwrap(); let ast = parse2::(input).unwrap(); diff --git a/dan_layer/engine/tests/macros/src/template/mod.rs b/dan_layer/template_macros/src/template/mod.rs similarity index 100% rename from dan_layer/engine/tests/macros/src/template/mod.rs rename to dan_layer/template_macros/src/template/mod.rs From 60f3877f224d2311cd9e31770f8dbbed536ce742 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Tue, 2 Aug 2022 11:30:17 +0200 Subject: [PATCH 3/7] feat!: add burned outputs (#4364) Description --- This Pr adds in the ability to create burned outputs. There will be a follow-up PR addressing the kernel mutability as currently the fields are mutable and need to be signed to block mutability, see: https://github.com/tari-project/tari/issues/4365. For added reasoning why this is needed see RFC: https://github.com/tari-project/rfcs/pull/10 Full testing of this is also blocked by: https://github.com/tari-project/tari/issues/4360 How Has This Been Tested? --- Unit and integration tests. --- applications/tari_app_grpc/proto/types.proto | 2 + applications/tari_app_grpc/proto/wallet.proto | 14 + .../src/conversions/transaction_kernel.rs | 14 + .../src/grpc/wallet_grpc_server.rs | 35 + applications/test_faucet/src/main.rs | 3 +- .../sync/horizon_state_sync/error.rs | 2 + .../sync/horizon_state_sync/synchronizer.rs | 12 +- base_layer/core/src/blocks/genesis_block.rs | 10 +- .../core/src/chain_storage/lmdb_db/lmdb_db.rs | 31 + base_layer/core/src/proto/transaction.proto | 2 + base_layer/core/src/proto/transaction.rs | 7 + .../core/src/transactions/aggregated_body.rs | 2 +- .../transaction_components/error.rs | 4 +- .../transaction_components/kernel_builder.rs | 9 + .../transaction_components/kernel_features.rs | 13 + .../transaction_components/output_features.rs | 8 + .../transaction_components/output_type.rs | 38 +- .../transaction_components/test.rs | 4 +- .../transaction_kernel.rs | 29 +- .../transaction_output.rs | 5 + .../transaction_protocol/sender.rs | 14 +- .../src/validation/block_validators/orphan.rs | 3 + .../core/src/validation/chain_balance.rs | 8 +- base_layer/core/src/validation/error.rs | 2 + base_layer/core/src/validation/helpers.rs | 29 + base_layer/core/src/validation/mocks.rs | 1 + base_layer/core/src/validation/test.rs | 147 +- base_layer/core/src/validation/traits.rs | 1 + base_layer/tari_mining_helper_ffi/src/lib.rs | 4 +- .../wallet/src/transaction_service/handle.rs | 27 + .../wallet/src/transaction_service/service.rs | 120 + clients/wallet_grpc_client/index.js | 1 + integration_tests/README.md | 12 + integration_tests/features/Sync.feature | 15 + .../features/WalletTransactions.feature | 14 + .../features/support/wallet_steps.js | 28 + integration_tests/features/support/world.js | 62 +- integration_tests/helpers/walletClient.js | 4 + integration_tests/package-lock.json | 4150 ++++++++++++++++- 39 files changed, 4629 insertions(+), 257 deletions(-) diff --git a/applications/tari_app_grpc/proto/types.proto b/applications/tari_app_grpc/proto/types.proto index 420b3a4a42..106e64f4ff 100644 --- a/applications/tari_app_grpc/proto/types.proto +++ b/applications/tari_app_grpc/proto/types.proto @@ -151,6 +151,8 @@ message TransactionKernel { bytes hash = 8; // Version uint32 version = 9; + // Optional burned commitment + bytes burn_commitment = 10; } // A transaction input. diff --git a/applications/tari_app_grpc/proto/wallet.proto b/applications/tari_app_grpc/proto/wallet.proto index eb0719d5d3..16a1d665af 100644 --- a/applications/tari_app_grpc/proto/wallet.proto +++ b/applications/tari_app_grpc/proto/wallet.proto @@ -62,6 +62,8 @@ service Wallet { rpc RevalidateAllTransactions (RevalidateRequest) returns (RevalidateResponse); // This will send a XTR SHA Atomic swap transaction rpc SendShaAtomicSwapTransaction(SendShaAtomicSwapRequest) returns (SendShaAtomicSwapResponse); + // This will create a burn transaction + rpc CreateBurnTransaction(CreateBurnTransactionRequest) returns (CreateBurnTransactionResponse); // This will claim a XTR SHA Atomic swap transaction rpc ClaimShaAtomicSwapTransaction(ClaimShaAtomicSwapRequest) returns (ClaimShaAtomicSwapResponse); // This will claim a HTLC refund transaction @@ -106,6 +108,12 @@ message SendShaAtomicSwapRequest { PaymentRecipient recipient = 1; } +message CreateBurnTransactionRequest{ + uint64 amount = 1; + uint64 fee_per_gram = 2; + string message = 3; +} + message PaymentRecipient { string address = 1; uint64 amount = 2; @@ -131,6 +139,12 @@ message SendShaAtomicSwapResponse { string failure_message = 5; } +message CreateBurnTransactionResponse{ + uint64 transaction_id = 1; + bool is_success = 2; + string failure_message = 3; +} + message TransferResult { string address = 1; uint64 transaction_id = 2; diff --git a/applications/tari_app_grpc/src/conversions/transaction_kernel.rs b/applications/tari_app_grpc/src/conversions/transaction_kernel.rs index e14b9ce731..b89f85546d 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_kernel.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_kernel.rs @@ -45,6 +45,14 @@ impl TryFrom for TransactionKernel { .map_err(|_| "excess_sig could not be converted".to_string())?; let kernel_features = u8::try_from(kernel.features).map_err(|_| "kernel features must be a single byte")?; + let commitment = if kernel.burn_commitment.is_empty() { + None + } else { + Some( + Commitment::from_bytes(&kernel.burn_commitment) + .map_err(|err| format!("Burn commitment could not be converted:{}", err))?, + ) + }; Ok(Self::new( TransactionKernelVersion::try_from( @@ -56,6 +64,7 @@ impl TryFrom for TransactionKernel { kernel.lock_height, excess, excess_sig, + commitment, )) } } @@ -63,6 +72,10 @@ impl TryFrom for TransactionKernel { impl From for grpc::TransactionKernel { fn from(kernel: TransactionKernel) -> Self { let hash = kernel.hash(); + let commitment = match kernel.burn_commitment { + Some(c) => c.as_bytes().to_vec(), + None => vec![], + }; grpc::TransactionKernel { features: u32::from(kernel.features.bits()), @@ -75,6 +88,7 @@ impl From for grpc::TransactionKernel { }), hash, version: kernel.version as u32, + burn_commitment: commitment, } } } diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index 0531ab90b9..5bedda01c5 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -45,6 +45,8 @@ use tari_app_grpc::{ ClaimShaAtomicSwapResponse, CoinSplitRequest, CoinSplitResponse, + CreateBurnTransactionRequest, + CreateBurnTransactionResponse, CreateConstitutionDefinitionRequest, CreateConstitutionDefinitionResponse, CreateFollowOnAssetCheckpointRequest, @@ -560,6 +562,39 @@ impl wallet_server::Wallet for WalletGrpcServer { Ok(Response::new(TransferResponse { results })) } + async fn create_burn_transaction( + &self, + request: Request, + ) -> Result, Status> { + let message = request.into_inner(); + + let mut transaction_service = self.get_transaction_service(); + debug!(target: LOG_TARGET, "Trying to burn {} Tari", message.amount); + let response = match transaction_service + .burn_tari(message.amount.into(), message.fee_per_gram.into(), message.message) + .await + { + Ok(tx_id) => { + debug!(target: LOG_TARGET, "Transaction broadcast: {}", tx_id,); + CreateBurnTransactionResponse { + transaction_id: tx_id.as_u64(), + is_success: true, + failure_message: Default::default(), + } + }, + Err(e) => { + warn!(target: LOG_TARGET, "Failed to burn Tarid: {}", e); + CreateBurnTransactionResponse { + transaction_id: Default::default(), + is_success: false, + failure_message: e.to_string(), + } + }, + }; + + Ok(Response::new(response)) + } + async fn get_transaction_info( &self, request: Request, diff --git a/applications/test_faucet/src/main.rs b/applications/test_faucet/src/main.rs index 3ed646c0de..eb426cbf85 100644 --- a/applications/test_faucet/src/main.rs +++ b/applications/test_faucet/src/main.rs @@ -123,7 +123,8 @@ async fn write_keys(mut rx: mpsc::Receiver<(TransactionOutput, PrivateKey, Micro } let (pk, sig) = test_helpers::create_random_signature_from_s_key(key_sum, 0.into(), 0); let excess = Commitment::from_public_key(&pk); - let kernel = TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig); + let kernel = + TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig, None); let kernel = serde_json::to_string(&kernel).unwrap(); let _result = utxo_file.write_all(format!("{}\n", kernel).as_bytes()); diff --git a/base_layer/core/src/base_node/sync/horizon_state_sync/error.rs b/base_layer/core/src/base_node/sync/horizon_state_sync/error.rs index a3b054f4c4..938721c6e4 100644 --- a/base_layer/core/src/base_node/sync/horizon_state_sync/error.rs +++ b/base_layer/core/src/base_node/sync/horizon_state_sync/error.rs @@ -53,6 +53,8 @@ pub enum HorizonSyncError { JoinError(#[from] task::JoinError), #[error("A range proof verification has produced an error: {0}")] RangeProofError(#[from] RangeProofError), + #[error("An invalid transaction has been encountered: {0}")] + TransactionError(#[from] TransactionError), #[error("Invalid kernel signature: {0}")] InvalidKernelSignature(TransactionError), #[error("MMR did not match for {mmr_tree} at height {at_height}. Expected {actual_hex} to equal {expected_hex}")] diff --git a/base_layer/core/src/base_node/sync/horizon_state_sync/synchronizer.rs b/base_layer/core/src/base_node/sync/horizon_state_sync/synchronizer.rs index a59a5f5422..2694367aa8 100644 --- a/base_layer/core/src/base_node/sync/horizon_state_sync/synchronizer.rs +++ b/base_layer/core/src/base_node/sync/horizon_state_sync/synchronizer.rs @@ -743,7 +743,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { )); let header = self.db().fetch_chain_header(self.horizon_sync_height).await?; - let (calc_utxo_sum, calc_kernel_sum) = self.calculate_commitment_sums(&header).await?; + let (calc_utxo_sum, calc_kernel_sum, calc_burned_sum) = self.calculate_commitment_sums(&header).await?; self.final_state_validator .validate( @@ -751,6 +751,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { header.height(), &calc_utxo_sum, &calc_kernel_sum, + &calc_burned_sum, ) .map_err(HorizonSyncError::FinalStateValidationFailed)?; @@ -793,9 +794,10 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { async fn calculate_commitment_sums( &mut self, header: &ChainHeader, - ) -> Result<(Commitment, Commitment), HorizonSyncError> { + ) -> Result<(Commitment, Commitment, Commitment), HorizonSyncError> { let mut utxo_sum = HomomorphicCommitment::default(); let mut kernel_sum = HomomorphicCommitment::default(); + let mut burned_sum = HomomorphicCommitment::default(); let mut prev_mmr = 0; let mut prev_kernel_mmr = 0; @@ -810,7 +812,6 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { for h in 0..=height { let curr_header = db.fetch_chain_header(h)?; - trace!( target: LOG_TARGET, "Fetching utxos from db: height:{}, header.output_mmr:{}, prev_mmr:{}, end:{}", @@ -866,6 +867,9 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { trace!(target: LOG_TARGET, "Number of kernels returned: {}", kernels.len()); for k in kernels { kernel_sum = &k.excess + &kernel_sum; + if k.is_burned() { + burned_sum = k.get_burn_commitment()? + &burned_sum; + } } prev_kernel_mmr = curr_header.header().kernel_mmr_size; @@ -888,7 +892,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { db.write(txn)?; } - Ok((utxo_sum, kernel_sum)) + Ok((utxo_sum, kernel_sum, burned_sum)) }) .await? } diff --git a/base_layer/core/src/blocks/genesis_block.rs b/base_layer/core/src/blocks/genesis_block.rs index 98066194c7..dd18d940df 100644 --- a/base_layer/core/src/blocks/genesis_block.rs +++ b/base_layer/core/src/blocks/genesis_block.rs @@ -128,7 +128,8 @@ fn get_igor_genesis_block_raw() -> Block { "9474ba70976e2fa06f970bb83f7d0a4d4b45e6e29f834847b659d32102f90b51", ) .unwrap(), - sig, + sig,None + )], ); body.sort(); @@ -216,7 +217,7 @@ pub fn get_dibbler_genesis_block() -> ChainBlock { // println!("output mr: {}", block.header.output_mr.to_hex()); // Hardcode the Merkle roots once they've been computed above - block.header.kernel_mr = from_hex("8bec1140bfac718ab3acd8a5e19c1bb28e0e4a57663c2fc7e8c7155cc355aac3").unwrap(); + block.header.kernel_mr = from_hex("51acb4b74cc2e43a11be4f283b653a6fc95666dcf90f66f0c32742c5fb77e640").unwrap(); block.header.witness_mr = from_hex("1df4a4200338686763c784187f7077148986e088586cf4839147a3f56adc4af6").unwrap(); block.header.output_mr = from_hex("f9616ca84e798022f638546e6ce372d1344eee56e5cf47ba7e2bf58b5e28bf45").unwrap(); @@ -274,6 +275,7 @@ fn get_dibbler_genesis_block_raw() -> Block { 0, Commitment::from_hex("0cff7e89fa0468aa68f777cf600ae6f9e46fdc6e4e33540077e7303e8929295c").unwrap(), excess_sig, + None, ); let mut body = AggregateBody::new(vec![], vec![coinbase], vec![kernel]); body.sort(); @@ -314,7 +316,7 @@ fn get_dibbler_genesis_block_raw() -> Block { #[cfg(test)] mod test { use croaring::Bitmap; - use tari_common_types::types::HashDigest; + use tari_common_types::types::{Commitment, HashDigest}; use tari_mmr::{MerkleMountainRange, MutableMmr}; use super::*; @@ -385,7 +387,7 @@ mod test { let lock = db.db_read_access().unwrap(); ChainBalanceValidator::new(ConsensusManager::builder(Network::Dibbler).build(), Default::default()) - .validate(&*lock, 0, &utxo_sum, &kernel_sum) + .validate(&*lock, 0, &utxo_sum, &kernel_sum, &Commitment::default()) .unwrap(); } } diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs index d99ffb4f8f..93ea777874 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs @@ -1302,12 +1302,35 @@ impl LMDBDatabase { let leaf_count = witness_mmr.get_leaf_count()?; // Output hashes added before inputs so that inputs can spend outputs in this transaction (0-conf and combined) + let mut burned_outputs = Vec::new(); let outputs = outputs .into_iter() .enumerate() .map(|(i, output)| { output_mmr.push(output.hash())?; witness_mmr.push(output.witness_hash())?; + // lets check burn + if output.is_burned() { + let index = match output_mmr.find_leaf_index(&output.hash())? { + Some(index) => { + debug!(target: LOG_TARGET, "Output {} burned in current block", output); + burned_outputs.push(output.commitment.clone()); + index + }, + None => { + return Err(ChainStorageError::UnexpectedResult( + "Output MMR did not contain the expected output".to_string(), + )) + }, + }; + // We need to mark this as spent as well. + if !output_mmr.delete(index) { + return Err(ChainStorageError::InvalidOperation(format!( + "Could not delete index {} from the output MMR", + index + ))); + } + }; Ok((output, leaf_count + i + 1)) }) .collect::, ChainStorageError>>()?; @@ -1366,6 +1389,14 @@ impl LMDBDatabase { "utxo_commitment_index", )?; } + for commitment in burned_outputs { + lmdb_delete( + txn, + &self.utxo_commitment_index, + commitment.as_bytes(), + "utxo_commitment_index", + )?; + } // Merge current deletions with the tip bitmap let deleted_at_current_height = output_mmr.deleted().clone(); // Merge the new indexes with the blockchain deleted bitmap diff --git a/base_layer/core/src/proto/transaction.proto b/base_layer/core/src/proto/transaction.proto index 3ff6b1d7b7..4ab7f26d41 100644 --- a/base_layer/core/src/proto/transaction.proto +++ b/base_layer/core/src/proto/transaction.proto @@ -29,6 +29,8 @@ message TransactionKernel { Signature excess_sig = 7; // Version uint32 version = 8; + // Optional burned commitment + Commitment burn_commitment = 9; } // A transaction input. diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index b560eeb1d5..e5ea8dcb95 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -100,6 +100,10 @@ impl TryFrom for TransactionKernel { .ok_or_else(|| "excess_sig not provided".to_string())? .try_into()?; let kernel_features = u8::try_from(kernel.features).map_err(|_| "Kernel features must be a single byte")?; + let commitment = match kernel.burn_commitment { + Some(burn_commitment) => Some(Commitment::from_bytes(&burn_commitment.data).map_err(|e| e.to_string())?), + None => None, + }; Ok(TransactionKernel::new( TransactionKernelVersion::try_from( @@ -111,12 +115,14 @@ impl TryFrom for TransactionKernel { kernel.lock_height, excess, excess_sig, + commitment, )) } } impl From for proto::types::TransactionKernel { fn from(kernel: TransactionKernel) -> Self { + let commitment = kernel.burn_commitment.map(|commitment| commitment.into()); Self { features: u32::from(kernel.features.bits()), excess: Some(kernel.excess.into()), @@ -124,6 +130,7 @@ impl From for proto::types::TransactionKernel { fee: kernel.fee.into(), lock_height: kernel.lock_height, version: kernel.version as u32, + burn_commitment: commitment, } } } diff --git a/base_layer/core/src/transactions/aggregated_body.rs b/base_layer/core/src/transactions/aggregated_body.rs index 15282af3aa..e4c30d4891 100644 --- a/base_layer/core/src/transactions/aggregated_body.rs +++ b/base_layer/core/src/transactions/aggregated_body.rs @@ -264,7 +264,7 @@ impl AggregateBody { for kernel in self.kernels() { if kernel.lock_height > height { warn!(target: LOG_TARGET, "Kernel lock height was not reached: {}", kernel); - return Err(TransactionError::InvalidKernel); + return Err(TransactionError::InvalidKernel("Invalid lock height".to_string())); } } Ok(()) diff --git a/base_layer/core/src/transactions/transaction_components/error.rs b/base_layer/core/src/transactions/transaction_components/error.rs index 05a7c616dc..1ef013fb59 100644 --- a/base_layer/core/src/transactions/transaction_components/error.rs +++ b/base_layer/core/src/transactions/transaction_components/error.rs @@ -45,8 +45,8 @@ pub enum TransactionError { RangeProofError(#[from] RangeProofError), #[error("An error occurred while performing a commitment signature: {0}")] SigningError(#[from] CommitmentSignatureError), - #[error("Invalid kernel in body")] - InvalidKernel, + #[error("Invalid kernel in body : {0}")] + InvalidKernel(String), #[error("Invalid coinbase in body")] InvalidCoinbase, #[error("Invalid coinbase maturity in body")] diff --git a/base_layer/core/src/transactions/transaction_components/kernel_builder.rs b/base_layer/core/src/transactions/transaction_components/kernel_builder.rs index 08b9e9623f..6c364158a9 100644 --- a/base_layer/core/src/transactions/transaction_components/kernel_builder.rs +++ b/base_layer/core/src/transactions/transaction_components/kernel_builder.rs @@ -37,6 +37,7 @@ pub struct KernelBuilder { lock_height: u64, excess: Option, excess_sig: Option, + burn_commitment: Option, } /// Implementation of the transaction kernel @@ -58,6 +59,12 @@ impl KernelBuilder { self } + /// Build a transaction kernel with the provided burn commitment + pub fn with_burn_commitment(mut self, burn_commitment: Commitment) -> KernelBuilder { + self.burn_commitment = Some(burn_commitment); + self + } + /// Build a transaction kernel with the provided lock height pub fn with_lock_height(mut self, lock_height: u64) -> KernelBuilder { self.lock_height = lock_height; @@ -86,6 +93,7 @@ impl KernelBuilder { self.lock_height, self.excess.unwrap(), self.excess_sig.unwrap(), + self.burn_commitment, )) } } @@ -98,6 +106,7 @@ impl Default for KernelBuilder { lock_height: 0, excess: None, excess_sig: None, + burn_commitment: None, } } } diff --git a/base_layer/core/src/transactions/transaction_components/kernel_features.rs b/base_layer/core/src/transactions/transaction_components/kernel_features.rs index cbd29c22be..330d29bf90 100644 --- a/base_layer/core/src/transactions/transaction_components/kernel_features.rs +++ b/base_layer/core/src/transactions/transaction_components/kernel_features.rs @@ -36,13 +36,26 @@ bitflags! { pub struct KernelFeatures: u8 { /// Coinbase transaction const COINBASE_KERNEL = 1u8; + /// Burned output transaction + const BURN_KERNEL = 2u8; } } impl KernelFeatures { + /// Creates a coinbase kernel flag pub fn create_coinbase() -> KernelFeatures { KernelFeatures::COINBASE_KERNEL } + + /// Creates a burned kernel flag + pub fn create_burn() -> KernelFeatures { + KernelFeatures::BURN_KERNEL + } + + /// Does this feature include the burned flag? + pub fn is_burned(&self) -> bool { + self.contains(KernelFeatures::BURN_KERNEL) + } } impl ConsensusEncoding for KernelFeatures { diff --git a/base_layer/core/src/transactions/transaction_components/output_features.rs b/base_layer/core/src/transactions/transaction_components/output_features.rs index 86905d7da7..9eb3d3c666 100644 --- a/base_layer/core/src/transactions/transaction_components/output_features.rs +++ b/base_layer/core/src/transactions/transaction_components/output_features.rs @@ -147,6 +147,14 @@ impl OutputFeatures { } } + /// creates output features for a burned output + pub fn create_burn_output() -> OutputFeatures { + OutputFeatures { + output_type: OutputType::Burn, + ..Default::default() + } + } + pub fn for_asset_registration( metadata: Vec, public_key: PublicKey, diff --git a/base_layer/core/src/transactions/transaction_components/output_type.rs b/base_layer/core/src/transactions/transaction_components/output_type.rs index 50b02969b1..40cb2b76f6 100644 --- a/base_layer/core/src/transactions/transaction_components/output_type.rs +++ b/base_layer/core/src/transactions/transaction_components/output_type.rs @@ -42,30 +42,32 @@ pub enum OutputType { Standard = 0, /// Output is a coinbase output, must not be spent until maturity. Coinbase = 1, + /// Output is a burned output and can not be spent ever. + Burn = 2, /// Output defines a side-chain contract. - ContractDefinition = 2, + ContractDefinition = 3, /// Output defines the constitution for a side-chain contract. - ContractConstitution = 3, + ContractConstitution = 4, /// Output indicates validator node acceptance to run a contract. - ContractValidatorAcceptance = 4, + ContractValidatorAcceptance = 5, /// Output is a contract checkpoint. - ContractCheckpoint = 5, + ContractCheckpoint = 6, /// Output that defines a contract constitution proposal. - ContractConstitutionProposal = 6, + ContractConstitutionProposal = 7, /// Output that indicates acceptance of an existing contract constitution amendment proposal. - ContractConstitutionChangeAcceptance = 7, + ContractConstitutionChangeAcceptance = 8, /// Output that defines an amendment of a contract constitution. - ContractAmendment = 8, + ContractAmendment = 9, // TODO: Remove these deprecated flags - NonFungible = 9, - AssetRegistration = 10, - MintNonFungible = 11, - BurnNonFungible = 12, - SidechainInitialCheckpoint = 13, - SidechainCheckpoint = 14, - CommitteeInitialDefinition = 15, - CommitteeDefinition = 16, + NonFungible = 10, + AssetRegistration = 11, + MintNonFungible = 12, + BurnNonFungible = 13, + SidechainInitialCheckpoint = 14, + SidechainCheckpoint = 15, + CommitteeInitialDefinition = 16, + CommitteeDefinition = 17, } impl OutputType { @@ -144,8 +146,8 @@ mod tests { fn it_converts_from_byte_to_output_type() { assert_eq!(OutputType::from_byte(0), Some(OutputType::Standard)); assert_eq!(OutputType::from_byte(1), Some(OutputType::Coinbase)); - assert_eq!(OutputType::from_byte(15), Some(OutputType::CommitteeInitialDefinition)); - assert_eq!(OutputType::from_byte(16), Some(OutputType::CommitteeDefinition)); - assert_eq!(OutputType::from_byte(17), None); + assert_eq!(OutputType::from_byte(16), Some(OutputType::CommitteeInitialDefinition)); + assert_eq!(OutputType::from_byte(17), Some(OutputType::CommitteeDefinition)); + assert_eq!(OutputType::from_byte(18), None); } } diff --git a/base_layer/core/src/transactions/transaction_components/test.rs b/base_layer/core/src/transactions/transaction_components/test.rs index fd3d49e9fe..f44430c6dc 100644 --- a/base_layer/core/src/transactions/transaction_components/test.rs +++ b/base_layer/core/src/transactions/transaction_components/test.rs @@ -234,7 +234,7 @@ fn kernel_hash() { .unwrap(); assert_eq!( &k.hash().to_hex(), - "ce54718b33405e8fc96ed68044af21febc84c7a74c2aa9d792947f2571c7a61b" + "72158351bed5c9b3d9d626821ea1d775e31456f4d762d09cee21a9032d214e3c" ); } @@ -253,7 +253,7 @@ fn kernel_metadata() { .unwrap(); assert_eq!( &k.hash().to_hex(), - "db1522441628687beb21d4d8279e107e733aec9c8b7d513ef3c35b05c1e0150c" + "6bf18baef9296815dc9fa1a6ddee2e90a471c63ba86f8542311d2a73881ade18" ) } diff --git a/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs b/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs index d4ab79bbc7..efcebd6ed6 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs @@ -65,6 +65,8 @@ pub struct TransactionKernel { /// An aggregated signature of the metadata in this kernel, signed by the individual excess values and the offset /// excess of the sender. pub excess_sig: Signature, + /// This is an optional field that must be set if the transaction contains a burned output. + pub burn_commitment: Option, } impl TransactionKernel { @@ -75,6 +77,7 @@ impl TransactionKernel { lock_height: u64, excess: Commitment, excess_sig: Signature, + burn_commitment: Option, ) -> TransactionKernel { TransactionKernel { version, @@ -83,6 +86,7 @@ impl TransactionKernel { lock_height, excess, excess_sig, + burn_commitment, } } @@ -92,6 +96,7 @@ impl TransactionKernel { lock_height: u64, excess: Commitment, excess_sig: Signature, + burn_commitment: Option, ) -> TransactionKernel { TransactionKernel::new( TransactionKernelVersion::get_current_version(), @@ -100,6 +105,7 @@ impl TransactionKernel { lock_height, excess, excess_sig, + burn_commitment, ) } @@ -107,6 +113,11 @@ impl TransactionKernel { self.features.contains(KernelFeatures::COINBASE_KERNEL) } + /// Is this a burned output kernel? + pub fn is_burned(&self) -> bool { + self.features.contains(KernelFeatures::BURN_KERNEL) + } + pub fn verify_signature(&self) -> Result<(), TransactionError> { let excess = self.excess.as_public_key(); let r = self.excess_sig.get_public_nonce(); @@ -123,6 +134,14 @@ impl TransactionKernel { )) } } + + /// This gets the burn commitment if it exists + pub fn get_burn_commitment(&self) -> Result<&Commitment, TransactionError> { + match self.burn_commitment { + Some(ref burn_commitment) => Ok(burn_commitment), + None => Err(TransactionError::InvalidKernel("Burn commitment not found".to_string())), + } + } } impl Hashable for TransactionKernel { @@ -136,7 +155,7 @@ impl Display for TransactionKernel { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { write!( fmt, - "Fee: {}\nLock height: {}\nFeatures: {:?}\nExcess: {}\nExcess signature: {}\n", + "Fee: {}\nLock height: {}\nFeatures: {:?}\nExcess: {}\nExcess signature: {}\nCommitment: {}\n", self.fee, self.lock_height, self.features, @@ -144,6 +163,10 @@ impl Display for TransactionKernel { self.excess_sig .to_json() .unwrap_or_else(|_| "Failed to serialize signature".into()), + match self.burn_commitment { + Some(ref burn_commitment) => burn_commitment.to_hex(), + None => "None".to_string(), + } ) } } @@ -168,6 +191,7 @@ impl ConsensusEncoding for TransactionKernel { self.lock_height.consensus_encode(writer)?; self.excess.consensus_encode(writer)?; self.excess_sig.consensus_encode(writer)?; + self.burn_commitment.consensus_encode(writer)?; Ok(()) } } @@ -180,7 +204,8 @@ impl ConsensusDecoding for TransactionKernel { let lock_height = u64::consensus_decode(reader)?; let excess = Commitment::consensus_decode(reader)?; let excess_sig = Signature::consensus_decode(reader)?; - let kernel = TransactionKernel::new(version, features, fee, lock_height, excess, excess_sig); + let commitment = as ConsensusDecoding>::consensus_decode(reader)?; + let kernel = TransactionKernel::new(version, features, fee, lock_height, excess, excess_sig, commitment); Ok(kernel) } } diff --git a/base_layer/core/src/transactions/transaction_components/transaction_output.rs b/base_layer/core/src/transactions/transaction_components/transaction_output.rs index 3ec427e130..8866f863c5 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_output.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_output.rs @@ -234,6 +234,11 @@ impl TransactionOutput { matches!(self.features.output_type, OutputType::Coinbase) } + /// Returns true if the output is burned, otherwise false + pub fn is_burned(&self) -> bool { + matches!(self.features.output_type, OutputType::Burn) + } + /// Convenience function that returns the challenge for the metadata commitment signature pub fn get_metadata_signature_challenge(&self, partial_commitment_nonce: Option<&PublicKey>) -> Challenge { let nonce_commitment = match partial_commitment_nonce { diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index 79c078e987..221de28b05 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -539,7 +539,19 @@ impl SenderTransactionProtocol { let mut s_agg = info.signatures[0].clone(); info.signatures.iter().skip(1).for_each(|s| s_agg = &s_agg + s); let excess = PedersenCommitment::from_public_key(&info.public_excess); - let kernel = KernelBuilder::new() + let mut kernel_builder = KernelBuilder::new(); + if features.is_burned() { + let mut commitment = None; + for o in &info.outputs { + if o.is_burned() { + commitment = Some(o.commitment.clone()); + } + } + kernel_builder = kernel_builder.with_burn_commitment( + commitment.ok_or_else(|| TPE::IncompleteStateError("No burned output found".to_string()))?, + ); + } + let kernel = kernel_builder .with_fee(info.metadata.fee) .with_features(features) .with_lock_height(info.metadata.lock_height) diff --git a/base_layer/core/src/validation/block_validators/orphan.rs b/base_layer/core/src/validation/block_validators/orphan.rs index bf8c30a33d..63416a53d8 100644 --- a/base_layer/core/src/validation/block_validators/orphan.rs +++ b/base_layer/core/src/validation/block_validators/orphan.rs @@ -35,6 +35,7 @@ use crate::{ check_kernel_lock_height, check_maturity, check_sorting_and_duplicates, + check_total_burned, }, OrphanValidation, ValidationError, @@ -99,6 +100,8 @@ impl OrphanValidation for OrphanBlockValidator { "SV - No duplicate inputs / outputs for {} ", &block_id ); + check_total_burned(&block.body)?; + trace!(target: LOG_TARGET, "SV - Burned outputs ok for {} ", &block_id); // Check that the inputs are are allowed to be spent check_maturity(height, block.body.inputs())?; diff --git a/base_layer/core/src/validation/chain_balance.rs b/base_layer/core/src/validation/chain_balance.rs index 9d0d979461..be1858e3bd 100644 --- a/base_layer/core/src/validation/chain_balance.rs +++ b/base_layer/core/src/validation/chain_balance.rs @@ -59,22 +59,24 @@ impl FinalHorizonStateValidation for ChainBalanceValida height: u64, total_utxo_sum: &Commitment, total_kernel_sum: &Commitment, + total_burned_sum: &Commitment, ) -> Result<(), ValidationError> { let emission_h = self.get_emission_commitment_at(height); let total_offset = self.fetch_total_offset_commitment(height, backend)?; debug!( target: LOG_TARGET, - "Emission:{:?}. Offset:{:?}, total kernel: {:?}, height: {}, total_utxo: {:?}", + "Emission:{:?}. Offset:{:?}, total kernel: {:?}, height: {}, total_utxo: {:?}, total_burned: {:?}", emission_h, total_offset, total_kernel_sum, height, - total_utxo_sum + total_utxo_sum, + total_burned_sum, ); let input = &(&emission_h + total_kernel_sum) + &total_offset; - if total_utxo_sum != &input { + if (total_utxo_sum + total_burned_sum) != input { return Err(ValidationError::ChainBalanceValidationFailed(height)); } diff --git a/base_layer/core/src/validation/error.rs b/base_layer/core/src/validation/error.rs index 76c6558b7c..48192e7324 100644 --- a/base_layer/core/src/validation/error.rs +++ b/base_layer/core/src/validation/error.rs @@ -129,6 +129,8 @@ pub enum ValidationError { output_type: OutputType, contract_id: FixedHash, }, + #[error("Contains Invalid Burn: {0}")] + InvalidBurnError(String), } // ChainStorageError has a ValidationError variant, so to prevent a cyclic dependency we use a string representation in diff --git a/base_layer/core/src/validation/helpers.rs b/base_layer/core/src/validation/helpers.rs index db7d5e8c16..ac9c2dada6 100644 --- a/base_layer/core/src/validation/helpers.rs +++ b/base_layer/core/src/validation/helpers.rs @@ -20,6 +20,8 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::collections::HashSet; + use log::*; use tari_common_types::types::{Commitment, CommitmentFactory, PublicKey}; use tari_crypto::{ @@ -266,6 +268,33 @@ pub fn check_accounting_balance( }) } +/// THis function checks the total burned sum in the header ensuring that every burned output is counted in the total +/// sum. +#[allow(clippy::mutable_key_type)] +pub fn check_total_burned(body: &AggregateBody) -> Result<(), ValidationError> { + let mut burned_outputs = HashSet::new(); + for output in body.outputs() { + if output.is_burned() { + // we dont care about duplicate commitments are they should have already been checked + burned_outputs.insert(output.commitment.clone()); + } + } + for kernel in body.kernels() { + if kernel.is_burned() && burned_outputs.remove(kernel.get_burn_commitment()?) { + return Err(ValidationError::InvalidBurnError( + "Burned kernel does not match burned output".to_string(), + )); + } + } + + if !burned_outputs.is_empty() { + return Err(ValidationError::InvalidBurnError( + "Burned output has no matching burned kernel".to_string(), + )); + } + Ok(()) +} + pub fn check_coinbase_output( block: &Block, rules: &ConsensusManager, diff --git a/base_layer/core/src/validation/mocks.rs b/base_layer/core/src/validation/mocks.rs index f6c07d5639..f52ea98e4d 100644 --- a/base_layer/core/src/validation/mocks.rs +++ b/base_layer/core/src/validation/mocks.rs @@ -149,6 +149,7 @@ impl FinalHorizonStateValidation for MockValidator { _height: u64, _total_utxo_sum: &Commitment, _total_kernel_sum: &Commitment, + _total_burned_sum: &Commitment, ) -> Result<(), ValidationError> { if self.is_valid.load(Ordering::SeqCst) { Ok(()) diff --git a/base_layer/core/src/validation/test.rs b/base_layer/core/src/validation/test.rs index 552fd4fac5..3d3670b392 100644 --- a/base_layer/core/src/validation/test.rs +++ b/base_layer/core/src/validation/test.rs @@ -146,13 +146,15 @@ fn chain_balance_validation() { ); let (pk, sig) = create_random_signature_from_s_key(faucet_key, 0.into(), 0); let excess = Commitment::from_public_key(&pk); - let kernel = TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig); + let kernel = + TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig, None); // let _faucet_hash = faucet_utxo.hash(); let mut gen_block = genesis.block().clone(); gen_block.body.add_output(faucet_utxo); gen_block.body.add_kernels(&mut vec![kernel]); let mut utxo_sum = HomomorphicCommitment::default(); let mut kernel_sum = HomomorphicCommitment::default(); + let burned_sum = HomomorphicCommitment::default(); for output in gen_block.body.outputs() { utxo_sum = &output.commitment + &utxo_sum; } @@ -177,7 +179,7 @@ fn chain_balance_validation() { let validator = ChainBalanceValidator::new(consensus_manager.clone(), factories.clone()); // Validate the genesis state validator - .validate(&*db.db_read_access().unwrap(), 0, &utxo_sum, &kernel_sum) + .validate(&*db.db_read_access().unwrap(), 0, &utxo_sum, &kernel_sum, &burned_sum) .unwrap(); //---------------------------------- Add a new coinbase and header --------------------------------------------// @@ -229,7 +231,7 @@ fn chain_balance_validation() { utxo_sum = &coinbase.commitment + &utxo_sum; kernel_sum = &kernel.excess + &kernel_sum; validator - .validate(&*db.db_read_access().unwrap(), 1, &utxo_sum, &kernel_sum) + .validate(&*db.db_read_access().unwrap(), 1, &utxo_sum, &kernel_sum, &burned_sum) .unwrap(); //---------------------------------- Try to inflate --------------------------------------------// @@ -280,10 +282,147 @@ fn chain_balance_validation() { db.commit(txn).unwrap(); validator - .validate(&*db.db_read_access().unwrap(), 2, &utxo_sum, &kernel_sum) + .validate(&*db.db_read_access().unwrap(), 2, &utxo_sum, &kernel_sum, &burned_sum) .unwrap_err(); } +#[test] +#[allow(clippy::too_many_lines)] +fn chain_balance_validation_burned() { + let factories = CryptoFactories::default(); + let consensus_manager = ConsensusManagerBuilder::new(Network::Dibbler).build(); + let genesis = consensus_manager.get_genesis_block(); + let faucet_value = 5000 * uT; + let (faucet_utxo, faucet_key, _) = create_utxo( + faucet_value, + &factories, + &OutputFeatures::default(), + &script!(Nop), + &Covenant::default(), + MicroTari::zero(), + ); + let (pk, sig) = create_random_signature_from_s_key(faucet_key, 0.into(), 0); + let excess = Commitment::from_public_key(&pk); + let kernel = + TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig, None); + // let _faucet_hash = faucet_utxo.hash(); + let mut gen_block = genesis.block().clone(); + gen_block.body.add_output(faucet_utxo); + gen_block.body.add_kernels(&mut vec![kernel]); + let mut utxo_sum = HomomorphicCommitment::default(); + let mut kernel_sum = HomomorphicCommitment::default(); + let mut burned_sum = HomomorphicCommitment::default(); + for output in gen_block.body.outputs() { + utxo_sum = &output.commitment + &utxo_sum; + } + for kernel in gen_block.body.kernels() { + kernel_sum = &kernel.excess + &kernel_sum; + } + let genesis = ChainBlock::try_construct(Arc::new(gen_block), genesis.accumulated_data().clone()).unwrap(); + let total_faucet = faucet_value + consensus_manager.consensus_constants(0).faucet_value(); + let constants = ConsensusConstantsBuilder::new(Network::LocalNet) + .with_consensus_constants(consensus_manager.consensus_constants(0).clone()) + .with_faucet_value(total_faucet) + .build(); + // Create a LocalNet consensus manager that uses rincewind consensus constants and has a custom rincewind genesis + // block that contains an extra faucet utxo + let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet) + .with_block(genesis.clone()) + .add_consensus_constants(constants) + .build(); + + let db = create_store_with_consensus(consensus_manager.clone()); + + let validator = ChainBalanceValidator::new(consensus_manager.clone(), factories.clone()); + // Validate the genesis state + validator + .validate(&*db.db_read_access().unwrap(), 0, &utxo_sum, &kernel_sum, &burned_sum) + .unwrap(); + + //---------------------------------- Add block (coinbase + burned) --------------------------------------------// + let mut txn = DbTransaction::new(); + let coinbase_value = consensus_manager.get_block_reward_at(1) - MicroTari::from(100); + let (coinbase, coinbase_key, _) = create_utxo( + coinbase_value, + &factories, + &OutputFeatures::create_coinbase(1), + &script!(Nop), + &Covenant::default(), + MicroTari::zero(), + ); + let (pk, sig) = create_random_signature_from_s_key(coinbase_key, 0.into(), 0); + let excess = Commitment::from_public_key(&pk); + let kernel = KernelBuilder::new() + .with_signature(&sig) + .with_excess(&excess) + .with_features(KernelFeatures::COINBASE_KERNEL) + .build() + .unwrap(); + + let (burned, burned_key, _) = create_utxo( + 100.into(), + &factories, + &OutputFeatures::create_burn_output(), + &script!(Nop), + &Covenant::default(), + MicroTari::zero(), + ); + + let (pk2, sig2) = create_random_signature_from_s_key(burned_key, 0.into(), 0); + let excess2 = Commitment::from_public_key(&pk2); + let kernel2 = KernelBuilder::new() + .with_signature(&sig2) + .with_excess(&excess2) + .with_features(KernelFeatures::create_burn()) + .with_burn_commitment(burned.commitment.clone()) + .build() + .unwrap(); + burned_sum = &burned_sum + kernel2.get_burn_commitment().unwrap(); + let mut header1 = BlockHeader::from_previous(genesis.header()); + header1.kernel_mmr_size += 2; + header1.output_mmr_size += 2; + let achieved_difficulty = AchievedTargetDifficulty::try_construct( + genesis.header().pow_algo(), + genesis.accumulated_data().target_difficulty, + genesis.accumulated_data().achieved_difficulty, + ) + .unwrap(); + let accumulated_data = BlockHeaderAccumulatedData::builder(genesis.accumulated_data()) + .with_hash(header1.hash()) + .with_achieved_target_difficulty(achieved_difficulty) + .with_total_kernel_offset(header1.total_kernel_offset.clone()) + .build() + .unwrap(); + let header1 = ChainHeader::try_construct(header1, accumulated_data).unwrap(); + txn.insert_chain_header(header1.clone()); + + let mut mmr_position = 4; + let mut mmr_leaf_index = 4; + + txn.insert_kernel(kernel.clone(), header1.hash().clone(), mmr_position); + txn.insert_utxo(coinbase.clone(), header1.hash().clone(), 1, mmr_leaf_index, 0); + + mmr_position = 5; + mmr_leaf_index = 5; + + txn.insert_kernel(kernel2.clone(), header1.hash().clone(), mmr_position); + txn.insert_pruned_utxo( + burned.hash(), + burned.witness_hash(), + header1.hash().clone(), + header1.height(), + mmr_leaf_index, + 0, + ); + + db.commit(txn).unwrap(); + utxo_sum = &coinbase.commitment + &utxo_sum; + kernel_sum = &(&kernel.excess + &kernel_sum) + &kernel2.excess; + validator + .validate(&*db.db_read_access().unwrap(), 1, &utxo_sum, &kernel_sum, &burned_sum) + .unwrap(); +} + mod transaction_validator { use super::*; diff --git a/base_layer/core/src/validation/traits.rs b/base_layer/core/src/validation/traits.rs index 3e4fb97322..dbb2921bdb 100644 --- a/base_layer/core/src/validation/traits.rs +++ b/base_layer/core/src/validation/traits.rs @@ -72,5 +72,6 @@ pub trait FinalHorizonStateValidation: Send + Sync { height: u64, total_utxo_sum: &Commitment, total_kernel_sum: &Commitment, + total_burned_sum: &Commitment, ) -> Result<(), ValidationError>; } diff --git a/base_layer/tari_mining_helper_ffi/src/lib.rs b/base_layer/tari_mining_helper_ffi/src/lib.rs index d5cf0c3d91..2983407340 100644 --- a/base_layer/tari_mining_helper_ffi/src/lib.rs +++ b/base_layer/tari_mining_helper_ffi/src/lib.rs @@ -371,8 +371,8 @@ mod tests { #[test] fn detect_change_in_consensus_encoding() { - const NONCE: u64 = 15324301174278869343; - const DIFFICULTY: Difficulty = Difficulty::from_u64(6091); + const NONCE: u64 = 7157305302409646947; + const DIFFICULTY: Difficulty = Difficulty::from_u64(6126); // Use this to generate new NONCE and DIFFICULTY // Use ONLY if you know encoding has changed // let (difficulty, nonce) = generate_nonce_with_min_difficulty(MIN_DIFFICULTY).unwrap(); diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index bd6a341d1a..efa38339ac 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -80,6 +80,11 @@ pub enum TransactionServiceRequest { fee_per_gram: MicroTari, message: String, }, + BurnTari { + amount: MicroTari, + fee_per_gram: MicroTari, + message: String, + }, SendOneSidedTransaction { dest_pubkey: CommsPublicKey, amount: MicroTari, @@ -145,6 +150,7 @@ impl fmt::Display for TransactionServiceRequest { amount, message )), + Self::BurnTari { amount, message, .. } => f.write_str(&format!("Burning Tari ({}, {})", amount, message)), Self::SendOneSidedTransaction { dest_pubkey, amount, @@ -466,6 +472,27 @@ impl TransactionServiceHandle { } } + /// Burns the given amount of Tari from the wallet + pub async fn burn_tari( + &mut self, + amount: MicroTari, + fee_per_gram: MicroTari, + message: String, + ) -> Result { + match self + .handle + .call(TransactionServiceRequest::BurnTari { + amount, + fee_per_gram, + message, + }) + .await?? + { + TransactionServiceResponse::TransactionSent(tx_id) => Ok(tx_id), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + pub async fn send_one_sided_to_stealth_address_transaction( &mut self, dest_pubkey: CommsPublicKey, diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 51364eee02..b6e32c2b81 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -623,6 +623,14 @@ where ) .await .map(TransactionServiceResponse::TransactionSent), + TransactionServiceRequest::BurnTari { + amount, + fee_per_gram, + message, + } => self + .burn_tari(amount, fee_per_gram, message, transaction_broadcast_join_handles) + .await + .map(TransactionServiceResponse::TransactionSent), TransactionServiceRequest::SendShaAtomicSwapTransaction(dest_pubkey, amount, fee_per_gram, message) => { Ok(TransactionServiceResponse::ShaAtomicSwapTransactionSent( self.send_sha_atomic_swap_transaction( @@ -1320,6 +1328,118 @@ where .await } + /// Creates a transaction to burn some Tari + /// # Arguments + /// 'amount': The amount of Tari to send to the recipient + /// 'fee_per_gram': The amount of fee per transaction gram to be included in transaction + pub async fn burn_tari( + &mut self, + amount: MicroTari, + fee_per_gram: MicroTari, + message: String, + transaction_broadcast_join_handles: &mut FuturesUnordered< + JoinHandle>>, + >, + ) -> Result { + let tx_id = TxId::new_random(); + let output_features = OutputFeatures::create_burn_output(); + // Prepare sender part of the transaction + let mut stp = self + .output_manager_service + .prepare_transaction_to_send( + tx_id, + amount, + UtxoSelectionCriteria::default(), + output_features, + fee_per_gram, + None, + message.clone(), + TariScript::default(), + Covenant::default(), + MicroTari::zero(), + ) + .await?; + + // This call is needed to advance the state from `SingleRoundMessageReady` to `SingleRoundMessageReady`, + // but the returned value is not used + let _single_round_sender_data = stp + .build_single_round_message() + .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; + + self.output_manager_service + .confirm_pending_transaction(tx_id) + .await + .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; + let sender_message = TransactionSenderMessage::new_single_round_message(stp.get_single_round_message()?); + let spend_key = PrivateKey::random(&mut OsRng); + let rtp = ReceiverTransactionProtocol::new( + sender_message, + PrivateKey::random(&mut OsRng), + spend_key, + &self.resources.factories, + ); + + let recipient_reply = rtp.get_signed_data()?.clone(); + + // Start finalizing + + stp.add_single_recipient_info(recipient_reply, &self.resources.factories.range_proof) + .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; + + // Finalize + + stp.finalize( + KernelFeatures::create_burn(), + &self.resources.factories, + None, + self.last_seen_tip_height.unwrap_or(u64::MAX), + ) + .map_err(|e| { + error!( + target: LOG_TARGET, + "Transaction (TxId: {}) could not be finalized. Failure error: {:?}", tx_id, e, + ); + TransactionServiceProtocolError::new(tx_id, e.into()) + })?; + info!(target: LOG_TARGET, "Finalized burning transaction TxId: {}", tx_id); + + // This event being sent is important, but not critical to the protocol being successful. Send only fails if + // there are no subscribers. + let _result = self + .event_publisher + .send(Arc::new(TransactionEvent::TransactionCompletedImmediately(tx_id))); + + // Broadcast burn transaction + + let tx = stp + .get_transaction() + .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; + let fee = stp + .get_fee_amount() + .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; + self.submit_transaction( + transaction_broadcast_join_handles, + CompletedTransaction::new( + tx_id, + self.resources.node_identity.public_key().clone(), + CommsPublicKey::default(), + amount, + fee, + tx.clone(), + TransactionStatus::Completed, + message.clone(), + Utc::now().naive_utc(), + TransactionDirection::Outbound, + None, + None, + None, + ), + ) + .await?; + + Ok(tx_id) + } + /// Sends a one side payment transaction to a recipient /// # Arguments /// 'dest_pubkey': The Comms pubkey of the recipient node diff --git a/clients/wallet_grpc_client/index.js b/clients/wallet_grpc_client/index.js index a26043e178..10c96b8866 100644 --- a/clients/wallet_grpc_client/index.js +++ b/clients/wallet_grpc_client/index.js @@ -46,6 +46,7 @@ function Client(address) { "checkForUpdates", "revalidateAllTransactions", "SendShaAtomicSwapTransaction", + "CreateBurnTransaction", "claimShaAtomicSwapTransaction", "ClaimHtlcRefundTransaction", "registerAsset", diff --git a/integration_tests/README.md b/integration_tests/README.md index 0c00a531c1..802720a3af 100644 --- a/integration_tests/README.md +++ b/integration_tests/README.md @@ -8,6 +8,18 @@ ``` npm install ``` +- Open terminal in the `tari-project\clients\base_node_grpc_client` folder and run + ``` + npm install + ``` + - Open terminal in the `tari-project\clients\validator_node_grpc_client` folder and run + ``` + npm install + ``` + - Open terminal in the `tari-project\clients\wallet_grpc_client` folder and run + ``` + npm install + ``` ## Procedure to run diff --git a/integration_tests/features/Sync.feature b/integration_tests/features/Sync.feature index a76179a51b..6515e1c156 100644 --- a/integration_tests/features/Sync.feature +++ b/integration_tests/features/Sync.feature @@ -45,6 +45,21 @@ Feature: Block Sync Given I have a pruned node PNODE1 connected to node NODE1 with pruning horizon set to 5 Then all nodes are at height 20 + @critical @pruned @broken + Scenario: Pruned node should handle burned output + Given I have a seed node NODE + And I have 2 base nodes connected to all seed nodes + And I have wallet WALLET_A connected to all seed nodes + And I have mining node MINER connected to base node NODE and wallet WALLET_A + When mining node MINER mines 15 blocks + Then all nodes are at height 15 + When I wait for wallet WALLET_A to have at least 55000000000 uT + When I create a burn transaction of 1000000 uT from WALLET_A at fee 100 + When mining node MINER mines 10 blocks + Then all nodes are at height 25 + Given I have a pruned node PNODE1 connected to node NODE1 with pruning horizon set to 5 + Then all nodes are at height 20 + @critical Scenario: When a new node joins the network, it receives all peers Given I have 10 seed nodes diff --git a/integration_tests/features/WalletTransactions.feature b/integration_tests/features/WalletTransactions.feature index f2d2c4bf30..b2feea999e 100644 --- a/integration_tests/features/WalletTransactions.feature +++ b/integration_tests/features/WalletTransactions.feature @@ -387,3 +387,17 @@ Feature: Wallet Transactions Then I restart wallet WALLET_RECV When I wait 15 seconds When wallet WALLET_RECV detects last transaction is Cancelled + +@critical + Scenario: Create burn transaction + Given I have a seed node NODE + And I have 2 base nodes connected to all seed nodes + And I have wallet WALLET_A connected to all seed nodes + And I have mining node MINER connected to base node NODE and wallet WALLET_A + When mining node MINER mines 15 blocks + Then all nodes are at height 15 + When I wait for wallet WALLET_A to have at least 55000000000 uT + When I create a burn transaction of 1000000 uT from WALLET_A at fee 100 + When mining node MINER mines 10 blocks + Then all nodes are at height 25 + Then wallet WALLET_A detects all transactions as Mined_Confirmed \ No newline at end of file diff --git a/integration_tests/features/support/wallet_steps.js b/integration_tests/features/support/wallet_steps.js index aff8e9a3ed..a5eace6f6b 100644 --- a/integration_tests/features/support/wallet_steps.js +++ b/integration_tests/features/support/wallet_steps.js @@ -1147,6 +1147,34 @@ When( } ); +When( + /I create a burn transaction of (.*) uT from (.*) at fee (.*)/, + { timeout: 65 * 1000 }, + async function (amount, source, feePerGram) { + const sourceWallet = this.getWallet(source); + const sourceClient = await sourceWallet.connectClient(); + const sourceInfo = await sourceClient.identify(); + + const lastResult = await this.burn_tari(sourceWallet, amount, feePerGram); + expect(lastResult.is_success).to.equal(true); + + this.addTransaction(sourceInfo.public_key, lastResult.transaction_id); + //lets now wait for this transaction to be at least broadcast before we continue. + await waitFor( + async () => + sourceClient.isTransactionAtLeastBroadcast(lastResult.transaction_id), + true, + 60 * 1000, + 5 * 1000, + 5 + ); + let transactionPending = await sourceClient.isTransactionAtLeastBroadcast( + lastResult.transaction_id + ); + expect(transactionPending).to.equal(true); + } +); + When( /I cancel last transaction in wallet (.*)/, { timeout: 20 * 1000 }, diff --git a/integration_tests/features/support/world.js b/integration_tests/features/support/world.js index 932b1e6080..933e2a1753 100644 --- a/integration_tests/features/support/world.js +++ b/integration_tests/features/support/world.js @@ -585,6 +585,63 @@ class CustomWorld { return lastResult; } + async burn_tari( + sourceWallet, + tariAmount, + feePerGram, + message = "", + printMessage = true + ) { + const sourceWalletClient = await sourceWallet.connectClient(); + console.log(sourceWallet.name + " burning " + tariAmount + "uT"); + if (printMessage) { + console.log(message); + } + let success = false; + let retries = 1; + const retries_limit = 25; + let lastResult; + while (!success && retries <= retries_limit) { + await waitFor( + async () => { + try { + lastResult = await sourceWalletClient.burn({ + amount: tariAmount, + fee_per_gram: feePerGram, + message: message, + }); + } catch (error) { + console.log(error); + return false; + } + return true; + }, + true, + 20 * 1000, + 5 * 1000, + 5 + ); + success = lastResult.is_success; + if (!success) { + const wait_seconds = 5; + console.log( + " " + + lastResult.failure_message + + ", trying again after " + + wait_seconds + + "s (" + + retries + + " of " + + retries_limit + + ")" + ); + await sleep(wait_seconds * 1000); + retries++; + } + } + return lastResult; + } + async transfer( tariAmount, source, @@ -657,9 +714,9 @@ class CustomWorld { } async all_nodes_are_at_height(height) { + let result = true; await waitFor( async () => { - let result = true; await this.forEachClientAsync(async (client, name) => { await waitFor( async () => await client.getTipHeight(), @@ -670,7 +727,7 @@ class CustomWorld { console.log( `Node ${name} is at tip: ${currTip} (should be ${height})` ); - result = result && currTip == height; + result = result && currTip === height; }); return result; }, @@ -679,6 +736,7 @@ class CustomWorld { 5 * 1000, 5 ); + expect(result).to.equal(true); } } diff --git a/integration_tests/helpers/walletClient.js b/integration_tests/helpers/walletClient.js index 2f13cf6aaa..962a24114e 100644 --- a/integration_tests/helpers/walletClient.js +++ b/integration_tests/helpers/walletClient.js @@ -151,6 +151,10 @@ class WalletClient { return await this.client.transfer(args); } + async burn(args) { + return await this.client.CreateBurnTransaction(args); + } + async sendHtlc(args) { return await this.client.SendShaAtomicSwapTransaction(args); } diff --git a/integration_tests/package-lock.json b/integration_tests/package-lock.json index cc1ada79da..951f3f733f 100644 --- a/integration_tests/package-lock.json +++ b/integration_tests/package-lock.json @@ -1,8 +1,3930 @@ { "name": "integration_tests", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "integration_tests", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "archiver": "^5.3.1", + "axios": "^0.21.4", + "clone-deep": "^4.0.1", + "csv-parser": "^3.0.0", + "dateformat": "^3.0.3", + "glob": "^7.2.3", + "json5": "^2.2.1", + "sha3": "^2.1.3", + "tari_crypto": "v0.14.0", + "utf8": "^3.0.0", + "wallet-grpc-client": "file:../clients/wallet_grpc_client" + }, + "devDependencies": { + "@babel/core": "^7.18.6", + "@babel/eslint-parser": "^7.18.2", + "@babel/eslint-plugin": "^7.17.7", + "@cucumber/cucumber": "^8.4.0", + "@cucumber/pretty-formatter": "^1.0.0", + "@grpc/grpc-js": "^1.6.7", + "@grpc/proto-loader": "^0.5.5", + "blakejs": "^1.2.1", + "chai": "^4.3.6", + "cucumber-html-reporter": "^5.5.0", + "eslint": "^7.32.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^3.4.1", + "ffi-napi": "^4.0.3", + "grpc-promise": "^1.4.0", + "husky": "^6.0.0", + "prettier": "^2.7.1", + "ref-napi": "^3.0.3" + } + }, + "../clients/wallet_grpc_client": { + "name": "@tari/wallet-grpc-client", + "version": "0.0.1", + "dependencies": { + "@grpc/grpc-js": "^1.3.6", + "@grpc/proto-loader": "^0.5.5", + "grpc-promise": "^1.4.0" + } + }, + "../clients/wallet_grpc_client/node_modules/@grpc/grpc-js": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.6.tgz", + "integrity": "sha512-v7+LQFbqZKmd/Tvf5/j1Xlbq6jXL/4d+gUtm2TNX4QiEC3ELWADmGr2dGlUyLl6aKTuYfsN72vAsO5zmavYkEg==", + "dependencies": { + "@types/node": ">=12.12.47" + } + }, + "../clients/wallet_grpc_client/node_modules/@grpc/proto-loader": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.6.tgz", + "integrity": "sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "protobufjs": "^6.8.6" + } + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + }, + "../clients/wallet_grpc_client/node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + }, + "../clients/wallet_grpc_client/node_modules/@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, + "../clients/wallet_grpc_client/node_modules/@types/node": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.2.tgz", + "integrity": "sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw==" + }, + "../clients/wallet_grpc_client/node_modules/grpc-promise": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/grpc-promise/-/grpc-promise-1.4.0.tgz", + "integrity": "sha512-4BBXHXb5OjjBh7luylu8vFqL6H6aPn/LeqpQaSBeRzO/Xv95wHW/WkU9TJRqaCTMZ5wq9jTSvlJWp0vRJy1pVA==" + }, + "../clients/wallet_grpc_client/node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, + "../clients/wallet_grpc_client/node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "../clients/wallet_grpc_client/node_modules/protobufjs": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.6.tgz", + "integrity": "sha512-tzulrgDT0QD6U7BJ4TKVk2SDDg7wlP39P9yAx1RfLy7vP/7rsDRlWVfbWxElslu56+r7QOhB2NSDsabYYruoZQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.6.tgz", + "integrity": "sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.6", + "@babel/helper-compilation-targets": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helpers": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.18.2.tgz", + "integrity": "sha512-oFQYkE8SuH14+uR51JVAmdqwKYXGRjEXx7s+WiagVjqQ+HPE+nnwyF2qlVG8evUsUHmPcA+6YXMEDbIhEyQc5A==", + "dev": true, + "dependencies": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/eslint-plugin": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/eslint-plugin/-/eslint-plugin-7.17.7.tgz", + "integrity": "sha512-JATUoJJXSgwI0T8juxWYtK1JSgoLpIGUsCHIv+NMXcUDA2vIe6nvAHR9vnuJgs/P1hOFw7vPwibixzfqBBLIVw==", + "dev": true, + "dependencies": { + "eslint-rule-composer": "^0.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/eslint-parser": ">=7.11.0", + "eslint": ">=7.5.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.7.tgz", + "integrity": "sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz", + "integrity": "sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz", + "integrity": "sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz", + "integrity": "sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.6.tgz", + "integrity": "sha512-L//phhB4al5uucwzlimruukHB3jRd5JGClwRMD/ROrVjXfLqovYnvQrK/JK36WYyVwGGO7OD3kMyVTjx+WVPhw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.15.7", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.6.tgz", + "integrity": "sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.6", + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.6.tgz", + "integrity": "sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz", + "integrity": "sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", + "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.6.tgz", + "integrity": "sha512-zS/OKyqmD7lslOtFqbscH6gMLFYOfG1YPqCKfAW5KrTeolKqvB8UelR49Fpr6y93kYkW2Ik00mT1LOGiAGvizw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-function-name": "^7.18.6", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/types": "^7.18.6", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.18.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz", + "integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types/node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@cucumber/ci-environment": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-9.0.4.tgz", + "integrity": "sha512-da6H/wtVerhGUP4OCWTOmbNd4+gC1FhAcLzYgn6O68HgQbMwkmV3M8AwtbQWZkfF+Ph7z0M/UQYYdNIDu5V5MA==", + "dev": true + }, + "node_modules/@cucumber/cucumber": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-8.4.0.tgz", + "integrity": "sha512-9MXtmpc/+ZmQH9s++u/2AF/N+D35Z6p3GpwbSr7xxDtfjM1ZCD7j6OSx+E99JGXZAOdShKzI7EhiOqwRERjKYQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@cucumber/ci-environment": "9.0.4", + "@cucumber/cucumber-expressions": "15.2.0", + "@cucumber/gherkin": "24.0.0", + "@cucumber/gherkin-streams": "5.0.1", + "@cucumber/gherkin-utils": "8.0.0", + "@cucumber/html-formatter": "19.2.0", + "@cucumber/message-streams": "4.0.1", + "@cucumber/messages": "19.0.0", + "@cucumber/tag-expressions": "4.1.0", + "assertion-error-formatter": "^3.0.0", + "capital-case": "^1.0.4", + "chalk": "^4.1.2", + "cli-table3": "0.6.2", + "commander": "^9.0.0", + "duration": "^0.2.2", + "durations": "^3.4.2", + "figures": "^3.2.0", + "glob": "^7.1.6", + "has-ansi": "^4.0.1", + "indent-string": "^4.0.0", + "is-stream": "^2.0.0", + "knuth-shuffle-seeded": "^1.0.6", + "lodash.merge": "^4.6.2", + "lodash.mergewith": "^4.6.2", + "mz": "^2.7.0", + "progress": "^2.0.3", + "resolve": "^1.19.0", + "resolve-pkg": "^2.0.0", + "semver": "7.3.7", + "stack-chain": "^2.0.0", + "string-argv": "^0.3.1", + "strip-ansi": "6.0.1", + "supports-color": "^8.1.1", + "tmp": "^0.2.1", + "util-arity": "^1.1.0", + "verror": "^1.10.0", + "yup": "^0.32.11" + }, + "bin": { + "cucumber-js": "bin/cucumber.js" + }, + "engines": { + "node": "12 || 14 || >=16" + } + }, + "node_modules/@cucumber/cucumber-expressions": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-15.2.0.tgz", + "integrity": "sha512-qAzz9ogcTuosFZYfueSTWnD6KxiIAAu09HwLwz1XhYL/MhfLjyq1iQN6mOnKln/hr2jX/U98C92VAlquhXDo7Q==", + "dev": true, + "dependencies": { + "regexp-match-indices": "1.0.2" + } + }, + "node_modules/@cucumber/cucumber/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@cucumber/cucumber/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@cucumber/cucumber/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@cucumber/cucumber/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@cucumber/cucumber/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@cucumber/cucumber/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@cucumber/cucumber/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@cucumber/cucumber/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@cucumber/gherkin": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-24.0.0.tgz", + "integrity": "sha512-b7OsnvX1B8myDAKMc+RAiUX9bzgtNdjGsiMj10O13xu2HBWIOQ19EqBJ4xLO5CFG/lGk1J/+L0lANQVowxLVBg==", + "dev": true, + "dependencies": { + "@cucumber/messages": "^19.0.0" + } + }, + "node_modules/@cucumber/gherkin-streams": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-streams/-/gherkin-streams-5.0.1.tgz", + "integrity": "sha512-/7VkIE/ASxIP/jd4Crlp4JHXqdNFxPGQokqWqsaCCiqBiu5qHoKMxcWNlp9njVL/n9yN4S08OmY3ZR8uC5x74Q==", + "dev": true, + "dependencies": { + "commander": "9.1.0", + "source-map-support": "0.5.21" + }, + "bin": { + "gherkin-javascript": "bin/gherkin" + }, + "peerDependencies": { + "@cucumber/gherkin": ">=22.0.0", + "@cucumber/message-streams": ">=4.0.0", + "@cucumber/messages": ">=17.1.1" + } + }, + "node_modules/@cucumber/gherkin-streams/node_modules/commander": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.1.0.tgz", + "integrity": "sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@cucumber/gherkin-utils": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.0.tgz", + "integrity": "sha512-8uIZInEe3cO1cASmy3BA0PbVFUI+xWBnZAxmICbVOPsZaMB85MtESZLafzErgfRQPsHf6uYbVagP7MIjNPM5Jw==", + "dev": true, + "dependencies": { + "@cucumber/messages": "^19.0.0", + "@teppeis/multimaps": "2.0.0", + "commander": "9.3.0" + }, + "bin": { + "gherkin-utils": "bin/gherkin-utils" + } + }, + "node_modules/@cucumber/html-formatter": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-19.2.0.tgz", + "integrity": "sha512-qGms4588jmVF/G3fTbgZvxn6OQw9GaTFV007nZZ9/10M9DfrgRqjFjVxVI9TPV63xOLPicEVoqsKZtcECbdMSA==", + "dev": true, + "peerDependencies": { + "@cucumber/messages": ">=18" + } + }, + "node_modules/@cucumber/message-streams": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-4.0.1.tgz", + "integrity": "sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA==", + "dev": true, + "peerDependencies": { + "@cucumber/messages": ">=17.1.1" + } + }, + "node_modules/@cucumber/messages": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.0.0.tgz", + "integrity": "sha512-5kf5jTQZf3qI4vr7r/zs2UNg8e/v5wW698IAT+4htMQqBxZT5L0WSgbjz+2vXypHFwcEVIEfiwBaKG5ShthwFg==", + "dev": true, + "dependencies": { + "@types/uuid": "8.3.4", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/messages/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@cucumber/pretty-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/pretty-formatter/-/pretty-formatter-1.0.0.tgz", + "integrity": "sha512-wcnIMN94HyaHGsfq72dgCvr1d8q6VGH4Y6Gl5weJ2TNZw1qn2UY85Iki4c9VdaLUONYnyYH3+178YB+9RFe/Hw==", + "dev": true, + "dependencies": { + "ansi-styles": "^5.0.0", + "cli-table3": "^0.6.0", + "figures": "^3.2.0", + "ts-dedent": "^2.0.0" + }, + "peerDependencies": { + "@cucumber/cucumber": ">=7.0.0", + "@cucumber/messages": "*" + } + }, + "node_modules/@cucumber/pretty-formatter/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@cucumber/tag-expressions": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-4.1.0.tgz", + "integrity": "sha512-chTnjxV3vryL75N90wJIMdMafXmZoO2JgNJLYpsfcALL2/IQrRiny3vM9DgD5RDCSt1LNloMtb7rGey9YWxCsA==", + "dev": true + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.11.0", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz", + "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==", + "dev": true, + "dependencies": { + "@grpc/proto-loader": "^0.6.4", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.13.tgz", + "integrity": "sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==", + "dev": true, + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.11.3", + "yargs": "^16.2.0" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.5.6", + "integrity": "sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ==", + "dev": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "protobufjs": "^6.8.6" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.0", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", + "dev": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", + "dev": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "dev": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", + "dev": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", + "dev": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", + "dev": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", + "dev": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", + "dev": true + }, + "node_modules/@teppeis/multimaps": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-2.0.0.tgz", + "integrity": "sha512-TL1adzq1HdxUf9WYduLcQ/DNGYiz71U31QRgbnr0Ef1cPyOUOsBojxHVWpFeOSUucB6Lrs0LxFRA14ntgtkc9w==", + "dev": true, + "engines": { + "node": ">=10.17" + } + }, + "node_modules/@types/lodash": { + "version": "4.14.182", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", + "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.1", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.10.3", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "node_modules/acorn": { + "version": "7.4.1", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/archiver": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", + "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.3", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.0.0", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.7", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/assertion-error-formatter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz", + "integrity": "sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ==", + "dev": true, + "dependencies": { + "diff": "^4.0.1", + "pad-right": "^0.2.2", + "repeat-string": "^1.6.1" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/axios": { + "version": "0.21.4", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz", + "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001359", + "electron-to-chromium": "^1.4.172", + "node-releases": "^2.0.5", + "update-browserslist-db": "^1.0.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001363", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz", + "integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "dev": true + }, + "node_modules/cli-table3": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/commander": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz", + "integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/compress-commons": { + "version": "4.1.1", + "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.2", + "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csv-parser": { + "version": "3.0.0", + "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "csv-parser": "bin/csv-parser" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cucumber-html-reporter": { + "version": "5.5.0", + "integrity": "sha512-kF7vIwvTe7we7Wp/5uNZVZk+Ryozb688LpNvCNhou6N0RmLYPqaoV2aiN8GIB94JUBpribtlq6kDkEUHwxBVeQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "find": "^0.3.0", + "fs-extra": "^8.1.0", + "js-base64": "^2.3.2", + "jsonfile": "^5.0.0", + "lodash": "^4.17.11", + "node-emoji": "^1.10.0", + "open": "^6.4.0", + "uuid": "^3.3.3" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dateformat": { + "version": "3.0.3", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.2", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/duration": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", + "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.46" + } + }, + "node_modules/durations": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/durations/-/durations-3.4.2.tgz", + "integrity": "sha512-V/lf7y33dGaypZZetVI1eu7BmvkbC4dItq12OElLRpKuaU5JxQstV2zHwLv8P7cNbQ+KL1WD80zMCTx5dNC4dg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.182", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.182.tgz", + "integrity": "sha512-OpEjTADzGoXABjqobGhpy0D2YsTncAax7IkER68ycc4adaq0dqEG9//9aenKPy7BGA90bqQdLac0dPp6uMkcSg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/es5-ext": { + "version": "0.10.61", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", + "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "3.4.1", + "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "prettier": ">=1.13.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-rule-composer": { + "version": "0.3.0", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.11.0", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "7.3.5", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.2.0", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "dev": true, + "dependencies": { + "type": "^2.5.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", + "dev": true + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/ffi-napi": { + "version": "4.0.3", + "integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "debug": "^4.1.1", + "get-uv-event-loop-napi-h": "^1.0.5", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1", + "ref-napi": "^2.0.1 || ^3.0.2", + "ref-struct-di": "^1.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find": { + "version": "0.3.0", + "integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==", + "dev": true, + "dependencies": { + "traverse-chain": "~0.1.0" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.2", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-extra/node_modules/jsonfile": { + "version": "4.0.0", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-symbol-from-current-process-h": { + "version": "1.0.2", + "integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw==", + "dev": true + }, + "node_modules/get-uv-event-loop-napi-h": { + "version": "1.0.6", + "integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==", + "dev": true, + "dependencies": { + "get-symbol-from-current-process-h": "^1.0.1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "node_modules/grpc-promise": { + "version": "1.4.0", + "integrity": "sha512-4BBXHXb5OjjBh7luylu8vFqL6H6aPn/LeqpQaSBeRzO/Xv95wHW/WkU9TJRqaCTMZ5wq9jTSvlJWp0vRJy1pVA==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", + "integrity": "sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky": { + "version": "6.0.0", + "integrity": "sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "4.0.6", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-base64": { + "version": "2.6.4", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "5.0.0", + "integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==", + "dev": true, + "dependencies": { + "universalify": "^0.1.2" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/knuth-shuffle-seeded": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", + "integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==", + "dev": true, + "dependencies": { + "seed-random": "~2.2.0" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.7", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + }, + "node_modules/long": { + "version": "4.0.0", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + }, + "node_modules/loupe": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.1.tgz", + "integrity": "sha512-EN1D3jyVmaX4tnajVlfbREU4axL647hLec1h/PXAb8CPDMJiYitcWF2UeLVNttRqaIqQs4x+mRvXf+d+TlDrCA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.3.0", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", + "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "6.4.0", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pad-right": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", + "integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==", + "dev": true, + "dependencies": { + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/property-expr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", + "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==", + "dev": true + }, + "node_modules/protobufjs": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.1", + "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==", + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/ref-napi": { + "version": "3.0.3", + "integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "debug": "^4.1.1", + "get-symbol-from-current-process-h": "^1.0.2", + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.1" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/ref-struct-di": { + "version": "1.1.1", + "integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==", + "dev": true, + "dependencies": { + "debug": "^3.1.0" + } + }, + "node_modules/ref-struct-di/node_modules/debug": { + "version": "3.2.7", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "dev": true, + "dependencies": { + "regexp-tree": "^0.1.11" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", + "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-2.0.0.tgz", + "integrity": "sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==", + "dev": true + }, + "node_modules/semver": { + "version": "6.3.0", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sha3": { + "version": "2.1.4", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", + "dependencies": { + "buffer": "6.0.3" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/stack-chain": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-2.0.0.tgz", + "integrity": "sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/table": { + "version": "6.7.2", + "integrity": "sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.6.3", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tari_crypto": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/tari_crypto/-/tari_crypto-0.14.0.tgz", + "integrity": "sha512-vwwI3SJ08Dcv4tGL+YVGE5LDmWwJnaRjZV2UNP0XoKKgZX88oMyKUup7RyQTpIaGOBTaqblI0uK79w3U+62ZRQ==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "dev": true + }, + "node_modules/traverse-chain": { + "version": "0.1.0", + "integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=", + "dev": true + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", + "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "node_modules/util-arity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", + "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "3.4.0", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/wallet-grpc-client": { + "resolved": "../clients/wallet_grpc_client", + "link": true + }, + "node_modules/which": { + "version": "2.0.2", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/zip-stream": { + "version": "4.1.0", + "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", + "dependencies": { + "archiver-utils": "^2.1.0", + "compress-commons": "^4.1.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + } + }, "dependencies": { "@ampproject/remapping": { "version": "2.2.0", @@ -205,7 +4127,6 @@ }, "@babel/helper-validator-identifier": { "version": "7.15.7", - "resolved": false, "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, @@ -501,13 +4422,15 @@ "version": "19.2.0", "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-19.2.0.tgz", "integrity": "sha512-qGms4588jmVF/G3fTbgZvxn6OQw9GaTFV007nZZ9/10M9DfrgRqjFjVxVI9TPV63xOLPicEVoqsKZtcECbdMSA==", - "dev": true + "dev": true, + "requires": {} }, "@cucumber/message-streams": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-4.0.1.tgz", "integrity": "sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA==", - "dev": true + "dev": true, + "requires": {} }, "@cucumber/messages": { "version": "19.0.0", @@ -557,7 +4480,6 @@ }, "@eslint/eslintrc": { "version": "0.4.3", - "resolved": false, "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, "requires": { @@ -574,7 +4496,6 @@ "dependencies": { "globals": { "version": "13.11.0", - "resolved": false, "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", "dev": true, "requires": { @@ -583,7 +4504,6 @@ }, "type-fest": { "version": "0.20.2", - "resolved": false, "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true } @@ -616,7 +4536,6 @@ }, "@grpc/proto-loader": { "version": "0.5.6", - "resolved": false, "integrity": "sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ==", "dev": true, "requires": { @@ -626,7 +4545,6 @@ }, "@humanwhocodes/config-array": { "version": "0.5.0", - "resolved": false, "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", "dev": true, "requires": { @@ -637,7 +4555,6 @@ }, "@humanwhocodes/object-schema": { "version": "1.2.0", - "resolved": false, "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, @@ -681,31 +4598,26 @@ }, "@protobufjs/aspromise": { "version": "1.1.2", - "resolved": false, "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", "dev": true }, "@protobufjs/base64": { "version": "1.1.2", - "resolved": false, "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "dev": true }, "@protobufjs/codegen": { "version": "2.0.4", - "resolved": false, "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", "dev": true }, "@protobufjs/eventemitter": { "version": "1.1.0", - "resolved": false, "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", "dev": true }, "@protobufjs/fetch": { "version": "1.1.0", - "resolved": false, "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", "dev": true, "requires": { @@ -715,31 +4627,26 @@ }, "@protobufjs/float": { "version": "1.0.2", - "resolved": false, "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", "dev": true }, "@protobufjs/inquire": { "version": "1.1.0", - "resolved": false, "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", "dev": true }, "@protobufjs/path": { "version": "1.1.2", - "resolved": false, "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", "dev": true }, "@protobufjs/pool": { "version": "1.1.0", - "resolved": false, "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", "dev": true }, "@protobufjs/utf8": { "version": "1.1.0", - "resolved": false, "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", "dev": true }, @@ -757,13 +4664,11 @@ }, "@types/long": { "version": "4.0.1", - "resolved": false, "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==", "dev": true }, "@types/node": { "version": "16.10.3", - "resolved": false, "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", "dev": true }, @@ -775,19 +4680,17 @@ }, "acorn": { "version": "7.4.1", - "resolved": false, "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, "acorn-jsx": { "version": "5.3.2", - "resolved": false, "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "ajv": { "version": "6.12.6", - "resolved": false, "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { @@ -799,19 +4702,16 @@ }, "ansi-colors": { "version": "4.1.1", - "resolved": false, "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { "version": "5.0.1", - "resolved": false, "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { "version": "3.2.1", - "resolved": false, "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { @@ -840,7 +4740,6 @@ }, "archiver-utils": { "version": "2.1.0", - "resolved": false, "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "requires": { "glob": "^7.1.4", @@ -857,7 +4756,6 @@ "dependencies": { "readable-stream": { "version": "2.3.7", - "resolved": false, "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", @@ -873,7 +4771,6 @@ }, "argparse": { "version": "1.0.10", - "resolved": false, "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { @@ -888,7 +4785,6 @@ }, "assertion-error": { "version": "1.1.0", - "resolved": false, "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, @@ -905,7 +4801,6 @@ }, "astral-regex": { "version": "2.0.0", - "resolved": false, "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, @@ -916,7 +4811,6 @@ }, "axios": { "version": "0.21.4", - "resolved": false, "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "requires": { "follow-redirects": "^1.14.0" @@ -924,17 +4818,14 @@ }, "balanced-match": { "version": "1.0.2", - "resolved": false, "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", - "resolved": false, "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "bl": { "version": "4.1.0", - "resolved": false, "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "requires": { "buffer": "^5.5.0", @@ -944,7 +4835,6 @@ "dependencies": { "buffer": { "version": "5.7.1", - "resolved": false, "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "requires": { "base64-js": "^1.3.1", @@ -961,7 +4851,6 @@ }, "brace-expansion": { "version": "1.1.11", - "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "^1.0.0", @@ -982,7 +4871,6 @@ }, "buffer": { "version": "6.0.3", - "resolved": false, "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "requires": { "base64-js": "^1.3.1", @@ -1002,7 +4890,6 @@ }, "callsites": { "version": "3.1.0", - "resolved": false, "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, @@ -1040,7 +4927,6 @@ }, "chalk": { "version": "2.4.2", - "resolved": false, "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { @@ -1051,7 +4937,6 @@ }, "check-error": { "version": "1.0.2", - "resolved": false, "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, @@ -1084,7 +4969,6 @@ }, "clone-deep": { "version": "4.0.1", - "resolved": false, "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "requires": { "is-plain-object": "^2.0.4", @@ -1094,7 +4978,6 @@ }, "color-convert": { "version": "1.9.3", - "resolved": false, "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { @@ -1103,7 +4986,6 @@ }, "color-name": { "version": "1.1.3", - "resolved": false, "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, @@ -1115,7 +4997,6 @@ }, "compress-commons": { "version": "4.1.1", - "resolved": false, "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", "requires": { "buffer-crc32": "^0.2.13", @@ -1126,12 +5007,10 @@ }, "concat-map": { "version": "0.0.1", - "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "convert-source-map": { "version": "1.8.0", - "resolved": false, "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "requires": { @@ -1150,7 +5029,6 @@ }, "crc32-stream": { "version": "4.0.2", - "resolved": false, "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", "requires": { "crc-32": "^1.2.0", @@ -1159,7 +5037,6 @@ }, "cross-spawn": { "version": "7.0.3", - "resolved": false, "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { @@ -1170,7 +5047,6 @@ }, "csv-parser": { "version": "3.0.0", - "resolved": false, "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", "requires": { "minimist": "^1.2.0" @@ -1178,7 +5054,6 @@ }, "cucumber-html-reporter": { "version": "5.5.0", - "resolved": false, "integrity": "sha512-kF7vIwvTe7we7Wp/5uNZVZk+Ryozb688LpNvCNhou6N0RmLYPqaoV2aiN8GIB94JUBpribtlq6kDkEUHwxBVeQ==", "dev": true, "requires": { @@ -1205,12 +5080,10 @@ }, "dateformat": { "version": "3.0.3", - "resolved": false, "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" }, "debug": { "version": "4.3.2", - "resolved": false, "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { @@ -1219,7 +5092,6 @@ }, "deep-eql": { "version": "3.0.1", - "resolved": false, "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { @@ -1228,7 +5100,6 @@ }, "deep-is": { "version": "0.1.4", - "resolved": false, "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, @@ -1240,7 +5111,6 @@ }, "doctrine": { "version": "3.0.0", - "resolved": false, "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { @@ -1271,13 +5141,11 @@ }, "emoji-regex": { "version": "8.0.0", - "resolved": false, "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "end-of-stream": { "version": "1.4.4", - "resolved": false, "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "requires": { "once": "^1.4.0" @@ -1285,7 +5153,6 @@ }, "enquirer": { "version": "2.3.6", - "resolved": false, "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "requires": { @@ -1326,19 +5193,16 @@ }, "escalade": { "version": "3.1.1", - "resolved": false, "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", - "resolved": false, "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "eslint": { "version": "7.32.0", - "resolved": false, "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "requires": { @@ -1386,7 +5250,6 @@ "dependencies": { "@babel/code-frame": { "version": "7.12.11", - "resolved": false, "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "requires": { @@ -1395,7 +5258,6 @@ }, "ansi-styles": { "version": "4.3.0", - "resolved": false, "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { @@ -1404,7 +5266,6 @@ }, "chalk": { "version": "4.1.2", - "resolved": false, "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { @@ -1414,7 +5275,6 @@ }, "color-convert": { "version": "2.0.1", - "resolved": false, "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { @@ -1423,19 +5283,16 @@ }, "color-name": { "version": "1.1.4", - "resolved": false, "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "escape-string-regexp": { "version": "4.0.0", - "resolved": false, "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "globals": { "version": "13.11.0", - "resolved": false, "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", "dev": true, "requires": { @@ -1444,13 +5301,11 @@ }, "has-flag": { "version": "4.0.0", - "resolved": false, "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "semver": { "version": "7.3.5", - "resolved": false, "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { @@ -1459,7 +5314,6 @@ }, "supports-color": { "version": "7.2.0", - "resolved": false, "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { @@ -1468,7 +5322,6 @@ }, "type-fest": { "version": "0.20.2", - "resolved": false, "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true } @@ -1478,11 +5331,11 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-prettier": { "version": "3.4.1", - "resolved": false, "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", "dev": true, "requires": { @@ -1491,13 +5344,11 @@ }, "eslint-rule-composer": { "version": "0.3.0", - "resolved": false, "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", "dev": true }, "eslint-scope": { "version": "5.1.1", - "resolved": false, "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { @@ -1507,7 +5358,6 @@ }, "eslint-utils": { "version": "2.1.0", - "resolved": false, "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "requires": { @@ -1516,7 +5366,6 @@ "dependencies": { "eslint-visitor-keys": { "version": "1.3.0", - "resolved": false, "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true } @@ -1524,13 +5373,11 @@ }, "eslint-visitor-keys": { "version": "2.1.0", - "resolved": false, "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true }, "espree": { "version": "7.3.1", - "resolved": false, "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, "requires": { @@ -1541,7 +5388,6 @@ "dependencies": { "eslint-visitor-keys": { "version": "1.3.0", - "resolved": false, "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true } @@ -1549,13 +5395,11 @@ }, "esprima": { "version": "4.0.1", - "resolved": false, "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "esquery": { "version": "1.4.0", - "resolved": false, "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { @@ -1564,7 +5408,6 @@ "dependencies": { "estraverse": { "version": "5.2.0", - "resolved": false, "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", "dev": true } @@ -1572,7 +5415,6 @@ }, "esrecurse": { "version": "4.3.0", - "resolved": false, "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { @@ -1581,7 +5423,6 @@ "dependencies": { "estraverse": { "version": "5.2.0", - "resolved": false, "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", "dev": true } @@ -1589,13 +5430,11 @@ }, "estraverse": { "version": "4.3.0", - "resolved": false, "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "esutils": { "version": "2.0.3", - "resolved": false, "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, @@ -1624,31 +5463,26 @@ }, "fast-deep-equal": { "version": "3.1.3", - "resolved": false, "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-diff": { "version": "1.2.0", - "resolved": false, "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, "fast-json-stable-stringify": { "version": "2.1.0", - "resolved": false, "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-levenshtein": { "version": "2.0.6", - "resolved": false, "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, "ffi-napi": { "version": "4.0.3", - "resolved": false, "integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==", "dev": true, "requires": { @@ -1671,7 +5505,6 @@ }, "file-entry-cache": { "version": "6.0.1", - "resolved": false, "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { @@ -1680,7 +5513,6 @@ }, "find": { "version": "0.3.0", - "resolved": false, "integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==", "dev": true, "requires": { @@ -1689,7 +5521,6 @@ }, "flat-cache": { "version": "3.0.4", - "resolved": false, "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { @@ -1699,7 +5530,6 @@ }, "flatted": { "version": "3.2.2", - "resolved": false, "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, @@ -1710,12 +5540,10 @@ }, "fs-constants": { "version": "1.0.0", - "resolved": false, "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs-extra": { "version": "8.1.0", - "resolved": false, "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "requires": { @@ -1726,7 +5554,6 @@ "dependencies": { "jsonfile": { "version": "4.0.0", - "resolved": false, "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { @@ -1742,19 +5569,16 @@ }, "function-bind": { "version": "1.1.1", - "resolved": false, "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "functional-red-black-tree": { "version": "1.0.1", - "resolved": false, "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, "gensync": { "version": "1.0.0-beta.2", - "resolved": false, "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, @@ -1766,19 +5590,16 @@ }, "get-func-name": { "version": "2.0.0", - "resolved": false, "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, "get-symbol-from-current-process-h": { "version": "1.0.2", - "resolved": false, "integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw==", "dev": true }, "get-uv-event-loop-napi-h": { "version": "1.0.6", - "resolved": false, "integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==", "dev": true, "requires": { @@ -1810,7 +5631,6 @@ }, "glob-parent": { "version": "5.1.2", - "resolved": false, "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { @@ -1819,24 +5639,20 @@ }, "globals": { "version": "11.12.0", - "resolved": false, "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "graceful-fs": { "version": "4.2.8", - "resolved": false, "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, "grpc-promise": { "version": "1.4.0", - "resolved": false, "integrity": "sha512-4BBXHXb5OjjBh7luylu8vFqL6H6aPn/LeqpQaSBeRzO/Xv95wHW/WkU9TJRqaCTMZ5wq9jTSvlJWp0vRJy1pVA==", "dev": true }, "has": { "version": "1.0.3", - "resolved": false, "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { @@ -1862,30 +5678,25 @@ }, "has-flag": { "version": "3.0.0", - "resolved": false, "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "husky": { "version": "6.0.0", - "resolved": false, "integrity": "sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ==", "dev": true }, "ieee754": { "version": "1.2.1", - "resolved": false, "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { "version": "4.0.6", - "resolved": false, "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "import-fresh": { "version": "3.3.0", - "resolved": false, "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { @@ -1895,7 +5706,6 @@ }, "imurmurhash": { "version": "0.1.4", - "resolved": false, "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, @@ -1916,7 +5726,6 @@ }, "inherits": { "version": "2.0.4", - "resolved": false, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-core-module": { @@ -1930,7 +5739,6 @@ }, "is-extglob": { "version": "2.1.1", - "resolved": false, "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, @@ -1942,7 +5750,6 @@ }, "is-glob": { "version": "4.0.3", - "resolved": false, "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { @@ -1951,7 +5758,6 @@ }, "is-plain-object": { "version": "2.0.4", - "resolved": false, "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "requires": { "isobject": "^3.0.1" @@ -1965,7 +5771,6 @@ }, "is-wsl": { "version": "1.1.0", - "resolved": false, "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, @@ -1976,30 +5781,25 @@ }, "isexe": { "version": "2.0.0", - "resolved": false, "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "isobject": { "version": "3.0.1", - "resolved": false, "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "js-base64": { "version": "2.6.4", - "resolved": false, "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", "dev": true }, "js-tokens": { "version": "4.0.0", - "resolved": false, "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { "version": "3.14.1", - "resolved": false, "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { @@ -2009,19 +5809,16 @@ }, "jsesc": { "version": "2.5.2", - "resolved": false, "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, "json-schema-traverse": { "version": "0.4.1", - "resolved": false, "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": false, "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, @@ -2032,7 +5829,6 @@ }, "jsonfile": { "version": "5.0.0", - "resolved": false, "integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==", "dev": true, "requires": { @@ -2042,7 +5838,6 @@ }, "kind-of": { "version": "6.0.3", - "resolved": false, "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "knuth-shuffle-seeded": { @@ -2064,7 +5859,6 @@ "dependencies": { "readable-stream": { "version": "2.3.7", - "resolved": false, "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", @@ -2080,7 +5874,6 @@ }, "levn": { "version": "0.4.1", - "resolved": false, "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { @@ -2090,7 +5883,6 @@ }, "lodash": { "version": "4.17.21", - "resolved": false, "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, @@ -2102,13 +5894,11 @@ }, "lodash.camelcase": { "version": "4.3.0", - "resolved": false, "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, "lodash.clonedeep": { "version": "4.5.0", - "resolved": false, "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, @@ -2134,7 +5924,6 @@ }, "lodash.merge": { "version": "4.6.2", - "resolved": false, "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, @@ -2146,7 +5935,6 @@ }, "lodash.truncate": { "version": "4.4.2", - "resolved": false, "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, @@ -2157,7 +5945,6 @@ }, "long": { "version": "4.0.0", - "resolved": false, "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", "dev": true }, @@ -2181,7 +5968,6 @@ }, "lru-cache": { "version": "6.0.0", - "resolved": false, "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { @@ -2190,7 +5976,6 @@ }, "minimatch": { "version": "3.0.4", - "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "^1.1.7" @@ -2203,7 +5988,6 @@ }, "ms": { "version": "2.1.2", - "resolved": false, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, @@ -2226,7 +6010,6 @@ }, "natural-compare": { "version": "1.4.0", - "resolved": false, "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, @@ -2248,13 +6031,11 @@ }, "node-addon-api": { "version": "3.2.1", - "resolved": false, "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true }, "node-emoji": { "version": "1.11.0", - "resolved": false, "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "dev": true, "requires": { @@ -2263,7 +6044,6 @@ }, "node-gyp-build": { "version": "4.3.0", - "resolved": false, "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", "dev": true }, @@ -2275,7 +6055,6 @@ }, "normalize-path": { "version": "3.0.0", - "resolved": false, "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "object-assign": { @@ -2294,7 +6073,6 @@ }, "open": { "version": "6.4.0", - "resolved": false, "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", "dev": true, "requires": { @@ -2303,7 +6081,6 @@ }, "optionator": { "version": "0.9.1", - "resolved": false, "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "requires": { @@ -2326,7 +6103,6 @@ }, "parent-module": { "version": "1.0.1", - "resolved": false, "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { @@ -2340,19 +6116,16 @@ }, "path-key": { "version": "3.1.1", - "resolved": false, "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { "version": "1.0.7", - "resolved": false, "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "pathval": { "version": "1.1.1", - "resolved": false, "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, @@ -2364,7 +6137,6 @@ }, "prelude-ls": { "version": "1.2.1", - "resolved": false, "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, @@ -2376,7 +6148,6 @@ }, "prettier-linter-helpers": { "version": "1.0.0", - "resolved": false, "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "requires": { @@ -2385,12 +6156,10 @@ }, "process-nextick-args": { "version": "2.0.1", - "resolved": false, "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "progress": { "version": "2.0.3", - "resolved": false, "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, @@ -2423,13 +6192,11 @@ }, "punycode": { "version": "2.1.1", - "resolved": false, "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "readable-stream": { "version": "3.6.0", - "resolved": false, "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "requires": { "inherits": "^2.0.3", @@ -2439,7 +6206,6 @@ }, "readdir-glob": { "version": "1.1.1", - "resolved": false, "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==", "requires": { "minimatch": "^3.0.4" @@ -2447,7 +6213,6 @@ }, "ref-napi": { "version": "3.0.3", - "resolved": false, "integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==", "dev": true, "requires": { @@ -2459,7 +6224,6 @@ }, "ref-struct-di": { "version": "1.1.1", - "resolved": false, "integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==", "dev": true, "requires": { @@ -2468,7 +6232,6 @@ "dependencies": { "debug": { "version": "3.2.7", - "resolved": false, "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { @@ -2506,7 +6269,6 @@ }, "regexpp": { "version": "3.2.0", - "resolved": false, "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, @@ -2524,7 +6286,6 @@ }, "require-from-string": { "version": "2.0.2", - "resolved": false, "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, @@ -2541,7 +6302,6 @@ }, "resolve-from": { "version": "4.0.0", - "resolved": false, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, @@ -2564,7 +6324,6 @@ }, "rimraf": { "version": "3.0.2", - "resolved": false, "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { @@ -2573,7 +6332,6 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "seed-random": { @@ -2584,13 +6342,11 @@ }, "semver": { "version": "6.3.0", - "resolved": false, "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "sha3": { "version": "2.1.4", - "resolved": false, "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", "requires": { "buffer": "6.0.3" @@ -2598,7 +6354,6 @@ }, "shallow-clone": { "version": "3.0.1", - "resolved": false, "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "requires": { "kind-of": "^6.0.2" @@ -2606,7 +6361,6 @@ }, "shebang-command": { "version": "2.0.0", - "resolved": false, "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { @@ -2615,13 +6369,11 @@ }, "shebang-regex": { "version": "3.0.0", - "resolved": false, "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "slice-ansi": { "version": "4.0.0", - "resolved": false, "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { @@ -2632,7 +6384,6 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", - "resolved": false, "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { @@ -2641,7 +6392,6 @@ }, "color-convert": { "version": "2.0.1", - "resolved": false, "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { @@ -2650,7 +6400,6 @@ }, "color-name": { "version": "1.1.4", - "resolved": false, "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true } @@ -2674,7 +6423,6 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": false, "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -2684,6 +6432,14 @@ "integrity": "sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-argv": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", @@ -2701,17 +6457,8 @@ "strip-ansi": "^6.0.1" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "6.0.1", - "resolved": false, "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { @@ -2720,13 +6467,11 @@ }, "strip-json-comments": { "version": "3.1.1", - "resolved": false, "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { "version": "5.5.0", - "resolved": false, "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { @@ -2741,7 +6486,6 @@ }, "table": { "version": "6.7.2", - "resolved": false, "integrity": "sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==", "dev": true, "requires": { @@ -2755,7 +6499,6 @@ "dependencies": { "ajv": { "version": "8.6.3", - "resolved": false, "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", "dev": true, "requires": { @@ -2767,7 +6510,6 @@ }, "json-schema-traverse": { "version": "1.0.0", - "resolved": false, "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true } @@ -2775,7 +6517,6 @@ }, "tar-stream": { "version": "2.2.0", - "resolved": false, "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "requires": { "bl": "^4.0.3", @@ -2792,7 +6533,6 @@ }, "text-table": { "version": "0.2.0", - "resolved": false, "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, @@ -2837,7 +6577,6 @@ }, "traverse-chain": { "version": "0.1.0", - "resolved": false, "integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=", "dev": true }, @@ -2861,7 +6600,6 @@ }, "type-check": { "version": "0.4.0", - "resolved": false, "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { @@ -2870,13 +6608,11 @@ }, "type-detect": { "version": "4.0.8", - "resolved": false, "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, "universalify": { "version": "0.1.2", - "resolved": false, "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, @@ -2901,7 +6637,6 @@ }, "uri-js": { "version": "4.4.1", - "resolved": false, "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { @@ -2910,7 +6645,6 @@ }, "utf8": { "version": "3.0.0", - "resolved": false, "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" }, "util-arity": { @@ -2926,13 +6660,11 @@ }, "uuid": { "version": "3.4.0", - "resolved": false, "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, "v8-compile-cache": { "version": "2.3.0", - "resolved": false, "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, @@ -3075,7 +6807,6 @@ }, "which": { "version": "2.0.2", - "resolved": false, "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { @@ -3084,7 +6815,6 @@ }, "word-wrap": { "version": "1.2.3", - "resolved": false, "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, @@ -3138,7 +6868,6 @@ }, "yallist": { "version": "4.0.0", - "resolved": false, "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, @@ -3180,7 +6909,6 @@ }, "zip-stream": { "version": "4.1.0", - "resolved": false, "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", "requires": { "archiver-utils": "^2.1.0", From 3b30fdb99af14a43eb0e9762861f5fabc2481cca Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Tue, 2 Aug 2022 12:19:26 +0200 Subject: [PATCH 4/7] fix: burned output check (#4374) Description --- fixes the burned output check. Adds in a unit test to ensure its correct How Has This Been Tested? --- Unit and integration --- base_layer/core/src/validation/helpers.rs | 56 ++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/base_layer/core/src/validation/helpers.rs b/base_layer/core/src/validation/helpers.rs index ac9c2dada6..b1eca6ea4d 100644 --- a/base_layer/core/src/validation/helpers.rs +++ b/base_layer/core/src/validation/helpers.rs @@ -280,7 +280,7 @@ pub fn check_total_burned(body: &AggregateBody) -> Result<(), ValidationError> { } } for kernel in body.kernels() { - if kernel.is_burned() && burned_outputs.remove(kernel.get_burn_commitment()?) { + if kernel.is_burned() && !burned_outputs.remove(kernel.get_burn_commitment()?) { return Err(ValidationError::InvalidBurnError( "Burned kernel does not match burned output".to_string(), )); @@ -1039,4 +1039,58 @@ mod test { unpack_enum!(TransactionError::InvalidCoinbase = err); } } + + use crate::{covenants::Covenant, transactions::transaction_components::KernelFeatures}; + + #[test] + fn check_burned_succeeds_for_valid_outputs() { + let mut kernel1 = test_helpers::create_test_kernel(0.into(), 0); + let mut kernel2 = test_helpers::create_test_kernel(0.into(), 0); + + let (output1, _, _) = test_helpers::create_utxo( + 100.into(), + &CryptoFactories::default(), + &OutputFeatures::create_burn_output(), + &TariScript::default(), + &Covenant::default(), + 0.into(), + ); + let (output2, _, _) = test_helpers::create_utxo( + 101.into(), + &CryptoFactories::default(), + &OutputFeatures::create_burn_output(), + &TariScript::default(), + &Covenant::default(), + 0.into(), + ); + let (output3, _, _) = test_helpers::create_utxo( + 102.into(), + &CryptoFactories::default(), + &OutputFeatures::create_burn_output(), + &TariScript::default(), + &Covenant::default(), + 0.into(), + ); + + kernel1.features = KernelFeatures::create_burn(); + kernel1.burn_commitment = Some(output1.commitment.clone()); + kernel2.features = KernelFeatures::create_burn(); + kernel2.burn_commitment = Some(output2.commitment.clone()); + let kernel3 = kernel1.clone(); + + let mut body = AggregateBody::new(Vec::new(), vec![output1.clone(), output2.clone()], vec![ + kernel1.clone(), + kernel2.clone(), + ]); + assert!(check_total_burned(&body).is_ok()); + // lets add an extra kernel + body.add_kernels(&mut vec![kernel3]); + assert!(check_total_burned(&body).is_err()); + // lets add a kernel commitment mismatch + body.add_outputs(&mut vec![output3.clone()]); + assert!(check_total_burned(&body).is_err()); + // Lets try one with a commitment with no kernel + let body2 = AggregateBody::new(Vec::new(), vec![output1, output2, output3], vec![kernel1, kernel2]); + assert!(check_total_burned(&body2).is_err()); + } } From 1c5ec0d9dc6d9f9f1f256332be5b9e9b0b022315 Mon Sep 17 00:00:00 2001 From: jorgeantonio21 Date: Tue, 2 Aug 2022 11:24:52 +0100 Subject: [PATCH 5/7] fix: low entropy mac passphrase in cipher seed (see issue #4182) (#4296) Description --- The following is an attempt to improve `CipherSeed` mnemonic generation by tackling MAC being keyed directly with a low entropy passphrase. We use proper domain separation to attain this. Motivation and Context --- The generation of MAC, within the context of a `CipherSeed` instance, is obtained through keying a (low) entropy passphrase. In order to reduce the chances of success of an attack involving offline key pre-computation, it is desirable to hash the passphrase, before MAC keying, using proper domain separation. The current PR is an attempt in this direction. How Has This Been Tested? --- With previous unit tests. --- base_layer/key_manager/src/cipher_seed.rs | 273 +++++++++++++++--- base_layer/key_manager/src/lib.rs | 16 + base_layer/key_manager/src/wasm.rs | 6 +- .../output_manager_service_tests/service.rs | 48 +-- base_layer/wallet/tests/wallet.rs | 6 +- base_layer/wallet_ffi/src/lib.rs | 6 +- 6 files changed, 277 insertions(+), 78 deletions(-) diff --git a/base_layer/key_manager/src/cipher_seed.rs b/base_layer/key_manager/src/cipher_seed.rs index f760c4ab36..cd7b0c6816 100644 --- a/base_layer/key_manager/src/cipher_seed.rs +++ b/base_layer/key_manager/src/cipher_seed.rs @@ -26,9 +26,14 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; -use argon2::{password_hash::SaltString, Argon2, PasswordHasher}; +use argon2::{ + password_hash::{Salt, SaltString}, + Argon2, + Params, + PasswordHasher, + Version, +}; use arrayvec::ArrayVec; -use blake2::{digest::VariableOutput, VarBlake2b}; use chacha20::{ cipher::{NewCipher, StreamCipher}, ChaCha20, @@ -36,18 +41,22 @@ use chacha20::{ Nonce, }; use crc32fast::Hasher as CrcHasher; -use digest::Update; use rand::{rngs::OsRng, RngCore}; use serde::{Deserialize, Serialize}; use tari_utilities::ByteArray; use crate::{ + base_layer_key_manager_argon2_encoding, + base_layer_key_manager_chacha20_encoding, + base_layer_key_manager_mac_generation, error::KeyManagerError, mnemonic::{from_bytes, to_bytes, to_bytes_with_language, Mnemonic, MnemonicLanguage}, }; const CIPHER_SEED_VERSION: u8 = 0u8; pub const DEFAULT_CIPHER_SEED_PASSPHRASE: &str = "TARI_CIPHER_SEED"; +const ARGON2_SALT_BYTES: usize = 16; +pub const CIPHER_SEED_BIRTHDAY_BYTES: usize = 2; pub const CIPHER_SEED_ENTROPY_BYTES: usize = 16; pub const CIPHER_SEED_SALT_BYTES: usize = 5; pub const CIPHER_SEED_MAC_BYTES: usize = 5; @@ -70,7 +79,17 @@ pub const CIPHER_SEED_MAC_BYTES: usize = 5; /// checksum 4 bytes /// /// In its enciphered form we will use the MAC-the-Encrypt pattern of AE so that the birthday and entropy will be -/// encrypted. The version and salt are associated data that are included in the MAC but not encrypted. +/// encrypted. +/// +/// It is important to note that we don't generate the MAC directly from the provided low entropy passphrase. +/// Instead, the intent is to use a password-based key derivation function to generate a derived key of higher +/// effective entropy through the use of a carefully-designed function like Argon2 that's built for this purpose. +/// The corresponding derived key has output of length 64-bytes, and we use the first and last 32-bytes for MAC and +/// ChaCha20 encryption, respectively. In such way, we follow the motto of not reusing the same derived keys more +/// than once. Another key ingredient in our approach is the use of domain separation, via the current hashing API. +/// See https://github.com/tari-project/tari/issues/4182 for more information. +/// +/// The version and salt are associated data that are included in the MAC but not encrypted. /// The enciphered data will look as follows: /// version 1 byte /// ciphertext 23 bytes @@ -134,19 +153,18 @@ impl CipherSeed { let passphrase = passphrase.unwrap_or_else(|| DEFAULT_CIPHER_SEED_PASSPHRASE.to_string()); - // Construct HMAC and include the version and salt as Associated Data - let blake2_mac_hasher: VarBlake2b = VarBlake2b::new(CIPHER_SEED_MAC_BYTES) - .expect("Should be able to create blake2 hasher; will only panic if output size is 0 or greater than 64"); - let mut hmac = [0u8; CIPHER_SEED_MAC_BYTES]; - blake2_mac_hasher - .chain(plaintext.clone()) - .chain([CIPHER_SEED_VERSION]) - .chain(self.salt) - .chain(passphrase.as_bytes()) - .finalize_variable(|res| hmac.copy_from_slice(res)); + // generate the current MAC + let mut mac = Self::generate_mac( + &self.birthday.to_le_bytes(), + &self.entropy(), + &[CIPHER_SEED_VERSION], + &self.salt, + passphrase.as_str(), + )?; - plaintext.append(&mut hmac.to_vec()); + plaintext.append(&mut mac); + // apply cipher stream Self::apply_stream_cipher(&mut plaintext, &passphrase, &self.salt)?; let mut final_seed = vec![CIPHER_SEED_VERSION]; @@ -193,9 +211,10 @@ impl CipherSeed { let mut enciphered_seed = body.split_off(1); let received_version = body[0]; + // apply cipher stream Self::apply_stream_cipher(&mut enciphered_seed, &passphrase, salt.as_slice())?; - let decrypted_hmac = enciphered_seed.split_off(enciphered_seed.len() - CIPHER_SEED_MAC_BYTES); + let decrypted_mac = enciphered_seed.split_off(enciphered_seed.len() - CIPHER_SEED_MAC_BYTES); let decrypted_entropy_vec: ArrayVec<_, CIPHER_SEED_ENTROPY_BYTES> = enciphered_seed.split_off(2).into_iter().collect(); @@ -203,22 +222,20 @@ impl CipherSeed { .into_inner() .map_err(|_| KeyManagerError::InvalidData)?; - let mut birthday_bytes: [u8; 2] = [0u8; 2]; + let mut birthday_bytes: [u8; CIPHER_SEED_BIRTHDAY_BYTES] = [0u8; CIPHER_SEED_BIRTHDAY_BYTES]; birthday_bytes.copy_from_slice(&enciphered_seed); let decrypted_birthday = u16::from_le_bytes(birthday_bytes); - let blake2_mac_hasher: VarBlake2b = VarBlake2b::new(CIPHER_SEED_MAC_BYTES) - .expect("Should be able to create blake2 hasher; will only panic if output size is 0 or greater than 64"); - let mut hmac = [0u8; CIPHER_SEED_MAC_BYTES]; - blake2_mac_hasher - .chain(&birthday_bytes) - .chain(&decrypted_entropy) - .chain([CIPHER_SEED_VERSION]) - .chain(salt.as_slice()) - .chain(passphrase.as_bytes()) - .finalize_variable(|res| hmac.copy_from_slice(res)); - - if decrypted_hmac != hmac.to_vec() { + // generate the MAC + let mac = Self::generate_mac( + &decrypted_birthday.to_le_bytes(), + &decrypted_entropy, + &[CIPHER_SEED_VERSION], + salt.as_slice(), + passphrase.as_str(), + )?; + + if decrypted_mac != mac { return Err(KeyManagerError::DecryptionFailed); } @@ -234,25 +251,19 @@ impl CipherSeed { } fn apply_stream_cipher(data: &mut Vec, passphrase: &str, salt: &[u8]) -> Result<(), KeyManagerError> { - let argon2 = Argon2::default(); - let blake2_nonce_hasher: VarBlake2b = VarBlake2b::new(size_of::()) - .expect("Should be able to create blake2 hasher; will only panic if output size is 0 or greater than 64"); + // encryption nonce for ChaCha20 encryption, generated as a domain separated hash of the given salt. Following + // https://libsodium.gitbook.io/doc/advanced/stream_ciphers/chacha20, as of the IEF variant, the produced encryption + // nonce should be 96-bit long + let encryption_nonce = &base_layer_key_manager_chacha20_encoding().chain(salt).finalize(); - let mut encryption_nonce = [0u8; size_of::()]; - blake2_nonce_hasher - .chain(salt) - .finalize_variable(|res| encryption_nonce.copy_from_slice(res)); - let nonce_ga = Nonce::from_slice(&encryption_nonce); + let encryption_nonce = &encryption_nonce.as_ref()[..size_of::()]; - // Create salt string stretched to the chacha nonce size, we only have space for 5 bytes of salt in the seed but - // will use key stretching to produce a longer nonce for the passphrase hash and the encryption nonce. - let salt_b64 = SaltString::b64_encode(&encryption_nonce)?; + let nonce_ga = Nonce::from_slice(encryption_nonce); - let derived_encryption_key = argon2 - .hash_password_simple(passphrase.as_bytes(), salt_b64.as_str())? - .hash - .ok_or_else(|| KeyManagerError::CryptographicError("Problem generating encryption key hash".to_string()))?; - let key = Key::from_slice(derived_encryption_key.as_bytes()); + // we take the last 32 bytes of the generated derived encryption key for ChaCha20 cipher, see documentation + let derived_encryption_key = Self::generate_domain_separated_passphrase_hash(passphrase, salt)?[32..].to_vec(); + + let key = Key::from_slice(derived_encryption_key.as_slice()); let mut cipher = ChaCha20::new(key, nonce_ga); cipher.apply_keystream(data.as_mut_slice()); @@ -268,6 +279,90 @@ impl CipherSeed { } } +impl CipherSeed { + fn generate_mac( + birthday: &[u8], + entropy: &[u8], + cipher_seed_version: &[u8], + salt: &[u8], + passphrase: &str, + ) -> Result, KeyManagerError> { + // birthday should be 2 bytes long + if birthday.len() != CIPHER_SEED_BIRTHDAY_BYTES { + return Err(KeyManagerError::InvalidData); + } + + // entropy should be 16 bytes long + if entropy.len() != CIPHER_SEED_ENTROPY_BYTES { + return Err(KeyManagerError::InvalidData); + } + + // cipher_seed_version should be 1 byte long + if cipher_seed_version.len() != 1 { + return Err(KeyManagerError::InvalidData); + } + + // salt should be 5 bytes long + if salt.len() != CIPHER_SEED_SALT_BYTES { + return Err(KeyManagerError::InvalidData); + } + + // we take the first 32 bytes of the generated derived encryption key for MAC generation, see documentation + let passphrase_key = Self::generate_domain_separated_passphrase_hash(passphrase, salt)?[..32].to_vec(); + + Ok(base_layer_key_manager_mac_generation() + .chain(birthday) + .chain(entropy) + .chain(cipher_seed_version) + .chain(salt) + .chain(passphrase_key.as_slice()) + .finalize() + .as_ref()[..CIPHER_SEED_MAC_BYTES] + .to_vec()) + } + + fn generate_domain_separated_passphrase_hash(passphrase: &str, salt: &[u8]) -> Result, KeyManagerError> { + let argon2 = Argon2::default(); + + // we produce a domain separated hash of the given salt, for Argon2 encryption use. As suggested in + // https://en.wikipedia.org/wiki/Argon2, we shall use a 16-byte length hash salt + let argon2_salt = base_layer_key_manager_argon2_encoding().chain(salt).finalize(); + let argon2_salt = &argon2_salt.as_ref()[..ARGON2_SALT_BYTES]; + + // produce a base64 salt string + let argon2_salt = SaltString::b64_encode(argon2_salt)?; + + // to generate two 32-byte keys, we produce a 64-byte argon2 output, as the default output size + // for argon is 32, we have to update its parameters accordingly + + // the following choice of parameters is based on + // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id + let params = Params { + m_cost: 37 * 1024, // m-cost should be 37 Mib = 37 * 1024 Kib + t_cost: 1, // t-cost + p_cost: 1, // p-cost + output_size: 64, // 64 bytes output size, + version: Version::V0x13, // version + }; + + // Argon2id algorithm: https://docs.rs/argon2/0.2.4/argon2/enum.Algorithm.html#variant.Argon2id + let algorithm = argon2::Algorithm::Argon2id; + + // generate the given derived encryption key + let derived_encryption_key = argon2 + .hash_password( + passphrase.as_bytes(), + Some(algorithm.ident()), + params, + Salt::try_from(argon2_salt.as_str())?, + )? + .hash + .ok_or_else(|| KeyManagerError::CryptographicError("Problem generating encryption key hash".to_string()))?; + + Ok(derived_encryption_key.as_bytes().into()) + } +} + impl Drop for CipherSeed { fn drop(&mut self) { use clear_on_drop::clear::Clear; @@ -311,6 +406,8 @@ impl Mnemonic for CipherSeed { #[cfg(test)] mod test { + use crc32fast::Hasher as CrcHasher; + use crate::{ cipher_seed::CipherSeed, error::KeyManagerError, @@ -325,6 +422,7 @@ mod test { let deciphered_seed = CipherSeed::from_enciphered_bytes(&enciphered_seed, Some("Passphrase".to_string())).unwrap(); + assert_eq!(seed, deciphered_seed); match CipherSeed::from_enciphered_bytes(&enciphered_seed, Some("WrongPassphrase".to_string())) { @@ -339,7 +437,9 @@ mod test { _ => panic!("Version should not match"), } + // recover correct version enciphered_seed[0] = 0; + // Prevent the 1 our 256 chances that it was already a zero if enciphered_seed[1] == 0 { enciphered_seed[1] = 1; @@ -350,6 +450,89 @@ mod test { Err(KeyManagerError::CrcError) => (), _ => panic!("Crc should not match"), } + + // the following consists of three tests in which checksum is correctly changed by adversary, + // after changing either birthday, entropy and salt. The MAC decryption should fail in all these + // three scenarios. + + // change birthday + enciphered_seed[1] += 1; + + // clone the correct checksum + let checksum: Vec = enciphered_seed[(enciphered_seed.len() - 4)..].to_vec().clone(); + + // generate a new checksum that coincides with the modified value + let mut crc_hasher = CrcHasher::new(); + crc_hasher.update(&enciphered_seed[..(enciphered_seed.len() - 4)]); + + let calculated_checksum: [u8; 4] = crc_hasher.finalize().to_le_bytes(); + + // change checksum accordingly, from the viewpoint of an attacker + let n = enciphered_seed.len(); + enciphered_seed[(n - 4)..].copy_from_slice(&calculated_checksum); + + // the MAC decryption should fail in this case + match CipherSeed::from_enciphered_bytes(&enciphered_seed, Some("passphrase".to_string())) { + Err(KeyManagerError::DecryptionFailed) => (), + _ => panic!("Decryption should fail"), + } + + // recover original data + enciphered_seed[1] -= 1; + enciphered_seed[(n - 4)..].copy_from_slice(&checksum[..]); + + // change entropy and repeat test + + enciphered_seed[5] += 1; + + // clone the correct checksum + let checksum: Vec = enciphered_seed[(enciphered_seed.len() - 4)..].to_vec().clone(); + + // generate a new checksum that coincides with the modified value + let mut crc_hasher = CrcHasher::new(); + crc_hasher.update(&enciphered_seed[..(enciphered_seed.len() - 4)]); + + let calculated_checksum: [u8; 4] = crc_hasher.finalize().to_le_bytes(); + + // change checksum accordingly, from the viewpoint of an attacker + let n = enciphered_seed.len(); + enciphered_seed[(n - 4)..].copy_from_slice(&calculated_checksum); + + // the MAC decryption should fail in this case + match CipherSeed::from_enciphered_bytes(&enciphered_seed, Some("passphrase".to_string())) { + Err(KeyManagerError::DecryptionFailed) => (), + _ => panic!("Decryption should fail"), + } + + // recover original data + enciphered_seed[5] -= 1; + enciphered_seed[(n - 4)..].copy_from_slice(&checksum[..]); + + // change salt and repeat test + enciphered_seed[26] += 1; + + // clone the correct checksum + let checksum: Vec = enciphered_seed[(enciphered_seed.len() - 4)..].to_vec().clone(); + + // generate a new checksum that coincides with the modified value + let mut crc_hasher = CrcHasher::new(); + crc_hasher.update(&enciphered_seed[..(enciphered_seed.len() - 4)]); + + let calculated_checksum: [u8; 4] = crc_hasher.finalize().to_le_bytes(); + + // change checksum accordingly, from the viewpoint of an attacker + let n = enciphered_seed.len(); + enciphered_seed[(n - 4)..].copy_from_slice(&calculated_checksum); + + // the MAC decryption should fail in this case + match CipherSeed::from_enciphered_bytes(&enciphered_seed, Some("passphrase".to_string())) { + Err(KeyManagerError::DecryptionFailed) => (), + _ => panic!("Decryption should fail"), + } + + // recover original data + enciphered_seed[26] -= 1; + enciphered_seed[(n - 4)..].copy_from_slice(&checksum[..]); } #[test] diff --git a/base_layer/key_manager/src/lib.rs b/base_layer/key_manager/src/lib.rs index 8222d0ecf1..e389047889 100644 --- a/base_layer/key_manager/src/lib.rs +++ b/base_layer/key_manager/src/lib.rs @@ -1,6 +1,8 @@ // Copyright 2022 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +use tari_crypto::{hash::blake2::Blake256, hash_domain, hashing::DomainSeparatedHasher}; + pub mod cipher_seed; pub mod diacritics; pub mod error; @@ -11,3 +13,17 @@ pub mod mnemonic_wordlists; #[allow(clippy::unused_unit)] #[cfg(feature = "wasm")] pub mod wasm; + +hash_domain!(KeyManagerHashDomain, "com.tari.base_layer.key_manager"); + +pub fn base_layer_key_manager_mac_generation() -> DomainSeparatedHasher { + DomainSeparatedHasher::::new("cipher_seed.mac_generation") +} + +pub fn base_layer_key_manager_argon2_encoding() -> DomainSeparatedHasher { + DomainSeparatedHasher::::new("cipher_seed.argon2_encoding") +} + +pub fn base_layer_key_manager_chacha20_encoding() -> DomainSeparatedHasher { + DomainSeparatedHasher::::new("cipher_seed.chacha20_encoding") +} diff --git a/base_layer/key_manager/src/wasm.rs b/base_layer/key_manager/src/wasm.rs index 4d00c18301..53f20eff5c 100644 --- a/base_layer/key_manager/src/wasm.rs +++ b/base_layer/key_manager/src/wasm.rs @@ -180,8 +180,8 @@ mod test { #[wasm_bindgen_test] fn it_creates_key_manager_from() { let bytes = &[ - 0, 119, 156, 172, 30, 41, 29, 120, 191, 26, 160, 11, 200, 249, 193, 163, 245, 33, 159, 148, 127, 31, 238, - 92, 96, 103, 4, 29, 218, 204, 39, 254, 245, + 0, 39, 244, 247, 169, 80, 140, 100, 229, 187, 101, 180, 150, 85, 3, 144, 57, 152, 18, 95, 227, 235, 174, + 186, 145, 234, 30, 75, 253, 139, 131, 84, 51, ]; let seed = CipherSeed::from_enciphered_bytes(bytes, None).unwrap(); let seed = JsValue::from_serde(&seed).unwrap(); @@ -193,7 +193,7 @@ mod test { let next_key = response.key_manager.next_key().unwrap(); assert_eq!( next_key.k.to_hex(), - "5c06999ed20e18bbb76245826141f8ae8700a648d87ec4da5a2a7507ce4b5f0e".to_string() + "61ceb437f919ddc756f8fc3c572c804a51ed6dc1e4d219205a8ebd37b8b04701".to_string() ) } diff --git a/base_layer/wallet/tests/output_manager_service_tests/service.rs b/base_layer/wallet/tests/output_manager_service_tests/service.rs index 32ab13f453..1d9e4af38f 100644 --- a/base_layer/wallet/tests/output_manager_service_tests/service.rs +++ b/base_layer/wallet/tests/output_manager_service_tests/service.rs @@ -181,30 +181,30 @@ async fn setup_output_manager_service = [ - "cactus", "pool", "fuel", "skull", "chair", "casino", "season", "disorder", "flat", "crash", "wrist", - "whisper", "decorate", "narrow", "oxygen", "remember", "minor", "among", "happy", "cricket", "embark", "blue", - "ship", "sick", + "theme", "spatial", "winner", "appear", "board", "float", "tennis", "grant", "story", "film", "accuse", + "october", "corn", "seven", "brain", "typical", "fiction", "eight", "inspire", "rapid", "whisper", "title", + "piano", "crew", ] .iter() .map(|w| w.to_string()) diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 590a19cdc2..c9bd2b3cc0 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -9275,9 +9275,9 @@ mod test { let recovery_in_progress_ptr = &mut recovery_in_progress as *mut bool; let mnemonic = vec![ - "parade", "genius", "cradle", "milk", "perfect", "ride", "online", "world", "lady", "apple", "rent", - "business", "oppose", "force", "tumble", "escape", "tongue", "camera", "ceiling", "edge", "shine", - "gauge", "fossil", "orphan", + "theme", "spatial", "winner", "appear", "board", "float", "tennis", "grant", "story", "film", "accuse", + "october", "corn", "seven", "brain", "typical", "fiction", "eight", "inspire", "rapid", "whisper", + "title", "piano", "crew", ]; let seed_words = seed_words_create(); From 1cabd70f2a945fb15466cb1d2a2ed183fb021b1f Mon Sep 17 00:00:00 2001 From: Andrei Gubarev <1062334+agubarev@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:44:25 +0300 Subject: [PATCH 6/7] fix: wallet seed_words command is broken (see issue #4363) (#4370) Description --- https://github.com/tari-project/tari/issues/4363 Motivation and Context --- Seed word command is broken on GRPC ` thread 'tokio-runtime-worker' panicked at 'called `Option::unwrap()` on a `None` value', applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs:1210:50` How Has This Been Tested? --- --- .../src/grpc/wallet_grpc_server.rs | 36 ++++++++----------- base_layer/wallet/src/wallet.rs | 2 +- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index 5bedda01c5..b817610bc1 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -23,6 +23,7 @@ use std::{ convert::{TryFrom, TryInto}, fs, + path::PathBuf, }; use clap::Parser; @@ -1240,47 +1241,38 @@ impl wallet_server::Wallet for WalletGrpcServer { } } + /// Returns the contents of a seed words file, provided via CLI async fn seed_words(&self, _: Request) -> Result, Status> { let cli = Cli::parse(); - let file_path = cli.seed_words_file_name.unwrap(); - if !file_path.is_file() { - return Err(Status::not_found("file not found")); - } - - let file_name = file_path.clone().into_os_string().into_string().unwrap(); - - if file_name.is_empty() { - return Err(Status::not_found("seed_words_file_name is empty")); - } + let filepath: PathBuf = match cli.seed_words_file_name { + Some(filepath) => filepath, + None => return Err(Status::not_found("file path is empty")), + }; - let contents = fs::read_to_string(file_path)?; - let words = contents + let words = fs::read_to_string(filepath)? .split(' ') .collect::>() .iter() .map(|&x| x.into()) .collect::>(); + Ok(Response::new(SeedWordsResponse { words })) } + /// Deletes the seed words file, provided via CLI async fn delete_seed_words_file( &self, _: Request, ) -> Result, Status> { let cli = Cli::parse(); - let file_path = cli.seed_words_file_name.unwrap(); - - if !file_path.is_file() { - return Err(Status::not_found("file not found")); - } - let file_name = file_path.clone().into_os_string().into_string().unwrap(); + // WARNING: the filepath used is supplied as an argument + fs::remove_file(match cli.seed_words_file_name { + Some(filepath) => filepath, + None => return Err(Status::not_found("file path is empty")), + })?; - if file_name.is_empty() { - return Err(Status::not_found("seed_words_file_name is empty")); - } - fs::remove_file(file_path)?; Ok(Response::new(FileDeletedResponse {})) } } diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index ebc6801d1a..34834d4aae 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -697,10 +697,10 @@ where /// Remove encryption from all the Wallet db backends. If any backends do not have encryption applied then this will /// fail pub async fn remove_encryption(&mut self) -> Result<(), WalletError> { - self.db.remove_encryption().await?; self.output_manager_service.remove_encryption().await?; self.transaction_service.remove_encryption().await?; self.key_manager_service.remove_encryption().await?; + self.db.remove_encryption().await?; Ok(()) } From 815c478985e34384f97a998658fce1d0d462758c Mon Sep 17 00:00:00 2001 From: jorgeantonio21 Date: Tue, 2 Aug 2022 14:23:12 +0100 Subject: [PATCH 7/7] fix: add more recent zero point for wallet birthday (see issue #4176) (#4275) Description --- Add logic to tackle issue #4176, which aims at improving birthday representation for Cipher Seed generation. Motivation and Context --- The goal of this PR is to resolve issue #4176. The current version is supposed to allow a more recent genesis for wallet birthdays (it actually depends on the genesis block of the Tari network, we wish to work with). Moreover, it updates the version. Moreover, if the birthday does not fit within the `u16` type range, we update to a new version. How Has This Been Tested? --- Add simple tests to check if logic is correct. --- base_layer/key_manager/src/cipher_seed.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/base_layer/key_manager/src/cipher_seed.rs b/base_layer/key_manager/src/cipher_seed.rs index cd7b0c6816..c2d0ae2b8f 100644 --- a/base_layer/key_manager/src/cipher_seed.rs +++ b/base_layer/key_manager/src/cipher_seed.rs @@ -23,7 +23,8 @@ use std::{ convert::TryFrom, mem::size_of, - time::{SystemTime, UNIX_EPOCH}, + ops::Add, + time::{Duration, SystemTime, UNIX_EPOCH}, }; use argon2::{ @@ -54,6 +55,8 @@ use crate::{ }; const CIPHER_SEED_VERSION: u8 = 0u8; +// seconds elapsed from unix epoch until '2022-01-01' == 60 * 60 * 24 * 365 * 52 +pub const BIRTHDAY_GENESIS_FROM_UNIX_EPOCH: u64 = 1639872000; pub const DEFAULT_CIPHER_SEED_PASSPHRASE: &str = "TARI_CIPHER_SEED"; const ARGON2_SALT_BYTES: usize = 16; pub const CIPHER_SEED_BIRTHDAY_BYTES: usize = 2; @@ -119,7 +122,12 @@ impl CipherSeed { #[cfg(not(target_arch = "wasm32"))] pub fn new() -> Self { const SECONDS_PER_DAY: u64 = 24 * 60 * 60; - let days = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() / SECONDS_PER_DAY; + let birthday_genesis_date = UNIX_EPOCH.add(Duration::from_secs(BIRTHDAY_GENESIS_FROM_UNIX_EPOCH)); + let days = SystemTime::now() + .duration_since(birthday_genesis_date) + .unwrap() + .as_secs() / + SECONDS_PER_DAY; let birthday = u16::try_from(days).unwrap_or(0u16); CipherSeed::new_with_birthday(birthday) }