From b519c4f17869da8e4fb7b13682cc1d598e2d25a7 Mon Sep 17 00:00:00 2001 From: Bruno Deferrari Date: Wed, 15 Jan 2025 22:58:13 -0300 Subject: [PATCH 1/3] feat(rpc): Add heartbeat handler --- Cargo.lock | 39 +-- node/Cargo.toml | 3 + node/common/src/service/block_producer/mod.rs | 4 + node/common/src/service/rpc/mod.rs | 11 +- node/native/src/http_server.rs | 16 ++ node/src/action_kind.rs | 8 +- .../block_producer/block_producer_reducer.rs | 10 + .../block_producer_effectful_actions.rs | 5 +- .../block_producer_effectful_effects.rs | 5 + .../block_producer_effectful_service.rs | 2 + node/src/event_source/event.rs | 1 + node/src/event_source/event_source_effects.rs | 3 + node/src/rpc/heartbeat.rs | 263 ++++++++++++++++++ node/src/rpc/mod.rs | 5 + node/src/rpc/rpc_actions.rs | 4 + node/src/rpc/rpc_reducer.rs | 4 + .../src/rpc_effectful/rpc_effectful_action.rs | 3 + .../rpc_effectful/rpc_effectful_effects.rs | 133 ++++++--- node/src/rpc_effectful/rpc_service.rs | 20 +- node/src/stats/stats_block_producer.rs | 3 +- node/testing/src/service/mod.rs | 7 + node/testing/src/service/rpc_service.rs | 1 + tools/bootstrap-sandbox/Cargo.toml | 2 +- 23 files changed, 475 insertions(+), 77 deletions(-) create mode 100644 node/src/rpc/heartbeat.rs diff --git a/Cargo.lock b/Cargo.lock index 0590e63daa..dc646952e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,7 +242,7 @@ dependencies = [ [[package]] name = "ark-ec" version = "0.3.0" -source = "git+https://github.com/openmina/algebra?rev=d0343f5#d0343f56c517675d2c340d066727d470ce22d552" +source = "git+https://github.com/openmina/algebra?rev=aea157a#aea157a8e81ddc9f16bae5123b5dda169e3842c9" dependencies = [ "ark-ff", "ark-serialize 0.3.0", @@ -256,7 +256,7 @@ dependencies = [ [[package]] name = "ark-ff" version = "0.3.0" -source = "git+https://github.com/openmina/algebra?rev=d0343f5#d0343f56c517675d2c340d066727d470ce22d552" +source = "git+https://github.com/openmina/algebra?rev=aea157a#aea157a8e81ddc9f16bae5123b5dda169e3842c9" dependencies = [ "ark-ff-asm", "ark-ff-macros", @@ -274,7 +274,7 @@ dependencies = [ [[package]] name = "ark-ff-asm" version = "0.3.0" -source = "git+https://github.com/openmina/algebra?rev=d0343f5#d0343f56c517675d2c340d066727d470ce22d552" +source = "git+https://github.com/openmina/algebra?rev=aea157a#aea157a8e81ddc9f16bae5123b5dda169e3842c9" dependencies = [ "quote", "syn 1.0.109", @@ -283,7 +283,7 @@ dependencies = [ [[package]] name = "ark-ff-macros" version = "0.3.0" -source = "git+https://github.com/openmina/algebra?rev=d0343f5#d0343f56c517675d2c340d066727d470ce22d552" +source = "git+https://github.com/openmina/algebra?rev=aea157a#aea157a8e81ddc9f16bae5123b5dda169e3842c9" dependencies = [ "num-bigint", "num-traits", @@ -294,7 +294,7 @@ dependencies = [ [[package]] name = "ark-poly" version = "0.3.0" -source = "git+https://github.com/openmina/algebra?rev=d0343f5#d0343f56c517675d2c340d066727d470ce22d552" +source = "git+https://github.com/openmina/algebra?rev=aea157a#aea157a8e81ddc9f16bae5123b5dda169e3842c9" dependencies = [ "ark-ff", "ark-serialize 0.3.0", @@ -307,7 +307,7 @@ dependencies = [ [[package]] name = "ark-serialize" version = "0.3.0" -source = "git+https://github.com/openmina/algebra?rev=d0343f5#d0343f56c517675d2c340d066727d470ce22d552" +source = "git+https://github.com/openmina/algebra?rev=aea157a#aea157a8e81ddc9f16bae5123b5dda169e3842c9" dependencies = [ "ark-serialize-derive", "ark-std 0.3.0", @@ -328,7 +328,7 @@ dependencies = [ [[package]] name = "ark-serialize-derive" version = "0.3.0" -source = "git+https://github.com/openmina/algebra?rev=d0343f5#d0343f56c517675d2c340d066727d470ce22d552" +source = "git+https://github.com/openmina/algebra?rev=aea157a#aea157a8e81ddc9f16bae5123b5dda169e3842c9" dependencies = [ "proc-macro2", "quote", @@ -2503,7 +2503,7 @@ dependencies = [ [[package]] name = "groupmap" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ec", "ark-ff", @@ -3012,7 +3012,7 @@ dependencies = [ [[package]] name = "internal-tracing" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" [[package]] name = "io-lifetimes" @@ -3176,7 +3176,7 @@ dependencies = [ [[package]] name = "kimchi" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ec", "ark-ff", @@ -3856,7 +3856,7 @@ dependencies = [ [[package]] name = "mina-curves" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ec", "ark-ff", @@ -3865,7 +3865,7 @@ dependencies = [ [[package]] name = "mina-hasher" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ff", "bitvec", @@ -3918,7 +3918,7 @@ dependencies = [ [[package]] name = "mina-poseidon" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ec", "ark-ff", @@ -3935,7 +3935,7 @@ dependencies = [ [[package]] name = "mina-signer" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ec", "ark-ff", @@ -4309,7 +4309,10 @@ version = "0.13.0" dependencies = [ "anyhow", "ark-ff", + "base64 0.22.0", + "blake2", "derive_more", + "hex", "lazy_static", "linkme", "mina-hasher", @@ -4553,7 +4556,7 @@ dependencies = [ [[package]] name = "o1-utils" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ec", "ark-ff", @@ -4664,7 +4667,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" name = "openmina-bootstrap-sandbox" version = "0.13.0" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", "binprot", "bs58 0.5.0", "env_logger", @@ -5425,7 +5428,7 @@ dependencies = [ [[package]] name = "poly-commitment" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ec", "ark-ff", @@ -7829,7 +7832,7 @@ dependencies = [ [[package]] name = "turshi" version = "0.1.0" -source = "git+https://github.com/openmina/proof-systems?rev=fd30bd6#fd30bd632303b6c1d7ef3bc5319c1d58096e5130" +source = "git+https://github.com/openmina/proof-systems?rev=dec49a9#dec49a95fd483d8a90b7b602d13bfa8ea9485491" dependencies = [ "ark-ff", "hex", diff --git a/node/Cargo.toml b/node/Cargo.toml index 2f2851ee6d..4f719ec61a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -8,6 +8,9 @@ license = "Apache-2.0" workspace = true [dependencies] +base64 = "0.22" +blake2 = "0.10" +hex = "0.4" rand = "0.8.0" serde = "1.0.147" serde_json = { version = "1.0.82", features = ["unbounded_depth", "arbitrary_precision"] } diff --git a/node/common/src/service/block_producer/mod.rs b/node/common/src/service/block_producer/mod.rs index 4c51d5b7be..c2de6c81c7 100644 --- a/node/common/src/service/block_producer/mod.rs +++ b/node/common/src/service/block_producer/mod.rs @@ -162,6 +162,10 @@ impl node::service::BlockProducerService for crate::NodeService { .prove_sender .send((provers, block_hash, input)); } + + fn with_producer_keypair(&self, f: impl FnOnce(&AccountSecretKey) -> T) -> Option { + Some(f(&self.block_producer.as_ref()?.keypair)) + } } fn dump_failed_block_proof_input( diff --git a/node/common/src/service/rpc/mod.rs b/node/common/src/service/rpc/mod.rs index 5063b76ced..76d491fe6f 100644 --- a/node/common/src/service/rpc/mod.rs +++ b/node/common/src/service/rpc/mod.rs @@ -10,10 +10,11 @@ pub mod transition_frontier; use node::rpc::{ RpcBestChainResponse, RpcBlockProducerStatsGetResponse, RpcConsensusConstantsGetResponse, RpcDiscoveryBoostrapStatsResponse, RpcDiscoveryRoutingTableResponse, RpcHealthCheckResponse, - RpcLedgerAccountsResponse, RpcLedgerSlimAccountsResponse, RpcMessageProgressResponse, - RpcPeersGetResponse, RpcReadinessCheckResponse, RpcRequest, RpcStateGetError, - RpcStatusGetResponse, RpcTransactionInjectResponse, RpcTransactionPoolResponse, - RpcTransactionStatusGetResponse, RpcTransitionFrontierUserCommandsResponse, + RpcHeartbeatGetResponse, RpcLedgerAccountsResponse, RpcLedgerSlimAccountsResponse, + RpcMessageProgressResponse, RpcPeersGetResponse, RpcReadinessCheckResponse, RpcRequest, + RpcStateGetError, RpcStatusGetResponse, RpcTransactionInjectResponse, + RpcTransactionPoolResponse, RpcTransactionStatusGetResponse, + RpcTransitionFrontierUserCommandsResponse, }; use serde::{Deserialize, Serialize}; @@ -216,6 +217,8 @@ impl node::rpc_effectful::RpcService for NodeService { } rpc_service_impl!(respond_status_get, RpcStatusGetResponse); + rpc_service_impl!(respond_heartbeat_get, RpcHeartbeatGetResponse); + rpc_service_impl!(respond_sync_stats_get, RpcSyncStatsGetResponse); rpc_service_impl!(respond_action_stats_get, RpcActionStatsGetResponse); rpc_service_impl!( diff --git a/node/native/src/http_server.rs b/node/native/src/http_server.rs index 05dc4b675c..c2daf04323 100644 --- a/node/native/src/http_server.rs +++ b/node/native/src/http_server.rs @@ -166,6 +166,21 @@ pub async fn run(port: u16, rpc_sender: RpcSender) { } }); + let rpc_sender_clone = rpc_sender.clone(); + let make_heartbeat = warp::path!("make_heartbeat") + .and(warp::post()) + .then(move || { + let rpc_sender_clone = rpc_sender_clone.clone(); + async move { + let result: RpcHeartbeatGetResponse = rpc_sender_clone + .oneshot_request(RpcRequest::HeartbeatGet) + .await + .flatten(); + + with_json_reply(&result, StatusCode::OK) + } + }); + let rpc_sender_clone = rpc_sender.clone(); let peers_get = warp::path!("state" / "peers") .and(warp::get()) @@ -571,6 +586,7 @@ pub async fn run(port: u16, rpc_sender: RpcSender) { build_env_get, routes, status, + make_heartbeat, peers_get, message_progress_get, stats, diff --git a/node/src/action_kind.rs b/node/src/action_kind.rs index ef5ba28fa5..2a7813c180 100644 --- a/node/src/action_kind.rs +++ b/node/src/action_kind.rs @@ -126,6 +126,7 @@ pub enum ActionKind { BlockProducerWonSlotTransactionsGet, BlockProducerWonSlotTransactionsSuccess, BlockProducerWonSlotWait, + BlockProducerEffectfulBlockInjected, BlockProducerEffectfulBlockProveInit, BlockProducerEffectfulBlockProveSuccess, BlockProducerEffectfulBlockUnprovenBuild, @@ -489,6 +490,7 @@ pub enum ActionKind { RpcFinish, RpcGlobalStateGet, RpcHealthCheck, + RpcHeartbeatGet, RpcLedgerAccountsGetInit, RpcLedgerAccountsGetPending, RpcLedgerAccountsGetSuccess, @@ -533,6 +535,7 @@ pub enum ActionKind { RpcEffectfulDiscoveryRoutingTable, RpcEffectfulGlobalStateGet, RpcEffectfulHealthCheck, + RpcEffectfulHeartbeatGet, RpcEffectfulLedgerAccountsGetSuccess, RpcEffectfulMessageProgressGet, RpcEffectfulP2pConnectionIncomingError, @@ -714,7 +717,7 @@ pub enum ActionKind { } impl ActionKind { - pub const COUNT: u16 = 604; + pub const COUNT: u16 = 607; } impl std::fmt::Display for ActionKind { @@ -1037,6 +1040,7 @@ impl ActionKindGet for BlockProducerEffectfulAction { Self::BlockUnprovenBuild => ActionKind::BlockProducerEffectfulBlockUnprovenBuild, Self::BlockProveInit => ActionKind::BlockProducerEffectfulBlockProveInit, Self::BlockProveSuccess => ActionKind::BlockProducerEffectfulBlockProveSuccess, + Self::BlockInjected { .. } => ActionKind::BlockProducerEffectfulBlockInjected, } } } @@ -1046,6 +1050,7 @@ impl ActionKindGet for RpcAction { match self { Self::GlobalStateGet { .. } => ActionKind::RpcGlobalStateGet, Self::StatusGet { .. } => ActionKind::RpcStatusGet, + Self::HeartbeatGet { .. } => ActionKind::RpcHeartbeatGet, Self::ActionStatsGet { .. } => ActionKind::RpcActionStatsGet, Self::SyncStatsGet { .. } => ActionKind::RpcSyncStatsGet, Self::BlockProducerStatsGet { .. } => ActionKind::RpcBlockProducerStatsGet, @@ -1114,6 +1119,7 @@ impl ActionKindGet for RpcEffectfulAction { match self { Self::GlobalStateGet { .. } => ActionKind::RpcEffectfulGlobalStateGet, Self::StatusGet { .. } => ActionKind::RpcEffectfulStatusGet, + Self::HeartbeatGet { .. } => ActionKind::RpcEffectfulHeartbeatGet, Self::ActionStatsGet { .. } => ActionKind::RpcEffectfulActionStatsGet, Self::SyncStatsGet { .. } => ActionKind::RpcEffectfulSyncStatsGet, Self::BlockProducerStatsGet { .. } => ActionKind::RpcEffectfulBlockProducerStatsGet, diff --git a/node/src/block_producer/block_producer_reducer.rs b/node/src/block_producer/block_producer_reducer.rs index fb47032ba5..73197bff30 100644 --- a/node/src/block_producer/block_producer_reducer.rs +++ b/node/src/block_producer/block_producer_reducer.rs @@ -360,6 +360,16 @@ impl BlockProducerEnabled { let (dispatcher, global_state) = state_context.into_dispatcher_and_state(); + // Store the produced block in stats, used by heartbeats + let block = global_state + .block_producer + .as_ref() + .and_then(|bp| bp.current.injected_block()) + .cloned(); + if let Some(block) = block { + dispatcher.push(BlockProducerEffectfulAction::BlockInjected { block }); + } + #[cfg(feature = "p2p-libp2p")] broadcast_injected_block(global_state, dispatcher); diff --git a/node/src/block_producer_effectful/block_producer_effectful_actions.rs b/node/src/block_producer_effectful/block_producer_effectful_actions.rs index a81b4702b9..9cc51d379f 100644 --- a/node/src/block_producer_effectful/block_producer_effectful_actions.rs +++ b/node/src/block_producer_effectful/block_producer_effectful_actions.rs @@ -1,6 +1,6 @@ use super::vrf_evaluator_effectful::BlockProducerVrfEvaluatorEffectfulAction; use crate::block_producer::{BlockProducerWonSlot, BlockProducerWonSlotDiscardReason}; -use openmina_core::ActionEvent; +use openmina_core::{block::ArcBlockWithHash, ActionEvent}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)] @@ -17,6 +17,9 @@ pub enum BlockProducerEffectfulAction { BlockUnprovenBuild, BlockProveInit, BlockProveSuccess, + BlockInjected { + block: ArcBlockWithHash, + }, } impl redux::EnablingCondition for BlockProducerEffectfulAction { diff --git a/node/src/block_producer_effectful/block_producer_effectful_effects.rs b/node/src/block_producer_effectful/block_producer_effectful_effects.rs index ab7a5cdd3f..287370df11 100644 --- a/node/src/block_producer_effectful/block_producer_effectful_effects.rs +++ b/node/src/block_producer_effectful/block_producer_effectful_effects.rs @@ -221,5 +221,10 @@ pub fn block_producer_effects( } store.dispatch(BlockProducerAction::WonSlotSearch); } + BlockProducerEffectfulAction::BlockInjected { block } => { + if let Some(stats) = store.service.stats() { + stats.block_producer().last_produced_block = Some(block.clone()); + } + } } } diff --git a/node/src/block_producer_effectful/block_producer_effectful_service.rs b/node/src/block_producer_effectful/block_producer_effectful_service.rs index 145f1e249c..41ea20b4fe 100644 --- a/node/src/block_producer_effectful/block_producer_effectful_service.rs +++ b/node/src/block_producer_effectful/block_producer_effectful_service.rs @@ -7,6 +7,7 @@ use mina_p2p_messages::v2::{ MinaBaseStagedLedgerHashStableV1, ProverExtendBlockchainInputStableV2, StagedLedgerDiffDiffStableV2, StateHash, }; +use openmina_node_account::AccountSecretKey; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] @@ -24,4 +25,5 @@ pub struct StagedLedgerDiffCreateOutput { pub trait BlockProducerService { fn provers(&self) -> BlockProver; fn prove(&mut self, block_hash: StateHash, input: Box); + fn with_producer_keypair(&self, f: impl FnOnce(&AccountSecretKey) -> T) -> Option; } diff --git a/node/src/event_source/event.rs b/node/src/event_source/event.rs index 66bdfa78d9..edd22bd477 100644 --- a/node/src/event_source/event.rs +++ b/node/src/event_source/event.rs @@ -32,6 +32,7 @@ impl std::fmt::Display for Event { match req.as_ref() { RpcRequest::StateGet(filter) => write!(f, "StateGet, {filter:?}"), RpcRequest::StatusGet => write!(f, "StatusGet"), + RpcRequest::HeartbeatGet => write!(f, "HeartbeatGet"), RpcRequest::ActionStatsGet(query) => write!(f, "ActionStatsGet, {query:?}"), RpcRequest::SyncStatsGet(query) => write!(f, "SyncStatsGet, {query:?}"), RpcRequest::BlockProducerStatsGet => write!(f, "BlockProducerStatsGet"), diff --git a/node/src/event_source/event_source_effects.rs b/node/src/event_source/event_source_effects.rs index 3ac099f44f..a00770b000 100644 --- a/node/src/event_source/event_source_effects.rs +++ b/node/src/event_source/event_source_effects.rs @@ -307,6 +307,9 @@ pub fn event_source_effects(store: &mut Store, action: EventSourc RpcRequest::StatusGet => { store.dispatch(RpcAction::StatusGet { rpc_id }); } + RpcRequest::HeartbeatGet => { + store.dispatch(RpcAction::HeartbeatGet { rpc_id }); + } RpcRequest::ActionStatsGet(query) => { store.dispatch(RpcAction::ActionStatsGet { rpc_id, query }); } diff --git a/node/src/rpc/heartbeat.rs b/node/src/rpc/heartbeat.rs new file mode 100644 index 0000000000..2224ae3df8 --- /dev/null +++ b/node/src/rpc/heartbeat.rs @@ -0,0 +1,263 @@ +use ledger::FpExt; +use mina_p2p_messages::bigint::BigInt; +use mina_signer::Signature; +use redux::Timestamp; +use serde::Serialize; + +use super::RpcNodeStatus; +use crate::p2p::PeerId; +use openmina_node_account::{AccountPublicKey, AccountSecretKey}; + +/// Matches the representation used by o1js where each field is a string +/// containing a decimal representation of the field. +#[derive(Serialize, Debug, Clone, PartialEq, Eq)] +pub struct SignatureJson { + pub field: String, + pub scalar: String, +} + +impl From for SignatureJson { + fn from(sig: Signature) -> Self { + Self { + field: sig.rx.to_decimal(), + scalar: sig.s.to_decimal(), + } + } +} + +impl TryInto for SignatureJson { + type Error = String; + + fn try_into(self) -> Result { + let rx = BigInt::from_decimal(&self.field) + .map_err(|_| "Failed to parse decimals as BigInt")? + .try_into() + .map_err(|_| "Failed to convert rx BigInt to field element")?; + let s = BigInt::from_decimal(&self.scalar) + .map_err(|_| "Failed to parse decimals as BigInt")? + .try_into() + .map_err(|_| "Failed to convert rx BigInt to field element")?; + + Ok(Signature::new(rx, s)) + } +} + +/// A signed heartbeat message from a node +#[derive(Serialize, Debug, Clone)] +pub struct SignedNodeHeartbeat { + pub version: u8, + /// base64 encoded json of the payload + pub payload: String, + pub submitter: AccountPublicKey, + pub signature: SignatureJson, +} + +impl SignedNodeHeartbeat { + /// Verifies that the signature is valid for this heartbeat + pub fn verify_signature(&self) -> bool { + use blake2::digest::{Update, VariableOutput}; + use mina_signer::{CompressedPubKey, PubKey, Signer}; + + let signature = match self.signature.clone().try_into() { + Ok(sig) => sig, + Err(_) => return false, + }; + + let pk: CompressedPubKey = match self.submitter.clone().try_into() { + Ok(pk) => pk, + Err(_) => return false, + }; + + let pk = match PubKey::from_address(&pk.into_address()) { + Ok(pk) => pk, + Err(_) => return false, + }; + + // Calculate digest from payload + let mut hasher = blake2::Blake2bVar::new(32).expect("Invalid Blake2bVar output size"); + let mut blake2_hash = [0u8; 32]; + hasher.update(self.payload.as_bytes()); + hasher.finalize_variable(&mut blake2_hash).unwrap(); + + let digest = NodeHeartbeatPayloadDigest(blake2_hash); + let mut signer = mina_signer::create_legacy::( + mina_signer::NetworkId::TESTNET, + ); + + signer.verify(&signature, &pk, &digest) + } +} + +/// Node heartbeat +#[derive(Serialize, Debug, Clone)] +pub struct NodeHeartbeat { + pub status: RpcNodeStatus, + pub node_timestamp: Timestamp, + pub peer_id: PeerId, + // binprot+base64 encoded block + pub last_produced_block: Option, +} + +/// Blake2b hash of the encoded heartbeat payload +#[derive(Clone, Debug)] +pub struct NodeHeartbeatPayloadDigest([u8; 32]); + +impl mina_hasher::Hashable for NodeHeartbeatPayloadDigest { + type D = mina_signer::NetworkId; + + fn to_roinput(&self) -> mina_hasher::ROInput { + let mut hex = [0u8; 64]; + hex::encode_to_slice(self.0, &mut hex).unwrap(); + + // Bits must be reversed to match the JS implementation + for b in hex.iter_mut() { + *b = b.reverse_bits(); + } + + mina_hasher::ROInput::new().append_bytes(&hex) + } + + fn domain_string(network_id: Self::D) -> Option { + match network_id { + Self::D::MAINNET => openmina_core::network::mainnet::SIGNATURE_PREFIX, + Self::D::TESTNET => openmina_core::network::devnet::SIGNATURE_PREFIX, + } + .to_string() + .into() + } +} + +impl NodeHeartbeat { + const CURRENT_VERSION: u8 = 1; + + /// Creates base64 encoded payload and its Blake2b digest + fn payload_and_digest(&self) -> (String, NodeHeartbeatPayloadDigest) { + use base64::{engine::general_purpose::URL_SAFE, Engine as _}; + use blake2::{ + digest::{Update, VariableOutput}, + Blake2bVar, + }; + + let payload = serde_json::to_string(self).unwrap(); + let encoded_payload = URL_SAFE.encode(&payload); + + let mut hasher = Blake2bVar::new(32).expect("Invalid Blake2bVar output size"); + let mut blake2_hash = [0u8; 32]; + + hasher.update(encoded_payload.as_bytes()); + hasher.finalize_variable(&mut blake2_hash).unwrap(); + + (encoded_payload, NodeHeartbeatPayloadDigest(blake2_hash)) + } + + /// Signs the heartbeat using the provided secret key + pub fn sign(&self, secret_key: &AccountSecretKey) -> SignedNodeHeartbeat { + let (payload, digest) = self.payload_and_digest(); + let submitter = secret_key.public_key(); + + let signature = { + use mina_signer::{Keypair, Signer}; + let mut signer = mina_signer::create_legacy::( + mina_signer::NetworkId::TESTNET, + ); + let kp = Keypair::from(secret_key.clone()); + + let signature = signer.sign(&kp, &digest); + signature.into() + }; + + SignedNodeHeartbeat { + version: Self::CURRENT_VERSION, + payload, + submitter, + signature, + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + + use crate::rpc::{ + RpcNodeStatusSnarkPool, RpcNodeStatusTransactionPool, RpcNodeStatusTransitionFrontier, + RpcNodeStatusTransitionFrontierSync, + }; + + use super::*; + use redux::Timestamp; + + #[test] + fn test_heartbeat_signing() { + let heartbeat = create_test_heartbeat(); + let secret_key = AccountSecretKey::deterministic(0); + let signed = heartbeat.sign(&secret_key); + + println!("Private key: {}", secret_key); + println!("Public key: {}", secret_key.public_key()); + println!("Payload: {}", signed.payload); + println!("Signature: {:?}", signed.signature); + + assert_eq!(&signed.payload, "eyJzdGF0dXMiOnsiY2hhaW5faWQiOm51bGwsInRyYW5zaXRpb25fZnJvbnRpZXIiOnsiYmVzdF90aXAiOm51bGwsInN5bmMiOnsidGltZSI6bnVsbCwic3RhdHVzIjoiU3luY2VkIiwicGhhc2UiOiJSdW5uaW5nIiwidGFyZ2V0IjpudWxsfX0sInBlZXJzIjpbXSwic25hcmtfcG9vbCI6eyJ0b3RhbF9qb2JzIjowLCJzbmFya3MiOjB9LCJ0cmFuc2FjdGlvbl9wb29sIjp7InRyYW5zYWN0aW9ucyI6MCwidHJhbnNhY3Rpb25zX2Zvcl9wcm9wYWdhdGlvbiI6MCwidHJhbnNhY3Rpb25fY2FuZGlkYXRlcyI6MH0sImN1cnJlbnRfYmxvY2tfcHJvZHVjdGlvbl9hdHRlbXB0IjpudWxsfSwibm9kZV90aW1lc3RhbXAiOjAsInBlZXJfaWQiOiIyYkVnQnJQVHpMOHdvdjJENEt6MzRXVkxDeFI0dUNhcnNCbUhZWFdLUUE1d3ZCUXpkOUgiLCJsYXN0X3Byb2R1Y2VkX2Jsb2NrIjpudWxsfQ=="); + assert_eq!( + &signed.signature.field, + "25500978175045040705256298774101531557080530394536110798266178142513301557846" + ); + assert_eq!( + &signed.signature.scalar, + "27991123709623419396663280967637181749724990269901703962618583375785482061803" + ); + assert!(signed.verify_signature()); + } + + #[test] + fn test_heartbeat_signature_deterministic() { + let heartbeat = create_test_heartbeat(); + let secret_key = AccountSecretKey::deterministic(0); + + let signed1 = heartbeat.sign(&secret_key); + let signed2 = heartbeat.sign(&secret_key); + + assert_eq!(signed1.payload, signed2.payload); + assert_eq!(signed1.signature, signed2.signature); + } + + #[test] + fn test_heartbeat_different_keys_different_sigs() { + let heartbeat = create_test_heartbeat(); + let sk1 = AccountSecretKey::deterministic(0); + let sk2 = AccountSecretKey::deterministic(1); + + let signed1 = heartbeat.sign(&sk1); + let signed2 = heartbeat.sign(&sk2); + + assert_eq!(signed1.payload, signed2.payload); + assert_ne!(signed1.signature, signed2.signature); + assert_ne!(signed1.submitter, signed2.submitter); + } + + fn create_test_heartbeat() -> NodeHeartbeat { + NodeHeartbeat { + status: RpcNodeStatus { + chain_id: None, + transition_frontier: RpcNodeStatusTransitionFrontier { + best_tip: None, + sync: RpcNodeStatusTransitionFrontierSync { + time: None, + status: "Synced".to_string(), + phase: "Running".to_string(), + target: None, + }, + }, + peers: vec![], + snark_pool: RpcNodeStatusSnarkPool::default(), + transaction_pool: RpcNodeStatusTransactionPool::default(), + current_block_production_attempt: None, + }, + node_timestamp: Timestamp::ZERO, + peer_id: "2bEgBrPTzL8wov2D4Kz34WVLCxR4uCarsBmHYXWKQA5wvBQzd9H" + .parse() + .unwrap(), + last_produced_block: None, + } + } +} diff --git a/node/src/rpc/mod.rs b/node/src/rpc/mod.rs index 86ad943b27..910f5a48a5 100644 --- a/node/src/rpc/mod.rs +++ b/node/src/rpc/mod.rs @@ -28,6 +28,9 @@ pub use rpc_reducer::collect_rpc_peers_info; mod rpc_impls; +mod heartbeat; +pub use heartbeat::{NodeHeartbeat, SignedNodeHeartbeat}; + pub use openmina_core::requests::{RpcId, RpcIdType}; use ledger::scan_state::scan_state::transaction_snark::OneOrTwo; @@ -54,6 +57,7 @@ use crate::stats::sync::SyncStatsSnapshot; pub enum RpcRequest { StateGet(Option), StatusGet, + HeartbeatGet, ActionStatsGet(ActionStatsQuery), SyncStatsGet(SyncStatsQuery), BlockProducerStatsGet, @@ -339,6 +343,7 @@ pub enum RpcStateGetError { pub type RpcStateGetResponse = Result; pub type RpcStatusGetResponse = Option; +pub type RpcHeartbeatGetResponse = Option; pub type RpcActionStatsGetResponse = Option; pub type RpcSyncStatsGetResponse = Option>; pub type RpcBlockProducerStatsGetResponse = Option; diff --git a/node/src/rpc/rpc_actions.rs b/node/src/rpc/rpc_actions.rs index a933af061e..657f3ac41e 100644 --- a/node/src/rpc/rpc_actions.rs +++ b/node/src/rpc/rpc_actions.rs @@ -28,6 +28,9 @@ pub enum RpcAction { StatusGet { rpc_id: RpcId, }, + HeartbeatGet { + rpc_id: RpcId, + }, // Stats ActionStatsGet { @@ -220,6 +223,7 @@ impl redux::EnablingCondition for RpcAction { match self { RpcAction::GlobalStateGet { .. } => true, RpcAction::StatusGet { .. } => true, + RpcAction::HeartbeatGet { .. } => true, RpcAction::ActionStatsGet { .. } => true, RpcAction::SyncStatsGet { .. } => true, RpcAction::BlockProducerStatsGet { .. } => true, diff --git a/node/src/rpc/rpc_reducer.rs b/node/src/rpc/rpc_reducer.rs index 7c847bc4c1..d314eec63d 100644 --- a/node/src/rpc/rpc_reducer.rs +++ b/node/src/rpc/rpc_reducer.rs @@ -42,6 +42,10 @@ impl RpcState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(RpcEffectfulAction::StatusGet { rpc_id: *rpc_id }); } + RpcAction::HeartbeatGet { rpc_id } => { + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(RpcEffectfulAction::HeartbeatGet { rpc_id: *rpc_id }); + } RpcAction::ActionStatsGet { rpc_id, query } => { let dispatcher = state_context.into_dispatcher(); dispatcher.push(RpcEffectfulAction::ActionStatsGet { diff --git a/node/src/rpc_effectful/rpc_effectful_action.rs b/node/src/rpc_effectful/rpc_effectful_action.rs index d1c7d41714..1159ed2885 100644 --- a/node/src/rpc_effectful/rpc_effectful_action.rs +++ b/node/src/rpc_effectful/rpc_effectful_action.rs @@ -28,6 +28,9 @@ pub enum RpcEffectfulAction { StatusGet { rpc_id: RpcId, }, + HeartbeatGet { + rpc_id: RpcId, + }, ActionStatsGet { rpc_id: RpcId, query: ActionStatsQuery, diff --git a/node/src/rpc_effectful/rpc_effectful_effects.rs b/node/src/rpc_effectful/rpc_effectful_effects.rs index feb513a973..f62a83bc1c 100644 --- a/node/src/rpc_effectful/rpc_effectful_effects.rs +++ b/node/src/rpc_effectful/rpc_effectful_effects.rs @@ -6,8 +6,8 @@ use crate::{ p2p_ready, rpc::{ AccountQuery, AccountSlim, ActionStatsQuery, ActionStatsResponse, CurrentMessageProgress, - MessagesStats, RootLedgerSyncProgress, RootStagedLedgerSyncProgress, RpcAction, - RpcBlockProducerStats, RpcMessageProgressResponse, RpcNodeStatus, + MessagesStats, NodeHeartbeat, RootLedgerSyncProgress, RootStagedLedgerSyncProgress, + RpcAction, RpcBlockProducerStats, RpcMessageProgressResponse, RpcNodeStatus, RpcNodeStatusTransactionPool, RpcNodeStatusTransitionFrontier, RpcNodeStatusTransitionFrontierBlockSummary, RpcNodeStatusTransitionFrontierSync, RpcRequestExtraData, RpcScanStateSummary, RpcScanStateSummaryBlock, @@ -26,9 +26,9 @@ use ledger::{ scan_state::currency::{Balance, Magnitude}, Account, }; -use mina_p2p_messages::{rpc_kernel::QueryHeader, v2::MinaBaseTransactionStatusStableV2}; +use mina_p2p_messages::{rpc_kernel::QueryHeader, v2}; use mina_signer::CompressedPubKey; -use openmina_core::block::ArcBlockWithHash; +use openmina_core::{block::ArcBlockWithHash, bug_condition}; use p2p::channels::streaming_rpc::{ staged_ledger_parts::calc_total_pieces_to_transfer, P2pStreamingRpcReceiveProgress, }; @@ -53,48 +53,35 @@ pub fn rpc_effects(store: &mut Store, action: ActionWithMeta { - let state = store.state.get(); - let chain_id = state.p2p.ready().map(|p2p| p2p.chain_id.to_hex()); - let block_summary = - |b: &ArcBlockWithHash| RpcNodeStatusTransitionFrontierBlockSummary { - hash: b.hash().clone(), - height: b.height(), - global_slot: b.global_slot(), - }; - let current_block_production_attempt = store + let status = compute_node_status(store); + let _ = store.service.respond_status_get(rpc_id, Some(status)); + } + RpcEffectfulAction::HeartbeatGet { rpc_id } => { + let status = compute_node_status(store); + let last_produced_block = store .service .stats() - .and_then(|stats| Some(stats.block_producer().collect_attempts().last()?.clone())); - let status = RpcNodeStatus { - chain_id, - transition_frontier: RpcNodeStatusTransitionFrontier { - best_tip: state.transition_frontier.best_tip().map(block_summary), - sync: RpcNodeStatusTransitionFrontierSync { - time: state.transition_frontier.sync.time(), - status: state.transition_frontier.sync.to_string(), - phase: state.transition_frontier.sync.sync_phase().to_string(), - target: state.transition_frontier.sync.best_tip().map(block_summary), - }, - }, - peers: rpc::collect_rpc_peers_info(state), - snark_pool: state.snark_pool.jobs_iter().fold( - Default::default(), - |mut acc, job| { - if job.snark.is_some() { - acc.snarks = acc.snarks.saturating_add(1); - } - acc.total_jobs = acc.total_jobs.saturating_add(1); - acc - }, - ), - transaction_pool: RpcNodeStatusTransactionPool { - transactions: state.transaction_pool.size(), - transactions_for_propagation: state.transaction_pool.for_propagation_size(), - transaction_candidates: state.transaction_pool.candidates.transactions_count(), - }, - current_block_production_attempt, + .and_then(|stats| stats.block_producer().last_produced_block.take()); + + let last_produced_block = match base64_encode_block(last_produced_block) { + Ok(block) => block, + Err(error) => { + bug_condition!("HeartbeatGet: Failed to encode block, returning None: {error}"); + None + } }; - let _ = store.service.respond_status_get(rpc_id, Some(status)); + + let heartbeat = NodeHeartbeat { + status, + node_timestamp: meta.time(), + peer_id: store.state().p2p.my_id(), + last_produced_block, + }; + let response = store + .service() + .with_producer_keypair(move |sk| heartbeat.sign(sk)); + + let _ = store.service.respond_heartbeat_get(rpc_id, response); } RpcEffectfulAction::ActionStatsGet { rpc_id, query } => match query { ActionStatsQuery::SinceStart => { @@ -374,7 +361,7 @@ pub fn rpc_effects(store: &mut Store, action: ActionWithMeta(store: &mut Store, action: ActionWithMeta(store: &mut Store) -> RpcNodeStatus { + let state = store.state.get(); + let chain_id = state.p2p.ready().map(|p2p| p2p.chain_id.to_hex()); + let block_summary = |b: &ArcBlockWithHash| RpcNodeStatusTransitionFrontierBlockSummary { + hash: b.hash().clone(), + height: b.height(), + global_slot: b.global_slot(), + }; + let current_block_production_attempt = store + .service + .stats() + .and_then(|stats| Some(stats.block_producer().collect_attempts().last()?.clone())); + let status = RpcNodeStatus { + chain_id, + transition_frontier: RpcNodeStatusTransitionFrontier { + best_tip: state.transition_frontier.best_tip().map(block_summary), + sync: RpcNodeStatusTransitionFrontierSync { + time: state.transition_frontier.sync.time(), + status: state.transition_frontier.sync.to_string(), + phase: state.transition_frontier.sync.sync_phase().to_string(), + target: state.transition_frontier.sync.best_tip().map(block_summary), + }, + }, + peers: rpc::collect_rpc_peers_info(state), + snark_pool: state + .snark_pool + .jobs_iter() + .fold(Default::default(), |mut acc, job| { + if job.snark.is_some() { + acc.snarks = acc.snarks.saturating_add(1); + } + acc.total_jobs = acc.total_jobs.saturating_add(1); + acc + }), + transaction_pool: RpcNodeStatusTransactionPool { + transactions: state.transaction_pool.size(), + transactions_for_propagation: state.transaction_pool.for_propagation_size(), + transaction_candidates: state.transaction_pool.candidates.transactions_count(), + }, + current_block_production_attempt, + }; + status +} + +fn base64_encode_block(block: Option) -> std::io::Result> { + use base64::{engine::general_purpose::URL_SAFE, Engine as _}; + use mina_p2p_messages::binprot::BinProtWrite; + + let Some(block) = block else { return Ok(None) }; + + let mut buf = Vec::with_capacity(10 * 1024 * 1024); + v2::MinaBlockBlockStableV2::binprot_write(&block.block, &mut buf)?; + + let base64_encoded = URL_SAFE.encode(&buf); + + Ok(Some(base64_encoded)) +} diff --git a/node/src/rpc_effectful/rpc_service.rs b/node/src/rpc_effectful/rpc_service.rs index 11e524f3e0..8400db0575 100644 --- a/node/src/rpc_effectful/rpc_service.rs +++ b/node/src/rpc_effectful/rpc_service.rs @@ -3,13 +3,14 @@ use crate::{ rpc::{ RpcActionStatsGetResponse, RpcBestChainResponse, RpcBlockProducerStatsGetResponse, RpcDiscoveryBoostrapStatsResponse, RpcDiscoveryRoutingTableResponse, - RpcHealthCheckResponse, RpcId, RpcLedgerAccountsResponse, RpcLedgerSlimAccountsResponse, - RpcMessageProgressResponse, RpcP2pConnectionOutgoingResponse, RpcPeersGetResponse, - RpcReadinessCheckResponse, RpcScanStateSummaryGetResponse, RpcSnarkPoolGetResponse, - RpcSnarkPoolJobGetResponse, RpcSnarkerConfigGetResponse, RpcSnarkerJobCommitResponse, - RpcSnarkerJobSpecResponse, RpcSnarkerWorkersResponse, RpcStatusGetResponse, - RpcSyncStatsGetResponse, RpcTransactionInjectResponse, RpcTransactionPoolResponse, - RpcTransactionStatusGetResponse, RpcTransitionFrontierUserCommandsResponse, + RpcHealthCheckResponse, RpcHeartbeatGetResponse, RpcId, RpcLedgerAccountsResponse, + RpcLedgerSlimAccountsResponse, RpcMessageProgressResponse, + RpcP2pConnectionOutgoingResponse, RpcPeersGetResponse, RpcReadinessCheckResponse, + RpcScanStateSummaryGetResponse, RpcSnarkPoolGetResponse, RpcSnarkPoolJobGetResponse, + RpcSnarkerConfigGetResponse, RpcSnarkerJobCommitResponse, RpcSnarkerJobSpecResponse, + RpcSnarkerWorkersResponse, RpcStatusGetResponse, RpcSyncStatsGetResponse, + RpcTransactionInjectResponse, RpcTransactionPoolResponse, RpcTransactionStatusGetResponse, + RpcTransitionFrontierUserCommandsResponse, }, State, }; @@ -52,6 +53,11 @@ pub trait RpcService { rpc_id: RpcId, response: RpcStatusGetResponse, ) -> Result<(), RespondError>; + fn respond_heartbeat_get( + &mut self, + rpc_id: RpcId, + response: RpcHeartbeatGetResponse, + ) -> Result<(), RespondError>; fn respond_action_stats_get( &mut self, rpc_id: RpcId, diff --git a/node/src/stats/stats_block_producer.rs b/node/src/stats/stats_block_producer.rs index afdc28dc8a..0c4d88ef88 100644 --- a/node/src/stats/stats_block_producer.rs +++ b/node/src/stats/stats_block_producer.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeMap, VecDeque}; use ledger::AccountIndex; use mina_p2p_messages::v2; -use openmina_core::block::AppliedBlock; +use openmina_core::block::{AppliedBlock, ArcBlockWithHash}; use serde::{Deserialize, Serialize}; use crate::{ @@ -16,6 +16,7 @@ const MAX_HISTORY: usize = 2048; pub struct BlockProducerStats { pub(super) attempts: VecDeque, pub vrf_evaluator: BTreeMap, + pub last_produced_block: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/node/testing/src/service/mod.rs b/node/testing/src/service/mod.rs index b32a8f3492..b8c4650918 100644 --- a/node/testing/src/service/mod.rs +++ b/node/testing/src/service/mod.rs @@ -574,6 +574,13 @@ impl BlockProducerService for NodeTestingService { } } } + + fn with_producer_keypair( + &self, + _f: impl FnOnce(&node::account::AccountSecretKey) -> T, + ) -> Option { + None + } } impl ExternalSnarkWorkerService for NodeTestingService { diff --git a/node/testing/src/service/rpc_service.rs b/node/testing/src/service/rpc_service.rs index 91a923ba55..475a3339c1 100644 --- a/node/testing/src/service/rpc_service.rs +++ b/node/testing/src/service/rpc_service.rs @@ -15,6 +15,7 @@ macro_rules! to_real { impl RpcService for super::NodeTestingService { to_real!(respond_state_get, (&State, Option<&str>)); to_real!(respond_status_get, node::rpc::RpcStatusGetResponse); + to_real!(respond_heartbeat_get, node::rpc::RpcHeartbeatGetResponse); to_real!(respond_sync_stats_get, node::rpc::RpcSyncStatsGetResponse); to_real!( respond_block_producer_stats_get, diff --git a/tools/bootstrap-sandbox/Cargo.toml b/tools/bootstrap-sandbox/Cargo.toml index 1d17fac5d4..825d48f924 100644 --- a/tools/bootstrap-sandbox/Cargo.toml +++ b/tools/bootstrap-sandbox/Cargo.toml @@ -13,7 +13,7 @@ thiserror = { version = "1.0" } bs58 = { version = "0.5.0", features = ["check"] } rand = { version = "0.8.5" } -base64 = { version = "0.21.7" } +base64 = { version = "0.22" } tokio = { version = "1.37", features = ["macros", "rt-multi-thread"] } From 3c804d2e932d391cec2a21155f853fbeb784f4f1 Mon Sep 17 00:00:00 2001 From: Bruno Deferrari Date: Wed, 15 Jan 2025 22:59:24 -0300 Subject: [PATCH 2/3] feat: Add google cloud function to receive heartbeats --- frontend/functions/.gitignore | 1 + .../functions/__tests__/signature.test.js | 51 ++ frontend/functions/index.js | 81 --- frontend/functions/index.ts | 124 ++++ frontend/functions/jest.config.js | 8 + frontend/functions/package-lock.json | 623 ++++++++++-------- frontend/functions/package.json | 21 +- frontend/functions/tsconfig.json | 16 + 8 files changed, 554 insertions(+), 371 deletions(-) create mode 100644 frontend/functions/__tests__/signature.test.js delete mode 100644 frontend/functions/index.js create mode 100644 frontend/functions/index.ts create mode 100644 frontend/functions/jest.config.js create mode 100644 frontend/functions/tsconfig.json diff --git a/frontend/functions/.gitignore b/frontend/functions/.gitignore index b15a37a52b..40f062c1d2 100644 --- a/frontend/functions/.gitignore +++ b/frontend/functions/.gitignore @@ -1,2 +1,3 @@ node_modules/ *.local +coverage/ diff --git a/frontend/functions/__tests__/signature.test.js b/frontend/functions/__tests__/signature.test.js new file mode 100644 index 0000000000..a30df931fc --- /dev/null +++ b/frontend/functions/__tests__/signature.test.js @@ -0,0 +1,51 @@ +const { validateSignature } = require('../index'); + +describe('validateSignature', () => { + let signedHeartbeat; + + beforeAll(() => { + signedHeartbeat = JSON.parse(`{ + "version": 1, + "payload": "eyJzdGF0dXMiOnsiY2hhaW5faWQiOm51bGwsInRyYW5zaXRpb25fZnJvbnRpZXIiOnsiYmVzdF90aXAiOm51bGwsInN5bmMiOnsidGltZSI6bnVsbCwic3RhdHVzIjoiU3luY2VkIiwicGhhc2UiOiJSdW5uaW5nIiwidGFyZ2V0IjpudWxsfX0sInBlZXJzIjpbXSwic25hcmtfcG9vbCI6eyJ0b3RhbF9qb2JzIjowLCJzbmFya3MiOjB9LCJ0cmFuc2FjdGlvbl9wb29sIjp7InRyYW5zYWN0aW9ucyI6MCwidHJhbnNhY3Rpb25zX2Zvcl9wcm9wYWdhdGlvbiI6MCwidHJhbnNhY3Rpb25fY2FuZGlkYXRlcyI6MH0sImN1cnJlbnRfYmxvY2tfcHJvZHVjdGlvbl9hdHRlbXB0IjpudWxsfSwibm9kZV90aW1lc3RhbXAiOjAsInBlZXJfaWQiOiIyYkVnQnJQVHpMOHdvdjJENEt6MzRXVkxDeFI0dUNhcnNCbUhZWFdLUUE1d3ZCUXpkOUgiLCJsYXN0X3Byb2R1Y2VkX2Jsb2NrIjpudWxsfQ==", + "submitter": "B62qnLjgW4LAnrxkcdLc7Snb49qx6aP5qsmPsp6ueZN4XPMC621cqGc", + "signature": { + "field": "25500978175045040705256298774101531557080530394536110798266178142513301557846", + "scalar": "27991123709623419396663280967637181749724990269901703962618583375785482061803" + } + }`); + }); + + test('should validate correct signature', () => { + const result = validateSignature( + signedHeartbeat.payload, + signedHeartbeat.signature, + signedHeartbeat.submitter + ); + expect(result).toBe(true); + }); + + test('should reject invalid signature length', () => { + const result = validateSignature( + signedHeartbeat.payload, + 'invalid-signature', + signedHeartbeat.submitter + ); + expect(result).toBe(false); + }); + + test('should reject tampered data', () => { + const tamperedPayload = signedHeartbeat.payload + 'tampered'; + const result = validateSignature( + tamperedPayload, + signedHeartbeat.signature, + signedHeartbeat.submitter + ); + expect(result).toBe(false); + }); + + test('should handle null values', () => { + expect(validateSignature(null, null, null)).toBe(false); + expect(validateSignature(signedHeartbeat.payload, null, signedHeartbeat.submitter)).toBe(false); + expect(validateSignature(signedHeartbeat.payload, signedHeartbeat.signature, null)).toBe(false); + }); +}); diff --git a/frontend/functions/index.js b/frontend/functions/index.js deleted file mode 100644 index 24363680be..0000000000 --- a/frontend/functions/index.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Import function triggers from their respective submodules: - * - * const {onCall} = require("firebase-functions/v2/https"); - * const {onDocumentWritten} = require("firebase-functions/v2/firestore"); - * - * See a full list of supported triggers at https://firebase.google.com/docs/functions - */ -// -// const {onRequest} = require('firebase-functions/v2/https'); -// const logger = require('firebase-functions/logger'); -// -// Create and deploy your first functions -// https://firebase.google.com/docs/functions/get-started -// -// exports.helloWorld = onRequest((request, response) => { -// logger.info('Hello logs!', {structuredData: true}); -// response.send('Hello from Firebase!'); -// }); - -const admin = require('firebase-admin'); -const functions = require('firebase-functions'); - -admin.initializeApp(); - -const allowedPublicKeys = new Set([ - 'publicKey1Base64Encoded', - 'publicKey2Base64Encoded', - // Add more as needed -]); - -function validateSignature(data, signature, publicKeyBase64) { - - return true; -} - -exports.handleValidationAndStore = functions.region('us-central1').https.onCall(async (data, context) => { - console.log('Received data:', data); - const {publicKey, data: inputData, signature} = data; - - // Check if the publicKey is in the allowed set - if (!allowedPublicKeys.has(publicKey)) { - throw new functions.https.HttpsError('permission-denied', 'Public key not authorized'); - } - - // Rate limiting based on public key - const rateLimitRef = admin.firestore().collection('publicKeyRateLimits').doc(publicKey); - - try { - await admin.firestore().runTransaction(async (transaction) => { - const doc = await transaction.get(rateLimitRef); - const now = Date.now(); - const cutoff = now - 15 * 1000; // 15 seconds ago - - if (doc.exists) { - const lastCall = doc.data().lastCall; - if (lastCall > cutoff) { - throw new functions.https.HttpsError('resource-exhausted', 'Rate limit exceeded for this public key'); - } - } - - transaction.set(rateLimitRef, {lastCall: now}, {merge: true}); - }); - - // Validate signature - if (!validateSignature(inputData, signature, publicKey)) { - throw new functions.https.HttpsError('unauthenticated', 'Signature validation failed'); - } - - // Store data in 'heartbeat' collection - await admin.firestore().collection('heartbeat').add(inputData); - - return {message: 'Data validated and stored successfully'}; - } catch (error) { - console.error('Error during data validation and storage:', error); - if (error instanceof functions.https.HttpsError) { - throw error; // Re-throw HttpsError for Firebase Functions to handle - } - throw new functions.https.HttpsError('internal', 'An error occurred during validation or storage'); - } -}); diff --git a/frontend/functions/index.ts b/frontend/functions/index.ts new file mode 100644 index 0000000000..e3c08f9ed7 --- /dev/null +++ b/frontend/functions/index.ts @@ -0,0 +1,124 @@ +import * as admin from 'firebase-admin'; +import * as functions from 'firebase-functions'; +import * as blake2 from 'blake2'; +import bs58check from 'bs58check'; +import Client from 'mina-signer'; + +interface SignatureJson { + field: string; + scalar: string; +} + +interface HeartbeatData { + publicKey: string; + data: string; + signature: SignatureJson; +} + +const minaClient = new Client({ network: 'testnet' }); + +admin.initializeApp(); + +// base58 encoded public keys that are allowed to submit data +const allowedPublicKeys: Set = new Set([ +]); + +function validateSignature( + data: string, + signature: SignatureJson, + publicKeyBase58: string +): boolean { + try { + const h = blake2.createHash('blake2b', { digestLength: 32 }); + h.update(Buffer.from(data)); + const digest: string = h.digest().toString('hex'); + + try { + // TODO: remove this validation later, since the list is + // hardcoded and we check that the key is there, + // we know it is valid. + let publicKeyBytes: Uint8Array; + try { + publicKeyBytes = bs58check.decode(publicKeyBase58); + } catch (e) { + console.error('Failed to decode public key:', e); + return false; + } + + if (publicKeyBytes[0] !== 0xcb) { + console.error('Invalid public key prefix'); + return false; + } + + return minaClient.verifyMessage({ + data: digest, + signature, + publicKey: publicKeyBase58, + }); + } catch (e) { + console.error('Error parsing signature or verifying:', e); + return false; + } + } catch (e) { + console.error('Error in signature validation:', e); + return false; + } +} + +export const handleValidationAndStore = functions + .region('us-central1') + .https.onCall(async (data: HeartbeatData, context: functions.https.CallableContext) => { + console.log('Received data:', data); + const { publicKey, data: inputData, signature } = data; + + if (!allowedPublicKeys.has(publicKey)) { + throw new functions.https.HttpsError( + 'permission-denied', + 'Public key not authorized' + ); + } + + const rateLimitRef = admin.firestore().collection('publicKeyRateLimits').doc(publicKey); + + try { + await admin.firestore().runTransaction(async (transaction) => { + const doc = await transaction.get(rateLimitRef); + const now = Date.now(); + const cutoff = now - 15 * 1000; + + if (doc.exists) { + const lastCall = doc.data()?.lastCall; + if (lastCall > cutoff) { + throw new functions.https.HttpsError( + 'resource-exhausted', + 'Rate limit exceeded for this public key' + ); + } + } + + transaction.set(rateLimitRef, { lastCall: now }, { merge: true }); + }); + + if (!validateSignature(inputData, signature, publicKey)) { + throw new functions.https.HttpsError( + 'unauthenticated', + 'Signature validation failed' + ); + } + + await admin.firestore().collection('heartbeat').add(data); + + return { message: 'Data validated and stored successfully' }; + } catch (error) { + console.error('Error during data validation and storage:', error); + if (error instanceof functions.https.HttpsError) { + throw error; + } + throw new functions.https.HttpsError( + 'internal', + 'An error occurred during validation or storage' + ); + } + }); + +export { validateSignature }; diff --git a/frontend/functions/jest.config.js b/frontend/functions/jest.config.js new file mode 100644 index 0000000000..7d5f854ffb --- /dev/null +++ b/frontend/functions/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + preset: "ts-jest", + testEnvironment: 'node', + testMatch: ['**/__tests__/**/*.test.js'], + collectCoverage: true, + coverageReporters: ['text', 'lcov'], + coverageDirectory: 'coverage' +}; diff --git a/frontend/functions/package-lock.json b/frontend/functions/package-lock.json index 0fb58db86f..13d355426c 100644 --- a/frontend/functions/package-lock.json +++ b/frontend/functions/package-lock.json @@ -6,11 +6,20 @@ "": { "name": "functions", "dependencies": { + "blake2": "^5.0.0", + "bs58check": "^3.0.1", "firebase-admin": "^12.1.0", - "firebase-functions": "^5.0.0" + "firebase-functions": "^5.0.0", + "mina-signer": "^3.0.7" }, "devDependencies": { - "firebase-functions-test": "^3.1.0" + "@types/blake2": "^4.0.1", + "@types/bs58check": "^2.1.0", + "@types/jest": "^29.5.14", + "firebase-functions-test": "^3.1.0", + "jest": "^29.0.0", + "ts-jest": "^29.2.5", + "typescript": "^4.9.5" }, "engines": { "node": "18" @@ -22,7 +31,6 @@ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -37,7 +45,6 @@ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -53,7 +60,6 @@ "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -64,7 +70,6 @@ "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -96,7 +101,6 @@ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -114,8 +118,7 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@babel/generator": { "version": "7.26.3", @@ -123,7 +126,6 @@ "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.26.3", "@babel/types": "^7.26.3", @@ -141,7 +143,6 @@ "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/compat-data": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -159,7 +160,6 @@ "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -174,7 +174,6 @@ "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", @@ -193,7 +192,6 @@ "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -204,7 +202,6 @@ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -215,7 +212,6 @@ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -226,7 +222,6 @@ "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -237,7 +232,6 @@ "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/template": "^7.25.9", "@babel/types": "^7.26.0" @@ -252,7 +246,6 @@ "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/types": "^7.26.3" }, @@ -269,7 +262,6 @@ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -283,7 +275,6 @@ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -297,7 +288,6 @@ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -311,7 +301,6 @@ "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -328,7 +317,6 @@ "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -345,7 +333,6 @@ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -359,7 +346,6 @@ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -373,7 +359,6 @@ "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -390,7 +375,6 @@ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -404,7 +388,6 @@ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -418,7 +401,6 @@ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -432,7 +414,6 @@ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -446,7 +427,6 @@ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -460,7 +440,6 @@ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -474,7 +453,6 @@ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -491,7 +469,6 @@ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -508,7 +485,6 @@ "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -525,7 +501,6 @@ "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/parser": "^7.25.9", @@ -541,7 +516,6 @@ "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.3", @@ -561,7 +535,6 @@ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -579,8 +552,7 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@babel/types": { "version": "7.26.3", @@ -588,7 +560,6 @@ "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -602,8 +573,7 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@fastify/busboy": { "version": "3.1.1", @@ -823,7 +793,6 @@ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -841,7 +810,6 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -852,7 +820,6 @@ "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -871,7 +838,6 @@ "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", @@ -920,7 +886,6 @@ "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", @@ -937,7 +902,6 @@ "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" @@ -952,7 +916,6 @@ "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "jest-get-type": "^29.6.3" }, @@ -966,7 +929,6 @@ "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", @@ -985,7 +947,6 @@ "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -1002,7 +963,6 @@ "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", @@ -1047,7 +1007,6 @@ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -1061,7 +1020,6 @@ "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", @@ -1077,7 +1035,6 @@ "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", @@ -1094,7 +1051,6 @@ "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", @@ -1111,7 +1067,6 @@ "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -1139,7 +1094,6 @@ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -1158,7 +1112,6 @@ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1174,7 +1127,6 @@ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.0.0" } @@ -1185,7 +1137,6 @@ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.0.0" } @@ -1195,8 +1146,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -1204,7 +1154,6 @@ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1221,6 +1170,18 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/@noble/hashes": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", + "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -1300,8 +1261,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@sinonjs/commons": { "version": "3.0.1", @@ -1309,7 +1269,6 @@ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "type-detect": "4.0.8" } @@ -1320,7 +1279,6 @@ "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@sinonjs/commons": "^3.0.0" } @@ -1341,7 +1299,6 @@ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -1356,7 +1313,6 @@ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/types": "^7.0.0" } @@ -1367,7 +1323,6 @@ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -1379,11 +1334,20 @@ "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/types": "^7.20.7" } }, + "node_modules/@types/blake2": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/blake2/-/blake2-4.0.4.tgz", + "integrity": "sha512-r84TojGHMbBoH91XQjqoc1N89xy/LmcGb15k9OSdB2APb+xQfNcfbcFGMa9RbMmFsnIKCRDMpuKHBM04AwdgxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -1394,6 +1358,16 @@ "@types/node": "*" } }, + "node_modules/@types/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-xpXaQlOIY1KoXlA/ytHGHpEIU87PJt+g9SH7nC6HdCgaBwT2IEZIwBMHbjuX6BpnfbiUMlmwqurdLDwXpcdmSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/caseless": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", @@ -1448,7 +1422,6 @@ "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*" } @@ -1464,8 +1437,7 @@ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", @@ -1473,7 +1445,6 @@ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -1484,11 +1455,21 @@ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/jsonwebtoken": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", @@ -1578,8 +1559,7 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/tough-cookie": { "version": "4.0.5", @@ -1594,7 +1574,6 @@ "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/yargs-parser": "*" } @@ -1604,8 +1583,7 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/abort-controller": { "version": "3.0.0", @@ -1649,7 +1627,6 @@ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "type-fest": "^0.21.3" }, @@ -1692,7 +1669,6 @@ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1707,7 +1683,6 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -1728,6 +1703,13 @@ "node": ">=8" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", @@ -1751,7 +1733,6 @@ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -1774,7 +1755,6 @@ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", @@ -1792,7 +1772,6 @@ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", @@ -1810,7 +1789,6 @@ "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -1827,7 +1805,6 @@ "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", @@ -1855,7 +1832,6 @@ "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -1872,8 +1848,13 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" + }, + "node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==", + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -1906,6 +1887,25 @@ "node": "*" } }, + "node_modules/blake2": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/blake2/-/blake2-5.0.0.tgz", + "integrity": "sha512-MLpq1DwBB9rC0IHuRc2gXLEAeNNTTYHEtvYCA5lK4RmoUPRmQLSLQrwgJvou62BvH9KP7whe8n+xxw45++fnYg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "nan": "^2.17.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "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==", + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -1936,7 +1936,6 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1948,7 +1947,6 @@ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -1976,7 +1974,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -1990,13 +1987,44 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "license": "MIT", + "dependencies": { + "base-x": "^4.0.0" + } + }, + "node_modules/bs58check": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz", + "integrity": "sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bs58": "^5.0.0" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "node-int64": "^0.4.0" } @@ -2012,8 +2040,7 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/bytes": { "version": "3.1.2", @@ -2059,7 +2086,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -2070,7 +2096,6 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -2094,8 +2119,7 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "CC-BY-4.0", - "peer": true + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "4.1.2", @@ -2103,7 +2127,6 @@ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2121,7 +2144,6 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -2138,7 +2160,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -2148,8 +2169,7 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", @@ -2172,7 +2192,6 @@ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" @@ -2183,8 +2202,7 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/color-convert": { "version": "2.0.1", @@ -2224,8 +2242,7 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -2253,8 +2270,7 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cookie": { "version": "0.7.1", @@ -2290,7 +2306,6 @@ "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -2313,7 +2328,6 @@ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2338,7 +2352,6 @@ "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -2354,7 +2367,6 @@ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2394,7 +2406,6 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -2405,7 +2416,6 @@ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -2452,13 +2462,28 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.79", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz", "integrity": "sha512-nYOxJNxQ9Om4EC88BE4pPoNI8xwSFf8pU/BAeOl4Hh/b/i6V4biTAzwV7pXi3ARKeoYO5JZKMIXTryXSVer5RA==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/emittery": { "version": "0.13.1", @@ -2466,7 +2491,6 @@ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2506,7 +2530,6 @@ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -2563,7 +2586,6 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -2574,7 +2596,6 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2608,7 +2629,6 @@ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -2632,7 +2652,6 @@ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -2643,7 +2662,6 @@ "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", @@ -2729,8 +2747,7 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-xml-parser": { "version": "4.5.1", @@ -2773,18 +2790,49 @@ "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "bser": "2.1.1" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2816,7 +2864,6 @@ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -2930,8 +2977,7 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", @@ -2944,7 +2990,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -3016,7 +3061,6 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -3061,7 +3105,6 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -3085,7 +3128,6 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -3100,7 +3142,6 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3122,7 +3163,6 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -3200,8 +3240,7 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/gtoken": { "version": "7.1.0", @@ -3223,7 +3262,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -3274,8 +3312,7 @@ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/http-errors": { "version": "2.0.0", @@ -3397,7 +3434,6 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=10.17.0" } @@ -3420,7 +3456,6 @@ "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -3441,7 +3476,6 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.19" } @@ -3453,7 +3487,6 @@ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3479,8 +3512,7 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/is-core-module": { "version": "2.16.1", @@ -3488,7 +3520,6 @@ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "hasown": "^2.0.2" }, @@ -3515,7 +3546,6 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -3526,7 +3556,6 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.12.0" } @@ -3549,8 +3578,7 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -3558,7 +3586,6 @@ "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=8" } @@ -3569,7 +3596,6 @@ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -3587,7 +3613,6 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -3601,7 +3626,6 @@ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -3617,7 +3641,6 @@ "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -3633,7 +3656,6 @@ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -3651,8 +3673,7 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/istanbul-reports": { "version": "3.1.7", @@ -3660,7 +3681,6 @@ "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -3669,13 +3689,31 @@ "node": ">=8" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -3703,7 +3741,6 @@ "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", @@ -3719,7 +3756,6 @@ "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -3752,7 +3788,6 @@ "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -3787,7 +3822,6 @@ "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", @@ -3834,7 +3868,6 @@ "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", @@ -3851,7 +3884,6 @@ "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "detect-newline": "^3.0.0" }, @@ -3865,7 +3897,6 @@ "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -3883,7 +3914,6 @@ "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -3902,7 +3932,6 @@ "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -3913,7 +3942,6 @@ "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -3940,7 +3968,6 @@ "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" @@ -3955,7 +3982,6 @@ "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", @@ -3972,7 +3998,6 @@ "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", @@ -3994,7 +4019,6 @@ "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -4010,7 +4034,6 @@ "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -4029,7 +4052,6 @@ "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -4040,7 +4062,6 @@ "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -4062,7 +4083,6 @@ "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" @@ -4077,7 +4097,6 @@ "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", @@ -4111,7 +4130,6 @@ "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -4146,7 +4164,6 @@ "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", @@ -4179,7 +4196,6 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -4193,7 +4209,6 @@ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -4212,7 +4227,6 @@ "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -4231,7 +4245,6 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -4245,7 +4258,6 @@ "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", @@ -4266,7 +4278,6 @@ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -4283,7 +4294,6 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -4303,13 +4313,18 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/js-yaml": { "version": "3.14.1", @@ -4317,7 +4332,6 @@ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -4332,7 +4346,6 @@ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -4355,8 +4368,7 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", @@ -4364,7 +4376,6 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -4526,7 +4537,6 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -4537,7 +4547,6 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -4552,8 +4561,7 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/locate-path": { "version": "5.0.0", @@ -4561,7 +4569,6 @@ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -4625,6 +4632,13 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -4643,7 +4657,6 @@ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "yallist": "^3.0.2" } @@ -4682,7 +4695,6 @@ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.5.3" }, @@ -4699,7 +4711,6 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -4707,13 +4718,19 @@ "node": ">=10" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "tmpl": "1.0.5" } @@ -4750,8 +4767,7 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/methods": { "version": "1.1.2", @@ -4768,7 +4784,6 @@ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -4817,18 +4832,26 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } }, + "node_modules/mina-signer": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/mina-signer/-/mina-signer-3.0.7.tgz", + "integrity": "sha512-7eYp/6WWj2VzJjvfC8dNeGMud/brdBrzkUsCdysFFXnfV2/FVpVhAGCMfaS6hs0HJtS4+eplmiD2hXfshQS8CQ==", + "license": "Apache-2.0", + "dependencies": { + "blakejs": "^1.2.1", + "js-sha256": "^0.9.0" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4842,13 +4865,18 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/nan": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", + "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/negotiator": { "version": "0.6.3", @@ -4894,16 +4922,14 @@ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -4911,7 +4937,6 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -4922,7 +4947,6 @@ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.0.0" }, @@ -4989,7 +5013,6 @@ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -5022,7 +5045,6 @@ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -5036,7 +5058,6 @@ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-try": "^2.0.0" }, @@ -5053,7 +5074,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -5064,7 +5084,6 @@ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -5093,7 +5112,6 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -5104,7 +5122,6 @@ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5115,7 +5132,6 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -5125,8 +5141,7 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/path-to-regexp": { "version": "0.1.12", @@ -5139,8 +5154,7 @@ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -5148,7 +5162,6 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8.6" }, @@ -5162,7 +5175,6 @@ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 6" } @@ -5173,7 +5185,6 @@ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "find-up": "^4.0.0" }, @@ -5187,7 +5198,6 @@ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -5203,7 +5213,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -5217,7 +5226,6 @@ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -5291,8 +5299,7 @@ "url": "https://opencollective.com/fast-check" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/qs": { "version": "6.13.0", @@ -5338,8 +5345,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/readable-stream": { "version": "3.6.2", @@ -5372,7 +5378,6 @@ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -5394,7 +5399,6 @@ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "resolve-from": "^5.0.0" }, @@ -5408,7 +5412,6 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -5419,7 +5422,6 @@ "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -5481,7 +5483,6 @@ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -5564,7 +5565,6 @@ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -5578,7 +5578,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -5660,16 +5659,14 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/slash": { "version": "3.0.0", @@ -5677,7 +5674,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -5688,7 +5684,6 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5699,7 +5694,6 @@ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -5710,8 +5704,7 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/stack-utils": { "version": "2.0.6", @@ -5719,7 +5712,6 @@ "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -5769,7 +5761,6 @@ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -5812,7 +5803,6 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -5823,7 +5813,6 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -5834,7 +5823,6 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" }, @@ -5862,7 +5850,6 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -5876,7 +5863,6 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -5973,7 +5959,6 @@ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -5988,8 +5973,7 @@ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -5997,7 +5981,6 @@ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-number": "^7.0.0" }, @@ -6028,6 +6011,68 @@ "dev": true, "license": "ISC" }, + "node_modules/ts-jest": { + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6040,7 +6085,6 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -6051,7 +6095,6 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=10" }, @@ -6072,6 +6115,20 @@ "node": ">= 0.6" } }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", @@ -6107,7 +6164,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -6154,7 +6210,6 @@ "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -6179,7 +6234,6 @@ "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "makeerror": "1.0.12" } @@ -6231,7 +6285,6 @@ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -6273,7 +6326,6 @@ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -6297,8 +6349,7 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", diff --git a/frontend/functions/package.json b/frontend/functions/package.json index 2e86341e08..2f8c6c097b 100644 --- a/frontend/functions/package.json +++ b/frontend/functions/package.json @@ -6,18 +6,31 @@ "shell": "firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", - "logs": "firebase functions:log" + "logs": "firebase functions:log", + "test": "jest", + "test:watch": "jest --watch", + "build": "tsc", + "build:watch": "tsc --watch" }, "engines": { "node": "18" }, - "main": "index.js", + "main": "index.ts", "dependencies": { + "blake2": "^5.0.0", + "bs58check": "^3.0.1", "firebase-admin": "^12.1.0", - "firebase-functions": "^5.0.0" + "firebase-functions": "^5.0.0", + "mina-signer": "^3.0.7" }, "devDependencies": { - "firebase-functions-test": "^3.1.0" + "@types/blake2": "^4.0.1", + "@types/bs58check": "^2.1.0", + "@types/jest": "^29.5.14", + "firebase-functions-test": "^3.1.0", + "jest": "^29.0.0", + "ts-jest": "^29.2.5", + "typescript": "^4.9.5" }, "private": true } diff --git a/frontend/functions/tsconfig.json b/frontend/functions/tsconfig.json new file mode 100644 index 0000000000..8fb649875a --- /dev/null +++ b/frontend/functions/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "noUnusedLocals": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "esModuleInterop": true + }, + "compileOnSave": true, + "include": [ + "src" + ] +} From c4af8feddd4c88b23d5ab5831998a7e4e02a3df6 Mon Sep 17 00:00:00 2001 From: Bruno Deferrari Date: Thu, 16 Jan 2025 09:32:55 -0300 Subject: [PATCH 3/3] feat(hearbeat): Move submitter validation logic to it's own file --- frontend/functions/index.ts | 7 ++----- frontend/functions/submitterValidator.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 frontend/functions/submitterValidator.ts diff --git a/frontend/functions/index.ts b/frontend/functions/index.ts index e3c08f9ed7..bf6709769b 100644 --- a/frontend/functions/index.ts +++ b/frontend/functions/index.ts @@ -3,6 +3,7 @@ import * as functions from 'firebase-functions'; import * as blake2 from 'blake2'; import bs58check from 'bs58check'; import Client from 'mina-signer'; +import { submitterAllowed } from './submitterValidator'; interface SignatureJson { field: string; @@ -19,10 +20,6 @@ const minaClient = new Client({ network: 'testnet' }); admin.initializeApp(); -// base58 encoded public keys that are allowed to submit data -const allowedPublicKeys: Set = new Set([ -]); - function validateSignature( data: string, signature: SignatureJson, @@ -71,7 +68,7 @@ export const handleValidationAndStore = functions console.log('Received data:', data); const { publicKey, data: inputData, signature } = data; - if (!allowedPublicKeys.has(publicKey)) { + if (!submitterAllowed(publicKey)) { throw new functions.https.HttpsError( 'permission-denied', 'Public key not authorized' diff --git a/frontend/functions/submitterValidator.ts b/frontend/functions/submitterValidator.ts new file mode 100644 index 0000000000..3643e66977 --- /dev/null +++ b/frontend/functions/submitterValidator.ts @@ -0,0 +1,10 @@ +// base58 encoded public keys that are allowed to submit data +const allowedPublicKeys: Set = new Set([ +]); + +export function submitterAllowed(publicKeyBase58: string): boolean { + if (allowedPublicKeys.size === 0) { + return true; + } + return allowedPublicKeys.has(publicKeyBase58); +}