From d1bc270e8e7f55356c4190749a5026483b15f9c3 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 26 May 2024 17:41:59 +0300 Subject: [PATCH 001/158] support numeric interface (ip) arg without port in --rpclisten-borsh or --rpclisten-json --- rpc/wrpc/server/src/address.rs | 12 +++++++++++- utils/src/networking.rs | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/rpc/wrpc/server/src/address.rs b/rpc/wrpc/server/src/address.rs index 7dac4d75d9..6fa51eac86 100644 --- a/rpc/wrpc/server/src/address.rs +++ b/rpc/wrpc/server/src/address.rs @@ -29,7 +29,17 @@ impl WrpcNetAddress { }; format!("0.0.0.0:{port}").parse().unwrap() } - WrpcNetAddress::Custom(address) => *address, + WrpcNetAddress::Custom(address) => { + if address.has_port() { + *address + } else { + let port = match encoding { + WrpcEncoding::Borsh => network_type.default_borsh_rpc_port(), + WrpcEncoding::SerdeJson => network_type.default_json_rpc_port(), + }; + format!("{address}:{port}").parse().unwrap() + } + } } } } diff --git a/utils/src/networking.rs b/utils/src/networking.rs index bb38b4d046..417471e8bd 100644 --- a/utils/src/networking.rs +++ b/utils/src/networking.rs @@ -275,6 +275,10 @@ impl ContextualNetAddress { Self { ip, port } } + pub fn has_port(&self) -> bool { + self.port.is_some() + } + pub fn normalize(&self, default_port: u16) -> NetAddress { NetAddress::new(self.ip, self.port.unwrap_or(default_port)) } From 109da8494c0a59a5344ffcd5909908ebb65c702f Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 26 May 2024 19:32:27 +0300 Subject: [PATCH 002/158] isActive for UtxoProcessor and UtxoContext --- wallet/core/src/wasm/utxo/context.rs | 1 + wallet/core/src/wasm/utxo/processor.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/wallet/core/src/wasm/utxo/context.rs b/wallet/core/src/wasm/utxo/context.rs index 2b69eb0c47..c66a3f647e 100644 --- a/wallet/core/src/wasm/utxo/context.rs +++ b/wallet/core/src/wasm/utxo/context.rs @@ -147,6 +147,7 @@ impl UtxoContext { self.inner().clear().await } + #[wasm_bindgen(getter, js_name = "isActive")] pub fn active(&self) -> bool { let processor = self.inner().processor(); processor.try_rpc_ctl().map(|ctl| ctl.is_connected()).unwrap_or(false) && processor.is_connected() && processor.is_running() diff --git a/wallet/core/src/wasm/utxo/processor.rs b/wallet/core/src/wasm/utxo/processor.rs index 0e41d8f773..2d35a4a9a8 100644 --- a/wallet/core/src/wasm/utxo/processor.rs +++ b/wallet/core/src/wasm/utxo/processor.rs @@ -153,6 +153,12 @@ impl UtxoProcessor { self.inner.processor.set_network_id(network_id.as_ref()); Ok(()) } + + #[wasm_bindgen(getter, js_name = "isActive")] + pub fn is_active(&self) -> bool { + let processor = &self.inner.processor; + processor.try_rpc_ctl().map(|ctl| ctl.is_connected()).unwrap_or(false) && processor.is_connected() && processor.is_running() + } } impl TryCastFromJs for UtxoProcessor { From 03b21bad74a36dd7f8b1dbfc96eb684747c2576d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 26 May 2024 19:32:52 +0300 Subject: [PATCH 003/158] Script utility functions + WASM changelog update --- consensus/client/src/lib.rs | 4 +- consensus/client/src/utils.rs | 77 ++++++++++++++++++++++ consensus/client/src/vtx.rs | 35 ---------- consensus/core/src/tx.rs | 4 +- consensus/core/src/tx/script_public_key.rs | 6 ++ crypto/addresses/src/lib.rs | 2 + wasm/CHANGELOG.md | 7 ++ 7 files changed, 97 insertions(+), 38 deletions(-) create mode 100644 consensus/client/src/utils.rs delete mode 100644 consensus/client/src/vtx.rs diff --git a/consensus/client/src/lib.rs b/consensus/client/src/lib.rs index 4935b16f76..c897773f77 100644 --- a/consensus/client/src/lib.rs +++ b/consensus/client/src/lib.rs @@ -13,7 +13,7 @@ cfg_if::cfg_if! { mod header; mod input; mod transaction; - mod vtx; + mod utils; mod hash; mod sign; mod script; @@ -24,7 +24,7 @@ cfg_if::cfg_if! { pub use input::*; pub use transaction::*; pub use serializable::*; - pub use vtx::*; + pub use utils::*; pub use hash::*; // pub use signing::*; pub use script::*; diff --git a/consensus/client/src/utils.rs b/consensus/client/src/utils.rs new file mode 100644 index 0000000000..47d3a90b61 --- /dev/null +++ b/consensus/client/src/utils.rs @@ -0,0 +1,77 @@ +use crate::imports::*; +use crate::result::Result; +use kaspa_addresses::*; +use kaspa_consensus_core::tx::ScriptPublicKeyT; +use kaspa_txscript::{script_class::ScriptClass, standard}; +use kaspa_utils::hex::ToHex; +use kaspa_wasm_core::types::{BinaryT, HexString}; + +/// Creates a new script to pay a transaction output to the specified address. +/// @category Wallet SDK +#[wasm_bindgen(js_name = payToAddressScript)] +pub fn pay_to_address_script(address: AddressT) -> Result { + let address = Address::try_cast_from(address)?; + Ok(standard::pay_to_address_script(address.as_ref())) +} + +/// Takes a script and returns an equivalent pay-to-script-hash script. +/// @param redeem_script - The redeem script ({@link HexString} or Uint8Array). +/// @category Wallet SDK +#[wasm_bindgen(js_name = payToScriptHashScript)] +pub fn pay_to_script_hash_script(redeem_script: BinaryT) -> Result { + let redeem_script = redeem_script.try_as_vec_u8()?; + Ok(standard::pay_to_script_hash_script(redeem_script.as_slice())) +} + +/// Generates a signature script that fits a pay-to-script-hash script. +/// @param redeem_script - The redeem script ({@link HexString} or Uint8Array). +/// @param signature - The signature ({@link HexString} or Uint8Array). +/// @category Wallet SDK +#[wasm_bindgen(js_name = payToScriptHashSignatureScript)] +pub fn pay_to_script_hash_signature_script(redeem_script: BinaryT, signature: BinaryT) -> Result { + let redeem_script = redeem_script.try_as_vec_u8()?; + let signature = signature.try_as_vec_u8()?; + let script = standard::pay_to_script_hash_signature_script(redeem_script, signature)?; + Ok(script.to_hex().into()) +} + +/// Returns the address encoded in a script public key. +/// @param script_public_key - The script public key ({@link ScriptPublicKey}). +/// @param prefix - The address prefix. +/// @category Wallet SDK +#[wasm_bindgen(js_name = addressFromScriptPublicKey)] +pub fn address_from_script_public_key(script_public_key: ScriptPublicKeyT, prefix: String) -> Result { + let script_public_key = ScriptPublicKey::try_cast_from(script_public_key)?; + let prefix = Prefix::try_from(prefix.as_str())?; + match standard::extract_script_pub_key_address(script_public_key.as_ref(), prefix) { + Ok(address) => Ok(AddressOrUndefinedT::from(JsValue::from(address))), + Err(_) => Ok(AddressOrUndefinedT::from(JsValue::UNDEFINED)), + } +} + +/// Returns true if the script passed is a pay-to-pubkey. +/// @param script - The script ({@link HexString} or Uint8Array). +/// @category Wallet SDK +#[wasm_bindgen(js_name = isScriptPayToPubkey)] +pub fn is_script_pay_to_pubkey(script: BinaryT) -> Result { + let script = script.try_as_vec_u8()?; + Ok(ScriptClass::is_pay_to_pubkey(script.as_slice())) +} + +/// Returns returns true if the script passed is an ECDSA pay-to-pubkey. +/// @param script - The script ({@link HexString} or Uint8Array). +/// @category Wallet SDK +#[wasm_bindgen(js_name = isScriptPayToPubkeyECDSA)] +pub fn is_script_pay_to_pubkey_ecdsa(script: BinaryT) -> Result { + let script = script.try_as_vec_u8()?; + Ok(ScriptClass::is_pay_to_pubkey_ecdsa(script.as_slice())) +} + +/// Returns true if the script passed is a pay-to-script-hash (P2SH) format, false otherwise. +/// @param script - The script ({@link HexString} or Uint8Array). +/// @category Wallet SDK +#[wasm_bindgen(js_name = isScriptPayToScriptHash)] +pub fn is_script_pay_to_script_hash(script: BinaryT) -> Result { + let script = script.try_as_vec_u8()?; + Ok(ScriptClass::is_pay_to_script_hash(script.as_slice())) +} diff --git a/consensus/client/src/vtx.rs b/consensus/client/src/vtx.rs deleted file mode 100644 index e5fdd92363..0000000000 --- a/consensus/client/src/vtx.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::imports::*; -// use crate::serializable::{numeric,string}; -use crate::result::Result; -use kaspa_addresses::Address; -use serde::de::DeserializeOwned; -// use serde::de::DeserializeOwned; - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct VirtualTransactionT -where - T: Clone + serde::Serialize, -{ - //} + Deserialize { - pub version: u32, - pub generator: Option, - pub transactions: Vec, - pub addresses: Option>, -} - -impl VirtualTransactionT -where - T: Clone + Serialize, -{ - pub fn deserialize(json: &str) -> Result - where - T: DeserializeOwned, - { - Ok(serde_json::from_str(json)?) - } - - pub fn serialize(&self) -> String { - serde_json::to_string(self).unwrap() - } -} diff --git a/consensus/core/src/tx.rs b/consensus/core/src/tx.rs index 1376337016..fc3b0c35fd 100644 --- a/consensus/core/src/tx.rs +++ b/consensus/core/src/tx.rs @@ -4,7 +4,9 @@ use borsh::{BorshDeserialize, BorshSerialize}; use kaspa_utils::hex::ToHex; use kaspa_utils::mem_size::MemSizeEstimator; use kaspa_utils::{serde_bytes, serde_bytes_fixed_ref}; -pub use script_public_key::{scriptvec, ScriptPublicKey, ScriptPublicKeyVersion, ScriptPublicKeys, ScriptVec, SCRIPT_VECTOR_SIZE}; +pub use script_public_key::{ + scriptvec, ScriptPublicKey, ScriptPublicKeyT, ScriptPublicKeyVersion, ScriptPublicKeys, ScriptVec, SCRIPT_VECTOR_SIZE, +}; use serde::{Deserialize, Serialize}; use std::sync::atomic::AtomicU64; use std::sync::atomic::Ordering::SeqCst; diff --git a/consensus/core/src/tx/script_public_key.rs b/consensus/core/src/tx/script_public_key.rs index 7f3ade6943..459be3f4d2 100644 --- a/consensus/core/src/tx/script_public_key.rs +++ b/consensus/core/src/tx/script_public_key.rs @@ -328,6 +328,12 @@ impl ScriptPublicKey { } } +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "ScriptPublicKey | HexString")] + pub type ScriptPublicKeyT; +} + #[wasm_bindgen] impl ScriptPublicKey { #[wasm_bindgen(constructor)] diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index fdba63ef7f..b6e1110168 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -512,6 +512,8 @@ extern "C" { pub type AddressOrStringArrayT; #[wasm_bindgen(extends = js_sys::Array, typescript_type = "Address[]")] pub type AddressArrayT; + #[wasm_bindgen(typescript_type = "Address | undefined")] + pub type AddressOrUndefinedT; } impl TryFrom for Vec
{ diff --git a/wasm/CHANGELOG.md b/wasm/CHANGELOG.md index 5882d9cbf5..e601c32324 100644 --- a/wasm/CHANGELOG.md +++ b/wasm/CHANGELOG.md @@ -1,4 +1,11 @@ Latest online documentation available at: https://kaspa.aspectron.org/docs/ + +### Release 2024-05-26 + +- Adding utility functions: `payToAddressScript`, `payToScriptHashScript`, `payToScriptHashSignatureScript`, `addressFromScriptPublicKey`, `isScriptPayToPubkey`, `isScriptPayToPubkeyECDSA`, `isScriptPayToScriptHash`. +- Adding `UtxoProcessor::isActive` property to check if the processor is in active state (connected and running). This property can be used to validate the processor state before invoking it's functions (that can throw is the UtxoProcessor is offline). +- Rename `UtxoContext::active` to `UtxoContext::isActive` for consistency. + ### Release 2024-04-27 - IAccountsCreateRequest interface simplified by flattering it and now it is union for future expansion for multisig etc. - IWalletEvent interface updated for Events with TransactionRecord From 8d993cf68e8c17a69bbd4d8c8856ce21ac3ca11d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 31 May 2024 18:37:02 +0300 Subject: [PATCH 004/158] versioned serialization for wRPC --- Cargo.lock | 142 ++- Cargo.toml | 47 +- consensus/core/Cargo.toml | 1 + consensus/core/src/api/stats.rs | 22 +- notify/Cargo.toml | 1 + notify/src/scope.rs | 116 ++ rpc/core/Cargo.toml | 2 + rpc/core/src/api/notifications.rs | 97 +- rpc/core/src/model/message.rs | 1857 +++++++++++++++++++++++++++-- rpc/core/src/model/mod.rs | 1 + rpc/core/src/model/tests.rs | 1216 +++++++++++++++++++ rpc/macros/src/lib.rs | 6 + rpc/macros/src/wrpc/client.rs | 7 +- rpc/macros/src/wrpc/mod.rs | 1 + rpc/macros/src/wrpc/server.rs | 7 +- rpc/macros/src/wrpc/test.rs | 60 + rpc/wrpc/client/Cargo.toml | 3 +- rpc/wrpc/client/src/client.rs | 17 +- rpc/wrpc/proxy/src/main.rs | 1 + rpc/wrpc/server/Cargo.toml | 1 + rpc/wrpc/server/src/connection.rs | 3 +- rpc/wrpc/server/src/router.rs | 15 +- rpc/wrpc/server/src/service.rs | 1 + utils/src/networking.rs | 2 +- 24 files changed, 3408 insertions(+), 218 deletions(-) create mode 100644 rpc/core/src/model/tests.rs create mode 100644 rpc/macros/src/wrpc/test.rs diff --git a/Cargo.lock b/Cargo.lock index 22cd64f4f9..2237ed580f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1911,6 +1911,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -2121,7 +2140,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -2144,6 +2163,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -2152,6 +2172,7 @@ dependencies = [ "pin-project-lite", "smallvec", "tokio", + "want", ] [[package]] @@ -2168,15 +2189,18 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.28", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -2186,6 +2210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", + "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.0", @@ -2193,6 +2218,9 @@ dependencies = [ "pin-project-lite", "socket2 0.5.7", "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -2661,6 +2689,7 @@ dependencies = [ "web-sys", "workflow-core", "workflow-log", + "workflow-serializer", "workflow-wasm", ] @@ -2797,7 +2826,7 @@ dependencies = [ "faster-hex 0.6.1", "futures", "futures-util", - "h2", + "h2 0.3.26", "itertools 0.11.0", "kaspa-core", "kaspa-grpc-core", @@ -2827,7 +2856,7 @@ dependencies = [ "async-trait", "faster-hex 0.6.1", "futures", - "h2", + "h2 0.3.26", "kaspa-consensus-core", "kaspa-core", "kaspa-notify", @@ -2856,7 +2885,7 @@ dependencies = [ "async-trait", "faster-hex 0.6.1", "futures", - "h2", + "h2 0.3.26", "itertools 0.11.0", "kaspa-consensus-core", "kaspa-core", @@ -3074,6 +3103,7 @@ dependencies = [ "workflow-core", "workflow-log", "workflow-perf-monitor", + "workflow-serializer", ] [[package]] @@ -3114,7 +3144,7 @@ dependencies = [ "borsh", "ctrlc", "futures", - "h2", + "h2 0.3.26", "hex", "itertools 0.11.0", "kaspa-consensus-core", @@ -3225,6 +3255,7 @@ dependencies = [ "kaspa-utils", "log", "paste", + "rand 0.8.5", "serde", "serde-wasm-bindgen", "serde_json", @@ -3233,6 +3264,7 @@ dependencies = [ "uuid 1.6.1", "wasm-bindgen", "workflow-core", + "workflow-serializer", "workflow-wasm", ] @@ -3653,6 +3685,7 @@ dependencies = [ "workflow-http", "workflow-log", "workflow-rpc", + "workflow-serializer", "workflow-wasm", ] @@ -3715,6 +3748,7 @@ dependencies = [ "workflow-core", "workflow-log", "workflow-rpc", + "workflow-serializer", ] [[package]] @@ -5054,20 +5088,22 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -5076,7 +5112,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pemfile 2.1.2", "serde", "serde_json", "serde_urlencoded", @@ -5228,6 +5264,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -6081,7 +6133,7 @@ dependencies = [ "base64 0.21.7", "bytes", "flate2", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", @@ -6090,7 +6142,7 @@ dependencies = [ "pin-project", "prost", "rustls", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "tokio", "tokio-rustls", "tokio-stream", @@ -6785,9 +6837,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.50.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if 1.0.0", "windows-sys 0.48.0", @@ -6796,8 +6848,6 @@ dependencies = [ [[package]] name = "workflow-chrome" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109b6289f65b3e1cdfa6f2d9e8eb454453d5763c5061350e2300473c48d91b99" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6811,8 +6861,6 @@ dependencies = [ [[package]] name = "workflow-core" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcea01cb6122ac3f20dc14f8e4104e2c0cd9c718c17ddb3fc115f9b2ed99f9ae" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6837,13 +6885,12 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "workflow-core-macros", + "workflow-log", ] [[package]] name = "workflow-core-macros" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe24820a62e2b544c75c000cff72781383495a0e05157ec3e29b2abafe1ca2cb" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6859,8 +6906,6 @@ dependencies = [ [[package]] name = "workflow-dom" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91264d4e789f23c6730c2f3adede04a24b6a9eb9797f9d4ab23de370ba04c27f" dependencies = [ "futures", "js-sys", @@ -6877,8 +6922,6 @@ dependencies = [ [[package]] name = "workflow-http" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b191def1625c3aa5e7d62d1ebbbb3e639113a4a2f122418e4cf8d3379374f8" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6893,8 +6936,6 @@ dependencies = [ [[package]] name = "workflow-log" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "077a8f720aa45c8cd867de1ccc73e068c4084d9fea46d11be7697a108e6a00ba" dependencies = [ "cfg-if 1.0.0", "console", @@ -6909,8 +6950,6 @@ dependencies = [ [[package]] name = "workflow-macro-tools" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a8af8b8951fa0cf94df4057b8cf583e067a525d3d997370db7797f33ba201f" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6922,8 +6961,6 @@ dependencies = [ [[package]] name = "workflow-node" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7748eb6c76779993ed7f4457356d6b57f48f97f9e264c64c3405098330bcb8c7" dependencies = [ "borsh", "futures", @@ -6943,8 +6980,6 @@ dependencies = [ [[package]] name = "workflow-nw" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010fff3468303b39fb0d5d267847a3d293ed083afbf83f4184fb1a749be56010" dependencies = [ "ahash 0.8.11", "async-trait", @@ -6967,8 +7002,6 @@ dependencies = [ [[package]] name = "workflow-panic-hook" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c1ed51290daf255e5fd83dfe6bd754b108e371b971afbb5c5fd1ea8fe148af" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -6992,8 +7025,6 @@ dependencies = [ [[package]] name = "workflow-rpc" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14784fbad27d0403fc752d835c4c4683cfc6af970a484ea83f40ce7ad6dc7745" dependencies = [ "ahash 0.8.11", "async-std", @@ -7022,8 +7053,6 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c372e99d1336a137b907274a3c50fc195e30141c87fc6da4dba54e7d4b09b8ec" dependencies = [ "parse-variants", "proc-macro-error", @@ -7032,11 +7061,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "workflow-serializer" +version = "0.12.1" +dependencies = [ + "ahash 0.8.11", + "borsh", + "serde", +] + [[package]] name = "workflow-store" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "762861614298160b9205302bec4f2b7eb45853413d10a90ad8edca44bafc324b" dependencies = [ "async-std", "base64 0.21.7", @@ -7063,8 +7099,6 @@ dependencies = [ [[package]] name = "workflow-task" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4023e2598734e04aa4e968a4dd1cd2b5d0c344edc38b40970926d5742f5afa0" dependencies = [ "futures", "thiserror", @@ -7075,8 +7109,6 @@ dependencies = [ [[package]] name = "workflow-task-macros" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057801365ce04c520a2a694bc5bfdf1784f1a33fff97af4cd735f94eb12947b1" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7091,8 +7123,6 @@ dependencies = [ [[package]] name = "workflow-terminal" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "895c236dd5cf493e01fc31733c4687b3e67032f610d594ce3b8e5cafd14eaf33" dependencies = [ "async-std", "async-trait", @@ -7120,8 +7150,6 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1fe67beb12d31f2e69715898aa32abd2349ffc8fe0555617f0d77500cebc56" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7136,8 +7164,6 @@ dependencies = [ [[package]] name = "workflow-wasm" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ffbd1de665304ba6040a1ab4e0867fd9174446491d257bc6a1474ae25d4a6c" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7157,8 +7183,6 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "082644f52215ecc86b4b8a20a763e482adee52c338208ade268f47fe25eb07ca" dependencies = [ "js-sys", "proc-macro-error", @@ -7171,8 +7195,6 @@ dependencies = [ [[package]] name = "workflow-websocket" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6967baf2bd85deb2a014a32d34c1664ded9333e10d11d43ffc179fa09cc55db8" dependencies = [ "ahash 0.8.11", "async-channel 2.2.1", diff --git a/Cargo.toml b/Cargo.toml index 283981df88..d418d3a3c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -270,32 +270,34 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.12.1" } -workflow-d3 = { version = "0.12.1" } -workflow-dom = { version = "0.12.1" } -workflow-http = { version = "0.12.1" } -workflow-log = { version = "0.12.1" } -workflow-node = { version = "0.12.1" } -workflow-nw = { version = "0.12.1" } -workflow-rpc = { version = "0.12.1" } -workflow-store = { version = "0.12.1" } -workflow-terminal = { version = "0.12.1" } -workflow-wasm = { version = "0.12.1" } +# workflow-core = { version = "0.12.1" } +# workflow-d3 = { version = "0.12.1" } +# workflow-dom = { version = "0.12.1" } +# workflow-http = { version = "0.12.1" } +# workflow-log = { version = "0.12.1" } +# workflow-node = { version = "0.12.1" } +# workflow-nw = { version = "0.12.1" } +# workflow-rpc = { version = "0.12.1" } +# workflow-serializer = { version = "0.12.1" } +# workflow-store = { version = "0.12.1" } +# workflow-terminal = { version = "0.12.1" } +# workflow-wasm = { version = "0.12.1" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs # into a sibling folder from https://github.com/workflow-rs/workflow-rs -# workflow-core = { path = "../workflow-rs/core" } -# workflow-d3 = { path = "../workflow-rs/d3" } -# workflow-dom = { path = "../workflow-rs/dom" } -# workflow-http = { path = "../workflow-rs/http" } -# workflow-log = { path = "../workflow-rs/log" } -# workflow-node = { path = "../workflow-rs/node" } -# workflow-nw = { path = "../workflow-rs/nw" } -# workflow-rpc = { path = "../workflow-rs/rpc" } -# workflow-store = { path = "../workflow-rs/store" } -# workflow-terminal = { path = "../workflow-rs/terminal" } -# workflow-wasm = { path = "../workflow-rs/wasm" } +workflow-core = { path = "../workflow-rs/core" } +workflow-d3 = { path = "../workflow-rs/d3" } +workflow-dom = { path = "../workflow-rs/dom" } +workflow-http = { path = "../workflow-rs/http" } +workflow-log = { path = "../workflow-rs/log" } +workflow-node = { path = "../workflow-rs/node" } +workflow-nw = { path = "../workflow-rs/nw" } +workflow-rpc = { path = "../workflow-rs/rpc" } +workflow-serializer = { path = "../workflow-rs/serializer" } +workflow-store = { path = "../workflow-rs/store" } +workflow-terminal = { path = "../workflow-rs/terminal" } +workflow-wasm = { path = "../workflow-rs/wasm" } # --- # workflow-core = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } @@ -306,6 +308,7 @@ workflow-wasm = { version = "0.12.1" } # workflow-node = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } # workflow-nw = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } # workflow-rpc = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } +# workflow-serializer = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } # workflow-store = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } # workflow-terminal = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } # workflow-wasm = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } diff --git a/consensus/core/Cargo.toml b/consensus/core/Cargo.toml index 4e4bd9ea06..7a0fddf97d 100644 --- a/consensus/core/Cargo.toml +++ b/consensus/core/Cargo.toml @@ -41,6 +41,7 @@ thiserror.workspace = true wasm-bindgen.workspace = true workflow-core.workspace = true workflow-log.workspace = true +workflow-serializer.workspace = true workflow-wasm.workspace = true [dev-dependencies] diff --git a/consensus/core/src/api/stats.rs b/consensus/core/src/api/stats.rs index fd59f09ae5..90b79e11ca 100644 --- a/consensus/core/src/api/stats.rs +++ b/consensus/core/src/api/stats.rs @@ -1,7 +1,7 @@ -use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; +use workflow_serializer::prelude::*; -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize, Default)] +#[derive(Clone, Debug, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct BlockCount { pub header_count: u64, @@ -14,6 +14,24 @@ impl BlockCount { } } +impl Serializer for BlockCount { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.header_count, writer)?; + store!(u64, &self.block_count, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + let header_count: u64 = load!(u64, buf)?; + let block_count: u64 = load!(u64, buf)?; + + Ok(Self { header_count, block_count }) + } +} + #[derive(Clone, Default)] pub struct VirtualStateStats { /// Number of direct parents of virtual diff --git a/notify/Cargo.toml b/notify/Cargo.toml index 09d3f58651..3ef23ff86e 100644 --- a/notify/Cargo.toml +++ b/notify/Cargo.toml @@ -34,6 +34,7 @@ thiserror.workspace = true triggered.workspace = true workflow-core.workspace = true workflow-log.workspace = true +workflow-serializer.workspace = true [dev-dependencies] criterion.workspace = true diff --git a/notify/src/scope.rs b/notify/src/scope.rs index 0d9e33544e..12c7e2e9c7 100644 --- a/notify/src/scope.rs +++ b/notify/src/scope.rs @@ -3,6 +3,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use derive_more::Display; use kaspa_addresses::Address; use serde::{Deserialize, Serialize}; +use workflow_serializer::prelude::*; macro_rules! scope_enum { ($(#[$meta:meta])* $vis:vis enum $name:ident { @@ -53,9 +54,32 @@ impl Scope { } } +impl Serializer for Scope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Scope, self, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + load!(Scope, buf) + } +} + #[derive(Clone, Display, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct BlockAddedScope {} +impl Serializer for BlockAddedScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + Ok(Self {}) + } +} + #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct VirtualChainChangedScope { pub include_accepted_transaction_ids: bool, @@ -73,12 +97,47 @@ impl std::fmt::Display for VirtualChainChangedScope { } } +impl Serializer for VirtualChainChangedScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(bool, &self.include_accepted_transaction_ids, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + let include_accepted_transaction_ids: bool = load!(bool, buf)?; + Ok(Self { include_accepted_transaction_ids }) + } +} + #[derive(Clone, Display, Debug, PartialEq, Eq, Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct FinalityConflictScope {} +impl Serializer for FinalityConflictScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + Ok(Self {}) + } +} + #[derive(Clone, Display, Debug, PartialEq, Eq, Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct FinalityConflictResolvedScope {} +impl Serializer for FinalityConflictResolvedScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + Ok(Self {}) + } +} + #[derive(Clone, Debug, Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct UtxosChangedScope { pub addresses: Vec
, @@ -109,14 +168,71 @@ impl UtxosChangedScope { } } +impl Serializer for UtxosChangedScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec
, &self.addresses, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + let addresses: Vec
= load!(Vec
, buf)?; + Ok(Self { addresses }) + } +} + #[derive(Clone, Display, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct SinkBlueScoreChangedScope {} +impl Serializer for SinkBlueScoreChangedScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + Ok(Self {}) + } +} + #[derive(Clone, Display, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct VirtualDaaScoreChangedScope {} +impl Serializer for VirtualDaaScoreChangedScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + Ok(Self {}) + } +} + #[derive(Clone, Display, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct PruningPointUtxoSetOverrideScope {} +impl Serializer for PruningPointUtxoSetOverrideScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + Ok(Self {}) + } +} + #[derive(Clone, Display, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct NewBlockTemplateScope {} + +impl Serializer for NewBlockTemplateScope { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + Ok(Self {}) + } +} diff --git a/rpc/core/Cargo.toml b/rpc/core/Cargo.toml index cfa4895f25..ef010a0205 100644 --- a/rpc/core/Cargo.toml +++ b/rpc/core/Cargo.toml @@ -49,7 +49,9 @@ thiserror.workspace = true uuid.workspace = true wasm-bindgen.workspace = true workflow-core.workspace = true +workflow-serializer.workspace = true workflow-wasm.workspace = true +rand.workspace = true [dev-dependencies] serde_json.workspace = true diff --git a/rpc/core/src/api/notifications.rs b/rpc/core/src/api/notifications.rs index 6449f25c02..5d8eccf2f9 100644 --- a/rpc/core/src/api/notifications.rs +++ b/rpc/core/src/api/notifications.rs @@ -1,5 +1,4 @@ use crate::model::message::*; -use borsh::{BorshDeserialize, BorshSerialize}; use derive_more::Display; use kaspa_notify::{ events::EventType, @@ -13,10 +12,11 @@ use kaspa_notify::{ use serde::{Deserialize, Serialize}; use std::sync::Arc; use wasm_bindgen::JsValue; +use workflow_serializer::prelude::*; use workflow_wasm::serde::to_value; full_featured! { -#[derive(Clone, Debug, Display, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Display, Serialize, Deserialize)] pub enum Notification { #[display(fmt = "BlockAdded notification: block hash {}", "_0.block.header.hash")] BlockAdded(BlockAddedNotification), @@ -113,14 +113,91 @@ impl NotificationTrait for Notification { } } -#[cfg(test)] -mod test { - use super::*; +impl Serializer for Notification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + match self { + Notification::BlockAdded(notification) => { + store!(u32, &0, writer)?; + serialize!(BlockAddedNotification, notification, writer)?; + } + Notification::VirtualChainChanged(notification) => { + store!(u32, &1, writer)?; + serialize!(VirtualChainChangedNotification, notification, writer)?; + } + Notification::FinalityConflict(notification) => { + store!(u32, &2, writer)?; + serialize!(FinalityConflictNotification, notification, writer)?; + } + Notification::FinalityConflictResolved(notification) => { + store!(u32, &3, writer)?; + serialize!(FinalityConflictResolvedNotification, notification, writer)?; + } + Notification::UtxosChanged(notification) => { + store!(u32, &4, writer)?; + serialize!(UtxosChangedNotification, notification, writer)?; + } + Notification::SinkBlueScoreChanged(notification) => { + store!(u32, &5, writer)?; + serialize!(SinkBlueScoreChangedNotification, notification, writer)?; + } + Notification::VirtualDaaScoreChanged(notification) => { + store!(u32, &6, writer)?; + serialize!(VirtualDaaScoreChangedNotification, notification, writer)?; + } + Notification::PruningPointUtxoSetOverride(notification) => { + store!(u32, &7, writer)?; + serialize!(PruningPointUtxoSetOverrideNotification, notification, writer)?; + } + Notification::NewBlockTemplate(notification) => { + store!(u32, &8, writer)?; + serialize!(NewBlockTemplateNotification, notification, writer)?; + } + } + Ok(()) + } - #[test] - fn test_notification_from_bytes() { - let bytes = &vec![6, 169, 167, 75, 2, 0, 0, 0, 0][..]; - let notification = Notification::try_from_slice(bytes); - println!("notification: {notification:?}"); + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version: u32 = load!(u32, buf)?; + match load!(u32, buf)? { + 0 => { + let notification = deserialize!(BlockAddedNotification, buf)?; + Ok(Notification::BlockAdded(notification)) + } + 1 => { + let notification = deserialize!(VirtualChainChangedNotification, buf)?; + Ok(Notification::VirtualChainChanged(notification)) + } + 2 => { + let notification = deserialize!(FinalityConflictNotification, buf)?; + Ok(Notification::FinalityConflict(notification)) + } + 3 => { + let notification = deserialize!(FinalityConflictResolvedNotification, buf)?; + Ok(Notification::FinalityConflictResolved(notification)) + } + 4 => { + let notification = deserialize!(UtxosChangedNotification, buf)?; + Ok(Notification::UtxosChanged(notification)) + } + 5 => { + let notification = deserialize!(SinkBlueScoreChangedNotification, buf)?; + Ok(Notification::SinkBlueScoreChanged(notification)) + } + 6 => { + let notification = deserialize!(VirtualDaaScoreChangedNotification, buf)?; + Ok(Notification::VirtualDaaScoreChanged(notification)) + } + 7 => { + let notification = deserialize!(PruningPointUtxoSetOverrideNotification, buf)?; + Ok(Notification::PruningPointUtxoSetOverride(notification)) + } + 8 => { + let notification = deserialize!(NewBlockTemplateNotification, buf)?; + Ok(Notification::NewBlockTemplate(notification)) + } + _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid variant")), + } } } + diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 7366bf3ccb..ca01bc45fc 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -8,6 +8,7 @@ use std::{ fmt::{Display, Formatter}, sync::Arc, }; +use workflow_serializer::prelude::*; pub type RpcExtraData = Vec; @@ -15,7 +16,7 @@ pub type RpcExtraData = Vec; /// Blocks are generally expected to have been generated using the getBlockTemplate call. /// /// See: [`GetBlockTemplateRequest`] -#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubmitBlockRequest { pub block: RpcBlock, @@ -28,6 +29,24 @@ impl SubmitBlockRequest { } } +impl Serializer for SubmitBlockRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcBlock, &self.block, writer)?; + store!(bool, &self.allow_non_daa_blocks, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let block = load!(RpcBlock, buf)?; + let allow_non_daa_blocks = load!(bool, buf)?; + + Ok(Self { block, allow_non_daa_blocks }) + } +} + #[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "camelCase")] pub enum SubmitBlockRejectReason { @@ -64,17 +83,32 @@ impl SubmitBlockReport { } } -#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubmitBlockResponse { pub report: SubmitBlockReport, } +impl Serializer for SubmitBlockResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(SubmitBlockReport, &self.report, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let report = load!(SubmitBlockReport, buf)?; + + Ok(Self { report }) + } +} + /// GetBlockTemplateRequest requests a current block template. /// Callers are expected to solve the block template and submit it using the submitBlock call /// /// See: [`SubmitBlockRequest`] -#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlockTemplateRequest { /// Which kaspa address should the coinbase block reward transaction pay into @@ -88,7 +122,25 @@ impl GetBlockTemplateRequest { } } -#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBlockTemplateRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcAddress, &self.pay_address, writer)?; + store!(RpcExtraData, &self.extra_data, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let pay_address = load!(RpcAddress, buf)?; + let extra_data = load!(RpcExtraData, buf)?; + + Ok(Self { pay_address, extra_data }) + } +} + +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlockTemplateResponse { pub block: RpcBlock, @@ -100,8 +152,26 @@ pub struct GetBlockTemplateResponse { pub is_synced: bool, } +impl Serializer for GetBlockTemplateResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcBlock, &self.block, writer)?; + store!(bool, &self.is_synced, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let block = load!(RpcBlock, buf)?; + let is_synced = load!(bool, buf)?; + + Ok(Self { block, is_synced }) + } +} + /// GetBlockRequest requests information about a specific block -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlockRequest { /// The hash of the requested block @@ -116,18 +186,64 @@ impl GetBlockRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBlockRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcHash, &self.hash, writer)?; + store!(bool, &self.include_transactions, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let hash = load!(RpcHash, buf)?; + let include_transactions = load!(bool, buf)?; + + Ok(Self { hash, include_transactions }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlockResponse { pub block: RpcBlock, } +impl Serializer for GetBlockResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcBlock, &self.block, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let block = load!(RpcBlock, buf)?; + + Ok(Self { block }) + } +} + /// GetInfoRequest returns info about the node. -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetInfoRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetInfoRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetInfoResponse { pub p2p_id: String, @@ -139,11 +255,51 @@ pub struct GetInfoResponse { pub has_message_id: bool, } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetInfoResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(String, &self.p2p_id, writer)?; + store!(u64, &self.mempool_size, writer)?; + store!(String, &self.server_version, writer)?; + store!(bool, &self.is_utxo_indexed, writer)?; + store!(bool, &self.is_synced, writer)?; + store!(bool, &self.has_notify_command, writer)?; + store!(bool, &self.has_message_id, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let p2p_id = load!(String, buf)?; + let mempool_size = load!(u64, buf)?; + let server_version = load!(String, buf)?; + let is_utxo_indexed = load!(bool, buf)?; + let is_synced = load!(bool, buf)?; + let has_notify_command = load!(bool, buf)?; + let has_message_id = load!(bool, buf)?; + + Ok(Self { p2p_id, mempool_size, server_version, is_utxo_indexed, is_synced, has_notify_command, has_message_id }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetCurrentNetworkRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetCurrentNetworkRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetCurrentNetworkResponse { pub network: RpcNetworkType, @@ -155,11 +311,37 @@ impl GetCurrentNetworkResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetCurrentNetworkResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcNetworkType, &self.network, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let network = load!(RpcNetworkType, buf)?; + Ok(Self { network }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetPeerAddressesRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetPeerAddressesRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetPeerAddressesResponse { pub known_addresses: Vec, @@ -172,11 +354,39 @@ impl GetPeerAddressesResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetPeerAddressesResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.known_addresses, writer)?; + store!(Vec, &self.banned_addresses, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let known_addresses = load!(Vec, buf)?; + let banned_addresses = load!(Vec, buf)?; + Ok(Self { known_addresses, banned_addresses }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSinkRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetSinkRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSinkResponse { pub sink: RpcHash, @@ -188,7 +398,21 @@ impl GetSinkResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetSinkResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcHash, &self.sink, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let sink = load!(RpcHash, buf)?; + Ok(Self { sink }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMempoolEntryRequest { pub transaction_id: RpcTransactionId, @@ -203,7 +427,27 @@ impl GetMempoolEntryRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetMempoolEntryRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcTransactionId, &self.transaction_id, writer)?; + store!(bool, &self.include_orphan_pool, writer)?; + store!(bool, &self.filter_transaction_pool, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let transaction_id = load!(RpcTransactionId, buf)?; + let include_orphan_pool = load!(bool, buf)?; + let filter_transaction_pool = load!(bool, buf)?; + + Ok(Self { transaction_id, include_orphan_pool, filter_transaction_pool }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMempoolEntryResponse { pub mempool_entry: RpcMempoolEntry, @@ -215,7 +459,21 @@ impl GetMempoolEntryResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetMempoolEntryResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcMempoolEntry, &self.mempool_entry, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let mempool_entry = load!(RpcMempoolEntry, buf)?; + Ok(Self { mempool_entry }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMempoolEntriesRequest { pub include_orphan_pool: bool, @@ -229,7 +487,25 @@ impl GetMempoolEntriesRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetMempoolEntriesRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(bool, &self.include_orphan_pool, writer)?; + store!(bool, &self.filter_transaction_pool, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let include_orphan_pool = load!(bool, buf)?; + let filter_transaction_pool = load!(bool, buf)?; + + Ok(Self { include_orphan_pool, filter_transaction_pool }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMempoolEntriesResponse { pub mempool_entries: Vec, @@ -241,11 +517,37 @@ impl GetMempoolEntriesResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetMempoolEntriesResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.mempool_entries, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let mempool_entries = load!(Vec, buf)?; + Ok(Self { mempool_entries }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetConnectedPeerInfoRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetConnectedPeerInfoRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetConnectedPeerInfoResponse { pub peer_info: Vec, @@ -257,7 +559,21 @@ impl GetConnectedPeerInfoResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetConnectedPeerInfoResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.peer_info, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let peer_info = load!(Vec, buf)?; + Ok(Self { peer_info }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AddPeerRequest { pub peer_address: RpcContextualPeerAddress, @@ -270,11 +586,41 @@ impl AddPeerRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for AddPeerRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcContextualPeerAddress, &self.peer_address, writer)?; + store!(bool, &self.is_permanent, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let peer_address = load!(RpcContextualPeerAddress, buf)?; + let is_permanent = load!(bool, buf)?; + + Ok(Self { peer_address, is_permanent }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AddPeerResponse {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for AddPeerResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubmitTransactionRequest { pub transaction: RpcTransaction, @@ -287,7 +633,26 @@ impl SubmitTransactionRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for SubmitTransactionRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + // TODO + store!(RpcTransaction, &self.transaction, writer)?; + store!(bool, &self.allow_orphan, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let transaction = load!(RpcTransaction, buf)?; + let allow_orphan = load!(bool, buf)?; + + Ok(Self { transaction, allow_orphan }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubmitTransactionResponse { pub transaction_id: RpcTransactionId, @@ -299,7 +664,23 @@ impl SubmitTransactionResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for SubmitTransactionResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcTransactionId, &self.transaction_id, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let transaction_id = load!(RpcTransactionId, buf)?; + + Ok(Self { transaction_id }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSubnetworkRequest { pub subnetwork_id: RpcSubnetworkId, @@ -311,7 +692,23 @@ impl GetSubnetworkRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetSubnetworkRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcSubnetworkId, &self.subnetwork_id, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let subnetwork_id = load!(RpcSubnetworkId, buf)?; + + Ok(Self { subnetwork_id }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSubnetworkResponse { pub gas_limit: u64, @@ -323,7 +720,23 @@ impl GetSubnetworkResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetSubnetworkResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.gas_limit, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let gas_limit = load!(u64, buf)?; + + Ok(Self { gas_limit }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetVirtualChainFromBlockRequest { pub start_hash: RpcHash, @@ -336,7 +749,25 @@ impl GetVirtualChainFromBlockRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetVirtualChainFromBlockRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcHash, &self.start_hash, writer)?; + store!(bool, &self.include_accepted_transaction_ids, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let start_hash = load!(RpcHash, buf)?; + let include_accepted_transaction_ids = load!(bool, buf)?; + + Ok(Self { start_hash, include_accepted_transaction_ids }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetVirtualChainFromBlockResponse { pub removed_chain_block_hashes: Vec, @@ -354,7 +785,27 @@ impl GetVirtualChainFromBlockResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetVirtualChainFromBlockResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.removed_chain_block_hashes, writer)?; + store!(Vec, &self.added_chain_block_hashes, writer)?; + store!(Vec, &self.accepted_transaction_ids, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let removed_chain_block_hashes = load!(Vec, buf)?; + let added_chain_block_hashes = load!(Vec, buf)?; + let accepted_transaction_ids = load!(Vec, buf)?; + + Ok(Self { removed_chain_block_hashes, added_chain_block_hashes, accepted_transaction_ids }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlocksRequest { pub low_hash: Option, @@ -368,7 +819,27 @@ impl GetBlocksRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBlocksRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Option, &self.low_hash, writer)?; + store!(bool, &self.include_blocks, writer)?; + store!(bool, &self.include_transactions, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let low_hash = load!(Option, buf)?; + let include_blocks = load!(bool, buf)?; + let include_transactions = load!(bool, buf)?; + + Ok(Self { low_hash, include_blocks, include_transactions }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlocksResponse { pub block_hashes: Vec, @@ -381,17 +852,59 @@ impl GetBlocksResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBlocksResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.block_hashes, writer)?; + store!(Vec, &self.blocks, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let block_hashes = load!(Vec, buf)?; + let blocks = load!(Vec, buf)?; + + Ok(Self { block_hashes, blocks }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlockCountRequest {} +impl Serializer for GetBlockCountRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + pub type GetBlockCountResponse = BlockCount; -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlockDagInfoRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBlockDagInfoRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlockDagInfoResponse { pub network: RpcNetworkId, @@ -434,7 +947,52 @@ impl GetBlockDagInfoResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBlockDagInfoResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcNetworkId, &self.network, writer)?; + store!(u64, &self.block_count, writer)?; + store!(u64, &self.header_count, writer)?; + store!(Vec, &self.tip_hashes, writer)?; + store!(f64, &self.difficulty, writer)?; + store!(u64, &self.past_median_time, writer)?; + store!(Vec, &self.virtual_parent_hashes, writer)?; + store!(RpcHash, &self.pruning_point_hash, writer)?; + store!(u64, &self.virtual_daa_score, writer)?; + store!(RpcHash, &self.sink, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let network = load!(RpcNetworkId, buf)?; + let block_count = load!(u64, buf)?; + let header_count = load!(u64, buf)?; + let tip_hashes = load!(Vec, buf)?; + let difficulty = load!(f64, buf)?; + let past_median_time = load!(u64, buf)?; + let virtual_parent_hashes = load!(Vec, buf)?; + let pruning_point_hash = load!(RpcHash, buf)?; + let virtual_daa_score = load!(u64, buf)?; + let sink = load!(RpcHash, buf)?; + + Ok(Self { + network, + block_count, + header_count, + tip_hashes, + difficulty, + past_median_time, + virtual_parent_hashes, + pruning_point_hash, + virtual_daa_score, + sink, + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ResolveFinalityConflictRequest { pub finality_block_hash: RpcHash, @@ -446,19 +1004,71 @@ impl ResolveFinalityConflictRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for ResolveFinalityConflictRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcHash, &self.finality_block_hash, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let finality_block_hash = load!(RpcHash, buf)?; + + Ok(Self { finality_block_hash }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ResolveFinalityConflictResponse {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for ResolveFinalityConflictResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ShutdownRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for ShutdownRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ShutdownResponse {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for ShutdownResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetHeadersRequest { pub start_hash: RpcHash, @@ -472,7 +1082,27 @@ impl GetHeadersRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetHeadersRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcHash, &self.start_hash, writer)?; + store!(u64, &self.limit, writer)?; + store!(bool, &self.is_ascending, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let start_hash = load!(RpcHash, buf)?; + let limit = load!(u64, buf)?; + let is_ascending = load!(bool, buf)?; + + Ok(Self { start_hash, limit, is_ascending }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetHeadersResponse { pub headers: Vec, @@ -484,7 +1114,23 @@ impl GetHeadersResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetHeadersResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.headers, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let headers = load!(Vec, buf)?; + + Ok(Self { headers }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBalanceByAddressRequest { pub address: RpcAddress, @@ -496,7 +1142,23 @@ impl GetBalanceByAddressRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBalanceByAddressRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcAddress, &self.address, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let address = load!(RpcAddress, buf)?; + + Ok(Self { address }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBalanceByAddressResponse { pub balance: u64, @@ -508,7 +1170,23 @@ impl GetBalanceByAddressResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBalanceByAddressResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.balance, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let balance = load!(u64, buf)?; + + Ok(Self { balance }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBalancesByAddressesRequest { pub addresses: Vec, @@ -520,7 +1198,23 @@ impl GetBalancesByAddressesRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBalancesByAddressesRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.addresses, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let addresses = load!(Vec, buf)?; + + Ok(Self { addresses }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBalancesByAddressesResponse { pub entries: Vec, @@ -532,11 +1226,39 @@ impl GetBalancesByAddressesResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetBalancesByAddressesResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.entries, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let entries = load!(Vec, buf)?; + + Ok(Self { entries }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSinkBlueScoreRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetSinkBlueScoreRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSinkBlueScoreResponse { pub blue_score: u64, @@ -548,7 +1270,23 @@ impl GetSinkBlueScoreResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetSinkBlueScoreResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.blue_score, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let blue_score = load!(u64, buf)?; + + Ok(Self { blue_score }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetUtxosByAddressesRequest { pub addresses: Vec, @@ -560,7 +1298,23 @@ impl GetUtxosByAddressesRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetUtxosByAddressesRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.addresses, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let addresses = load!(Vec, buf)?; + + Ok(Self { addresses }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetUtxosByAddressesResponse { pub entries: Vec, @@ -572,7 +1326,23 @@ impl GetUtxosByAddressesResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetUtxosByAddressesResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.entries, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let entries = load!(Vec, buf)?; + + Ok(Self { entries }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BanRequest { pub ip: RpcIpAddress, @@ -584,11 +1354,39 @@ impl BanRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for BanRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcIpAddress, &self.ip, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let ip = load!(RpcIpAddress, buf)?; + + Ok(Self { ip }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BanResponse {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for BanResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct UnbanRequest { pub ip: RpcIpAddress, @@ -600,11 +1398,39 @@ impl UnbanRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for UnbanRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcIpAddress, &self.ip, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let ip = load!(RpcIpAddress, buf)?; + + Ok(Self { ip }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct UnbanResponse {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for UnbanResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EstimateNetworkHashesPerSecondRequest { pub window_size: u32, @@ -617,7 +1443,25 @@ impl EstimateNetworkHashesPerSecondRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for EstimateNetworkHashesPerSecondRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u32, &self.window_size, writer)?; + store!(Option, &self.start_hash, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let window_size = load!(u32, buf)?; + let start_hash = load!(Option, buf)?; + + Ok(Self { window_size, start_hash }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EstimateNetworkHashesPerSecondResponse { pub network_hashes_per_second: u64, @@ -629,7 +1473,23 @@ impl EstimateNetworkHashesPerSecondResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for EstimateNetworkHashesPerSecondResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.network_hashes_per_second, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let network_hashes_per_second = load!(u64, buf)?; + + Ok(Self { network_hashes_per_second }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMempoolEntriesByAddressesRequest { pub addresses: Vec, @@ -644,7 +1504,27 @@ impl GetMempoolEntriesByAddressesRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetMempoolEntriesByAddressesRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.addresses, writer)?; + store!(bool, &self.include_orphan_pool, writer)?; + store!(bool, &self.filter_transaction_pool, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let addresses = load!(Vec, buf)?; + let include_orphan_pool = load!(bool, buf)?; + let filter_transaction_pool = load!(bool, buf)?; + + Ok(Self { addresses, include_orphan_pool, filter_transaction_pool }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMempoolEntriesByAddressesResponse { pub entries: Vec, @@ -656,11 +1536,39 @@ impl GetMempoolEntriesByAddressesResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetMempoolEntriesByAddressesResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.entries, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let entries = load!(Vec, buf)?; + + Ok(Self { entries }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetCoinSupplyRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetCoinSupplyRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetCoinSupplyResponse { pub max_sompi: u64, @@ -673,17 +1581,55 @@ impl GetCoinSupplyResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetCoinSupplyResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.max_sompi, writer)?; + store!(u64, &self.circulating_sompi, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let max_sompi = load!(u64, buf)?; + let circulating_sompi = load!(u64, buf)?; + + Ok(Self { max_sompi, circulating_sompi }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PingRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for PingRequest { + fn serialize(&self, _writer: &mut W) -> std::io::Result<()> { + Ok(()) + } + + fn deserialize(_buf: &mut &[u8]) -> std::io::Result { + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PingResponse {} +impl Serializer for PingResponse { + fn serialize(&self, _writer: &mut W) -> std::io::Result<()> { + Ok(()) + } + + fn deserialize(_buf: &mut &[u8]) -> std::io::Result { + Ok(Self {}) + } +} + // TODO - custom wRPC commands (need review and implementation in gRPC) -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMetricsRequest { pub process_metrics: bool, @@ -692,7 +1638,29 @@ pub struct GetMetricsRequest { pub consensus_metrics: bool, } -#[derive(Default, Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetMetricsRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(bool, &self.process_metrics, writer)?; + store!(bool, &self.connection_metrics, writer)?; + store!(bool, &self.bandwidth_metrics, writer)?; + store!(bool, &self.consensus_metrics, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let process_metrics = load!(bool, buf)?; + let connection_metrics = load!(bool, buf)?; + let bandwidth_metrics = load!(bool, buf)?; + let consensus_metrics = load!(bool, buf)?; + + Ok(Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics }) + } +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ProcessMetrics { pub resident_set_size: u64, @@ -706,7 +1674,49 @@ pub struct ProcessMetrics { pub disk_io_write_per_sec: f32, } -#[derive(Default, Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for ProcessMetrics { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.resident_set_size, writer)?; + store!(u64, &self.virtual_memory_size, writer)?; + store!(u32, &self.core_num, writer)?; + store!(f32, &self.cpu_usage, writer)?; + store!(u32, &self.fd_num, writer)?; + store!(u64, &self.disk_io_read_bytes, writer)?; + store!(u64, &self.disk_io_write_bytes, writer)?; + store!(f32, &self.disk_io_read_per_sec, writer)?; + store!(f32, &self.disk_io_write_per_sec, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let resident_set_size = load!(u64, buf)?; + let virtual_memory_size = load!(u64, buf)?; + let core_num = load!(u32, buf)?; + let cpu_usage = load!(f32, buf)?; + let fd_num = load!(u32, buf)?; + let disk_io_read_bytes = load!(u64, buf)?; + let disk_io_write_bytes = load!(u64, buf)?; + let disk_io_read_per_sec = load!(f32, buf)?; + let disk_io_write_per_sec = load!(f32, buf)?; + + Ok(Self { + resident_set_size, + virtual_memory_size, + core_num, + cpu_usage, + fd_num, + disk_io_read_bytes, + disk_io_write_bytes, + disk_io_read_per_sec, + disk_io_write_per_sec, + }) + } +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ConnectionMetrics { pub borsh_live_connections: u32, @@ -719,7 +1729,43 @@ pub struct ConnectionMetrics { pub active_peers: u32, } -#[derive(Default, Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for ConnectionMetrics { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u32, &self.borsh_live_connections, writer)?; + store!(u64, &self.borsh_connection_attempts, writer)?; + store!(u64, &self.borsh_handshake_failures, writer)?; + store!(u32, &self.json_live_connections, writer)?; + store!(u64, &self.json_connection_attempts, writer)?; + store!(u64, &self.json_handshake_failures, writer)?; + store!(u32, &self.active_peers, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let borsh_live_connections = load!(u32, buf)?; + let borsh_connection_attempts = load!(u64, buf)?; + let borsh_handshake_failures = load!(u64, buf)?; + let json_live_connections = load!(u32, buf)?; + let json_connection_attempts = load!(u64, buf)?; + let json_handshake_failures = load!(u64, buf)?; + let active_peers = load!(u32, buf)?; + + Ok(Self { + borsh_live_connections, + borsh_connection_attempts, + borsh_handshake_failures, + json_live_connections, + json_connection_attempts, + json_handshake_failures, + active_peers, + }) + } +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BandwidthMetrics { pub borsh_bytes_tx: u64, @@ -732,7 +1778,46 @@ pub struct BandwidthMetrics { pub grpc_bytes_rx: u64, } -#[derive(Default, Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for BandwidthMetrics { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.borsh_bytes_tx, writer)?; + store!(u64, &self.borsh_bytes_rx, writer)?; + store!(u64, &self.json_bytes_tx, writer)?; + store!(u64, &self.json_bytes_rx, writer)?; + store!(u64, &self.p2p_bytes_tx, writer)?; + store!(u64, &self.p2p_bytes_rx, writer)?; + store!(u64, &self.grpc_bytes_tx, writer)?; + store!(u64, &self.grpc_bytes_rx, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let borsh_bytes_tx = load!(u64, buf)?; + let borsh_bytes_rx = load!(u64, buf)?; + let json_bytes_tx = load!(u64, buf)?; + let json_bytes_rx = load!(u64, buf)?; + let p2p_bytes_tx = load!(u64, buf)?; + let p2p_bytes_rx = load!(u64, buf)?; + let grpc_bytes_tx = load!(u64, buf)?; + let grpc_bytes_rx = load!(u64, buf)?; + + Ok(Self { + borsh_bytes_tx, + borsh_bytes_rx, + json_bytes_tx, + json_bytes_rx, + p2p_bytes_tx, + p2p_bytes_rx, + grpc_bytes_tx, + grpc_bytes_rx, + }) + } +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ConsensusMetrics { pub node_blocks_submitted_count: u64, @@ -754,7 +1839,67 @@ pub struct ConsensusMetrics { pub network_virtual_daa_score: u64, } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for ConsensusMetrics { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.node_blocks_submitted_count, writer)?; + store!(u64, &self.node_headers_processed_count, writer)?; + store!(u64, &self.node_dependencies_processed_count, writer)?; + store!(u64, &self.node_bodies_processed_count, writer)?; + store!(u64, &self.node_transactions_processed_count, writer)?; + store!(u64, &self.node_chain_blocks_processed_count, writer)?; + store!(u64, &self.node_mass_processed_count, writer)?; + store!(u64, &self.node_database_blocks_count, writer)?; + store!(u64, &self.node_database_headers_count, writer)?; + store!(u64, &self.network_mempool_size, writer)?; + store!(u32, &self.network_tip_hashes_count, writer)?; + store!(f64, &self.network_difficulty, writer)?; + store!(u64, &self.network_past_median_time, writer)?; + store!(u32, &self.network_virtual_parent_hashes_count, writer)?; + store!(u64, &self.network_virtual_daa_score, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let node_blocks_submitted_count = load!(u64, buf)?; + let node_headers_processed_count = load!(u64, buf)?; + let node_dependencies_processed_count = load!(u64, buf)?; + let node_bodies_processed_count = load!(u64, buf)?; + let node_transactions_processed_count = load!(u64, buf)?; + let node_chain_blocks_processed_count = load!(u64, buf)?; + let node_mass_processed_count = load!(u64, buf)?; + let node_database_blocks_count = load!(u64, buf)?; + let node_database_headers_count = load!(u64, buf)?; + let network_mempool_size = load!(u64, buf)?; + let network_tip_hashes_count = load!(u32, buf)?; + let network_difficulty = load!(f64, buf)?; + let network_past_median_time = load!(u64, buf)?; + let network_virtual_parent_hashes_count = load!(u32, buf)?; + let network_virtual_daa_score = load!(u64, buf)?; + + Ok(Self { + node_blocks_submitted_count, + node_headers_processed_count, + node_dependencies_processed_count, + node_bodies_processed_count, + node_transactions_processed_count, + node_chain_blocks_processed_count, + node_mass_processed_count, + node_database_blocks_count, + node_database_headers_count, + network_mempool_size, + network_tip_hashes_count, + network_difficulty, + network_past_median_time, + network_virtual_parent_hashes_count, + network_virtual_daa_score, + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMetricsResponse { pub server_time: u64, @@ -776,11 +1921,47 @@ impl GetMetricsResponse { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetMetricsResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.server_time, writer)?; + serialize!(Option, &self.process_metrics, writer)?; + serialize!(Option, &self.connection_metrics, writer)?; + serialize!(Option, &self.bandwidth_metrics, writer)?; + serialize!(Option, &self.consensus_metrics, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let server_time = load!(u64, buf)?; + let process_metrics = deserialize!(Option, buf)?; + let connection_metrics = deserialize!(Option, buf)?; + let bandwidth_metrics = deserialize!(Option, buf)?; + let consensus_metrics = deserialize!(Option, buf)?; + + Ok(Self { server_time, process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetServerInfoRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetServerInfoRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetServerInfoResponse { pub rpc_api_version: [u16; 4], @@ -791,17 +1972,70 @@ pub struct GetServerInfoResponse { pub virtual_daa_score: u64, } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetServerInfoResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + // TODO + store!([u16; 4], &self.rpc_api_version, writer)?; + store!(String, &self.server_version, writer)?; + store!(RpcNetworkId, &self.network_id, writer)?; + store!(bool, &self.has_utxo_index, writer)?; + store!(bool, &self.is_synced, writer)?; + store!(u64, &self.virtual_daa_score, writer)?; + + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let rpc_api_version = load!([u16; 4], buf)?; + let server_version = load!(String, buf)?; + let network_id = load!(RpcNetworkId, buf)?; + let has_utxo_index = load!(bool, buf)?; + let is_synced = load!(bool, buf)?; + let virtual_daa_score = load!(u64, buf)?; + + Ok(Self { rpc_api_version, server_version, network_id, has_utxo_index, is_synced, virtual_daa_score }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSyncStatusRequest {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetSyncStatusRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSyncStatusResponse { pub is_synced: bool, } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetSyncStatusResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(bool, &self.is_synced, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let is_synced = load!(bool, buf)?; + Ok(Self { is_synced }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetDaaScoreTimestampEstimateRequest { pub daa_scores: Vec, @@ -813,7 +2047,21 @@ impl GetDaaScoreTimestampEstimateRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for GetDaaScoreTimestampEstimateRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.daa_scores, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let daa_scores = load!(Vec, buf)?; + Ok(Self { daa_scores }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetDaaScoreTimestampEstimateResponse { pub timestamps: Vec, @@ -825,6 +2073,20 @@ impl GetDaaScoreTimestampEstimateResponse { } } +impl Serializer for GetDaaScoreTimestampEstimateResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.timestamps, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let timestamps = load!(Vec, buf)?; + Ok(Self { timestamps }) + } +} + // ---------------------------------------------------------------------------- // Subscriptions & notifications // ---------------------------------------------------------------------------- @@ -835,7 +2097,7 @@ impl GetDaaScoreTimestampEstimateResponse { /// NotifyBlockAddedRequest registers this connection for blockAdded notifications. /// /// See: BlockAddedNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyBlockAddedRequest { pub command: Command, @@ -846,20 +2108,60 @@ impl NotifyBlockAddedRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyBlockAddedRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let command = load!(Command, buf)?; + Ok(Self { command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyBlockAddedResponse {} +impl Serializer for NotifyBlockAddedResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + /// BlockAddedNotification is sent whenever a blocks has been added (NOT accepted) /// into the DAG. /// /// See: NotifyBlockAddedRequest -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BlockAddedNotification { pub block: Arc, } +impl Serializer for BlockAddedNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcBlock, &self.block, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let block = load!(RpcBlock, buf)?; + Ok(Self { block: block.into() }) + } +} + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // VirtualChainChangedNotification @@ -867,7 +2169,7 @@ pub struct BlockAddedNotification { // virtualDaaScoreChanged notifications. // // See: VirtualChainChangedNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyVirtualChainChangedRequest { pub include_accepted_transaction_ids: bool, @@ -880,15 +2182,43 @@ impl NotifyVirtualChainChangedRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyVirtualChainChangedRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(bool, &self.include_accepted_transaction_ids, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let include_accepted_transaction_ids = load!(bool, buf)?; + let command = load!(Command, buf)?; + Ok(Self { include_accepted_transaction_ids, command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyVirtualChainChangedResponse {} +impl Serializer for NotifyVirtualChainChangedResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + // VirtualChainChangedNotification is sent whenever the DAG's selected parent // chain had changed. // // See: NotifyVirtualChainChangedRequest -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VirtualChainChangedNotification { pub removed_chain_block_hashes: Arc>, @@ -896,10 +2226,32 @@ pub struct VirtualChainChangedNotification { pub accepted_transaction_ids: Arc>, } +impl Serializer for VirtualChainChangedNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.removed_chain_block_hashes, writer)?; + store!(Vec, &self.added_chain_block_hashes, writer)?; + store!(Vec, &self.accepted_transaction_ids, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let removed_chain_block_hashes = load!(Vec, buf)?; + let added_chain_block_hashes = load!(Vec, buf)?; + let accepted_transaction_ids = load!(Vec, buf)?; + Ok(Self { + removed_chain_block_hashes: removed_chain_block_hashes.into(), + added_chain_block_hashes: added_chain_block_hashes.into(), + accepted_transaction_ids: accepted_transaction_ids.into(), + }) + } +} + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // FinalityConflictNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyFinalityConflictRequest { pub command: Command, @@ -911,20 +2263,60 @@ impl NotifyFinalityConflictRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyFinalityConflictRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let command = load!(Command, buf)?; + Ok(Self { command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyFinalityConflictResponse {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyFinalityConflictResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FinalityConflictNotification { pub violating_block_hash: RpcHash, } +impl Serializer for FinalityConflictNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcHash, &self.violating_block_hash, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let violating_block_hash = load!(RpcHash, buf)?; + Ok(Self { violating_block_hash }) + } +} + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // FinalityConflictResolvedNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyFinalityConflictResolvedRequest { pub command: Command, @@ -936,16 +2328,56 @@ impl NotifyFinalityConflictResolvedRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyFinalityConflictResolvedRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let command = load!(Command, buf)?; + Ok(Self { command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyFinalityConflictResolvedResponse {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyFinalityConflictResolvedResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FinalityConflictResolvedNotification { pub finality_block_hash: RpcHash, } +impl Serializer for FinalityConflictResolvedNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(RpcHash, &self.finality_block_hash, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let finality_block_hash = load!(RpcHash, buf)?; + Ok(Self { finality_block_hash }) + } +} + // ~~~~~~~~~~~~~~~~~~~~~~~~ // UtxosChangedNotification @@ -958,7 +2390,7 @@ pub struct FinalityConflictResolvedNotification { // This call is only available when this kaspad was started with `--utxoindex` // // See: UtxosChangedNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyUtxosChangedRequest { pub addresses: Vec, @@ -971,14 +2403,42 @@ impl NotifyUtxosChangedRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyUtxosChangedRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.addresses, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let addresses = load!(Vec, buf)?; + let command = load!(Command, buf)?; + Ok(Self { addresses, command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyUtxosChangedResponse {} +impl Serializer for NotifyUtxosChangedResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + // UtxosChangedNotificationMessage is sent whenever the UTXO index had been updated. // // See: NotifyUtxosChangedRequest -#[derive(Clone, Debug, Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct UtxosChangedNotification { pub added: Arc>, @@ -1015,6 +2475,22 @@ impl UtxosChangedNotification { } } +impl Serializer for UtxosChangedNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Vec, &self.added, writer)?; + store!(Vec, &self.removed, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let added = load!(Vec, buf)?; + let removed = load!(Vec, buf)?; + Ok(Self { added: added.into(), removed: removed.into() }) + } +} + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SinkBlueScoreChangedNotification @@ -1022,7 +2498,7 @@ impl UtxosChangedNotification { // sinkBlueScoreChanged notifications. // // See: SinkBlueScoreChangedNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifySinkBlueScoreChangedRequest { pub command: Command, @@ -1034,20 +2510,60 @@ impl NotifySinkBlueScoreChangedRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifySinkBlueScoreChangedRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let command = load!(Command, buf)?; + Ok(Self { command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifySinkBlueScoreChangedResponse {} +impl Serializer for NotifySinkBlueScoreChangedResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + // SinkBlueScoreChangedNotification is sent whenever the blue score // of the virtual's selected parent changes. // /// See: NotifySinkBlueScoreChangedRequest -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SinkBlueScoreChangedNotification { pub sink_blue_score: u64, } +impl Serializer for SinkBlueScoreChangedNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.sink_blue_score, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let sink_blue_score = load!(u64, buf)?; + Ok(Self { sink_blue_score }) + } +} + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // VirtualDaaScoreChangedNotification @@ -1055,7 +2571,7 @@ pub struct SinkBlueScoreChangedNotification { // virtualDaaScoreChanged notifications. // // See: VirtualDaaScoreChangedNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyVirtualDaaScoreChangedRequest { pub command: Command, @@ -1067,24 +2583,64 @@ impl NotifyVirtualDaaScoreChangedRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyVirtualDaaScoreChangedRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let command = load!(Command, buf)?; + Ok(Self { command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyVirtualDaaScoreChangedResponse {} +impl Serializer for NotifyVirtualDaaScoreChangedResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + // VirtualDaaScoreChangedNotification is sent whenever the DAA score // of the virtual changes. // // See NotifyVirtualDaaScoreChangedRequest -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VirtualDaaScoreChangedNotification { pub virtual_daa_score: u64, } +impl Serializer for VirtualDaaScoreChangedNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.virtual_daa_score, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let virtual_daa_score = load!(u64, buf)?; + Ok(Self { virtual_daa_score }) + } +} + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PruningPointUtxoSetOverrideNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyPruningPointUtxoSetOverrideRequest { pub command: Command, @@ -1096,21 +2652,59 @@ impl NotifyPruningPointUtxoSetOverrideRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyPruningPointUtxoSetOverrideRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let command = load!(Command, buf)?; + Ok(Self { command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyPruningPointUtxoSetOverrideResponse {} -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyPruningPointUtxoSetOverrideResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PruningPointUtxoSetOverrideNotification {} +impl Serializer for PruningPointUtxoSetOverrideNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // NewBlockTemplateNotification /// NotifyNewBlockTemplateRequest registers this connection for blockAdded notifications. /// /// See: NewBlockTemplateNotification -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyNewBlockTemplateRequest { pub command: Command, @@ -1121,22 +2715,60 @@ impl NotifyNewBlockTemplateRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for NotifyNewBlockTemplateRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(Command, &self.command, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let command = load!(Command, buf)?; + Ok(Self { command }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NotifyNewBlockTemplateResponse {} +impl Serializer for NotifyNewBlockTemplateResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + /// NewBlockTemplateNotification is sent whenever a blocks has been added (NOT accepted) /// into the DAG. /// /// See: NotifyNewBlockTemplateRequest -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NewBlockTemplateNotification {} +impl Serializer for NewBlockTemplateNotification { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + Ok(Self {}) + } +} + /// /// wRPC response for RpcApiOps::Subscribe request /// -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubscribeResponse { id: u64, @@ -1148,9 +2780,34 @@ impl SubscribeResponse { } } +impl Serializer for SubscribeResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.id, writer)?; + Ok(()) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf)?; + let id = load!(u64, buf)?; + Ok(Self { id }) + } +} + /// /// wRPC response for RpcApiOps::Unsubscribe request /// -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct UnsubscribeResponse {} + +impl Serializer for UnsubscribeResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer) + } + + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let _version = load!(u32, buf); + Ok(Self {}) + } +} diff --git a/rpc/core/src/model/mod.rs b/rpc/core/src/model/mod.rs index fd07a109ee..c1ff4ed59b 100644 --- a/rpc/core/src/model/mod.rs +++ b/rpc/core/src/model/mod.rs @@ -10,6 +10,7 @@ pub mod network; pub mod peer; pub mod script_class; pub mod subnets; +mod tests; pub mod tx; pub use address::*; diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs new file mode 100644 index 0000000000..adffaf4f6d --- /dev/null +++ b/rpc/core/src/model/tests.rs @@ -0,0 +1,1216 @@ +#[cfg(test)] +mod mockery { + + use crate::{model::*, RpcScriptClass}; + use kaspa_addresses::{Prefix, Version}; + use kaspa_consensus_core::api::BlockCount; + use kaspa_consensus_core::header::Header; + use kaspa_consensus_core::network::NetworkType; + use kaspa_consensus_core::subnets::SubnetworkId; + use kaspa_consensus_core::tx::{ScriptPublicKey, TransactionOutpoint, UtxoEntry}; + use kaspa_hashes::Hash; + use kaspa_math::Uint192; + use kaspa_notify::subscription::Command; + use kaspa_rpc_macros::test_wrpc_serializer as test; + use kaspa_utils::networking::{ContextualNetAddress, IpAddress, NetAddress}; + use rand::Rng; + use std::net::{IpAddr, Ipv4Addr}; + use std::sync::Arc; + use uuid::Uuid; + use workflow_serializer::prelude::*; + + // this trait is used to generate random + // values for testing on various data types + trait Mock { + fn mock() -> Self; + } + + impl Mock for Option + where + T: Mock, + { + fn mock() -> Self { + Some(T::mock()) + } + } + + impl Mock for Vec + where + T: Mock, + { + fn mock() -> Self { + vec![T::mock()] + } + } + + impl Mock for Arc + where + T: Mock, + { + fn mock() -> Self { + Arc::new(T::mock()) + } + } + + fn mock() -> T + where + T: Mock, + { + Mock::mock() + } + + // this function tests serialization and deserialization of a type + // by serializing it (A), deserializing it, serializing it again (B) + // and comparing A and B buffers. + fn test(kind: &str) + where + T: Serializer + Mock, + { + let data = T::mock(); + + const PREFIX: u32 = 0x12345678; + const SUFFIX: u32 = 0x90abcdef; + + let mut buffer1 = Vec::new(); + let writer = &mut buffer1; + store!(u32, &PREFIX, writer).unwrap(); + serialize!(T, &data, writer).unwrap(); + store!(u32, &SUFFIX, writer).unwrap(); + + let reader = &mut buffer1.as_slice(); + let prefix: u32 = load!(u32, reader).unwrap(); + // this will never occur, but it's a good practice to check in case + // the serialization/deserialization logic changes in the future + assert_eq!(prefix, PREFIX, "misalignment when consuming serialized buffer in `{kind}`"); + let tmp = deserialize!(T, reader).unwrap(); + let suffix: u32 = load!(u32, reader).unwrap(); + assert_eq!(suffix, SUFFIX, "misalignment when consuming serialized buffer in `{kind}`"); + + let mut buffer2 = Vec::new(); + let writer = &mut buffer2; + store!(u32, &PREFIX, writer).unwrap(); + serialize!(T, &tmp, writer).unwrap(); + store!(u32, &SUFFIX, writer).unwrap(); + + assert!(buffer1 == buffer2, "serialization/deserialization failure while testing `{kind}`"); + } + + #[macro_export] + macro_rules! impl_mock { + ($($type:ty),*) => { + $(impl Mock for $type { + fn mock() -> Self { + rand::thread_rng().gen() + } + })* + }; + } + + impl_mock!(bool, u8, u16, u32, f32, u64, i64, f64); + + impl Mock for Uint192 { + fn mock() -> Self { + Uint192([mock(), mock(), mock()]) + } + } + + impl Mock for SubnetworkId { + fn mock() -> Self { + let mut bytes: [u8; 20] = [0; 20]; + rand::thread_rng().fill(&mut bytes); + SubnetworkId::from_bytes(bytes) + } + } + + impl Mock for Hash { + fn mock() -> Self { + let mut bytes: [u8; 32] = [0; 32]; + rand::thread_rng().fill(&mut bytes); + Hash::from_bytes(bytes) + } + } + + impl Mock for RpcAddress { + fn mock() -> Self { + RpcAddress::new(Prefix::Mainnet, Version::PubKey, Hash::mock().as_bytes().as_slice()) + } + } + + impl Mock for Header { + fn mock() -> Self { + Header { + version: mock(), + timestamp: mock(), + bits: mock(), + nonce: mock(), + hash_merkle_root: mock(), + accepted_id_merkle_root: mock(), + utxo_commitment: mock(), + hash: mock(), + parents_by_level: vec![mock()], + daa_score: mock(), + blue_score: mock(), + blue_work: mock(), + pruning_point: mock(), + } + } + } + + impl Mock for RpcBlockVerboseData { + fn mock() -> Self { + RpcBlockVerboseData { + hash: mock(), + difficulty: mock(), + selected_parent_hash: mock(), + transaction_ids: mock(), + is_header_only: mock(), + blue_score: mock(), + children_hashes: mock(), + merge_set_blues_hashes: mock(), + merge_set_reds_hashes: mock(), + is_chain_block: mock(), + } + } + } + + impl Mock for RpcBlock { + fn mock() -> Self { + RpcBlock { header: mock(), transactions: mock(), verbose_data: mock() } + } + } + + impl Mock for RpcTransactionInputVerboseData { + fn mock() -> Self { + RpcTransactionInputVerboseData {} + } + } + + impl Mock for RpcTransactionInput { + fn mock() -> Self { + RpcTransactionInput { + previous_outpoint: mock(), + signature_script: Hash::mock().as_bytes().to_vec(), + sequence: mock(), + sig_op_count: mock(), + verbose_data: mock(), + } + } + } + + impl Mock for RpcTransactionOutputVerboseData { + fn mock() -> Self { + RpcTransactionOutputVerboseData { script_public_key_type: RpcScriptClass::PubKey, script_public_key_address: mock() } + } + } + + impl Mock for RpcTransactionOutput { + fn mock() -> Self { + RpcTransactionOutput { value: mock(), script_public_key: mock(), verbose_data: mock() } + } + } + + impl Mock for RpcTransactionVerboseData { + fn mock() -> Self { + RpcTransactionVerboseData { transaction_id: mock(), hash: mock(), mass: mock(), block_hash: mock(), block_time: mock() } + } + } + + impl Mock for RpcTransaction { + fn mock() -> Self { + RpcTransaction { + version: mock(), + inputs: mock(), + outputs: mock(), + lock_time: mock(), + subnetwork_id: mock(), + gas: mock(), + payload: Hash::mock().as_bytes().to_vec(), + mass: mock(), + verbose_data: mock(), + } + } + } + + impl Mock for RpcNodeId { + fn mock() -> Self { + RpcNodeId::new(Uuid::new_v4()) + } + } + + impl Mock for IpAddr { + fn mock() -> Self { + IpAddr::V4(Ipv4Addr::new(mock(), mock(), mock(), mock())) + } + } + + impl Mock for IpAddress { + fn mock() -> Self { + IpAddress::new(mock()) + } + } + + impl Mock for NetAddress { + fn mock() -> Self { + NetAddress::new(IpAddress::new(mock()), mock()) + } + } + + impl Mock for ContextualNetAddress { + fn mock() -> Self { + ContextualNetAddress::new(mock(), mock()) + } + } + + impl Mock for RpcPeerInfo { + fn mock() -> Self { + RpcPeerInfo { + id: mock(), + address: mock(), + last_ping_duration: mock(), + is_outbound: mock(), + time_offset: mock(), + user_agent: "0.4.2".to_string(), + advertised_protocol_version: mock(), + time_connected: mock(), + is_ibd_peer: mock(), + } + } + } + + impl Mock for RpcMempoolEntry { + fn mock() -> Self { + RpcMempoolEntry { fee: mock(), transaction: mock(), is_orphan: mock() } + } + } + + impl Mock for RpcMempoolEntryByAddress { + fn mock() -> Self { + RpcMempoolEntryByAddress { address: mock(), sending: mock(), receiving: mock() } + } + } + + impl Mock for ScriptPublicKey { + fn mock() -> Self { + let mut bytes: [u8; 36] = [0; 36]; + rand::thread_rng().fill(&mut bytes[..]); + ScriptPublicKey::from_vec(0, bytes.to_vec()) + } + } + + impl Mock for UtxoEntry { + fn mock() -> Self { + UtxoEntry { amount: mock(), script_public_key: mock(), block_daa_score: mock(), is_coinbase: true } + } + } + + impl Mock for TransactionOutpoint { + fn mock() -> Self { + TransactionOutpoint { transaction_id: mock(), index: mock() } + } + } + + impl Mock for RpcUtxosByAddressesEntry { + fn mock() -> Self { + RpcUtxosByAddressesEntry { address: mock(), outpoint: mock(), utxo_entry: mock() } + } + } + + impl Mock for ProcessMetrics { + fn mock() -> Self { + ProcessMetrics { + resident_set_size: mock(), + virtual_memory_size: mock(), + core_num: mock(), + cpu_usage: mock(), + fd_num: mock(), + disk_io_read_bytes: mock(), + disk_io_write_bytes: mock(), + disk_io_read_per_sec: mock(), + disk_io_write_per_sec: mock(), + } + } + } + + impl Mock for ConnectionMetrics { + fn mock() -> Self { + ConnectionMetrics { + borsh_live_connections: mock(), + borsh_connection_attempts: mock(), + borsh_handshake_failures: mock(), + json_live_connections: mock(), + json_connection_attempts: mock(), + json_handshake_failures: mock(), + active_peers: mock(), + } + } + } + + impl Mock for BandwidthMetrics { + fn mock() -> Self { + BandwidthMetrics { + borsh_bytes_tx: mock(), + borsh_bytes_rx: mock(), + json_bytes_tx: mock(), + json_bytes_rx: mock(), + p2p_bytes_tx: mock(), + p2p_bytes_rx: mock(), + grpc_bytes_tx: mock(), + grpc_bytes_rx: mock(), + } + } + } + + impl Mock for ConsensusMetrics { + fn mock() -> Self { + ConsensusMetrics { + node_blocks_submitted_count: mock(), + node_headers_processed_count: mock(), + node_dependencies_processed_count: mock(), + node_bodies_processed_count: mock(), + node_transactions_processed_count: mock(), + node_chain_blocks_processed_count: mock(), + node_mass_processed_count: mock(), + node_database_blocks_count: mock(), + node_database_headers_count: mock(), + network_mempool_size: mock(), + network_tip_hashes_count: mock(), + network_difficulty: mock(), + network_past_median_time: mock(), + network_virtual_parent_hashes_count: mock(), + network_virtual_daa_score: mock(), + } + } + } + + // -------------------------------------------- + // implementations for all the rpc request + // and response data structures. + + impl Mock for SubmitBlockRequest { + fn mock() -> Self { + SubmitBlockRequest { block: mock(), allow_non_daa_blocks: true } + } + } + + test!(SubmitBlockRequest); + + impl Mock for SubmitBlockResponse { + fn mock() -> Self { + SubmitBlockResponse { report: SubmitBlockReport::Success } + } + } + + test!(SubmitBlockResponse); + + impl Mock for GetBlockTemplateRequest { + fn mock() -> Self { + GetBlockTemplateRequest { pay_address: mock(), extra_data: vec![4, 2] } + } + } + + test!(GetBlockTemplateRequest); + + impl Mock for GetBlockTemplateResponse { + fn mock() -> Self { + GetBlockTemplateResponse { block: mock(), is_synced: true } + } + } + + test!(GetBlockTemplateResponse); + + impl Mock for GetBlockRequest { + fn mock() -> Self { + GetBlockRequest { hash: mock(), include_transactions: true } + } + } + + test!(GetBlockRequest); + + impl Mock for GetBlockResponse { + fn mock() -> Self { + GetBlockResponse { block: mock() } + } + } + + test!(GetBlockResponse); + + impl Mock for GetInfoRequest { + fn mock() -> Self { + GetInfoRequest {} + } + } + + test!(GetInfoRequest); + + impl Mock for GetInfoResponse { + fn mock() -> Self { + GetInfoResponse { + p2p_id: Hash::mock().to_string(), + mempool_size: mock(), + server_version: "0.4.2".to_string(), + is_utxo_indexed: true, + is_synced: false, + has_notify_command: true, + has_message_id: false, + } + } + } + + test!(GetInfoResponse); + + impl Mock for GetCurrentNetworkRequest { + fn mock() -> Self { + GetCurrentNetworkRequest {} + } + } + + test!(GetCurrentNetworkRequest); + + impl Mock for GetCurrentNetworkResponse { + fn mock() -> Self { + GetCurrentNetworkResponse { network: NetworkType::Mainnet } + } + } + + test!(GetCurrentNetworkResponse); + + impl Mock for GetPeerAddressesRequest { + fn mock() -> Self { + GetPeerAddressesRequest {} + } + } + + test!(GetPeerAddressesRequest); + + impl Mock for GetPeerAddressesResponse { + fn mock() -> Self { + GetPeerAddressesResponse { known_addresses: mock(), banned_addresses: mock() } + } + } + + test!(GetPeerAddressesResponse); + + impl Mock for GetSinkRequest { + fn mock() -> Self { + GetSinkRequest {} + } + } + + test!(GetSinkRequest); + + impl Mock for GetSinkResponse { + fn mock() -> Self { + GetSinkResponse { sink: mock() } + } + } + + test!(GetSinkResponse); + + impl Mock for GetMempoolEntryRequest { + fn mock() -> Self { + GetMempoolEntryRequest { transaction_id: mock(), include_orphan_pool: true, filter_transaction_pool: false } + } + } + + test!(GetMempoolEntryRequest); + + impl Mock for GetMempoolEntryResponse { + fn mock() -> Self { + GetMempoolEntryResponse { mempool_entry: RpcMempoolEntry { fee: mock(), transaction: mock(), is_orphan: false } } + } + } + + test!(GetMempoolEntryResponse); + + impl Mock for GetMempoolEntriesRequest { + fn mock() -> Self { + GetMempoolEntriesRequest { include_orphan_pool: true, filter_transaction_pool: false } + } + } + + test!(GetMempoolEntriesRequest); + + impl Mock for GetMempoolEntriesResponse { + fn mock() -> Self { + GetMempoolEntriesResponse { mempool_entries: mock() } + } + } + + test!(GetMempoolEntriesResponse); + + impl Mock for GetConnectedPeerInfoRequest { + fn mock() -> Self { + GetConnectedPeerInfoRequest {} + } + } + + test!(GetConnectedPeerInfoRequest); + + impl Mock for GetConnectedPeerInfoResponse { + fn mock() -> Self { + GetConnectedPeerInfoResponse { peer_info: mock() } + } + } + + test!(GetConnectedPeerInfoResponse); + + impl Mock for AddPeerRequest { + fn mock() -> Self { + AddPeerRequest { peer_address: mock(), is_permanent: mock() } + } + } + + test!(AddPeerRequest); + + impl Mock for AddPeerResponse { + fn mock() -> Self { + AddPeerResponse {} + } + } + + test!(AddPeerResponse); + + impl Mock for SubmitTransactionRequest { + fn mock() -> Self { + SubmitTransactionRequest { transaction: mock(), allow_orphan: mock() } + } + } + + test!(SubmitTransactionRequest); + + impl Mock for SubmitTransactionResponse { + fn mock() -> Self { + SubmitTransactionResponse { transaction_id: mock() } + } + } + + test!(SubmitTransactionResponse); + + impl Mock for GetSubnetworkRequest { + fn mock() -> Self { + GetSubnetworkRequest { subnetwork_id: mock() } + } + } + + test!(GetSubnetworkRequest); + + impl Mock for GetSubnetworkResponse { + fn mock() -> Self { + GetSubnetworkResponse { gas_limit: mock() } + } + } + + test!(GetSubnetworkResponse); + + impl Mock for GetVirtualChainFromBlockRequest { + fn mock() -> Self { + GetVirtualChainFromBlockRequest { start_hash: mock(), include_accepted_transaction_ids: mock() } + } + } + + test!(GetVirtualChainFromBlockRequest); + + impl Mock for RpcAcceptedTransactionIds { + fn mock() -> Self { + RpcAcceptedTransactionIds { accepting_block_hash: mock(), accepted_transaction_ids: mock() } + } + } + + impl Mock for GetVirtualChainFromBlockResponse { + fn mock() -> Self { + GetVirtualChainFromBlockResponse { + removed_chain_block_hashes: mock(), + added_chain_block_hashes: mock(), + accepted_transaction_ids: mock(), + } + } + } + + test!(GetVirtualChainFromBlockResponse); + + impl Mock for GetBlocksRequest { + fn mock() -> Self { + GetBlocksRequest { low_hash: mock(), include_blocks: mock(), include_transactions: mock() } + } + } + + test!(GetBlocksRequest); + + impl Mock for GetBlocksResponse { + fn mock() -> Self { + GetBlocksResponse { block_hashes: mock(), blocks: mock() } + } + } + + test!(GetBlocksResponse); + + impl Mock for GetBlockCountRequest { + fn mock() -> Self { + GetBlockCountRequest {} + } + } + + test!(GetBlockCountRequest); + + impl Mock for BlockCount { + fn mock() -> Self { + BlockCount { header_count: mock(), block_count: mock() } + } + } + + test!(BlockCount); + + impl Mock for GetBlockDagInfoRequest { + fn mock() -> Self { + GetBlockDagInfoRequest {} + } + } + + test!(GetBlockDagInfoRequest); + + impl Mock for GetBlockDagInfoResponse { + fn mock() -> Self { + GetBlockDagInfoResponse { + network: NetworkType::Mainnet.try_into().unwrap(), + block_count: mock(), + header_count: mock(), + tip_hashes: mock(), + difficulty: mock(), + past_median_time: mock(), + virtual_parent_hashes: mock(), + pruning_point_hash: mock(), + virtual_daa_score: mock(), + sink: mock(), + } + } + } + + test!(GetBlockDagInfoResponse); + + impl Mock for ResolveFinalityConflictRequest { + fn mock() -> Self { + ResolveFinalityConflictRequest { finality_block_hash: mock() } + } + } + + test!(ResolveFinalityConflictRequest); + + impl Mock for ResolveFinalityConflictResponse { + fn mock() -> Self { + ResolveFinalityConflictResponse {} + } + } + + test!(ResolveFinalityConflictResponse); + + impl Mock for ShutdownRequest { + fn mock() -> Self { + ShutdownRequest {} + } + } + + test!(ShutdownRequest); + + impl Mock for ShutdownResponse { + fn mock() -> Self { + ShutdownResponse {} + } + } + + test!(ShutdownResponse); + + impl Mock for GetHeadersRequest { + fn mock() -> Self { + GetHeadersRequest { start_hash: mock(), limit: mock(), is_ascending: mock() } + } + } + + test!(GetHeadersRequest); + + impl Mock for GetHeadersResponse { + fn mock() -> Self { + GetHeadersResponse { headers: mock() } + } + } + + test!(GetHeadersResponse); + + impl Mock for GetBalanceByAddressRequest { + fn mock() -> Self { + GetBalanceByAddressRequest { address: mock() } + } + } + + test!(GetBalanceByAddressRequest); + + impl Mock for GetBalanceByAddressResponse { + fn mock() -> Self { + GetBalanceByAddressResponse { balance: mock() } + } + } + + test!(GetBalanceByAddressResponse); + + impl Mock for GetBalancesByAddressesRequest { + fn mock() -> Self { + GetBalancesByAddressesRequest { addresses: mock() } + } + } + + test!(GetBalancesByAddressesRequest); + + impl Mock for RpcBalancesByAddressesEntry { + fn mock() -> Self { + RpcBalancesByAddressesEntry { address: mock(), balance: mock() } + } + } + + impl Mock for GetBalancesByAddressesResponse { + fn mock() -> Self { + GetBalancesByAddressesResponse { entries: mock() } + } + } + + test!(GetBalancesByAddressesResponse); + + impl Mock for GetSinkBlueScoreRequest { + fn mock() -> Self { + GetSinkBlueScoreRequest {} + } + } + + test!(GetSinkBlueScoreRequest); + + impl Mock for GetSinkBlueScoreResponse { + fn mock() -> Self { + GetSinkBlueScoreResponse { blue_score: mock() } + } + } + + test!(GetSinkBlueScoreResponse); + + impl Mock for GetUtxosByAddressesRequest { + fn mock() -> Self { + GetUtxosByAddressesRequest { addresses: mock() } + } + } + + test!(GetUtxosByAddressesRequest); + + impl Mock for GetUtxosByAddressesResponse { + fn mock() -> Self { + GetUtxosByAddressesResponse { entries: mock() } + } + } + + test!(GetUtxosByAddressesResponse); + + impl Mock for BanRequest { + fn mock() -> Self { + BanRequest { ip: mock() } + } + } + + test!(BanRequest); + + impl Mock for BanResponse { + fn mock() -> Self { + BanResponse {} + } + } + + test!(BanResponse); + + impl Mock for UnbanRequest { + fn mock() -> Self { + UnbanRequest { ip: mock() } + } + } + + test!(UnbanRequest); + + impl Mock for UnbanResponse { + fn mock() -> Self { + UnbanResponse {} + } + } + + test!(UnbanResponse); + + impl Mock for EstimateNetworkHashesPerSecondRequest { + fn mock() -> Self { + EstimateNetworkHashesPerSecondRequest { window_size: mock(), start_hash: mock() } + } + } + + test!(EstimateNetworkHashesPerSecondRequest); + + impl Mock for EstimateNetworkHashesPerSecondResponse { + fn mock() -> Self { + EstimateNetworkHashesPerSecondResponse { network_hashes_per_second: mock() } + } + } + + test!(EstimateNetworkHashesPerSecondResponse); + + impl Mock for GetMempoolEntriesByAddressesRequest { + fn mock() -> Self { + GetMempoolEntriesByAddressesRequest { addresses: mock(), include_orphan_pool: true, filter_transaction_pool: false } + } + } + + test!(GetMempoolEntriesByAddressesRequest); + + impl Mock for GetMempoolEntriesByAddressesResponse { + fn mock() -> Self { + GetMempoolEntriesByAddressesResponse { entries: mock() } + } + } + + test!(GetMempoolEntriesByAddressesResponse); + + impl Mock for GetCoinSupplyRequest { + fn mock() -> Self { + GetCoinSupplyRequest {} + } + } + + test!(GetCoinSupplyRequest); + + impl Mock for GetCoinSupplyResponse { + fn mock() -> Self { + GetCoinSupplyResponse { max_sompi: mock(), circulating_sompi: mock() } + } + } + + test!(GetCoinSupplyResponse); + + impl Mock for PingRequest { + fn mock() -> Self { + PingRequest {} + } + } + + test!(PingRequest); + + impl Mock for PingResponse { + fn mock() -> Self { + PingResponse {} + } + } + + test!(PingResponse); + + impl Mock for GetMetricsRequest { + fn mock() -> Self { + GetMetricsRequest { process_metrics: true, connection_metrics: true, bandwidth_metrics: true, consensus_metrics: true } + } + } + + test!(GetMetricsRequest); + + impl Mock for GetMetricsResponse { + fn mock() -> Self { + GetMetricsResponse { + server_time: mock(), + process_metrics: mock(), + connection_metrics: mock(), + bandwidth_metrics: mock(), + consensus_metrics: mock(), + } + } + } + + test!(GetMetricsResponse); + + impl Mock for GetServerInfoRequest { + fn mock() -> Self { + GetServerInfoRequest {} + } + } + + test!(GetServerInfoRequest); + + impl Mock for GetServerInfoResponse { + fn mock() -> Self { + GetServerInfoResponse { + rpc_api_version: [mock(), mock(), mock(), mock()], + server_version: "0.4.2".to_string(), + network_id: NetworkType::Mainnet.try_into().unwrap(), + has_utxo_index: true, + is_synced: false, + virtual_daa_score: mock(), + } + } + } + + test!(GetServerInfoResponse); + + impl Mock for GetSyncStatusRequest { + fn mock() -> Self { + GetSyncStatusRequest {} + } + } + + test!(GetSyncStatusRequest); + + impl Mock for GetSyncStatusResponse { + fn mock() -> Self { + GetSyncStatusResponse { is_synced: true } + } + } + + test!(GetSyncStatusResponse); + + impl Mock for GetDaaScoreTimestampEstimateRequest { + fn mock() -> Self { + GetDaaScoreTimestampEstimateRequest { daa_scores: mock() } + } + } + + test!(GetDaaScoreTimestampEstimateRequest); + + impl Mock for GetDaaScoreTimestampEstimateResponse { + fn mock() -> Self { + GetDaaScoreTimestampEstimateResponse { timestamps: mock() } + } + } + + test!(GetDaaScoreTimestampEstimateResponse); + + impl Mock for NotifyBlockAddedRequest { + fn mock() -> Self { + NotifyBlockAddedRequest { command: Command::Start } + } + } + + test!(NotifyBlockAddedRequest); + + impl Mock for NotifyBlockAddedResponse { + fn mock() -> Self { + NotifyBlockAddedResponse {} + } + } + + test!(NotifyBlockAddedResponse); + + impl Mock for BlockAddedNotification { + fn mock() -> Self { + BlockAddedNotification { block: mock() } + } + } + + test!(BlockAddedNotification); + + impl Mock for NotifyVirtualChainChangedRequest { + fn mock() -> Self { + NotifyVirtualChainChangedRequest { command: Command::Start, include_accepted_transaction_ids: true } + } + } + + test!(NotifyVirtualChainChangedRequest); + + impl Mock for NotifyVirtualChainChangedResponse { + fn mock() -> Self { + NotifyVirtualChainChangedResponse {} + } + } + + test!(NotifyVirtualChainChangedResponse); + + impl Mock for VirtualChainChangedNotification { + fn mock() -> Self { + VirtualChainChangedNotification { + removed_chain_block_hashes: mock(), + added_chain_block_hashes: mock(), + accepted_transaction_ids: mock(), + } + } + } + + test!(VirtualChainChangedNotification); + + impl Mock for NotifyFinalityConflictRequest { + fn mock() -> Self { + NotifyFinalityConflictRequest { command: Command::Start } + } + } + + test!(NotifyFinalityConflictRequest); + + impl Mock for NotifyFinalityConflictResponse { + fn mock() -> Self { + NotifyFinalityConflictResponse {} + } + } + + test!(NotifyFinalityConflictResponse); + + impl Mock for FinalityConflictNotification { + fn mock() -> Self { + FinalityConflictNotification { violating_block_hash: mock() } + } + } + + test!(FinalityConflictNotification); + + impl Mock for NotifyFinalityConflictResolvedRequest { + fn mock() -> Self { + NotifyFinalityConflictResolvedRequest { command: Command::Start } + } + } + + test!(NotifyFinalityConflictResolvedRequest); + + impl Mock for NotifyFinalityConflictResolvedResponse { + fn mock() -> Self { + NotifyFinalityConflictResolvedResponse {} + } + } + + test!(NotifyFinalityConflictResolvedResponse); + + impl Mock for FinalityConflictResolvedNotification { + fn mock() -> Self { + FinalityConflictResolvedNotification { finality_block_hash: mock() } + } + } + + test!(FinalityConflictResolvedNotification); + + impl Mock for NotifyUtxosChangedRequest { + fn mock() -> Self { + NotifyUtxosChangedRequest { addresses: mock(), command: Command::Start } + } + } + + test!(NotifyUtxosChangedRequest); + + impl Mock for NotifyUtxosChangedResponse { + fn mock() -> Self { + NotifyUtxosChangedResponse {} + } + } + + test!(NotifyUtxosChangedResponse); + + impl Mock for UtxosChangedNotification { + fn mock() -> Self { + UtxosChangedNotification { added: mock(), removed: mock() } + } + } + + test!(UtxosChangedNotification); + + impl Mock for NotifySinkBlueScoreChangedRequest { + fn mock() -> Self { + NotifySinkBlueScoreChangedRequest { command: Command::Start } + } + } + + test!(NotifySinkBlueScoreChangedRequest); + + impl Mock for NotifySinkBlueScoreChangedResponse { + fn mock() -> Self { + NotifySinkBlueScoreChangedResponse {} + } + } + + test!(NotifySinkBlueScoreChangedResponse); + + impl Mock for SinkBlueScoreChangedNotification { + fn mock() -> Self { + SinkBlueScoreChangedNotification { sink_blue_score: mock() } + } + } + + test!(SinkBlueScoreChangedNotification); + + impl Mock for NotifyVirtualDaaScoreChangedRequest { + fn mock() -> Self { + NotifyVirtualDaaScoreChangedRequest { command: Command::Start } + } + } + + test!(NotifyVirtualDaaScoreChangedRequest); + + impl Mock for NotifyVirtualDaaScoreChangedResponse { + fn mock() -> Self { + NotifyVirtualDaaScoreChangedResponse {} + } + } + + test!(NotifyVirtualDaaScoreChangedResponse); + + impl Mock for VirtualDaaScoreChangedNotification { + fn mock() -> Self { + VirtualDaaScoreChangedNotification { virtual_daa_score: mock() } + } + } + + test!(VirtualDaaScoreChangedNotification); + + impl Mock for NotifyPruningPointUtxoSetOverrideRequest { + fn mock() -> Self { + NotifyPruningPointUtxoSetOverrideRequest { command: Command::Start } + } + } + + test!(NotifyPruningPointUtxoSetOverrideRequest); + + impl Mock for NotifyPruningPointUtxoSetOverrideResponse { + fn mock() -> Self { + NotifyPruningPointUtxoSetOverrideResponse {} + } + } + + test!(NotifyPruningPointUtxoSetOverrideResponse); + + impl Mock for PruningPointUtxoSetOverrideNotification { + fn mock() -> Self { + PruningPointUtxoSetOverrideNotification {} + } + } + + test!(PruningPointUtxoSetOverrideNotification); + + impl Mock for NotifyNewBlockTemplateRequest { + fn mock() -> Self { + NotifyNewBlockTemplateRequest { command: Command::Start } + } + } + + test!(NotifyNewBlockTemplateRequest); + + impl Mock for NotifyNewBlockTemplateResponse { + fn mock() -> Self { + NotifyNewBlockTemplateResponse {} + } + } + + test!(NotifyNewBlockTemplateResponse); + + impl Mock for NewBlockTemplateNotification { + fn mock() -> Self { + NewBlockTemplateNotification {} + } + } + + test!(NewBlockTemplateNotification); + + impl Mock for SubscribeResponse { + fn mock() -> Self { + SubscribeResponse::new(mock()) + } + } + + test!(SubscribeResponse); + + impl Mock for UnsubscribeResponse { + fn mock() -> Self { + UnsubscribeResponse {} + } + } + + test!(UnsubscribeResponse); +} diff --git a/rpc/macros/src/lib.rs b/rpc/macros/src/lib.rs index 1c205c26ef..9ca49bf54a 100644 --- a/rpc/macros/src/lib.rs +++ b/rpc/macros/src/lib.rs @@ -39,3 +39,9 @@ pub fn build_wrpc_wasm_bindgen_subscriptions(input: TokenStream) -> TokenStream pub fn build_grpc_server_interface(input: TokenStream) -> TokenStream { grpc::server::build_grpc_server_interface(input) } + +#[proc_macro] +#[proc_macro_error] +pub fn test_wrpc_serializer(input: TokenStream) -> TokenStream { + wrpc::test::build_test(input) +} diff --git a/rpc/macros/src/wrpc/client.rs b/rpc/macros/src/wrpc/client.rs index f33fe57f31..49c09c491f 100644 --- a/rpc/macros/src/wrpc/client.rs +++ b/rpc/macros/src/wrpc/client.rs @@ -60,18 +60,19 @@ impl ToTokens for RpcTable { 'life0: 'async_trait, Self: 'async_trait, { + use workflow_serializer::prelude::*; Box::pin(async move { if let ::core::option::Option::Some(__ret) = ::core::option::Option::None::> { return __ret; } let __self = self; //let request = request; - let __ret: RpcResult<#response_type> = { - let resp: ClientResult<#response_type> = __self.inner.rpc_client.call(#rpc_api_ops::#handler, request).await; + let __ret: RpcResult> = { + let resp: ClientResult> = __self.inner.rpc_client.call(#rpc_api_ops::#handler, Serializable(request)).await; Ok(resp.map_err(|e| kaspa_rpc_core::error::RpcError::RpcSubsystem(e.to_string()))?) }; #[allow(unreachable_code)] - __ret + __ret.map(Serializable::into_inner) }) } diff --git a/rpc/macros/src/wrpc/mod.rs b/rpc/macros/src/wrpc/mod.rs index 1a15b06646..8c8238cdb8 100644 --- a/rpc/macros/src/wrpc/mod.rs +++ b/rpc/macros/src/wrpc/mod.rs @@ -1,3 +1,4 @@ pub mod client; pub mod server; +pub mod test; pub mod wasm; diff --git a/rpc/macros/src/wrpc/server.rs b/rpc/macros/src/wrpc/server.rs index 09a3e0c692..dfb1491d85 100644 --- a/rpc/macros/src/wrpc/server.rs +++ b/rpc/macros/src/wrpc/server.rs @@ -50,13 +50,13 @@ impl ToTokens for RpcTable { targets.push(quote! { #rpc_api_ops::#handler => { - interface.method(#rpc_api_ops::#handler, method!(|server_ctx: #server_ctx_type, connection_ctx: #connection_ctx_type, request: #request_type| async move { + interface.method(#rpc_api_ops::#handler, method!(|server_ctx: #server_ctx_type, connection_ctx: #connection_ctx_type, request: Serializable<#request_type>| async move { let verbose = server_ctx.verbose(); if verbose { workflow_log::log_info!("request: {:?}",request); } - let response: #response_type = server_ctx.rpc_service(&connection_ctx).#fn_call(request).await + let response: #response_type = server_ctx.rpc_service(&connection_ctx).#fn_call(request.into_inner()).await .map_err(|e|ServerError::Text(e.to_string()))?; if verbose { workflow_log::log_info!("response: {:?}",response); } - Ok(response) + Ok(Serializable(response)) })); } }); @@ -72,6 +72,7 @@ impl ToTokens for RpcTable { >::new(#server_ctx); for op in #rpc_api_ops::list() { + use workflow_serializer::prelude::*; match op { #(#targets)* _ => { } diff --git a/rpc/macros/src/wrpc/test.rs b/rpc/macros/src/wrpc/test.rs new file mode 100644 index 0000000000..92591b22b0 --- /dev/null +++ b/rpc/macros/src/wrpc/test.rs @@ -0,0 +1,60 @@ +use convert_case::{Case, Casing}; +use proc_macro2::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::{quote, ToTokens}; +use std::convert::Into; +use syn::{ + parse::{Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, + Error, Expr, Result, Token, +}; + +#[derive(Debug)] +struct TestTable { + rpc_op: Expr, +} + +impl Parse for TestTable { + fn parse(input: ParseStream) -> Result { + let parsed = Punctuated::::parse_terminated(input).unwrap(); + if parsed.len() != 1 { + return Err(Error::new_spanned(parsed, "usage: test!(GetInfo)".to_string())); + } + + let mut iter = parsed.iter(); + let rpc_op = iter.next().unwrap().clone(); + + Ok(TestTable { rpc_op }) + } +} + +impl ToTokens for TestTable { + fn to_tokens(&self, tokens: &mut TokenStream) { + let rpc_op = &self.rpc_op; + + let (name, _docs) = match rpc_op { + syn::Expr::Path(expr_path) => (expr_path.path.to_token_stream().to_string(), expr_path.attrs.clone()), + _ => (rpc_op.to_token_stream().to_string(), vec![]), + }; + let typename = Ident::new(&name.to_string(), Span::call_site()); + let fn_test = Ident::new(&format!("test_wrpc_serializer_{}", name.to_case(Case::Snake)), Span::call_site()); + + quote! { + + #[test] + fn #fn_test() { + test::<#typename>(#name); + } + + } + .to_tokens(tokens); + } +} + +pub fn build_test(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let rpc_table = parse_macro_input!(input as TestTable); + let ts = rpc_table.to_token_stream(); + // println!("MACRO: {}", ts.to_string()); + ts.into() +} diff --git a/rpc/wrpc/client/Cargo.toml b/rpc/wrpc/client/Cargo.toml index 199232ff00..c9dccc65b6 100644 --- a/rpc/wrpc/client/Cargo.toml +++ b/rpc/wrpc/client/Cargo.toml @@ -44,4 +44,5 @@ workflow-dom.workspace = true workflow-http.workspace = true workflow-log.workspace = true workflow-rpc.workspace = true -workflow-wasm.workspace = true \ No newline at end of file +workflow-serializer.workspace = true +workflow-wasm.workspace = true diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index 4e9fffc0c5..0d274069a0 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -18,7 +18,7 @@ use workflow_rpc::client::Ctl as WrpcCtl; pub use workflow_rpc::client::{ ConnectOptions, ConnectResult, ConnectStrategy, Resolver as RpcResolver, ResolverResult, WebSocketConfig, WebSocketError, }; - +use workflow_serializer::prelude::*; type RpcClientNotifier = Arc>; struct Inner { @@ -73,16 +73,16 @@ impl Inner { let notification_sender_ = notification_relay_channel.sender.clone(); interface.notification( notification_op, - workflow_rpc::client::Notification::new(move |notification: kaspa_rpc_core::Notification| { + workflow_rpc::client::Notification::new(move |notification: Serializable| { let notification_sender = notification_sender_.clone(); Box::pin(async move { // log_info!("notification receivers: {}", notification_sender.receiver_count()); // log_trace!("notification {:?}", notification); if notification_sender.receiver_count() > 1 { // log_info!("notification: posting to channel: {notification:?}"); - notification_sender.send(notification).await?; + notification_sender.send(notification.into_inner()).await?; } else { - log_warn!("WARNING: Kaspa RPC notification is not consumed by user: {:?}", notification); + log_warn!("WARNING: Kaspa RPC notification is not consumed by user: {:?}", notification.into_inner()); } Ok(()) }) @@ -121,14 +121,15 @@ impl Inner { /// Start sending notifications of some type to the client. async fn start_notify_to_client(&self, scope: Scope) -> RpcResult<()> { - let _response: SubscribeResponse = self.rpc_client.call(RpcApiOps::Subscribe, scope).await.map_err(|err| err.to_string())?; + let _response: Serializable = + self.rpc_client.call(RpcApiOps::Subscribe, Serializable(scope)).await.map_err(|err| err.to_string())?; Ok(()) } /// Stop sending notifications of some type to the client. async fn stop_notify_to_client(&self, scope: Scope) -> RpcResult<()> { - let _response: UnsubscribeResponse = - self.rpc_client.call(RpcApiOps::Unsubscribe, scope).await.map_err(|err| err.to_string())?; + let _response: Serializable = + self.rpc_client.call(RpcApiOps::Unsubscribe, Serializable(scope)).await.map_err(|err| err.to_string())?; Ok(()) } @@ -584,6 +585,7 @@ impl RpcApi for KaspaRpcClient { build_wrpc_client_interface!( RpcApiOps, [ + Ping, AddPeer, Ban, EstimateNetworkHashesPerSecond, @@ -612,7 +614,6 @@ impl RpcApi for KaspaRpcClient { GetUtxosByAddresses, GetSinkBlueScore, GetVirtualChainFromBlock, - Ping, ResolveFinalityConflict, Shutdown, SubmitBlock, diff --git a/rpc/wrpc/proxy/src/main.rs b/rpc/wrpc/proxy/src/main.rs index 43b3938c97..5450463e69 100644 --- a/rpc/wrpc/proxy/src/main.rs +++ b/rpc/wrpc/proxy/src/main.rs @@ -90,6 +90,7 @@ async fn main() -> Result<()> { rpc_handler.clone(), router.interface.clone(), Some(counters), + false, ); log_info!("Kaspa wRPC server is listening on {}", options.listen_address); diff --git a/rpc/wrpc/server/Cargo.toml b/rpc/wrpc/server/Cargo.toml index 885a01b751..f0f55aed75 100644 --- a/rpc/wrpc/server/Cargo.toml +++ b/rpc/wrpc/server/Cargo.toml @@ -33,6 +33,7 @@ tokio.workspace = true workflow-core.workspace = true workflow-log.workspace = true workflow-rpc.workspace = true +workflow-serializer.workspace = true [target.x86_64-unknown-linux-gnu.dependencies] # Adding explicitely the openssl dependency here is needed for a successful build with zigbuild diff --git a/rpc/wrpc/server/src/connection.rs b/rpc/wrpc/server/src/connection.rs index 86345e5d58..97a4e8adce 100644 --- a/rpc/wrpc/server/src/connection.rs +++ b/rpc/wrpc/server/src/connection.rs @@ -16,6 +16,7 @@ use workflow_rpc::{ server::{prelude::*, result::Result as WrpcResult}, types::{MsgT, OpsT}, }; +use workflow_serializer::prelude::*; // // FIXME: Use workflow_rpc::encoding::Encoding directly in the ConnectionT implementation by deriving Hash, Eq and PartialEq in situ @@ -157,7 +158,7 @@ impl ConnectionT for Connection { fn into_message(notification: &Self::Notification, encoding: &Self::Encoding) -> Self::Message { let op: RpcApiOps = notification.event_type().into(); - Self::create_serialized_notification_message(encoding.clone().into(), op, notification.clone()).unwrap() + Self::create_serialized_notification_message(encoding.clone().into(), op, Serializable(notification.clone())).unwrap() } async fn send(&self, message: Self::Message) -> core::result::Result<(), Self::Error> { diff --git a/rpc/wrpc/server/src/router.rs b/rpc/wrpc/server/src/router.rs index af46266811..f734caf03c 100644 --- a/rpc/wrpc/server/src/router.rs +++ b/rpc/wrpc/server/src/router.rs @@ -4,6 +4,7 @@ use kaspa_rpc_core::{api::ops::RpcApiOps, prelude::*}; use kaspa_rpc_macros::build_wrpc_server_interface; use std::sync::Arc; use workflow_rpc::server::prelude::*; +use workflow_serializer::prelude::*; /// A wrapper that creates an [`Interface`] instance and initializes /// RPC methods and notifications against this interface. The interface @@ -32,6 +33,7 @@ impl Router { Connection, RpcApiOps, [ + Ping, AddPeer, Ban, EstimateNetworkHashesPerSecond, @@ -61,7 +63,6 @@ impl Router { GetUtxosByAddresses, GetSinkBlueScore, GetVirtualChainFromBlock, - Ping, ResolveFinalityConflict, Shutdown, SubmitBlock, @@ -72,22 +73,22 @@ impl Router { interface.method( RpcApiOps::Subscribe, - workflow_rpc::server::Method::new(move |manager: Server, connection: Connection, scope: Scope| { + workflow_rpc::server::Method::new(move |manager: Server, connection: Connection, scope: Serializable| { Box::pin(async move { - manager.start_notify(&connection, scope).await.map_err(|err| err.to_string())?; - Ok(SubscribeResponse::new(connection.id())) + manager.start_notify(&connection, scope.into_inner()).await.map_err(|err| err.to_string())?; + Ok(Serializable(SubscribeResponse::new(connection.id()))) }) }), ); interface.method( RpcApiOps::Unsubscribe, - workflow_rpc::server::Method::new(move |manager: Server, connection: Connection, scope: Scope| { + workflow_rpc::server::Method::new(move |manager: Server, connection: Connection, scope: Serializable| { Box::pin(async move { - manager.stop_notify(&connection, scope).await.unwrap_or_else(|err| { + manager.stop_notify(&connection, scope.into_inner()).await.unwrap_or_else(|err| { workflow_log::log_trace!("wRPC server -> error calling stop_notify(): {err}"); }); - Ok(UnsubscribeResponse {}) + Ok(Serializable(UnsubscribeResponse {})) }) }), ); diff --git a/rpc/wrpc/server/src/service.rs b/rpc/wrpc/server/src/service.rs index 898ac5e295..866c4cdd64 100644 --- a/rpc/wrpc/server/src/service.rs +++ b/rpc/wrpc/server/src/service.rs @@ -123,6 +123,7 @@ impl WrpcService { rpc_handler.clone(), router.interface.clone(), Some(counters), + false, ); WrpcService { options, server, rpc_handler, shutdown: SingleTrigger::default() } diff --git a/utils/src/networking.rs b/utils/src/networking.rs index bb38b4d046..b2d7b92ffb 100644 --- a/utils/src/networking.rs +++ b/utils/src/networking.rs @@ -271,7 +271,7 @@ pub struct ContextualNetAddress { } impl ContextualNetAddress { - fn new(ip: IpAddress, port: Option) -> Self { + pub fn new(ip: IpAddress, port: Option) -> Self { Self { ip, port } } From 63abaf5bfc61a51ddc4bcc2ae5a64980010e42b3 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 31 May 2024 19:24:45 +0300 Subject: [PATCH 005/158] spelling --- cli/src/modules/guide.txt | 2 +- cli/src/wizards/wallet.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/modules/guide.txt b/cli/src/modules/guide.txt index ecc0f8d968..0b20ebaa48 100644 --- a/cli/src/modules/guide.txt +++ b/cli/src/modules/guide.txt @@ -43,7 +43,7 @@ named wallets. Only one wallet can be opened at a time. Keep in mind that a wal accounts, as such you only need one wallet, unless, for example, you want to separate wallets for personal and business needs (but you can also create isolated accounts within a wallet). -Make sure to record your mnemonic, even if working with a testnet, not to loose your +Make sure to record your mnemonic, even if working with a testnet, not to lose your testnet KAS. `open ` - opens the wallet (the wallet is open automatically after creation). diff --git a/cli/src/wizards/wallet.rs b/cli/src/wizards/wallet.rs index 8563a8619b..91d40ea52a 100644 --- a/cli/src/wizards/wallet.rs +++ b/cli/src/wizards/wallet.rs @@ -86,7 +86,7 @@ pub(crate) async fn create(ctx: &Arc, name: Option<&str>, import_with_ "\ PLEASE NOTE: The optional bip39 mnemonic passphrase, if provided, will be required to \ issue transactions. This passphrase will also be required when recovering your wallet \ - in addition to your private key or mnemonic. If you loose this passphrase, you will not \ + in addition to your private key or mnemonic. If you lose this passphrase, you will not \ be able to use or recover your wallet! \ \ If you do not want to use bip39 recovery passphrase, press ENTER.\ From bf8b1bee324ef50c30d97e3ec7ac4c1b1f532633 Mon Sep 17 00:00:00 2001 From: KaffinPX <73744616+KaffinPX@users.noreply.github.com> Date: Wed, 5 Jun 2024 07:55:34 +0300 Subject: [PATCH 006/158] Refactorize State into PoW (#43) * Add fromRaw with optional target_bits * Upload builds as GitHub Artifact * try moving calculateTarget into PoW class as static funct * Use FromHex trait * Make PoW constructor accept IHeader & refactorize some parts * Lint --- .github/workflows/ci.yaml | 5 ++ consensus/client/src/header.rs | 4 +- consensus/pow/src/wasm.rs | 76 +++++++++++++------ .../{mining-state.js => mining-pow.js} | 10 +-- 4 files changed, 64 insertions(+), 31 deletions(-) rename wasm/examples/nodejs/javascript/general/{mining-state.js => mining-pow.js} (87%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b6830560bd..7d058e7e13 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -339,6 +339,11 @@ jobs: - name: Build wasm release run: cd wasm && bash build-release + - name: Upload wasm binary to GitHub + uses: actions/upload-artifact@v4 + with: + name: kaspa-wasm32-sdk + path: wasm/release/kaspa-wasm32-sdk.zip build-release: name: Build Ubuntu Release runs-on: ubuntu-latest diff --git a/consensus/client/src/header.rs b/consensus/client/src/header.rs index 6294d21326..69990b7df2 100644 --- a/consensus/client/src/header.rs +++ b/consensus/client/src/header.rs @@ -37,7 +37,7 @@ export interface IHeader { #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "IHeader | Header")] - pub type IHeader; + pub type HeaderT; } /// @category Consensus @@ -64,7 +64,7 @@ impl Header { #[wasm_bindgen] impl Header { #[wasm_bindgen(constructor)] - pub fn constructor(js_value: IHeader) -> std::result::Result { + pub fn constructor(js_value: HeaderT) -> std::result::Result { Ok(js_value.try_into_owned()?) } diff --git a/consensus/pow/src/wasm.rs b/consensus/pow/src/wasm.rs index f5179e44a2..6da3d63bb3 100644 --- a/consensus/pow/src/wasm.rs +++ b/consensus/pow/src/wasm.rs @@ -1,74 +1,104 @@ use crate::matrix::Matrix; use js_sys::BigInt; use kaspa_consensus_client::Header; +use kaspa_consensus_client::HeaderT; use kaspa_consensus_core::hashing; use kaspa_hashes::Hash; use kaspa_hashes::PowHash; use kaspa_math::Uint256; +use kaspa_utils::hex::FromHex; use kaspa_utils::hex::ToHex; use num::Float; use wasm_bindgen::prelude::*; +use workflow_wasm::convert::TryCastFromJs; use workflow_wasm::error::Error; -use workflow_wasm::prelude::*; use workflow_wasm::result::Result; -/// @category PoW +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = js_sys::Array, typescript_type = "[boolean, bigint]")] + pub type WorkT; +} + +/// Represents a Kaspa header PoW manager +/// @category Mining #[wasm_bindgen(inspectable)] -pub struct State { +pub struct PoW { inner: crate::State, pre_pow_hash: Hash, } #[wasm_bindgen] -impl State { +impl PoW { #[wasm_bindgen(constructor)] - pub fn new(header: &Header) -> Self { + pub fn new(header: HeaderT, timestamp: Option) -> Result { // this function replicates crate::State::new() but caches // the pre_pow_hash value internally, making it available // via the `pre_pow_hash` property getter. - - // obtain locked inner + let header = Header::try_cast_from(header).map_err(Error::custom)?; + let header = header.as_ref(); let header = header.inner(); + // Get required target from header bits. let target = Uint256::from_compact_target_bits(header.bits); // Zero out the time and nonce. let pre_pow_hash = hashing::header::hash_override_nonce_time(header, 0, 0); // PRE_POW_HASH || TIME || 32 zero byte padding || NONCE - let hasher = PowHash::new(pre_pow_hash, header.timestamp); + let hasher = PowHash::new(pre_pow_hash, timestamp.unwrap_or(header.timestamp)); let matrix = Matrix::generate(pre_pow_hash); - Self { inner: crate::State { matrix, target, hasher }, pre_pow_hash } + Ok(Self { inner: crate::State { matrix, target, hasher }, pre_pow_hash }) } + /// The target based on the provided bits. #[wasm_bindgen(getter)] pub fn target(&self) -> Result { - self.inner.target.try_into().map_err(|err| Error::Custom(format!("{err:?}"))) + self.inner.target.try_into().map_err(|err| Error::custom(format!("{err:?}"))) } - #[wasm_bindgen(js_name=checkPow)] - pub fn check_pow(&self, nonce_jsv: JsValue) -> Result { - let nonce = nonce_jsv.try_as_u64()?; + /// Checks if the computed target meets or exceeds the difficulty specified in the template. + /// @returns A boolean indicating if it reached the target and a bigint representing the reached target. + #[wasm_bindgen(js_name=checkWork)] + pub fn check_work(&self, nonce: u64) -> Result { let (c, v) = self.inner.check_pow(nonce); let array = js_sys::Array::new(); array.push(&JsValue::from(c)); - array.push(&v.to_bigint().map_err(|err| Error::Custom(format!("{err:?}")))?.into()); + array.push(&v.to_bigint().map_err(|err| Error::custom(format!("{err:?}")))?.into()); - Ok(array) + Ok(array.unchecked_into()) } - #[wasm_bindgen(getter = prePowHash)] + /// Hash of the header without timestamp and nonce. + #[wasm_bindgen(getter = prePoWHash)] pub fn get_pre_pow_hash(&self) -> String { self.pre_pow_hash.to_hex() } + + /// Can be used for parsing Stratum templates. + #[wasm_bindgen(js_name=fromRaw)] + pub fn from_raw(pre_pow_hash: &str, timestamp: u64, target_bits: Option) -> Result { + // Convert the pre_pow_hash from hex string to Hash + let pre_pow_hash = Hash::from_hex(pre_pow_hash).map_err(|err| Error::custom(format!("{err:?}")))?; + + // Generate the target from compact target bits if provided + let target = Uint256::from_compact_target_bits(target_bits.unwrap_or_default()); + + // Initialize the matrix and hasher using pre_pow_hash and timestamp + let matrix = Matrix::generate(pre_pow_hash); + let hasher = PowHash::new(pre_pow_hash, timestamp); + + Ok(PoW { inner: crate::State { matrix, target, hasher }, pre_pow_hash }) + } } // https://github.com/tmrlvi/kaspa-miner/blob/bf361d02a46c580f55f46b5dfa773477634a5753/src/client/stratum.rs#L36 const DIFFICULTY_1_TARGET: (u64, i16) = (0xffffu64, 208); // 0xffff 2^208 -/// `calculate_difficulty` is based on set_difficulty function: -/// @category PoW -#[wasm_bindgen(js_name = calculateDifficulty)] -pub fn calculate_difficulty(difficulty: f32) -> std::result::Result { +/// Calculates target from difficulty, based on set_difficulty function on +/// +/// @category Mining +#[wasm_bindgen(js_name = calculateTarget)] +pub fn calculate_target(difficulty: f32) -> Result { let mut buf = [0u64, 0u64, 0u64, 0u64]; let (mantissa, exponent, _) = difficulty.recip().integer_decode(); let new_mantissa = mantissa * DIFFICULTY_1_TARGET.0; @@ -80,10 +110,8 @@ pub fn calculate_difficulty(difficulty: f32) -> std::result::Result> (64 - remainder); // top } else if new_mantissa.leading_zeros() < remainder as u32 { - return Err(JsError::new("Target is too big")); + return Err(Error::custom("Target is too big")); } - // let target_pool = Uint256(buf); - // workflow_log::log_info!("Difficulty: {:?}, Target: 0x{}", difficulty, target_pool.to_hex()); - Ok(Uint256(buf).try_into()?) + Ok(Uint256(buf).try_into().map_err(Error::custom)?) } diff --git a/wasm/examples/nodejs/javascript/general/mining-state.js b/wasm/examples/nodejs/javascript/general/mining-pow.js similarity index 87% rename from wasm/examples/nodejs/javascript/general/mining-state.js rename to wasm/examples/nodejs/javascript/general/mining-pow.js index 1741fd5459..73ba40df3c 100644 --- a/wasm/examples/nodejs/javascript/general/mining-state.js +++ b/wasm/examples/nodejs/javascript/general/mining-pow.js @@ -1,4 +1,4 @@ -const kaspa = require('../../../../nodejs/kaspa'); +const kaspa = require('../../../../nodejs/kaspa/kaspa'); const {parseArgs} = require("../utils"); kaspa.initConsolePanicHook(); @@ -32,12 +32,12 @@ kaspa.initConsolePanicHook(); console.log("header.blueWork:", header.blueWork); console.log("header.blueWork.toString(16):", header.blueWork.toString(16)); - console.log("creating state"); - const state = new kaspa.State(header); + console.log("creating PoW"); + const pow = new kaspa.PoW(header); const nonce = BigInt("0xffffffffffffffff"); console.log("nonce:", nonce); - const [a, v] = state.checkPow(nonce); - console.log("state:", state); + const [a, v] = pow.checkWork(nonce); + console.log("pow:", pow); console.log("[a,v]:", a, v); console.log("v.toString(16):", v.toString(16)); })(); From 6dc42dc0cb57aafcb8238bae62caadb59643cc76 Mon Sep 17 00:00:00 2001 From: aspect Date: Wed, 5 Jun 2024 07:55:57 +0300 Subject: [PATCH 007/158] TransactionDataT and BindingT (#44) --- wallet/core/src/storage/binding.rs | 39 +++++++++++++++++++ wallet/core/src/storage/mod.rs | 2 +- wallet/core/src/storage/transaction/record.rs | 16 ++++---- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/wallet/core/src/storage/binding.rs b/wallet/core/src/storage/binding.rs index 45eac4a7e9..18f988e2d2 100644 --- a/wallet/core/src/storage/binding.rs +++ b/wallet/core/src/storage/binding.rs @@ -5,6 +5,45 @@ use crate::imports::*; use crate::utxo::{UtxoContextBinding as UtxoProcessorBinding, UtxoContextId}; +#[wasm_bindgen(typescript_custom_section)] +const ITransactionRecord: &'static str = r#" + +/** + * Type of a binding record. + * @see {@link IBinding}, {@link ITransactionDataVariant}, {@link ITransactionRecord} + * @category Wallet SDK + */ +export enum BindingType { + /** + * The data structure is associated with a user-supplied id. + * @see {@link IBinding} + */ + Custom = "custom", + /** + * The data structure is associated with a wallet account. + * @see {@link IBinding}, {@link Account} + */ + Account = "account", +} + +/** + * Internal transaction data contained within the transaction record. + * @see {@link ITransactionRecord} + * @category Wallet SDK + */ +export interface IBinding { + type : BindingType; + data : HexString; +} +"#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Object, typescript_type = "IBinding")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type BindingT; +} + #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "kebab-case")] #[serde(tag = "type", content = "id")] diff --git a/wallet/core/src/storage/mod.rs b/wallet/core/src/storage/mod.rs index 21b30186a6..2516bcd483 100644 --- a/wallet/core/src/storage/mod.rs +++ b/wallet/core/src/storage/mod.rs @@ -18,7 +18,7 @@ pub mod transaction; pub use account::{AccountSettings, AccountStorable, AccountStorage}; pub use address::AddressBookEntry; -pub use binding::Binding; +pub use binding::{Binding, BindingT}; pub use hint::Hint; pub use id::IdT; pub use interface::{ diff --git a/wallet/core/src/storage/transaction/record.rs b/wallet/core/src/storage/transaction/record.rs index 7a04571331..ab43034fbf 100644 --- a/wallet/core/src/storage/transaction/record.rs +++ b/wallet/core/src/storage/transaction/record.rs @@ -4,7 +4,7 @@ use super::*; use crate::imports::*; -use crate::storage::Binding; +use crate::storage::{Binding, BindingT}; use crate::tx::PendingTransactionInner; use workflow_core::time::{unixtime_as_millis_u64, unixtime_to_locale_string}; use workflow_wasm::utils::try_get_js_value_prop; @@ -289,7 +289,9 @@ export interface ITransactionRecord { extern "C" { #[wasm_bindgen(extends = Object, typescript_type = "ITransactionRecord")] #[derive(Clone, Debug, PartialEq, Eq)] - pub type ITransactionRecord; + pub type TransactionRecordT; + #[wasm_bindgen(extends = Object, typescript_type = "ITransactionData")] + pub type TransactionDataT; } #[wasm_bindgen(inspectable)] @@ -785,13 +787,13 @@ impl TransactionRecord { #[wasm_bindgen] impl TransactionRecord { #[wasm_bindgen(getter, js_name = "binding")] - pub fn binding_as_js_value(&self) -> JsValue { - serde_wasm_bindgen::to_value(&self.binding).unwrap() + pub fn binding_as_js_value(&self) -> BindingT { + serde_wasm_bindgen::to_value(&self.binding).unwrap().unchecked_into() } #[wasm_bindgen(getter, js_name = "data")] - pub fn data_as_js_value(&self) -> JsValue { - try_get_js_value_prop(&serde_wasm_bindgen::to_value(&self.transaction_data).unwrap(), "data").unwrap() + pub fn data_as_js_value(&self) -> TransactionDataT { + try_get_js_value_prop(&serde_wasm_bindgen::to_value(&self.transaction_data).unwrap(), "data").unwrap().unchecked_into() } #[wasm_bindgen(getter, js_name = "type")] @@ -861,7 +863,7 @@ impl BorshDeserialize for TransactionRecord { // } // } -impl From for ITransactionRecord { +impl From for TransactionRecordT { fn from(record: TransactionRecord) -> Self { JsValue::from(record).unchecked_into() } From 804f4061680ecda9d33db85214cda8a937ee4054 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 11 Jun 2024 20:31:01 +0300 Subject: [PATCH 008/158] borsh update to 1.5.1 --- Cargo.lock | 182 +++-- Cargo.toml | 2 +- consensus/core/src/api/stats.rs | 8 +- consensus/core/src/tx.rs | 9 +- consensus/core/src/tx/script_public_key.rs | 8 +- crypto/addresses/src/lib.rs | 11 +- crypto/txscript/src/script_class.rs | 1 + notify/src/scope.rs | 46 +- notify/src/subscription/mod.rs | 1 + rpc/core/src/api/notifications.rs | 25 +- rpc/core/src/api/ops.rs | 69 +- rpc/core/src/model/message.rs | 714 +++++++++--------- utils/src/networking.rs | 28 +- wallet/bip32/src/xpublic_key.rs | 12 +- wallet/core/src/account/kind.rs | 27 +- wallet/core/src/account/variants/bip32.rs | 10 +- wallet/core/src/account/variants/keypair.rs | 12 +- wallet/core/src/account/variants/legacy.rs | 4 +- wallet/core/src/account/variants/multisig.rs | 12 +- wallet/core/src/api/transport.rs | 2 +- wallet/core/src/deterministic.rs | 4 +- wallet/core/src/encryption.rs | 2 +- wallet/core/src/serializer.rs | 4 +- wallet/core/src/storage/account.rs | 28 +- wallet/core/src/storage/keydata/data.rs | 9 +- wallet/core/src/storage/local/interface.rs | 2 +- wallet/core/src/storage/local/payload.rs | 12 +- .../src/storage/local/transaction/fsio.rs | 2 +- .../src/storage/local/transaction/indexdb.rs | 2 +- wallet/core/src/storage/local/wallet.rs | 18 +- wallet/core/src/storage/metadata.rs | 8 +- wallet/core/src/storage/transaction/data.rs | 100 +-- wallet/core/src/storage/transaction/record.rs | 24 +- wallet/core/src/tests/storage.rs | 4 +- wallet/core/src/utxo/balance.rs | 1 + wallet/macros/src/wallet/client.rs | 2 +- wallet/macros/src/wallet/server.rs | 2 +- 37 files changed, 701 insertions(+), 706 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2237ed580f..78d9494c4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,17 +50,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.14", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -691,47 +680,26 @@ dependencies = [ [[package]] name = "borsh" -version = "0.9.3" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" dependencies = [ "borsh-derive", - "hashbrown 0.11.2", + "cfg_aliases 0.2.1", ] [[package]] name = "borsh-derive" -version = "0.9.3" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", + "once_cell", "proc-macro-crate", - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" -dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "syn 2.0.60", + "syn_derive", ] [[package]] @@ -858,6 +826,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chacha20" version = "0.9.1" @@ -1949,15 +1923,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1970,7 +1935,7 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash", ] [[package]] @@ -2631,7 +2596,7 @@ dependencies = [ name = "kaspa-consensus-client" version = "0.14.1" dependencies = [ - "ahash 0.8.11", + "ahash", "cfg-if 1.0.0", "faster-hex 0.6.1", "hex", @@ -3200,7 +3165,7 @@ dependencies = [ name = "kaspa-resolver" version = "0.14.1" dependencies = [ - "ahash 0.8.11", + "ahash", "axum 0.7.5", "cfg-if 1.0.0", "clap 4.5.4", @@ -3216,7 +3181,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "toml 0.8.12", + "toml", "tower", "tower-http 0.5.2", "tracing-subscriber", @@ -3498,7 +3463,7 @@ name = "kaspa-wallet-core" version = "0.14.1" dependencies = [ "aes", - "ahash 0.8.11", + "ahash", "argon2", "async-channel 2.2.1", "async-std", @@ -3677,7 +3642,7 @@ dependencies = [ "serde-wasm-bindgen", "serde_json", "thiserror", - "toml 0.8.12", + "toml", "wasm-bindgen", "wasm-bindgen-futures", "workflow-core", @@ -3755,7 +3720,7 @@ dependencies = [ name = "kaspa-wrpc-wasm" version = "0.14.1" dependencies = [ - "ahash 0.8.11", + "ahash", "async-std", "cfg-if 1.0.0", "futures", @@ -3821,7 +3786,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "toml 0.8.12", + "toml", "workflow-log", ] @@ -4297,7 +4262,7 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.5.0", "cfg-if 1.0.0", - "cfg_aliases", + "cfg_aliases 0.1.1", "libc", ] @@ -4811,11 +4776,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "0.1.5" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml 0.5.11", + "toml_edit 0.21.1", ] [[package]] @@ -5803,6 +5768,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "sync_wrapper" version = "0.1.2" @@ -6078,15 +6055,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "toml" version = "0.8.12" @@ -6096,7 +6064,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.12", ] [[package]] @@ -6108,6 +6076,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.12" @@ -6118,7 +6097,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.7", ] [[package]] @@ -6826,6 +6805,15 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.6.7" @@ -6847,7 +6835,7 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6860,7 +6848,7 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.12.1" +version = "0.13.0" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6890,7 +6878,7 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.12.1" +version = "0.13.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6905,7 +6893,7 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.12.1" +version = "0.13.0" dependencies = [ "futures", "js-sys", @@ -6921,7 +6909,7 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6935,7 +6923,7 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cfg-if 1.0.0", "console", @@ -6949,7 +6937,7 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.12.1" +version = "0.13.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6960,7 +6948,7 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.12.1" +version = "0.13.0" dependencies = [ "borsh", "futures", @@ -6979,9 +6967,9 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.12.1" +version = "0.13.0" dependencies = [ - "ahash 0.8.11", + "ahash", "async-trait", "borsh", "futures", @@ -7001,7 +6989,7 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7024,9 +7012,9 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.12.1" +version = "0.13.0" dependencies = [ - "ahash 0.8.11", + "ahash", "async-std", "async-trait", "borsh", @@ -7052,7 +7040,7 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.12.1" +version = "0.13.0" dependencies = [ "parse-variants", "proc-macro-error", @@ -7063,16 +7051,16 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.12.1" +version = "0.13.0" dependencies = [ - "ahash 0.8.11", + "ahash", "borsh", "serde", ] [[package]] name = "workflow-store" -version = "0.12.1" +version = "0.13.0" dependencies = [ "async-std", "base64 0.21.7", @@ -7098,7 +7086,7 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.12.1" +version = "0.13.0" dependencies = [ "futures", "thiserror", @@ -7108,7 +7096,7 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.12.1" +version = "0.13.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7122,7 +7110,7 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.12.1" +version = "0.13.0" dependencies = [ "async-std", "async-trait", @@ -7149,7 +7137,7 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.12.1" +version = "0.13.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7163,7 +7151,7 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7182,7 +7170,7 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.12.1" +version = "0.13.0" dependencies = [ "js-sys", "proc-macro-error", @@ -7194,9 +7182,9 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.12.1" +version = "0.13.0" dependencies = [ - "ahash 0.8.11", + "ahash", "async-channel 2.2.1", "async-std", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index d418d3a3c6..dc0d81acc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,7 +148,7 @@ async-trait = "0.1.74" base64 = "0.21.5" bincode = { version = "1.3.3", default-features = false } blake2b_simd = "1.0.2" -borsh = { version = "0.9.1", features = ["rc"] } # please keep this fixed +borsh = { version = "1.5.1", features = ["derive", "rc"] } bs58 = { version = "0.5.0", features = ["check"], default-features = false } cc = "1.0.83" cfb-mode = "0.8.2" diff --git a/consensus/core/src/api/stats.rs b/consensus/core/src/api/stats.rs index 90b79e11ca..a004a41370 100644 --- a/consensus/core/src/api/stats.rs +++ b/consensus/core/src/api/stats.rs @@ -23,10 +23,10 @@ impl Serializer for BlockCount { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; - let header_count: u64 = load!(u64, buf)?; - let block_count: u64 = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; + let header_count: u64 = load!(u64, reader)?; + let block_count: u64 = load!(u64, reader)?; Ok(Self { header_count, block_count }) } diff --git a/consensus/core/src/tx.rs b/consensus/core/src/tx.rs index 1376337016..6d7b2dc0d6 100644 --- a/consensus/core/src/tx.rs +++ b/consensus/core/src/tx.rs @@ -137,8 +137,8 @@ impl Clone for TransactionMass { } impl BorshDeserialize for TransactionMass { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let mass: u64 = borsh::BorshDeserialize::deserialize(buf)?; + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let mass: u64 = borsh::BorshDeserialize::deserialize_reader(reader)?; Ok(Self(AtomicU64::new(mass))) } } @@ -163,7 +163,6 @@ pub struct Transaction { pub payload: Vec, #[serde(default)] - #[borsh_skip] // TODO: skipped for now as it is only required for consensus storage and miner grpc mass: TransactionMass, // A field that is used to cache the transaction ID. @@ -604,12 +603,12 @@ mod tests { fn test_spk_borsh() { // Tests for ScriptPublicKey Borsh ser/deser since we manually implemented them let spk = ScriptPublicKey::from_vec(12, vec![32; 20]); - let bin = spk.try_to_vec().unwrap(); + let bin = borsh::to_vec(&spk).unwrap(); let spk2: ScriptPublicKey = BorshDeserialize::try_from_slice(&bin).unwrap(); assert_eq!(spk, spk2); let spk = ScriptPublicKey::from_vec(55455, vec![11; 200]); - let bin = spk.try_to_vec().unwrap(); + let bin = borsh::to_vec(&spk).unwrap(); let spk2: ScriptPublicKey = BorshDeserialize::try_from_slice(&bin).unwrap(); assert_eq!(spk, spk2); } diff --git a/consensus/core/src/tx/script_public_key.rs b/consensus/core/src/tx/script_public_key.rs index 7f3ade6943..f52ace14a8 100644 --- a/consensus/core/src/tx/script_public_key.rs +++ b/consensus/core/src/tx/script_public_key.rs @@ -357,9 +357,9 @@ impl BorshSerialize for ScriptPublicKey { } impl BorshDeserialize for ScriptPublicKey { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { + fn deserialize_reader(reader: &mut R) -> std::io::Result { // Deserialize into vec first since we have no custom smallvec support - Ok(Self::from_vec(borsh::BorshDeserialize::deserialize(buf)?, borsh::BorshDeserialize::deserialize(buf)?)) + Ok(Self::from_vec(borsh::BorshDeserialize::deserialize_reader(reader)?, borsh::BorshDeserialize::deserialize_reader(reader)?)) } } @@ -403,12 +403,12 @@ mod tests { fn test_spk_borsh() { // Tests for ScriptPublicKey Borsh ser/deser since we manually implemented them let spk = ScriptPublicKey::from_vec(12, vec![32; 20]); - let bin = spk.try_to_vec().unwrap(); + let bin = borsh::to_vec(&spk).unwrap(); let spk2: ScriptPublicKey = BorshDeserialize::try_from_slice(&bin).unwrap(); assert_eq!(spk, spk2); let spk = ScriptPublicKey::from_vec(55455, vec![11; 200]); - let bin = spk.try_to_vec().unwrap(); + let bin = borsh::to_vec(&spk).unwrap(); let spk2: ScriptPublicKey = BorshDeserialize::try_from_slice(&bin).unwrap(); assert_eq!(spk, spk2); } diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index fdba63ef7f..117e66ac48 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -49,6 +49,7 @@ impl From for AddressError { /// Address prefix identifying the network type this address belongs to (such as `kaspa`, `kaspatest`, `kaspasim`, `kaspadev`). #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[borsh(use_discriminant = true)] pub enum Prefix { #[serde(rename = "kaspa")] Mainnet, @@ -117,6 +118,7 @@ impl TryFrom<&str> for Prefix { /// @category Address #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[repr(u8)] +#[borsh(use_discriminant = true)] #[wasm_bindgen(js_name = "AddressVersion")] pub enum Version { /// PubKey addresses always have the version byte set to 0 @@ -281,11 +283,10 @@ impl BorshSerialize for Address { } impl BorshDeserialize for Address { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - // Deserialize into vec first since we have no custom smallvec support - let prefix: Prefix = borsh::BorshDeserialize::deserialize(buf)?; - let version: Version = borsh::BorshDeserialize::deserialize(buf)?; - let payload: Vec = borsh::BorshDeserialize::deserialize(buf)?; + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let prefix: Prefix = borsh::BorshDeserialize::deserialize_reader(reader)?; + let version: Version = borsh::BorshDeserialize::deserialize_reader(reader)?; + let payload: Vec = borsh::BorshDeserialize::deserialize_reader(reader)?; Ok(Self::new(prefix, version, &payload)) } } diff --git a/crypto/txscript/src/script_class.rs b/crypto/txscript/src/script_class.rs index 8e7a7796c4..ad61f30d89 100644 --- a/crypto/txscript/src/script_class.rs +++ b/crypto/txscript/src/script_class.rs @@ -17,6 +17,7 @@ pub enum Error { /// Standard classes of script payment in the blockDAG #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[borsh(use_discriminant = true)] #[repr(u8)] pub enum ScriptClass { /// None of the recognized forms diff --git a/notify/src/scope.rs b/notify/src/scope.rs index 12c7e2e9c7..ea00016332 100644 --- a/notify/src/scope.rs +++ b/notify/src/scope.rs @@ -60,9 +60,9 @@ impl Serializer for Scope { store!(Scope, self, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; - load!(Scope, buf) + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; + load!(Scope, reader) } } @@ -74,8 +74,8 @@ impl Serializer for BlockAddedScope { store!(u32, &1, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; Ok(Self {}) } } @@ -103,9 +103,9 @@ impl Serializer for VirtualChainChangedScope { store!(bool, &self.include_accepted_transaction_ids, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; - let include_accepted_transaction_ids: bool = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; + let include_accepted_transaction_ids: bool = load!(bool, reader)?; Ok(Self { include_accepted_transaction_ids }) } } @@ -118,8 +118,8 @@ impl Serializer for FinalityConflictScope { store!(u32, &1, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; Ok(Self {}) } } @@ -132,8 +132,8 @@ impl Serializer for FinalityConflictResolvedScope { store!(u32, &1, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; Ok(Self {}) } } @@ -174,9 +174,9 @@ impl Serializer for UtxosChangedScope { store!(Vec
, &self.addresses, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; - let addresses: Vec
= load!(Vec
, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; + let addresses: Vec
= load!(Vec
, reader)?; Ok(Self { addresses }) } } @@ -189,8 +189,8 @@ impl Serializer for SinkBlueScoreChangedScope { store!(u32, &1, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; Ok(Self {}) } } @@ -203,8 +203,8 @@ impl Serializer for VirtualDaaScoreChangedScope { store!(u32, &1, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; Ok(Self {}) } } @@ -217,8 +217,8 @@ impl Serializer for PruningPointUtxoSetOverrideScope { store!(u32, &1, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; Ok(Self {}) } } @@ -231,8 +231,8 @@ impl Serializer for NewBlockTemplateScope { store!(u32, &1, writer)?; Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; Ok(Self {}) } } diff --git a/notify/src/subscription/mod.rs b/notify/src/subscription/mod.rs index 6cded477d8..ff468be74b 100644 --- a/notify/src/subscription/mod.rs +++ b/notify/src/subscription/mod.rs @@ -16,6 +16,7 @@ pub mod context; pub mod single; #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[borsh(use_discriminant = true)] pub enum Command { Start = 0, Stop = 1, diff --git a/rpc/core/src/api/notifications.rs b/rpc/core/src/api/notifications.rs index 5d8eccf2f9..3fbb2652e1 100644 --- a/rpc/core/src/api/notifications.rs +++ b/rpc/core/src/api/notifications.rs @@ -157,47 +157,46 @@ impl Serializer for Notification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version: u32 = load!(u32, buf)?; - match load!(u32, buf)? { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u32 = load!(u32, reader)?; + match load!(u32, reader)? { 0 => { - let notification = deserialize!(BlockAddedNotification, buf)?; + let notification = deserialize!(BlockAddedNotification, reader)?; Ok(Notification::BlockAdded(notification)) } 1 => { - let notification = deserialize!(VirtualChainChangedNotification, buf)?; + let notification = deserialize!(VirtualChainChangedNotification, reader)?; Ok(Notification::VirtualChainChanged(notification)) } 2 => { - let notification = deserialize!(FinalityConflictNotification, buf)?; + let notification = deserialize!(FinalityConflictNotification, reader)?; Ok(Notification::FinalityConflict(notification)) } 3 => { - let notification = deserialize!(FinalityConflictResolvedNotification, buf)?; + let notification = deserialize!(FinalityConflictResolvedNotification, reader)?; Ok(Notification::FinalityConflictResolved(notification)) } 4 => { - let notification = deserialize!(UtxosChangedNotification, buf)?; + let notification = deserialize!(UtxosChangedNotification, reader)?; Ok(Notification::UtxosChanged(notification)) } 5 => { - let notification = deserialize!(SinkBlueScoreChangedNotification, buf)?; + let notification = deserialize!(SinkBlueScoreChangedNotification, reader)?; Ok(Notification::SinkBlueScoreChanged(notification)) } 6 => { - let notification = deserialize!(VirtualDaaScoreChangedNotification, buf)?; + let notification = deserialize!(VirtualDaaScoreChangedNotification, reader)?; Ok(Notification::VirtualDaaScoreChanged(notification)) } 7 => { - let notification = deserialize!(PruningPointUtxoSetOverrideNotification, buf)?; + let notification = deserialize!(PruningPointUtxoSetOverrideNotification, reader)?; Ok(Notification::PruningPointUtxoSetOverride(notification)) } 8 => { - let notification = deserialize!(NewBlockTemplateNotification, buf)?; + let notification = deserialize!(NewBlockTemplateNotification, reader)?; Ok(Notification::NewBlockTemplate(notification)) } _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid variant")), } } } - diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index a02fbf7462..37e64a795e 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -17,9 +17,48 @@ pub const RPC_API_VERSION: [u16; 4] = [0, 1, 0, 0]; #[derive(Describe, Clone, Copy, Debug, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] +#[borsh(use_discriminant = true)] pub enum RpcApiOps { + NoOp = 0, + + // connection control (provisional) + Connect, + Disconnect, + + // subscription management + Subscribe, + Unsubscribe, + + // ~~~ + + // Subscription commands for starting/stopping notifications + NotifyBlockAdded = 10, + NotifyNewBlockTemplate, + NotifyUtxosChanged, + NotifyPruningPointUtxoSetOverride, + NotifyFinalityConflict, + NotifyFinalityConflictResolved, // for uniformity purpose only since subscribing to NotifyFinalityConflict means receiving both FinalityConflict and FinalityConflictResolved + NotifyVirtualDaaScoreChanged, + NotifyVirtualChainChanged, + NotifySinkBlueScoreChanged, + + // Notification ops required by wRPC + + // TODO: Remove these ops and use EventType as NotificationOps when workflow_rpc::server::interface::Interface + // will be generic over a MethodOps and NotificationOps instead of a single Ops param. + BlockAddedNotification = 60, + VirtualChainChangedNotification, + FinalityConflictNotification, + FinalityConflictResolvedNotification, + UtxosChangedNotification, + SinkBlueScoreChangedNotification, + VirtualDaaScoreChangedNotification, + PruningPointUtxoSetOverrideNotification, + NewBlockTemplateNotification, + + // RPC methods /// Ping the node to check if connection is alive - Ping = 0, + Ping = 110, /// Get metrics for consensus information and node performance GetMetrics, /// Get state information on the node @@ -86,34 +125,6 @@ pub enum RpcApiOps { GetCoinSupply, /// Get DAA Score timestamp estimate GetDaaScoreTimestampEstimate, - - // Subscription commands for starting/stopping notifications - NotifyBlockAdded, - NotifyNewBlockTemplate, - NotifyUtxosChanged, - NotifyPruningPointUtxoSetOverride, - NotifyFinalityConflict, - NotifyFinalityConflictResolved, // for uniformity purpose only since subscribing to NotifyFinalityConflict means receiving both FinalityConflict and FinalityConflictResolved - NotifyVirtualDaaScoreChanged, - NotifyVirtualChainChanged, - NotifySinkBlueScoreChanged, - - // ~ - Subscribe, - Unsubscribe, - - // Notification ops required by wRPC - // TODO: Remove these ops and use EventType as NotificationOps when workflow_rpc::server::interface::Interface - // will be generic over a MethodOps and NotificationOps instead of a single Ops param. - BlockAddedNotification, - VirtualChainChangedNotification, - FinalityConflictNotification, - FinalityConflictResolvedNotification, - UtxosChangedNotification, - SinkBlueScoreChangedNotification, - VirtualDaaScoreChangedNotification, - PruningPointUtxoSetOverrideNotification, - NewBlockTemplateNotification, } impl RpcApiOps { diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index ca01bc45fc..728158bf3e 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -38,10 +38,10 @@ impl Serializer for SubmitBlockRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let block = load!(RpcBlock, buf)?; - let allow_non_daa_blocks = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let block = load!(RpcBlock, reader)?; + let allow_non_daa_blocks = load!(bool, reader)?; Ok(Self { block, allow_non_daa_blocks }) } @@ -49,6 +49,7 @@ impl Serializer for SubmitBlockRequest { #[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "camelCase")] +#[borsh(use_discriminant = true)] pub enum SubmitBlockRejectReason { BlockInvalid = 1, IsInIBD = 2, @@ -73,6 +74,7 @@ impl Display for SubmitBlockRejectReason { #[derive(Eq, PartialEq, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "lowercase")] #[serde(tag = "type", content = "reason")] +#[borsh(use_discriminant = true)] pub enum SubmitBlockReport { Success, Reject(SubmitBlockRejectReason), @@ -96,9 +98,9 @@ impl Serializer for SubmitBlockResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let report = load!(SubmitBlockReport, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let report = load!(SubmitBlockReport, reader)?; Ok(Self { report }) } @@ -131,10 +133,10 @@ impl Serializer for GetBlockTemplateRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let pay_address = load!(RpcAddress, buf)?; - let extra_data = load!(RpcExtraData, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let pay_address = load!(RpcAddress, reader)?; + let extra_data = load!(RpcExtraData, reader)?; Ok(Self { pay_address, extra_data }) } @@ -161,10 +163,10 @@ impl Serializer for GetBlockTemplateResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let block = load!(RpcBlock, buf)?; - let is_synced = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let block = load!(RpcBlock, reader)?; + let is_synced = load!(bool, reader)?; Ok(Self { block, is_synced }) } @@ -195,10 +197,10 @@ impl Serializer for GetBlockRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let hash = load!(RpcHash, buf)?; - let include_transactions = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let hash = load!(RpcHash, reader)?; + let include_transactions = load!(bool, reader)?; Ok(Self { hash, include_transactions }) } @@ -218,9 +220,9 @@ impl Serializer for GetBlockResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let block = load!(RpcBlock, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let block = load!(RpcBlock, reader)?; Ok(Self { block }) } @@ -237,8 +239,8 @@ impl Serializer for GetInfoRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -269,15 +271,15 @@ impl Serializer for GetInfoResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let p2p_id = load!(String, buf)?; - let mempool_size = load!(u64, buf)?; - let server_version = load!(String, buf)?; - let is_utxo_indexed = load!(bool, buf)?; - let is_synced = load!(bool, buf)?; - let has_notify_command = load!(bool, buf)?; - let has_message_id = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let p2p_id = load!(String, reader)?; + let mempool_size = load!(u64, reader)?; + let server_version = load!(String, reader)?; + let is_utxo_indexed = load!(bool, reader)?; + let is_synced = load!(bool, reader)?; + let has_notify_command = load!(bool, reader)?; + let has_message_id = load!(bool, reader)?; Ok(Self { p2p_id, mempool_size, server_version, is_utxo_indexed, is_synced, has_notify_command, has_message_id }) } @@ -293,8 +295,8 @@ impl Serializer for GetCurrentNetworkRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -318,9 +320,9 @@ impl Serializer for GetCurrentNetworkResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let network = load!(RpcNetworkType, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let network = load!(RpcNetworkType, reader)?; Ok(Self { network }) } } @@ -335,8 +337,8 @@ impl Serializer for GetPeerAddressesRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -362,10 +364,10 @@ impl Serializer for GetPeerAddressesResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let known_addresses = load!(Vec, buf)?; - let banned_addresses = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let known_addresses = load!(Vec, reader)?; + let banned_addresses = load!(Vec, reader)?; Ok(Self { known_addresses, banned_addresses }) } } @@ -380,8 +382,8 @@ impl Serializer for GetSinkRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -405,9 +407,9 @@ impl Serializer for GetSinkResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let sink = load!(RpcHash, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let sink = load!(RpcHash, reader)?; Ok(Self { sink }) } } @@ -437,11 +439,11 @@ impl Serializer for GetMempoolEntryRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let transaction_id = load!(RpcTransactionId, buf)?; - let include_orphan_pool = load!(bool, buf)?; - let filter_transaction_pool = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let transaction_id = load!(RpcTransactionId, reader)?; + let include_orphan_pool = load!(bool, reader)?; + let filter_transaction_pool = load!(bool, reader)?; Ok(Self { transaction_id, include_orphan_pool, filter_transaction_pool }) } @@ -466,9 +468,9 @@ impl Serializer for GetMempoolEntryResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let mempool_entry = load!(RpcMempoolEntry, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let mempool_entry = load!(RpcMempoolEntry, reader)?; Ok(Self { mempool_entry }) } } @@ -496,10 +498,10 @@ impl Serializer for GetMempoolEntriesRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let include_orphan_pool = load!(bool, buf)?; - let filter_transaction_pool = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let include_orphan_pool = load!(bool, reader)?; + let filter_transaction_pool = load!(bool, reader)?; Ok(Self { include_orphan_pool, filter_transaction_pool }) } @@ -524,9 +526,9 @@ impl Serializer for GetMempoolEntriesResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let mempool_entries = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let mempool_entries = load!(Vec, reader)?; Ok(Self { mempool_entries }) } } @@ -541,8 +543,8 @@ impl Serializer for GetConnectedPeerInfoRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -566,9 +568,9 @@ impl Serializer for GetConnectedPeerInfoResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let peer_info = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let peer_info = load!(Vec, reader)?; Ok(Self { peer_info }) } } @@ -595,10 +597,10 @@ impl Serializer for AddPeerRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let peer_address = load!(RpcContextualPeerAddress, buf)?; - let is_permanent = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let peer_address = load!(RpcContextualPeerAddress, reader)?; + let is_permanent = load!(bool, reader)?; Ok(Self { peer_address, is_permanent }) } @@ -614,8 +616,8 @@ impl Serializer for AddPeerResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -643,10 +645,10 @@ impl Serializer for SubmitTransactionRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let transaction = load!(RpcTransaction, buf)?; - let allow_orphan = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let transaction = load!(RpcTransaction, reader)?; + let allow_orphan = load!(bool, reader)?; Ok(Self { transaction, allow_orphan }) } @@ -672,9 +674,9 @@ impl Serializer for SubmitTransactionResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let transaction_id = load!(RpcTransactionId, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let transaction_id = load!(RpcTransactionId, reader)?; Ok(Self { transaction_id }) } @@ -700,9 +702,9 @@ impl Serializer for GetSubnetworkRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let subnetwork_id = load!(RpcSubnetworkId, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let subnetwork_id = load!(RpcSubnetworkId, reader)?; Ok(Self { subnetwork_id }) } @@ -728,9 +730,9 @@ impl Serializer for GetSubnetworkResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let gas_limit = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let gas_limit = load!(u64, reader)?; Ok(Self { gas_limit }) } @@ -758,10 +760,10 @@ impl Serializer for GetVirtualChainFromBlockRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let start_hash = load!(RpcHash, buf)?; - let include_accepted_transaction_ids = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let start_hash = load!(RpcHash, reader)?; + let include_accepted_transaction_ids = load!(bool, reader)?; Ok(Self { start_hash, include_accepted_transaction_ids }) } @@ -795,11 +797,11 @@ impl Serializer for GetVirtualChainFromBlockResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let removed_chain_block_hashes = load!(Vec, buf)?; - let added_chain_block_hashes = load!(Vec, buf)?; - let accepted_transaction_ids = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let removed_chain_block_hashes = load!(Vec, reader)?; + let added_chain_block_hashes = load!(Vec, reader)?; + let accepted_transaction_ids = load!(Vec, reader)?; Ok(Self { removed_chain_block_hashes, added_chain_block_hashes, accepted_transaction_ids }) } @@ -829,11 +831,11 @@ impl Serializer for GetBlocksRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let low_hash = load!(Option, buf)?; - let include_blocks = load!(bool, buf)?; - let include_transactions = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let low_hash = load!(Option, reader)?; + let include_blocks = load!(bool, reader)?; + let include_transactions = load!(bool, reader)?; Ok(Self { low_hash, include_blocks, include_transactions }) } @@ -861,10 +863,10 @@ impl Serializer for GetBlocksResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let block_hashes = load!(Vec, buf)?; - let blocks = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let block_hashes = load!(Vec, reader)?; + let blocks = load!(Vec, reader)?; Ok(Self { block_hashes, blocks }) } @@ -880,8 +882,8 @@ impl Serializer for GetBlockCountRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -898,8 +900,8 @@ impl Serializer for GetBlockDagInfoRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -964,18 +966,18 @@ impl Serializer for GetBlockDagInfoResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let network = load!(RpcNetworkId, buf)?; - let block_count = load!(u64, buf)?; - let header_count = load!(u64, buf)?; - let tip_hashes = load!(Vec, buf)?; - let difficulty = load!(f64, buf)?; - let past_median_time = load!(u64, buf)?; - let virtual_parent_hashes = load!(Vec, buf)?; - let pruning_point_hash = load!(RpcHash, buf)?; - let virtual_daa_score = load!(u64, buf)?; - let sink = load!(RpcHash, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let network = load!(RpcNetworkId, reader)?; + let block_count = load!(u64, reader)?; + let header_count = load!(u64, reader)?; + let tip_hashes = load!(Vec, reader)?; + let difficulty = load!(f64, reader)?; + let past_median_time = load!(u64, reader)?; + let virtual_parent_hashes = load!(Vec, reader)?; + let pruning_point_hash = load!(RpcHash, reader)?; + let virtual_daa_score = load!(u64, reader)?; + let sink = load!(RpcHash, reader)?; Ok(Self { network, @@ -1012,9 +1014,9 @@ impl Serializer for ResolveFinalityConflictRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let finality_block_hash = load!(RpcHash, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let finality_block_hash = load!(RpcHash, reader)?; Ok(Self { finality_block_hash }) } @@ -1030,8 +1032,8 @@ impl Serializer for ResolveFinalityConflictResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -1046,8 +1048,8 @@ impl Serializer for ShutdownRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -1062,8 +1064,8 @@ impl Serializer for ShutdownResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -1092,11 +1094,11 @@ impl Serializer for GetHeadersRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let start_hash = load!(RpcHash, buf)?; - let limit = load!(u64, buf)?; - let is_ascending = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let start_hash = load!(RpcHash, reader)?; + let limit = load!(u64, reader)?; + let is_ascending = load!(bool, reader)?; Ok(Self { start_hash, limit, is_ascending }) } @@ -1122,9 +1124,9 @@ impl Serializer for GetHeadersResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let headers = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let headers = load!(Vec, reader)?; Ok(Self { headers }) } @@ -1150,9 +1152,9 @@ impl Serializer for GetBalanceByAddressRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let address = load!(RpcAddress, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let address = load!(RpcAddress, reader)?; Ok(Self { address }) } @@ -1178,9 +1180,9 @@ impl Serializer for GetBalanceByAddressResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let balance = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let balance = load!(u64, reader)?; Ok(Self { balance }) } @@ -1206,9 +1208,9 @@ impl Serializer for GetBalancesByAddressesRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let addresses = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let addresses = load!(Vec, reader)?; Ok(Self { addresses }) } @@ -1234,9 +1236,9 @@ impl Serializer for GetBalancesByAddressesResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let entries = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let entries = load!(Vec, reader)?; Ok(Self { entries }) } @@ -1252,8 +1254,8 @@ impl Serializer for GetSinkBlueScoreRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -1278,9 +1280,9 @@ impl Serializer for GetSinkBlueScoreResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let blue_score = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let blue_score = load!(u64, reader)?; Ok(Self { blue_score }) } @@ -1306,9 +1308,9 @@ impl Serializer for GetUtxosByAddressesRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let addresses = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let addresses = load!(Vec, reader)?; Ok(Self { addresses }) } @@ -1334,9 +1336,9 @@ impl Serializer for GetUtxosByAddressesResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let entries = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let entries = load!(Vec, reader)?; Ok(Self { entries }) } @@ -1362,9 +1364,9 @@ impl Serializer for BanRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let ip = load!(RpcIpAddress, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let ip = load!(RpcIpAddress, reader)?; Ok(Self { ip }) } @@ -1380,8 +1382,8 @@ impl Serializer for BanResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -1406,9 +1408,9 @@ impl Serializer for UnbanRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let ip = load!(RpcIpAddress, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let ip = load!(RpcIpAddress, reader)?; Ok(Self { ip }) } @@ -1424,8 +1426,8 @@ impl Serializer for UnbanResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -1452,10 +1454,10 @@ impl Serializer for EstimateNetworkHashesPerSecondRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let window_size = load!(u32, buf)?; - let start_hash = load!(Option, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let window_size = load!(u32, reader)?; + let start_hash = load!(Option, reader)?; Ok(Self { window_size, start_hash }) } @@ -1481,9 +1483,9 @@ impl Serializer for EstimateNetworkHashesPerSecondResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let network_hashes_per_second = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let network_hashes_per_second = load!(u64, reader)?; Ok(Self { network_hashes_per_second }) } @@ -1514,11 +1516,11 @@ impl Serializer for GetMempoolEntriesByAddressesRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let addresses = load!(Vec, buf)?; - let include_orphan_pool = load!(bool, buf)?; - let filter_transaction_pool = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let addresses = load!(Vec, reader)?; + let include_orphan_pool = load!(bool, reader)?; + let filter_transaction_pool = load!(bool, reader)?; Ok(Self { addresses, include_orphan_pool, filter_transaction_pool }) } @@ -1544,9 +1546,9 @@ impl Serializer for GetMempoolEntriesByAddressesResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let entries = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let entries = load!(Vec, reader)?; Ok(Self { entries }) } @@ -1562,8 +1564,8 @@ impl Serializer for GetCoinSupplyRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -1590,10 +1592,10 @@ impl Serializer for GetCoinSupplyResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let max_sompi = load!(u64, buf)?; - let circulating_sompi = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let max_sompi = load!(u64, reader)?; + let circulating_sompi = load!(u64, reader)?; Ok(Self { max_sompi, circulating_sompi }) } @@ -1608,7 +1610,7 @@ impl Serializer for PingRequest { Ok(()) } - fn deserialize(_buf: &mut &[u8]) -> std::io::Result { + fn deserialize(_reader: &mut R) -> std::io::Result { Ok(Self {}) } } @@ -1622,7 +1624,7 @@ impl Serializer for PingResponse { Ok(()) } - fn deserialize(_buf: &mut &[u8]) -> std::io::Result { + fn deserialize(_reader: &mut R) -> std::io::Result { Ok(Self {}) } } @@ -1649,12 +1651,12 @@ impl Serializer for GetMetricsRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let process_metrics = load!(bool, buf)?; - let connection_metrics = load!(bool, buf)?; - let bandwidth_metrics = load!(bool, buf)?; - let consensus_metrics = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let process_metrics = load!(bool, reader)?; + let connection_metrics = load!(bool, reader)?; + let bandwidth_metrics = load!(bool, reader)?; + let consensus_metrics = load!(bool, reader)?; Ok(Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics }) } @@ -1690,17 +1692,17 @@ impl Serializer for ProcessMetrics { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let resident_set_size = load!(u64, buf)?; - let virtual_memory_size = load!(u64, buf)?; - let core_num = load!(u32, buf)?; - let cpu_usage = load!(f32, buf)?; - let fd_num = load!(u32, buf)?; - let disk_io_read_bytes = load!(u64, buf)?; - let disk_io_write_bytes = load!(u64, buf)?; - let disk_io_read_per_sec = load!(f32, buf)?; - let disk_io_write_per_sec = load!(f32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let resident_set_size = load!(u64, reader)?; + let virtual_memory_size = load!(u64, reader)?; + let core_num = load!(u32, reader)?; + let cpu_usage = load!(f32, reader)?; + let fd_num = load!(u32, reader)?; + let disk_io_read_bytes = load!(u64, reader)?; + let disk_io_write_bytes = load!(u64, reader)?; + let disk_io_read_per_sec = load!(f32, reader)?; + let disk_io_write_per_sec = load!(f32, reader)?; Ok(Self { resident_set_size, @@ -1743,15 +1745,15 @@ impl Serializer for ConnectionMetrics { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let borsh_live_connections = load!(u32, buf)?; - let borsh_connection_attempts = load!(u64, buf)?; - let borsh_handshake_failures = load!(u64, buf)?; - let json_live_connections = load!(u32, buf)?; - let json_connection_attempts = load!(u64, buf)?; - let json_handshake_failures = load!(u64, buf)?; - let active_peers = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let borsh_live_connections = load!(u32, reader)?; + let borsh_connection_attempts = load!(u64, reader)?; + let borsh_handshake_failures = load!(u64, reader)?; + let json_live_connections = load!(u32, reader)?; + let json_connection_attempts = load!(u64, reader)?; + let json_handshake_failures = load!(u64, reader)?; + let active_peers = load!(u32, reader)?; Ok(Self { borsh_live_connections, @@ -1793,16 +1795,16 @@ impl Serializer for BandwidthMetrics { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let borsh_bytes_tx = load!(u64, buf)?; - let borsh_bytes_rx = load!(u64, buf)?; - let json_bytes_tx = load!(u64, buf)?; - let json_bytes_rx = load!(u64, buf)?; - let p2p_bytes_tx = load!(u64, buf)?; - let p2p_bytes_rx = load!(u64, buf)?; - let grpc_bytes_tx = load!(u64, buf)?; - let grpc_bytes_rx = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let borsh_bytes_tx = load!(u64, reader)?; + let borsh_bytes_rx = load!(u64, reader)?; + let json_bytes_tx = load!(u64, reader)?; + let json_bytes_rx = load!(u64, reader)?; + let p2p_bytes_tx = load!(u64, reader)?; + let p2p_bytes_rx = load!(u64, reader)?; + let grpc_bytes_tx = load!(u64, reader)?; + let grpc_bytes_rx = load!(u64, reader)?; Ok(Self { borsh_bytes_tx, @@ -1861,23 +1863,23 @@ impl Serializer for ConsensusMetrics { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let node_blocks_submitted_count = load!(u64, buf)?; - let node_headers_processed_count = load!(u64, buf)?; - let node_dependencies_processed_count = load!(u64, buf)?; - let node_bodies_processed_count = load!(u64, buf)?; - let node_transactions_processed_count = load!(u64, buf)?; - let node_chain_blocks_processed_count = load!(u64, buf)?; - let node_mass_processed_count = load!(u64, buf)?; - let node_database_blocks_count = load!(u64, buf)?; - let node_database_headers_count = load!(u64, buf)?; - let network_mempool_size = load!(u64, buf)?; - let network_tip_hashes_count = load!(u32, buf)?; - let network_difficulty = load!(f64, buf)?; - let network_past_median_time = load!(u64, buf)?; - let network_virtual_parent_hashes_count = load!(u32, buf)?; - let network_virtual_daa_score = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let node_blocks_submitted_count = load!(u64, reader)?; + let node_headers_processed_count = load!(u64, reader)?; + let node_dependencies_processed_count = load!(u64, reader)?; + let node_bodies_processed_count = load!(u64, reader)?; + let node_transactions_processed_count = load!(u64, reader)?; + let node_chain_blocks_processed_count = load!(u64, reader)?; + let node_mass_processed_count = load!(u64, reader)?; + let node_database_blocks_count = load!(u64, reader)?; + let node_database_headers_count = load!(u64, reader)?; + let network_mempool_size = load!(u64, reader)?; + let network_tip_hashes_count = load!(u32, reader)?; + let network_difficulty = load!(f64, reader)?; + let network_past_median_time = load!(u64, reader)?; + let network_virtual_parent_hashes_count = load!(u32, reader)?; + let network_virtual_daa_score = load!(u64, reader)?; Ok(Self { node_blocks_submitted_count, @@ -1933,13 +1935,13 @@ impl Serializer for GetMetricsResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let server_time = load!(u64, buf)?; - let process_metrics = deserialize!(Option, buf)?; - let connection_metrics = deserialize!(Option, buf)?; - let bandwidth_metrics = deserialize!(Option, buf)?; - let consensus_metrics = deserialize!(Option, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let server_time = load!(u64, reader)?; + let process_metrics = deserialize!(Option, reader)?; + let connection_metrics = deserialize!(Option, reader)?; + let bandwidth_metrics = deserialize!(Option, reader)?; + let consensus_metrics = deserialize!(Option, reader)?; Ok(Self { server_time, process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics }) } @@ -1955,8 +1957,8 @@ impl Serializer for GetServerInfoRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -1986,14 +1988,14 @@ impl Serializer for GetServerInfoResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let rpc_api_version = load!([u16; 4], buf)?; - let server_version = load!(String, buf)?; - let network_id = load!(RpcNetworkId, buf)?; - let has_utxo_index = load!(bool, buf)?; - let is_synced = load!(bool, buf)?; - let virtual_daa_score = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let rpc_api_version = load!([u16; 4], reader)?; + let server_version = load!(String, reader)?; + let network_id = load!(RpcNetworkId, reader)?; + let has_utxo_index = load!(bool, reader)?; + let is_synced = load!(bool, reader)?; + let virtual_daa_score = load!(u64, reader)?; Ok(Self { rpc_api_version, server_version, network_id, has_utxo_index, is_synced, virtual_daa_score }) } @@ -2009,8 +2011,8 @@ impl Serializer for GetSyncStatusRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2028,9 +2030,9 @@ impl Serializer for GetSyncStatusResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let is_synced = load!(bool, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let is_synced = load!(bool, reader)?; Ok(Self { is_synced }) } } @@ -2054,9 +2056,9 @@ impl Serializer for GetDaaScoreTimestampEstimateRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let daa_scores = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let daa_scores = load!(Vec, reader)?; Ok(Self { daa_scores }) } } @@ -2080,9 +2082,9 @@ impl Serializer for GetDaaScoreTimestampEstimateResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let timestamps = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let timestamps = load!(Vec, reader)?; Ok(Self { timestamps }) } } @@ -2115,9 +2117,9 @@ impl Serializer for NotifyBlockAddedRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let command = load!(Command, reader)?; Ok(Self { command }) } } @@ -2132,8 +2134,8 @@ impl Serializer for NotifyBlockAddedResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2155,9 +2157,9 @@ impl Serializer for BlockAddedNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let block = load!(RpcBlock, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let block = load!(RpcBlock, reader)?; Ok(Self { block: block.into() }) } } @@ -2190,10 +2192,10 @@ impl Serializer for NotifyVirtualChainChangedRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let include_accepted_transaction_ids = load!(bool, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let include_accepted_transaction_ids = load!(bool, reader)?; + let command = load!(Command, reader)?; Ok(Self { include_accepted_transaction_ids, command }) } } @@ -2208,8 +2210,8 @@ impl Serializer for NotifyVirtualChainChangedResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2235,11 +2237,11 @@ impl Serializer for VirtualChainChangedNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let removed_chain_block_hashes = load!(Vec, buf)?; - let added_chain_block_hashes = load!(Vec, buf)?; - let accepted_transaction_ids = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let removed_chain_block_hashes = load!(Vec, reader)?; + let added_chain_block_hashes = load!(Vec, reader)?; + let accepted_transaction_ids = load!(Vec, reader)?; Ok(Self { removed_chain_block_hashes: removed_chain_block_hashes.into(), added_chain_block_hashes: added_chain_block_hashes.into(), @@ -2270,9 +2272,9 @@ impl Serializer for NotifyFinalityConflictRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let command = load!(Command, reader)?; Ok(Self { command }) } } @@ -2287,8 +2289,8 @@ impl Serializer for NotifyFinalityConflictResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2306,9 +2308,9 @@ impl Serializer for FinalityConflictNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let violating_block_hash = load!(RpcHash, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let violating_block_hash = load!(RpcHash, reader)?; Ok(Self { violating_block_hash }) } } @@ -2335,9 +2337,9 @@ impl Serializer for NotifyFinalityConflictResolvedRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let command = load!(Command, reader)?; Ok(Self { command }) } } @@ -2352,8 +2354,8 @@ impl Serializer for NotifyFinalityConflictResolvedResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2371,9 +2373,9 @@ impl Serializer for FinalityConflictResolvedNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let finality_block_hash = load!(RpcHash, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let finality_block_hash = load!(RpcHash, reader)?; Ok(Self { finality_block_hash }) } } @@ -2411,10 +2413,10 @@ impl Serializer for NotifyUtxosChangedRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let addresses = load!(Vec, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let addresses = load!(Vec, reader)?; + let command = load!(Command, reader)?; Ok(Self { addresses, command }) } } @@ -2429,8 +2431,8 @@ impl Serializer for NotifyUtxosChangedResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2483,10 +2485,10 @@ impl Serializer for UtxosChangedNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let added = load!(Vec, buf)?; - let removed = load!(Vec, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let added = load!(Vec, reader)?; + let removed = load!(Vec, reader)?; Ok(Self { added: added.into(), removed: removed.into() }) } } @@ -2517,9 +2519,9 @@ impl Serializer for NotifySinkBlueScoreChangedRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let command = load!(Command, reader)?; Ok(Self { command }) } } @@ -2534,8 +2536,8 @@ impl Serializer for NotifySinkBlueScoreChangedResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2557,9 +2559,9 @@ impl Serializer for SinkBlueScoreChangedNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let sink_blue_score = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let sink_blue_score = load!(u64, reader)?; Ok(Self { sink_blue_score }) } } @@ -2590,9 +2592,9 @@ impl Serializer for NotifyVirtualDaaScoreChangedRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let command = load!(Command, reader)?; Ok(Self { command }) } } @@ -2607,8 +2609,8 @@ impl Serializer for NotifyVirtualDaaScoreChangedResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2630,9 +2632,9 @@ impl Serializer for VirtualDaaScoreChangedNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let virtual_daa_score = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let virtual_daa_score = load!(u64, reader)?; Ok(Self { virtual_daa_score }) } } @@ -2659,9 +2661,9 @@ impl Serializer for NotifyPruningPointUtxoSetOverrideRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let command = load!(Command, reader)?; Ok(Self { command }) } } @@ -2676,8 +2678,8 @@ impl Serializer for NotifyPruningPointUtxoSetOverrideResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2692,8 +2694,8 @@ impl Serializer for PruningPointUtxoSetOverrideNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2722,9 +2724,9 @@ impl Serializer for NotifyNewBlockTemplateRequest { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let command = load!(Command, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let command = load!(Command, reader)?; Ok(Self { command }) } } @@ -2739,8 +2741,8 @@ impl Serializer for NotifyNewBlockTemplateResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2759,8 +2761,8 @@ impl Serializer for NewBlockTemplateNotification { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; Ok(Self {}) } } @@ -2787,9 +2789,9 @@ impl Serializer for SubscribeResponse { Ok(()) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf)?; - let id = load!(u64, buf)?; + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let id = load!(u64, reader)?; Ok(Self { id }) } } @@ -2806,8 +2808,8 @@ impl Serializer for UnsubscribeResponse { store!(u32, &1, writer) } - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let _version = load!(u32, buf); + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader); Ok(Self {}) } } diff --git a/utils/src/networking.rs b/utils/src/networking.rs index b2d7b92ffb..fff9d25461 100644 --- a/utils/src/networking.rs +++ b/utils/src/networking.rs @@ -179,7 +179,7 @@ impl Deref for IpAddress { // impl BorshSerialize for IpAddress { - fn serialize(&self, writer: &mut W) -> ::core::result::Result<(), borsh::maybestd::io::Error> { + fn serialize(&self, writer: &mut W) -> ::core::result::Result<(), std::io::Error> { let variant_idx: u8 = match self.0 { IpAddr::V4(..) => 0u8, IpAddr::V6(..) => 1u8, @@ -198,20 +198,20 @@ impl BorshSerialize for IpAddress { } impl BorshDeserialize for IpAddress { - fn deserialize(buf: &mut &[u8]) -> ::core::result::Result { - let variant_idx: u8 = BorshDeserialize::deserialize(buf)?; + fn deserialize_reader(reader: &mut R) -> ::core::result::Result { + let variant_idx: u8 = BorshDeserialize::deserialize_reader(reader)?; let ip = match variant_idx { 0u8 => { - let octets: [u8; 4] = BorshDeserialize::deserialize(buf)?; + let octets: [u8; 4] = BorshDeserialize::deserialize_reader(reader)?; IpAddr::V4(Ipv4Addr::from(octets)) } 1u8 => { - let octets: [u8; 16] = BorshDeserialize::deserialize(buf)?; + let octets: [u8; 16] = BorshDeserialize::deserialize_reader(reader)?; IpAddr::V6(Ipv6Addr::from(octets)) } _ => { - let msg = borsh::maybestd::format!("Unexpected variant index: {:?}", variant_idx); - return Err(borsh::maybestd::io::Error::new(borsh::maybestd::io::ErrorKind::InvalidInput, msg)); + let msg = format!("Unexpected variant index: {:?}", variant_idx); + return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, msg)); } }; Ok(Self(ip)) @@ -381,15 +381,15 @@ impl Deref for PeerId { // impl BorshSerialize for PeerId { - fn serialize(&self, writer: &mut W) -> ::core::result::Result<(), borsh::maybestd::io::Error> { + fn serialize(&self, writer: &mut W) -> ::core::result::Result<(), std::io::Error> { borsh::BorshSerialize::serialize(&self.0.as_bytes(), writer)?; Ok(()) } } impl BorshDeserialize for PeerId { - fn deserialize(buf: &mut &[u8]) -> ::core::result::Result { - let bytes: uuid::Bytes = BorshDeserialize::deserialize(buf)?; + fn deserialize_reader(reader: &mut R) -> ::core::result::Result { + let bytes: uuid::Bytes = BorshDeserialize::deserialize_reader(reader)?; Ok(Self::new(Uuid::from_bytes(bytes))) } } @@ -403,12 +403,12 @@ mod tests { fn test_ip_address_borsh() { // Tests for IpAddress Borsh ser/deser since we manually implemented them let ip: IpAddress = Ipv4Addr::from([44u8; 4]).into(); - let bin = ip.try_to_vec().unwrap(); + let bin = borsh::to_vec(&ip).unwrap(); let ip2: IpAddress = BorshDeserialize::try_from_slice(&bin).unwrap(); assert_eq!(ip, ip2); let ip: IpAddress = Ipv6Addr::from([66u8; 16]).into(); - let bin = ip.try_to_vec().unwrap(); + let bin = borsh::to_vec(&ip).unwrap(); let ip2: IpAddress = BorshDeserialize::try_from_slice(&bin).unwrap(); assert_eq!(ip, ip2); } @@ -417,12 +417,12 @@ mod tests { fn test_peer_id_borsh() { // Tests for PeerId Borsh ser/deser since we manually implemented them let id: PeerId = Uuid::new_v4().into(); - let bin = id.try_to_vec().unwrap(); + let bin = borsh::to_vec(&id).unwrap(); let id2: PeerId = BorshDeserialize::try_from_slice(&bin).unwrap(); assert_eq!(id, id2); let id: PeerId = Uuid::from_bytes([123u8; 16]).into(); - let bin = id.try_to_vec().unwrap(); + let bin = borsh::to_vec(&id).unwrap(); let id2: PeerId = BorshDeserialize::try_from_slice(&bin).unwrap(); assert_eq!(id, id2); } diff --git a/wallet/bip32/src/xpublic_key.rs b/wallet/bip32/src/xpublic_key.rs index a52d8f1a1c..ac4eb720c9 100644 --- a/wallet/bip32/src/xpublic_key.rs +++ b/wallet/bip32/src/xpublic_key.rs @@ -174,8 +174,8 @@ impl BorshDeserialize for ExtendedPublicKey where K: PublicKey, { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let Header { version, magic } = Header::deserialize(buf)?; + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let Header { version, magic } = Header::deserialize_reader(reader)?; if magic != Self::STORAGE_MAGIC { return Err(std::io::Error::new(std::io::ErrorKind::Other, "Invalid extended public key magic value")); } @@ -183,13 +183,11 @@ where return Err(std::io::Error::new(std::io::ErrorKind::Other, "Invalid extended public key version")); } - let public_key_bytes: [u8; KEY_SIZE + 1] = buf[..KEY_SIZE + 1] - .try_into() - .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Invalid extended public key"))?; + let mut public_key_bytes: [u8; KEY_SIZE + 1] = [0; KEY_SIZE + 1]; + reader.read_exact(&mut public_key_bytes)?; let public_key = K::from_bytes(public_key_bytes) .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Invalid extended public key"))?; - *buf = &buf[KEY_SIZE + 1..]; - let attrs = ExtendedKeyAttrs::deserialize(buf)?; + let attrs = ExtendedKeyAttrs::deserialize_reader(reader)?; Ok(Self { public_key, attrs }) } } diff --git a/wallet/core/src/account/kind.rs b/wallet/core/src/account/kind.rs index faf968f296..5930bd6991 100644 --- a/wallet/core/src/account/kind.rs +++ b/wallet/core/src/account/kind.rs @@ -95,22 +95,17 @@ impl BorshSerialize for AccountKind { } impl BorshDeserialize for AccountKind { - fn deserialize(buf: &mut &[u8]) -> IoResult { - if buf.is_empty() { - Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid AccountKind length")) - } else { - let len = buf[0]; - if buf.len() < (len as usize + 1) { - Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid AccountKind length")) - } else { - let s = str64::make( - std::str::from_utf8(&buf[1..(len as usize + 1)]) - .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid UTF-8 sequence"))?, - ); - *buf = &buf[(len as usize + 1)..]; - Ok(Self(s)) - } - } + fn deserialize_reader(reader: &mut R) -> IoResult { + let len = ::deserialize_reader(reader)? as usize; + let mut buf = [0; 64]; + reader + .read_exact(&mut buf[0..len]) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, format!("Invalid AccountKind length ({err:?})")))?; + let s = str64::make( + std::str::from_utf8(&buf[..len]) + .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid UTF-8 sequence"))?, + ); + Ok(Self(s)) } } diff --git a/wallet/core/src/account/variants/bip32.rs b/wallet/core/src/account/variants/bip32.rs index 0b2909ad09..3a8b3ad40e 100644 --- a/wallet/core/src/account/variants/bip32.rs +++ b/wallet/core/src/account/variants/bip32.rs @@ -70,13 +70,13 @@ impl BorshSerialize for Payload { } impl BorshDeserialize for Payload { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; - let xpub_keys = BorshDeserialize::deserialize(buf)?; - let account_index = BorshDeserialize::deserialize(buf)?; - let ecdsa = BorshDeserialize::deserialize(buf)?; + let xpub_keys = BorshDeserialize::deserialize_reader(reader)?; + let account_index = BorshDeserialize::deserialize_reader(reader)?; + let ecdsa = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { xpub_keys, account_index, ecdsa }) } diff --git a/wallet/core/src/account/variants/keypair.rs b/wallet/core/src/account/variants/keypair.rs index b6c92907f3..d0d37f74e8 100644 --- a/wallet/core/src/account/variants/keypair.rs +++ b/wallet/core/src/account/variants/keypair.rs @@ -69,19 +69,17 @@ impl BorshSerialize for Payload { } impl BorshDeserialize for Payload { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { use secp256k1::constants::PUBLIC_KEY_SIZE; let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; - let public_key_bytes: [u8; PUBLIC_KEY_SIZE] = buf[..PUBLIC_KEY_SIZE] - .try_into() - .map_err(|_| IoError::new(IoErrorKind::Other, "Unable to deserialize keypair account (public_key buffer try_into)"))?; + let mut public_key_bytes: [u8; PUBLIC_KEY_SIZE] = [0; PUBLIC_KEY_SIZE]; + reader.read_exact(&mut public_key_bytes)?; let public_key = secp256k1::PublicKey::from_slice(&public_key_bytes) .map_err(|_| IoError::new(IoErrorKind::Other, "Unable to deserialize keypair account (invalid public key)"))?; - *buf = &buf[PUBLIC_KEY_SIZE..]; - let ecdsa = BorshDeserialize::deserialize(buf)?; + let ecdsa = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { public_key, ecdsa }) } diff --git a/wallet/core/src/account/variants/legacy.rs b/wallet/core/src/account/variants/legacy.rs index 9967fb8614..0ea69554df 100644 --- a/wallet/core/src/account/variants/legacy.rs +++ b/wallet/core/src/account/variants/legacy.rs @@ -59,9 +59,9 @@ impl BorshSerialize for Payload { } impl BorshDeserialize for Payload { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; Ok(Self {}) } diff --git a/wallet/core/src/account/variants/multisig.rs b/wallet/core/src/account/variants/multisig.rs index 9a8044aa20..253464543d 100644 --- a/wallet/core/src/account/variants/multisig.rs +++ b/wallet/core/src/account/variants/multisig.rs @@ -70,14 +70,14 @@ impl BorshSerialize for Payload { } impl BorshDeserialize for Payload { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; - let xpub_keys = BorshDeserialize::deserialize(buf)?; - let cosigner_index = BorshDeserialize::deserialize(buf)?; - let minimum_signatures = BorshDeserialize::deserialize(buf)?; - let ecdsa = BorshDeserialize::deserialize(buf)?; + let xpub_keys = BorshDeserialize::deserialize_reader(reader)?; + let cosigner_index = BorshDeserialize::deserialize_reader(reader)?; + let minimum_signatures = BorshDeserialize::deserialize_reader(reader)?; + let ecdsa = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { xpub_keys, cosigner_index, minimum_signatures, ecdsa }) } diff --git a/wallet/core/src/api/transport.rs b/wallet/core/src/api/transport.rs index 9f6485ec8a..4de2d78248 100644 --- a/wallet/core/src/api/transport.rs +++ b/wallet/core/src/api/transport.rs @@ -18,7 +18,7 @@ use crate::imports::*; use crate::result::Result; use crate::wallet::Wallet; use async_trait::async_trait; -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::BorshDeserialize; use kaspa_wallet_macros::{build_wallet_client_transport_interface, build_wallet_server_transport_interface}; use workflow_core::task::spawn; diff --git a/wallet/core/src/deterministic.rs b/wallet/core/src/deterministic.rs index c383da6012..44e6f77ee6 100644 --- a/wallet/core/src/deterministic.rs +++ b/wallet/core/src/deterministic.rs @@ -101,7 +101,7 @@ where T: AsSlice + BorshSerialize, { let mut hashes: [Hash; N] = [Hash::default(); N]; - let bytes = hashable.try_to_vec().unwrap(); + let bytes = borsh::to_vec(&hashable).unwrap(); hashes[0] = Hash::from_slice(sha256_hash(&bytes).as_ref()); for i in 1..N { hashes[i] = Hash::from_slice(sha256_hash(&hashes[i - 1].as_bytes()).as_ref()); @@ -143,7 +143,7 @@ pub fn from_multisig(prv_key_data_ids: &Option Result { - let bytes = self.0.try_to_vec()?; + let bytes = borsh::to_vec(&self.0)?; let encrypted = match encryption_kind { EncryptionKind::XChaCha20Poly1305 => encrypt_xchacha20poly1305(bytes.as_slice(), secret)?, }; diff --git a/wallet/core/src/serializer.rs b/wallet/core/src/serializer.rs index 391f813fa1..d7451a3c20 100644 --- a/wallet/core/src/serializer.rs +++ b/wallet/core/src/serializer.rs @@ -59,8 +59,8 @@ impl BorshSerialize for StorageHeader { } impl BorshDeserialize for StorageHeader { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let (magic, version): (u32, u32) = BorshDeserialize::deserialize(buf)?; + fn deserialize_reader(reader: &mut R) -> std::io::Result { + let (magic, version): (u32, u32) = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { magic, version }) } } diff --git a/wallet/core/src/storage/account.rs b/wallet/core/src/storage/account.rs index da0c2df32b..a33585a255 100644 --- a/wallet/core/src/storage/account.rs +++ b/wallet/core/src/storage/account.rs @@ -26,10 +26,10 @@ impl BorshSerialize for AccountSettings { } impl BorshDeserialize for AccountSettings { - fn deserialize(buf: &mut &[u8]) -> IoResult { - let _version: u32 = BorshDeserialize::deserialize(buf)?; - let name = BorshDeserialize::deserialize(buf)?; - let meta = BorshDeserialize::deserialize(buf)?; + fn deserialize_reader(reader: &mut R) -> IoResult { + let _version: u32 = BorshDeserialize::deserialize_reader(reader)?; + let name = BorshDeserialize::deserialize_reader(reader)?; + let meta = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { name, meta }) } @@ -63,7 +63,7 @@ impl AccountStorage { where A: AccountStorable, { - Ok(Self { id: *id, storage_key: *storage_key, kind, prv_key_data_ids, settings, serialized: serialized.try_to_vec()? }) + Ok(Self { id: *id, storage_key: *storage_key, kind, prv_key_data_ids, settings, serialized: borsh::to_vec(&serialized)? }) } pub fn id(&self) -> &AccountId { @@ -107,16 +107,16 @@ impl BorshSerialize for AccountStorage { } impl BorshDeserialize for AccountStorage { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; - - let kind = BorshDeserialize::deserialize(buf)?; - let id = BorshDeserialize::deserialize(buf)?; - let storage_key = BorshDeserialize::deserialize(buf)?; - let prv_key_data_ids = BorshDeserialize::deserialize(buf)?; - let settings = BorshDeserialize::deserialize(buf)?; - let serialized = BorshDeserialize::deserialize(buf)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + + let kind = BorshDeserialize::deserialize_reader(reader)?; + let id = BorshDeserialize::deserialize_reader(reader)?; + let storage_key = BorshDeserialize::deserialize_reader(reader)?; + let prv_key_data_ids = BorshDeserialize::deserialize_reader(reader)?; + let settings = BorshDeserialize::deserialize_reader(reader)?; + let serialized = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { kind, id, storage_key, prv_key_data_ids, settings, serialized }) } diff --git a/wallet/core/src/storage/keydata/data.rs b/wallet/core/src/storage/keydata/data.rs index 37a734854c..5b480d937c 100644 --- a/wallet/core/src/storage/keydata/data.rs +++ b/wallet/core/src/storage/keydata/data.rs @@ -44,11 +44,12 @@ impl BorshSerialize for PrvKeyDataVariant { } impl BorshDeserialize for PrvKeyDataVariant { - fn deserialize(buf: &mut &[u8]) -> IoResult { - let StorageHeader { version: _, .. } = StorageHeader::deserialize(buf)?.try_magic(Self::MAGIC)?.try_version(Self::VERSION)?; + fn deserialize_reader(reader: &mut R) -> IoResult { + let StorageHeader { version: _, .. } = + StorageHeader::deserialize_reader(reader)?.try_magic(Self::MAGIC)?.try_version(Self::VERSION)?; - let kind: PrvKeyDataVariantKind = BorshDeserialize::deserialize(buf)?; - let string: String = BorshDeserialize::deserialize(buf)?; + let kind: PrvKeyDataVariantKind = BorshDeserialize::deserialize_reader(reader)?; + let string: String = BorshDeserialize::deserialize_reader(reader)?; match kind { PrvKeyDataVariantKind::Mnemonic => Ok(Self::Mnemonic(string)), diff --git a/wallet/core/src/storage/local/interface.rs b/wallet/core/src/storage/local/interface.rs index 1e998eb61f..c4ada71e07 100644 --- a/wallet/core/src/storage/local/interface.rs +++ b/wallet/core/src/storage/local/interface.rs @@ -131,7 +131,7 @@ impl LocalStoreInner { async fn try_export(&self, wallet_secret: &Secret, _options: WalletExportOptions) -> Result> { let wallet = self.cache.read().unwrap().to_wallet(None, wallet_secret)?; - Ok(wallet.try_to_vec()?) + Ok(borsh::to_vec(&wallet)?) } fn storage(&self) -> Arc { diff --git a/wallet/core/src/storage/local/payload.rs b/wallet/core/src/storage/local/payload.rs index 2cc5c9091e..8424dd4e19 100644 --- a/wallet/core/src/storage/local/payload.rs +++ b/wallet/core/src/storage/local/payload.rs @@ -67,13 +67,13 @@ impl BorshSerialize for Payload { } impl BorshDeserialize for Payload { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; - let prv_key_data = BorshDeserialize::deserialize(buf)?; - let accounts = BorshDeserialize::deserialize(buf)?; - let address_book = BorshDeserialize::deserialize(buf)?; - let encrypt_transactions = BorshDeserialize::deserialize(buf)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + let prv_key_data = BorshDeserialize::deserialize_reader(reader)?; + let accounts = BorshDeserialize::deserialize_reader(reader)?; + let address_book = BorshDeserialize::deserialize_reader(reader)?; + let encrypt_transactions = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { prv_key_data, accounts, address_book, encrypt_transactions }) } diff --git a/wallet/core/src/storage/local/transaction/fsio.rs b/wallet/core/src/storage/local/transaction/fsio.rs index ac44a136d7..784ca80607 100644 --- a/wallet/core/src/storage/local/transaction/fsio.rs +++ b/wallet/core/src/storage/local/transaction/fsio.rs @@ -315,6 +315,6 @@ async fn write(path: &Path, record: &TransactionRecord, secret: Option<&Secret>, } else { Encryptable::from(record.clone()) }; - fs::write(path, &data.try_to_vec()?).await?; + fs::write(path, &borsh::to_vec(&data)?).await?; Ok(()) } diff --git a/wallet/core/src/storage/local/transaction/indexdb.rs b/wallet/core/src/storage/local/transaction/indexdb.rs index e4190aa1ff..55d3ee97be 100644 --- a/wallet/core/src/storage/local/transaction/indexdb.rs +++ b/wallet/core/src/storage/local/transaction/indexdb.rs @@ -494,7 +494,7 @@ fn transaction_record_to_js_value( } else { Encryptable::from(transaction_record.clone()) }; - let encryped_data_vec = encryped_data.try_to_vec()?; + let encryped_data_vec = borsh::to_vec(&encryped_data)?; let borsh_data_uint8_arr = Uint8Array::from(encryped_data_vec.as_slice()); let borsh_data_js_value = borsh_data_uint8_arr.into(); diff --git a/wallet/core/src/storage/local/wallet.rs b/wallet/core/src/storage/local/wallet.rs index afea36ad37..482ecd70ff 100644 --- a/wallet/core/src/storage/local/wallet.rs +++ b/wallet/core/src/storage/local/wallet.rs @@ -61,7 +61,7 @@ impl WalletStorage { cfg_if! { if #[cfg(target_arch = "wasm32")] { - let serialized = BorshSerialize::try_to_vec(self)?; + let serialized = borsh::to_vec(self)?; fs::write(store.filename(), serialized.as_slice()).await?; } else { // make this platform-specific to avoid creating @@ -101,8 +101,8 @@ impl BorshSerialize for WalletStorage { } impl BorshDeserialize for WalletStorage { - fn deserialize(buf: &mut &[u8]) -> IoResult { - let StorageHeader { magic, version, .. } = StorageHeader::deserialize(buf)?; + fn deserialize_reader(reader: &mut R) -> IoResult { + let StorageHeader { magic, version, .. } = StorageHeader::deserialize_reader(reader)?; if magic != Self::STORAGE_MAGIC { return Err(IoError::new( @@ -118,12 +118,12 @@ impl BorshDeserialize for WalletStorage { )); } - let title = BorshDeserialize::deserialize(buf)?; - let user_hint = BorshDeserialize::deserialize(buf)?; - let encryption_kind = BorshDeserialize::deserialize(buf)?; - let payload = BorshDeserialize::deserialize(buf)?; - let metadata = BorshDeserialize::deserialize(buf)?; - let transactions = BorshDeserialize::deserialize(buf)?; + let title = BorshDeserialize::deserialize_reader(reader)?; + let user_hint = BorshDeserialize::deserialize_reader(reader)?; + let encryption_kind = BorshDeserialize::deserialize_reader(reader)?; + let payload = BorshDeserialize::deserialize_reader(reader)?; + let metadata = BorshDeserialize::deserialize_reader(reader)?; + let transactions = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { title, user_hint, encryption_kind, payload, metadata, transactions }) } diff --git a/wallet/core/src/storage/metadata.rs b/wallet/core/src/storage/metadata.rs index 0eacb902d0..d421a17edd 100644 --- a/wallet/core/src/storage/metadata.rs +++ b/wallet/core/src/storage/metadata.rs @@ -47,12 +47,12 @@ impl BorshSerialize for AccountMetadata { } impl BorshDeserialize for AccountMetadata { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; - let id = BorshDeserialize::deserialize(buf)?; - let indexes = BorshDeserialize::deserialize(buf)?; + let id = BorshDeserialize::deserialize_reader(reader)?; + let indexes = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { id, indexes }) } diff --git a/wallet/core/src/storage/transaction/data.rs b/wallet/core/src/storage/transaction/data.rs index 51fff3df80..e976574fd1 100644 --- a/wallet/core/src/storage/transaction/data.rs +++ b/wallet/core/src/storage/transaction/data.rs @@ -282,42 +282,42 @@ impl BorshSerialize for TransactionData { } impl BorshDeserialize for TransactionData { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; - let kind: TransactionKind = BorshDeserialize::deserialize(buf)?; + let kind: TransactionKind = BorshDeserialize::deserialize_reader(reader)?; match kind { TransactionKind::Reorg => { - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::Reorg { utxo_entries, aggregate_input_value }) } TransactionKind::Incoming => { - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::Incoming { utxo_entries, aggregate_input_value }) } TransactionKind::Stasis => { - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::Stasis { utxo_entries, aggregate_input_value }) } TransactionKind::External => { - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::External { utxo_entries, aggregate_input_value }) } TransactionKind::Batch => { - let fees: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_output_value: u64 = BorshDeserialize::deserialize(buf)?; - let transaction: Transaction = BorshDeserialize::deserialize(buf)?; - let payment_value: Option = BorshDeserialize::deserialize(buf)?; - let change_value: u64 = BorshDeserialize::deserialize(buf)?; - let accepted_daa_score: Option = BorshDeserialize::deserialize(buf)?; - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; + let fees: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_output_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let transaction: Transaction = BorshDeserialize::deserialize_reader(reader)?; + let payment_value: Option = BorshDeserialize::deserialize_reader(reader)?; + let change_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let accepted_daa_score: Option = BorshDeserialize::deserialize_reader(reader)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::Batch { fees, aggregate_input_value, @@ -330,14 +330,14 @@ impl BorshDeserialize for TransactionData { }) } TransactionKind::Outgoing => { - let fees: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_output_value: u64 = BorshDeserialize::deserialize(buf)?; - let transaction: Transaction = BorshDeserialize::deserialize(buf)?; - let payment_value: Option = BorshDeserialize::deserialize(buf)?; - let change_value: u64 = BorshDeserialize::deserialize(buf)?; - let accepted_daa_score: Option = BorshDeserialize::deserialize(buf)?; - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; + let fees: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_output_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let transaction: Transaction = BorshDeserialize::deserialize_reader(reader)?; + let payment_value: Option = BorshDeserialize::deserialize_reader(reader)?; + let change_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let accepted_daa_score: Option = BorshDeserialize::deserialize_reader(reader)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::Outgoing { fees, aggregate_input_value, @@ -350,14 +350,14 @@ impl BorshDeserialize for TransactionData { }) } TransactionKind::TransferIncoming => { - let fees: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_output_value: u64 = BorshDeserialize::deserialize(buf)?; - let transaction: Transaction = BorshDeserialize::deserialize(buf)?; - let payment_value: Option = BorshDeserialize::deserialize(buf)?; - let change_value: u64 = BorshDeserialize::deserialize(buf)?; - let accepted_daa_score: Option = BorshDeserialize::deserialize(buf)?; - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; + let fees: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_output_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let transaction: Transaction = BorshDeserialize::deserialize_reader(reader)?; + let payment_value: Option = BorshDeserialize::deserialize_reader(reader)?; + let change_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let accepted_daa_score: Option = BorshDeserialize::deserialize_reader(reader)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::TransferIncoming { fees, aggregate_input_value, @@ -370,14 +370,14 @@ impl BorshDeserialize for TransactionData { }) } TransactionKind::TransferOutgoing => { - let fees: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_output_value: u64 = BorshDeserialize::deserialize(buf)?; - let transaction: Transaction = BorshDeserialize::deserialize(buf)?; - let payment_value: Option = BorshDeserialize::deserialize(buf)?; - let change_value: u64 = BorshDeserialize::deserialize(buf)?; - let accepted_daa_score: Option = BorshDeserialize::deserialize(buf)?; - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; + let fees: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_output_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let transaction: Transaction = BorshDeserialize::deserialize_reader(reader)?; + let payment_value: Option = BorshDeserialize::deserialize_reader(reader)?; + let change_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let accepted_daa_score: Option = BorshDeserialize::deserialize_reader(reader)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::TransferOutgoing { fees, aggregate_input_value, @@ -390,13 +390,13 @@ impl BorshDeserialize for TransactionData { }) } TransactionKind::Change => { - let aggregate_input_value: u64 = BorshDeserialize::deserialize(buf)?; - let aggregate_output_value: u64 = BorshDeserialize::deserialize(buf)?; - let transaction: Transaction = BorshDeserialize::deserialize(buf)?; - let payment_value: Option = BorshDeserialize::deserialize(buf)?; - let change_value: u64 = BorshDeserialize::deserialize(buf)?; - let accepted_daa_score: Option = BorshDeserialize::deserialize(buf)?; - let utxo_entries: Vec = BorshDeserialize::deserialize(buf)?; + let aggregate_input_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let aggregate_output_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let transaction: Transaction = BorshDeserialize::deserialize_reader(reader)?; + let payment_value: Option = BorshDeserialize::deserialize_reader(reader)?; + let change_value: u64 = BorshDeserialize::deserialize_reader(reader)?; + let accepted_daa_score: Option = BorshDeserialize::deserialize_reader(reader)?; + let utxo_entries: Vec = BorshDeserialize::deserialize_reader(reader)?; Ok(TransactionData::Change { aggregate_input_value, aggregate_output_value, diff --git a/wallet/core/src/storage/transaction/record.rs b/wallet/core/src/storage/transaction/record.rs index 7a04571331..7edc8ca92e 100644 --- a/wallet/core/src/storage/transaction/record.rs +++ b/wallet/core/src/storage/transaction/record.rs @@ -837,19 +837,19 @@ impl BorshSerialize for TransactionRecord { } impl BorshDeserialize for TransactionRecord { - fn deserialize(buf: &mut &[u8]) -> IoResult { + fn deserialize_reader(reader: &mut R) -> IoResult { let StorageHeader { version: _, .. } = - StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; - - let id = BorshDeserialize::deserialize(buf)?; - let unixtime = BorshDeserialize::deserialize(buf)?; - let value = BorshDeserialize::deserialize(buf)?; - let binding = BorshDeserialize::deserialize(buf)?; - let block_daa_score = BorshDeserialize::deserialize(buf)?; - let network_id = BorshDeserialize::deserialize(buf)?; - let transaction_data = BorshDeserialize::deserialize(buf)?; - let note = BorshDeserialize::deserialize(buf)?; - let metadata = BorshDeserialize::deserialize(buf)?; + StorageHeader::deserialize_reader(reader)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + + let id = BorshDeserialize::deserialize_reader(reader)?; + let unixtime = BorshDeserialize::deserialize_reader(reader)?; + let value = BorshDeserialize::deserialize_reader(reader)?; + let binding = BorshDeserialize::deserialize_reader(reader)?; + let block_daa_score = BorshDeserialize::deserialize_reader(reader)?; + let network_id = BorshDeserialize::deserialize_reader(reader)?; + let transaction_data = BorshDeserialize::deserialize_reader(reader)?; + let note = BorshDeserialize::deserialize_reader(reader)?; + let metadata = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { id, unixtime_msec: unixtime, value, binding, block_daa_score, network_id, transaction_data, note, metadata }) } diff --git a/wallet/core/src/tests/storage.rs b/wallet/core/src/tests/storage.rs index 7257311602..5af0bcbae5 100644 --- a/wallet/core/src/tests/storage.rs +++ b/wallet/core/src/tests/storage.rs @@ -20,11 +20,11 @@ where } pub fn validate(&self) -> Result { - let bytes = self.try_to_vec()?; + let bytes = borsh::to_vec(self)?; let transform = Self::try_from_slice(bytes.as_slice())?; assert_eq!(transform.before, 0xdeadbeef); assert_eq!(transform.after, 0xbaadf00d); - let transform_bytes = transform.try_to_vec()?; + let transform_bytes = borsh::to_vec(&transform)?; assert_eq!(bytes, transform_bytes); Ok(transform.storable) } diff --git a/wallet/core/src/utxo/balance.rs b/wallet/core/src/utxo/balance.rs index ce189e124e..f16ce94ff6 100644 --- a/wallet/core/src/utxo/balance.rs +++ b/wallet/core/src/utxo/balance.rs @@ -10,6 +10,7 @@ pub enum DeltaStyle { } #[derive(Default, Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[borsh(use_discriminant = true)] pub enum Delta { #[default] NoChange = 0, diff --git a/wallet/macros/src/wallet/client.rs b/wallet/macros/src/wallet/client.rs index 8bbc51b8a6..c942af013e 100644 --- a/wallet/macros/src/wallet/client.rs +++ b/wallet/macros/src/wallet/client.rs @@ -61,7 +61,7 @@ impl ToTokens for RpcTable { { match __self.codec { Codec::Borsh(ref codec) => { - Ok(#response_type::try_from_slice(&codec.call(op, request.try_to_vec()?).await?)?) + Ok(#response_type::try_from_slice(&codec.call(op, borsh::to_vec(&request)?).await?)?) }, Codec::Serde(ref codec) => { let request = serde_json::to_string(&request)?; diff --git a/wallet/macros/src/wallet/server.rs b/wallet/macros/src/wallet/server.rs index a1e7bf13e9..073d33021e 100644 --- a/wallet/macros/src/wallet/server.rs +++ b/wallet/macros/src/wallet/server.rs @@ -38,7 +38,7 @@ impl ToTokens for RpcTable { targets_borsh.push(quote! { #hash_64 => { - Ok(self.wallet_api().#fn_call(#request_type::try_from_slice(&request)?).await?.try_to_vec()?) + Ok(borsh::to_vec(&self.wallet_api().#fn_call(#request_type::try_from_slice(&request)?).await?)?) } }); From f60d88d322c6bb4087652a59edf455b3aed1acc2 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 12 Jun 2024 22:50:30 +0300 Subject: [PATCH 009/158] fix returning receive addr from bindings change addr fn. --- wallet/keys/src/pubkeygen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/keys/src/pubkeygen.rs b/wallet/keys/src/pubkeygen.rs index 272e638455..c5bf0f7972 100644 --- a/wallet/keys/src/pubkeygen.rs +++ b/wallet/keys/src/pubkeygen.rs @@ -193,7 +193,7 @@ impl PublicKeyGenerator { #[wasm_bindgen(js_name=changeAddressAsString)] #[allow(non_snake_case)] pub fn change_address_as_string(&self, networkType: &NetworkTypeT, index: u32) -> Result { - Ok(PublicKey::from(self.hd_wallet.receive_pubkey_manager().derive_pubkey(index)?) + Ok(PublicKey::from(self.hd_wallet.change_pubkey_manager().derive_pubkey(index)?) .to_address(networkType.try_into()?)? .to_string()) } From 0785c3f7bf836bc51f97d0c720dbcdc6e6435720 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 12 Jun 2024 22:50:39 +0300 Subject: [PATCH 010/158] lints --- consensus/pow/src/wasm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/pow/src/wasm.rs b/consensus/pow/src/wasm.rs index 6da3d63bb3..037aaaa819 100644 --- a/consensus/pow/src/wasm.rs +++ b/consensus/pow/src/wasm.rs @@ -113,5 +113,5 @@ pub fn calculate_target(difficulty: f32) -> Result { return Err(Error::custom("Target is too big")); } - Ok(Uint256(buf).try_into().map_err(Error::custom)?) + Uint256(buf).try_into().map_err(Error::custom) } From 1d70e77490a1e762d771a20a1bfaf2b56216fbf3 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 12 Jun 2024 22:50:57 +0300 Subject: [PATCH 011/158] update WASM changelog --- wasm/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wasm/CHANGELOG.md b/wasm/CHANGELOG.md index e601c32324..1534eca76d 100644 --- a/wasm/CHANGELOG.md +++ b/wasm/CHANGELOG.md @@ -1,5 +1,13 @@ Latest online documentation available at: https://kaspa.aspectron.org/docs/ +### Release 2024-06-12 + +- Fix `PublicKeyGenerator::change_address_as_string()` that was returning the receive address. +- WASM SDK now builds as a GitHub artifact during the CI process. +- `State` renamed to `PoW` +- Docs now have a PoW section that unifies all PoW-related classes and functions. +- `TransactionRecord.data` (`TransactionData`) now has correct TypeScript bindings. + ### Release 2024-05-26 - Adding utility functions: `payToAddressScript`, `payToScriptHashScript`, `payToScriptHashSignatureScript`, `addressFromScriptPublicKey`, `isScriptPayToPubkey`, `isScriptPayToPubkeyECDSA`, `isScriptPayToScriptHash`. From 9c989a49738ce002f1e6777ddd2544ba21d4078e Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 12 Jun 2024 22:51:14 +0300 Subject: [PATCH 012/158] fix WASM module reference in examples --- wasm/examples/nodejs/javascript/general/mining-pow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/examples/nodejs/javascript/general/mining-pow.js b/wasm/examples/nodejs/javascript/general/mining-pow.js index 73ba40df3c..58bf2e70fe 100644 --- a/wasm/examples/nodejs/javascript/general/mining-pow.js +++ b/wasm/examples/nodejs/javascript/general/mining-pow.js @@ -1,4 +1,4 @@ -const kaspa = require('../../../../nodejs/kaspa/kaspa'); +const kaspa = require('../../../../nodejs/kaspa'); const {parseArgs} = require("../utils"); kaspa.initConsolePanicHook(); From 7cee4a45f516b528667ed98d61c0548e71c67b5e Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 12 Jun 2024 22:51:42 +0300 Subject: [PATCH 013/158] migrate lints.clippy to workspace and inherit this in the relevant crates --- Cargo.toml | 2 ++ cli/Cargo.toml | 4 ++-- consensus/client/Cargo.toml | 4 ++-- consensus/core/Cargo.toml | 4 ++-- consensus/pow/Cargo.toml | 5 ++++- consensus/wasm/Cargo.toml | 4 ++-- crypto/addresses/Cargo.toml | 4 ++-- rpc/core/Cargo.toml | 4 ++-- rpc/wrpc/wasm/Cargo.toml | 4 ++-- wallet/core/Cargo.toml | 4 ++-- wallet/keys/Cargo.toml | 4 ++-- wasm/core/Cargo.toml | 4 ++-- 12 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 283981df88..d96b33f175 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -322,3 +322,5 @@ inherits = "release" debug = true strip = false +[workspace.lints.clippy] +empty_docs = "allow" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f2c80a5fa3..f35f7fd57f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -80,5 +80,5 @@ features = [ [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio.workspace = true -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/consensus/client/Cargo.toml b/consensus/client/Cargo.toml index 38cbed9a33..698348508f 100644 --- a/consensus/client/Cargo.toml +++ b/consensus/client/Cargo.toml @@ -38,5 +38,5 @@ itertools.workspace = true workflow-wasm.workspace = true workflow-log.workspace = true -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/consensus/core/Cargo.toml b/consensus/core/Cargo.toml index 4e4bd9ea06..9594c70617 100644 --- a/consensus/core/Cargo.toml +++ b/consensus/core/Cargo.toml @@ -53,5 +53,5 @@ web-sys.workspace = true name = "serde_benchmark" harness = false -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/consensus/pow/Cargo.toml b/consensus/pow/Cargo.toml index 13dc80f378..b2dd714e74 100644 --- a/consensus/pow/Cargo.toml +++ b/consensus/pow/Cargo.toml @@ -28,4 +28,7 @@ criterion.workspace = true [[bench]] name = "bench" -harness = false \ No newline at end of file +harness = false + +[lints] +workspace = true diff --git a/consensus/wasm/Cargo.toml b/consensus/wasm/Cargo.toml index ea211f3f94..747d961e24 100644 --- a/consensus/wasm/Cargo.toml +++ b/consensus/wasm/Cargo.toml @@ -35,5 +35,5 @@ wasm-bindgen.workspace = true workflow-wasm.workspace = true workflow-log.workspace = true -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/crypto/addresses/Cargo.toml b/crypto/addresses/Cargo.toml index dd9f8e3ac5..15fdb975ed 100644 --- a/crypto/addresses/Cargo.toml +++ b/crypto/addresses/Cargo.toml @@ -28,5 +28,5 @@ web-sys.workspace = true name = "bench" harness = false -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/rpc/core/Cargo.toml b/rpc/core/Cargo.toml index cfa4895f25..0a02607843 100644 --- a/rpc/core/Cargo.toml +++ b/rpc/core/Cargo.toml @@ -54,5 +54,5 @@ workflow-wasm.workspace = true [dev-dependencies] serde_json.workspace = true -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/rpc/wrpc/wasm/Cargo.toml b/rpc/wrpc/wasm/Cargo.toml index 83c78d26f8..9bd639fdb1 100644 --- a/rpc/wrpc/wasm/Cargo.toml +++ b/rpc/wrpc/wasm/Cargo.toml @@ -41,5 +41,5 @@ wasm-bindgen-futures.workspace = true workflow-core.workspace = true futures.workspace = true -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/wallet/core/Cargo.toml b/wallet/core/Cargo.toml index fb31afb310..9a6c4cfa6a 100644 --- a/wallet/core/Cargo.toml +++ b/wallet/core/Cargo.toml @@ -125,5 +125,5 @@ serde_repr.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio.workspace = true -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/wallet/keys/Cargo.toml b/wallet/keys/Cargo.toml index 7300c1de57..b352f42a92 100644 --- a/wallet/keys/Cargo.toml +++ b/wallet/keys/Cargo.toml @@ -46,5 +46,5 @@ zeroize.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio.workspace = true -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true diff --git a/wasm/core/Cargo.toml b/wasm/core/Cargo.toml index a8a49e0aa3..7ecda22a9a 100644 --- a/wasm/core/Cargo.toml +++ b/wasm/core/Cargo.toml @@ -16,5 +16,5 @@ wasm-bindgen.workspace = true js-sys.workspace = true faster-hex.workspace = true -[lints.clippy] -empty_docs = "allow" +[lints] +workspace = true From b578e324ad28aeb5348eb1d450c83dc1d41e7448 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 13 Jun 2024 17:58:57 +0300 Subject: [PATCH 014/158] Add provisional fields for storage metrics (db size) --- cli/src/modules/rpc.rs | 2 +- metrics/core/src/data.rs | 124 +++++++++++++++------------ metrics/core/src/lib.rs | 13 ++- rpc/core/src/api/rpc.rs | 10 ++- rpc/core/src/model/message.rs | 35 +++++++- rpc/core/src/model/tests.rs | 15 +++- rpc/grpc/core/proto/rpc.proto | 6 ++ rpc/grpc/core/src/convert/message.rs | 5 +- rpc/grpc/core/src/convert/metrics.rs | 12 +++ rpc/service/src/service.rs | 11 ++- rpc/wrpc/resolver/src/connection.rs | 2 +- testing/integration/src/rpc_tests.rs | 4 + 12 files changed, 171 insertions(+), 68 deletions(-) diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index c849154800..8b497c52be 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -38,7 +38,7 @@ impl Rpc { tprintln!(ctx, "ok"); } RpcApiOps::GetMetrics => { - let result = rpc.get_metrics(true, true, true, true).await?; + let result = rpc.get_metrics(true, true, true, true, true).await?; self.println(&ctx, result); } RpcApiOps::GetServerInfo => { diff --git a/metrics/core/src/data.rs b/metrics/core/src/data.rs index c920f0a049..67c083b101 100644 --- a/metrics/core/src/data.rs +++ b/metrics/core/src/data.rs @@ -56,6 +56,7 @@ impl MetricGroup { Metric::NodeDiskIoReadPerSec, Metric::NodeDiskIoWriteBytes, Metric::NodeDiskIoWritePerSec, + Metric::NodeStorageSizeBytes, ] .as_slice() .iter(), @@ -127,7 +128,8 @@ impl From for MetricGroup { | Metric::NodeDiskIoReadBytes | Metric::NodeDiskIoWriteBytes | Metric::NodeDiskIoReadPerSec - | Metric::NodeDiskIoWritePerSec => MetricGroup::Storage, + | Metric::NodeDiskIoWritePerSec + | Metric::NodeStorageSizeBytes => MetricGroup::Storage, // -- Metric::NodeBorshLiveConnections | Metric::NodeBorshConnectionAttempts @@ -194,6 +196,7 @@ pub enum Metric { NodeDiskIoWriteBytes, NodeDiskIoReadPerSec, NodeDiskIoWritePerSec, + NodeStorageSizeBytes, // --- NodeActivePeers, NodeBorshLiveConnections, @@ -252,62 +255,62 @@ pub enum Metric { impl Metric { // TODO - this will be refactored at a later date // as this requires changes and testing in /kos - pub fn group(&self) -> &'static str { - match self { - Metric::NodeCpuUsage - | Metric::NodeResidentSetSizeBytes - | Metric::NodeVirtualMemorySizeBytes - | Metric::NodeFileHandlesCount - | Metric::NodeDiskIoReadBytes - | Metric::NodeDiskIoWriteBytes - | Metric::NodeDiskIoReadPerSec - | Metric::NodeDiskIoWritePerSec - | Metric::NodeBorshLiveConnections - | Metric::NodeBorshConnectionAttempts - | Metric::NodeBorshHandshakeFailures - | Metric::NodeJsonLiveConnections - | Metric::NodeJsonConnectionAttempts - | Metric::NodeJsonHandshakeFailures - | Metric::NodeBorshBytesTx - | Metric::NodeBorshBytesRx - | Metric::NodeJsonBytesTx - | Metric::NodeJsonBytesRx - | Metric::NodeP2pBytesTx - | Metric::NodeP2pBytesRx - | Metric::NodeGrpcUserBytesTx - | Metric::NodeGrpcUserBytesRx - | Metric::NodeTotalBytesTx - | Metric::NodeTotalBytesRx - | Metric::NodeBorshBytesTxPerSecond - | Metric::NodeBorshBytesRxPerSecond - | Metric::NodeJsonBytesTxPerSecond - | Metric::NodeJsonBytesRxPerSecond - | Metric::NodeP2pBytesTxPerSecond - | Metric::NodeP2pBytesRxPerSecond - | Metric::NodeGrpcUserBytesTxPerSecond - | Metric::NodeGrpcUserBytesRxPerSecond - | Metric::NodeTotalBytesTxPerSecond - | Metric::NodeTotalBytesRxPerSecond - | Metric::NodeActivePeers => "system", - // -- - Metric::NodeBlocksSubmittedCount - | Metric::NodeHeadersProcessedCount - | Metric::NodeDependenciesProcessedCount - | Metric::NodeBodiesProcessedCount - | Metric::NodeTransactionsProcessedCount - | Metric::NodeChainBlocksProcessedCount - | Metric::NodeMassProcessedCount - | Metric::NodeDatabaseBlocksCount - | Metric::NodeDatabaseHeadersCount - | Metric::NetworkMempoolSize - | Metric::NetworkTransactionsPerSecond - | Metric::NetworkTipHashesCount - | Metric::NetworkDifficulty - | Metric::NetworkPastMedianTime - | Metric::NetworkVirtualParentHashesCount - | Metric::NetworkVirtualDaaScore => "kaspa", - } - } + // pub fn group(&self) -> &'static str { + // match self { + // Metric::NodeCpuUsage + // | Metric::NodeResidentSetSizeBytes + // | Metric::NodeVirtualMemorySizeBytes + // | Metric::NodeFileHandlesCount + // | Metric::NodeDiskIoReadBytes + // | Metric::NodeDiskIoWriteBytes + // | Metric::NodeDiskIoReadPerSec + // | Metric::NodeDiskIoWritePerSec + // | Metric::NodeBorshLiveConnections + // | Metric::NodeBorshConnectionAttempts + // | Metric::NodeBorshHandshakeFailures + // | Metric::NodeJsonLiveConnections + // | Metric::NodeJsonConnectionAttempts + // | Metric::NodeJsonHandshakeFailures + // | Metric::NodeBorshBytesTx + // | Metric::NodeBorshBytesRx + // | Metric::NodeJsonBytesTx + // | Metric::NodeJsonBytesRx + // | Metric::NodeP2pBytesTx + // | Metric::NodeP2pBytesRx + // | Metric::NodeGrpcUserBytesTx + // | Metric::NodeGrpcUserBytesRx + // | Metric::NodeTotalBytesTx + // | Metric::NodeTotalBytesRx + // | Metric::NodeBorshBytesTxPerSecond + // | Metric::NodeBorshBytesRxPerSecond + // | Metric::NodeJsonBytesTxPerSecond + // | Metric::NodeJsonBytesRxPerSecond + // | Metric::NodeP2pBytesTxPerSecond + // | Metric::NodeP2pBytesRxPerSecond + // | Metric::NodeGrpcUserBytesTxPerSecond + // | Metric::NodeGrpcUserBytesRxPerSecond + // | Metric::NodeTotalBytesTxPerSecond + // | Metric::NodeTotalBytesRxPerSecond + // | Metric::NodeActivePeers => "system", + // // -- + // Metric::NodeBlocksSubmittedCount + // | Metric::NodeHeadersProcessedCount + // | Metric::NodeDependenciesProcessedCount + // | Metric::NodeBodiesProcessedCount + // | Metric::NodeTransactionsProcessedCount + // | Metric::NodeChainBlocksProcessedCount + // | Metric::NodeMassProcessedCount + // | Metric::NodeDatabaseBlocksCount + // | Metric::NodeDatabaseHeadersCount + // | Metric::NetworkMempoolSize + // | Metric::NetworkTransactionsPerSecond + // | Metric::NetworkTipHashesCount + // | Metric::NetworkDifficulty + // | Metric::NetworkPastMedianTime + // | Metric::NetworkVirtualParentHashesCount + // | Metric::NetworkVirtualDaaScore => "kaspa", + // } + // } pub fn is_key_performance_metric(&self) -> bool { matches!( @@ -362,6 +365,7 @@ impl Metric { Metric::NodeDiskIoWriteBytes => as_mb(f, si, short), Metric::NodeDiskIoReadPerSec => format!("{}/s", as_data_size(f, si)), Metric::NodeDiskIoWritePerSec => format!("{}/s", as_data_size(f, si)), + Metric::NodeStorageSizeBytes => as_gb(f, si, short), // -- Metric::NodeBorshLiveConnections => f.trunc().separated_string(), Metric::NodeBorshConnectionAttempts => f.trunc().separated_string(), @@ -425,6 +429,7 @@ impl Metric { Metric::NodeDiskIoWriteBytes => ("Storage Write", "Stor Write"), Metric::NodeDiskIoReadPerSec => ("Storage Read/s", "Stor Read"), Metric::NodeDiskIoWritePerSec => ("Storage Write/s", "Stor Write"), + Metric::NodeStorageSizeBytes => ("Storage Size", "Stor Size"), // -- Metric::NodeActivePeers => ("Active p2p Peers", "Peers"), Metric::NodeBorshLiveConnections => ("Borsh Active Connections", "Borsh Conn"), @@ -493,6 +498,7 @@ pub struct MetricsData { pub node_disk_io_write_bytes: u64, pub node_disk_io_read_per_sec: f32, pub node_disk_io_write_per_sec: f32, + pub node_storage_size_bytes: u64, // --- pub node_borsh_live_connections: u32, pub node_borsh_connection_attempts: u64, @@ -615,6 +621,8 @@ pub struct MetricsSnapshot { pub network_past_median_time: f64, pub network_virtual_parent_hashes_count: f64, pub network_virtual_daa_score: f64, + // --- + pub node_storage_size_bytes: f64, } impl MetricsSnapshot { @@ -629,6 +637,7 @@ impl MetricsSnapshot { Metric::NodeDiskIoWriteBytes => self.node_disk_io_write_bytes, Metric::NodeDiskIoReadPerSec => self.node_disk_io_read_per_sec, Metric::NodeDiskIoWritePerSec => self.node_disk_io_write_per_sec, + Metric::NodeStorageSizeBytes => self.node_storage_size_bytes, // --- Metric::NodeActivePeers => self.node_active_peers, Metric::NodeBorshLiveConnections => self.node_borsh_active_connections, @@ -725,6 +734,7 @@ impl From<(&MetricsData, &MetricsData)> for MetricsSnapshot { node_disk_io_write_bytes: b.node_disk_io_write_bytes as f64, node_disk_io_read_per_sec: b.node_disk_io_read_per_sec as f64, node_disk_io_write_per_sec: b.node_disk_io_write_per_sec as f64, + node_storage_size_bytes: b.node_storage_size_bytes as f64, // --- node_borsh_active_connections: b.node_borsh_live_connections as f64, node_borsh_connection_attempts: b.node_borsh_connection_attempts as f64, diff --git a/metrics/core/src/lib.rs b/metrics/core/src/lib.rs index 4a3ca2a0fc..b30bdfb4b4 100644 --- a/metrics/core/src/lib.rs +++ b/metrics/core/src/lib.rs @@ -115,8 +115,14 @@ impl Metrics { // --- samplers async fn sample_metrics(self: &Arc, rpc: Arc, data: &mut MetricsData) -> Result<()> { - let GetMetricsResponse { server_time: _, consensus_metrics, connection_metrics, bandwidth_metrics, process_metrics } = - rpc.get_metrics(true, true, true, true).await?; + let GetMetricsResponse { + server_time: _, + consensus_metrics, + connection_metrics, + bandwidth_metrics, + process_metrics, + storage_metrics, + } = rpc.get_metrics(true, true, true, true, true).await?; if let Some(consensus_metrics) = consensus_metrics { data.node_blocks_submitted_count = consensus_metrics.node_blocks_submitted_count; @@ -180,6 +186,9 @@ impl Metrics { data.node_disk_io_write_per_sec = process_metrics.disk_io_write_per_sec; } + if let Some(storage_metrics) = storage_metrics { + data.node_storage_size_bytes = storage_metrics.storage_size_bytes; + } Ok(()) } } diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 36f8ef3085..293dc2d425 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -34,8 +34,16 @@ pub trait RpcApi: Sync + Send + AnySync { connection_metrics: bool, bandwidth_metrics: bool, consensus_metrics: bool, + storage_metrics: bool, ) -> RpcResult { - self.get_metrics_call(GetMetricsRequest { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics }).await + self.get_metrics_call(GetMetricsRequest { + process_metrics, + connection_metrics, + bandwidth_metrics, + consensus_metrics, + storage_metrics, + }) + .await } async fn get_metrics_call(&self, request: GetMetricsRequest) -> RpcResult; diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 728158bf3e..e4bbc7defb 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1638,6 +1638,7 @@ pub struct GetMetricsRequest { pub connection_metrics: bool, pub bandwidth_metrics: bool, pub consensus_metrics: bool, + pub storage_metrics: bool, } impl Serializer for GetMetricsRequest { @@ -1647,6 +1648,7 @@ impl Serializer for GetMetricsRequest { store!(bool, &self.connection_metrics, writer)?; store!(bool, &self.bandwidth_metrics, writer)?; store!(bool, &self.consensus_metrics, writer)?; + store!(bool, &self.storage_metrics, writer)?; Ok(()) } @@ -1657,8 +1659,9 @@ impl Serializer for GetMetricsRequest { let connection_metrics = load!(bool, reader)?; let bandwidth_metrics = load!(bool, reader)?; let consensus_metrics = load!(bool, reader)?; + let storage_metrics = load!(bool, reader)?; - Ok(Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics }) + Ok(Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics }) } } @@ -1901,6 +1904,28 @@ impl Serializer for ConsensusMetrics { } } +#[derive(Default, Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct StorageMetrics { + pub storage_size_bytes: u64, +} + +impl Serializer for StorageMetrics { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u64, &self.storage_size_bytes, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let storage_size_bytes = load!(u64, reader)?; + + Ok(Self { storage_size_bytes }) + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMetricsResponse { @@ -1909,6 +1934,7 @@ pub struct GetMetricsResponse { pub connection_metrics: Option, pub bandwidth_metrics: Option, pub consensus_metrics: Option, + pub storage_metrics: Option, } impl GetMetricsResponse { @@ -1918,8 +1944,9 @@ impl GetMetricsResponse { connection_metrics: Option, bandwidth_metrics: Option, consensus_metrics: Option, + storage_metrics: Option, ) -> Self { - Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, server_time } + Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics, server_time } } } @@ -1931,6 +1958,7 @@ impl Serializer for GetMetricsResponse { serialize!(Option, &self.connection_metrics, writer)?; serialize!(Option, &self.bandwidth_metrics, writer)?; serialize!(Option, &self.consensus_metrics, writer)?; + serialize!(Option, &self.storage_metrics, writer)?; Ok(()) } @@ -1942,8 +1970,9 @@ impl Serializer for GetMetricsResponse { let connection_metrics = deserialize!(Option, reader)?; let bandwidth_metrics = deserialize!(Option, reader)?; let consensus_metrics = deserialize!(Option, reader)?; + let storage_metrics = deserialize!(Option, reader)?; - Ok(Self { server_time, process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics }) + Ok(Self { server_time, process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index adffaf4f6d..f9a06032d5 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -382,6 +382,12 @@ mod mockery { } } + impl Mock for StorageMetrics { + fn mock() -> Self { + StorageMetrics { storage_size_bytes: mock() } + } + } + // -------------------------------------------- // implementations for all the rpc request // and response data structures. @@ -903,7 +909,13 @@ mod mockery { impl Mock for GetMetricsRequest { fn mock() -> Self { - GetMetricsRequest { process_metrics: true, connection_metrics: true, bandwidth_metrics: true, consensus_metrics: true } + GetMetricsRequest { + process_metrics: true, + connection_metrics: true, + bandwidth_metrics: true, + consensus_metrics: true, + storage_metrics: true, + } } } @@ -917,6 +929,7 @@ mod mockery { connection_metrics: mock(), bandwidth_metrics: mock(), consensus_metrics: mock(), + storage_metrics: mock(), } } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index e558c65485..21902835ed 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -806,11 +806,16 @@ message ConsensusMetrics{ uint64 virtualDaaScore = 18; } +message StorageMetrics{ + uint64 storageSizeBytes = 1; +} + message GetMetricsRequestMessage{ bool processMetrics = 1; bool connectionMetrics = 2; bool bandwidthMetrics = 3; bool consensusMetrics = 4; + bool storageMetrics = 5; } message GetMetricsResponseMessage{ @@ -819,6 +824,7 @@ message GetMetricsResponseMessage{ ConnectionMetrics connectionMetrics = 12; BandwidthMetrics bandwidthMetrics = 13; ConsensusMetrics consensusMetrics = 14; + StorageMetrics storageMetrics = 15; RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index 9babf29c84..27eb78d50c 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -403,6 +403,7 @@ from!(item: &kaspa_rpc_core::GetMetricsRequest, protowire::GetMetricsRequestMess connection_metrics: item.connection_metrics, bandwidth_metrics: item.bandwidth_metrics, consensus_metrics: item.consensus_metrics, + storage_metrics: item.storage_metrics, } }); from!(item: RpcResult<&kaspa_rpc_core::GetMetricsResponse>, protowire::GetMetricsResponseMessage, { @@ -412,6 +413,7 @@ from!(item: RpcResult<&kaspa_rpc_core::GetMetricsResponse>, protowire::GetMetric connection_metrics: item.connection_metrics.as_ref().map(|x| x.into()), bandwidth_metrics: item.bandwidth_metrics.as_ref().map(|x| x.into()), consensus_metrics: item.consensus_metrics.as_ref().map(|x| x.into()), + storage_metrics: item.storage_metrics.as_ref().map(|x| x.into()), error: None, } }); @@ -795,7 +797,7 @@ try_from!(&protowire::PingRequestMessage, kaspa_rpc_core::PingRequest); try_from!(&protowire::PingResponseMessage, RpcResult); try_from!(item: &protowire::GetMetricsRequestMessage, kaspa_rpc_core::GetMetricsRequest, { - Self { process_metrics: item.process_metrics, connection_metrics: item.connection_metrics, bandwidth_metrics:item.bandwidth_metrics, consensus_metrics: item.consensus_metrics } + Self { process_metrics: item.process_metrics, connection_metrics: item.connection_metrics, bandwidth_metrics:item.bandwidth_metrics, consensus_metrics: item.consensus_metrics, storage_metrics: item.storage_metrics } }); try_from!(item: &protowire::GetMetricsResponseMessage, RpcResult, { Self { @@ -804,6 +806,7 @@ try_from!(item: &protowire::GetMetricsResponseMessage, RpcResult { if let Some(connection_metrics) = metrics.connection_metrics { // update diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs index 3224cefee9..7115547232 100644 --- a/testing/integration/src/rpc_tests.rs +++ b/testing/integration/src/rpc_tests.rs @@ -461,6 +461,7 @@ async fn sanity_test() { connection_metrics: true, bandwidth_metrics: true, process_metrics: true, + storage_metrics: true, }) .await .unwrap(); @@ -473,6 +474,7 @@ async fn sanity_test() { connection_metrics: true, bandwidth_metrics: true, process_metrics: true, + storage_metrics: true, }) .await .unwrap(); @@ -485,6 +487,7 @@ async fn sanity_test() { connection_metrics: true, bandwidth_metrics: false, process_metrics: false, + storage_metrics: false, }) .await .unwrap(); @@ -497,6 +500,7 @@ async fn sanity_test() { connection_metrics: true, bandwidth_metrics: false, process_metrics: false, + storage_metrics: false, }) .await .unwrap(); From 5ae11efecc1013304b017f071f9de11ef47c790f Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 13 Jun 2024 22:39:24 +0300 Subject: [PATCH 015/158] fix file creation timestamp issue on some ext4 file systems (updated via workflow-store::fs) --- wallet/core/src/storage/local/transaction/fsio.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wallet/core/src/storage/local/transaction/fsio.rs b/wallet/core/src/storage/local/transaction/fsio.rs index 784ca80607..2d7d376107 100644 --- a/wallet/core/src/storage/local/transaction/fsio.rs +++ b/wallet/core/src/storage/local/transaction/fsio.rs @@ -65,7 +65,10 @@ impl TransactionStore { match fs::readdir(folder, true).await { Ok(mut files) => { // we reverse the order of the files so that the newest files are first - files.sort_by_key(|f| std::cmp::Reverse(f.metadata().unwrap().created())); + files.sort_by_key(|f| { + let meta = f.metadata().expect("fsio: missing file metadata"); + std::cmp::Reverse(meta.created().or_else(|| meta.modified()).unwrap_or_default()) + }); for file in files { if let Ok(id) = TransactionId::from_hex(file.file_name()) { From 95c1749cfbaa59784537fb7ff0c308e45eeab079 Mon Sep 17 00:00:00 2001 From: aspect Date: Fri, 14 Jun 2024 22:00:48 +0300 Subject: [PATCH 016/158] bigint values in TransactionRecord (#48) --- wallet/core/src/storage/transaction/record.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/wallet/core/src/storage/transaction/record.rs b/wallet/core/src/storage/transaction/record.rs index ab43034fbf..fc6613361d 100644 --- a/wallet/core/src/storage/transaction/record.rs +++ b/wallet/core/src/storage/transaction/record.rs @@ -320,11 +320,12 @@ pub struct TransactionRecord { #[serde(rename = "unixtimeMsec")] #[wasm_bindgen(js_name = unixtimeMsec)] pub unixtime_msec: Option, + #[wasm_bindgen(skip)] pub value: u64, #[wasm_bindgen(skip)] pub binding: Binding, #[serde(rename = "blockDaaScore")] - #[wasm_bindgen(js_name = blockDaaScore)] + #[wasm_bindgen(skip)] pub block_daa_score: u64, #[serde(rename = "network")] #[wasm_bindgen(js_name = network)] @@ -786,6 +787,16 @@ impl TransactionRecord { #[wasm_bindgen] impl TransactionRecord { + #[wasm_bindgen(getter)] + pub fn value_as_js_bigint(&self) -> BigInt { + self.value.into() + } + + #[wasm_bindgen(getter, js_name = "blockDaaScore")] + pub fn block_daa_score_as_js_bigint(&self) -> BigInt { + self.block_daa_score.into() + } + #[wasm_bindgen(getter, js_name = "binding")] pub fn binding_as_js_value(&self) -> BindingT { serde_wasm_bindgen::to_value(&self.binding).unwrap().unchecked_into() From 7e609dc43db2a21a115f1b9ec13ca299dfac836b Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 15 Jun 2024 03:25:09 +0300 Subject: [PATCH 017/158] change address decoding panics to errors --- crypto/addresses/src/bech32.rs | 7 ++++++- crypto/addresses/src/lib.rs | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/crypto/addresses/src/bech32.rs b/crypto/addresses/src/bech32.rs index 496e2a549d..32b025343a 100644 --- a/crypto/addresses/src/bech32.rs +++ b/crypto/addresses/src/bech32.rs @@ -122,11 +122,16 @@ impl Address { }) .collect::>(); err?; + if address.len() < 8 { + return Err(AddressError::BadPayload); + } + let (payload_u5, checksum_u5) = address_u5.split_at(address.len() - 8); let fivebit_prefix = prefix.as_str().as_bytes().iter().copied().map(|c| c & 0x1fu8); // Convert to number - let checksum_ = u64::from_be_bytes([vec![0u8; 3], conv5to8(checksum_u5)].concat().try_into().expect("Is exactly 8 bytes")); + let checksum_ = + u64::from_be_bytes([vec![0u8; 3], conv5to8(checksum_u5)].concat().try_into().map_err(|_| AddressError::BadChecksumSize)?); if checksum(payload_u5, fivebit_prefix) != checksum_ { return Err(AddressError::BadChecksum); diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index fdba63ef7f..66e3b15c67 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -28,9 +28,15 @@ pub enum AddressError { #[error("The address contains an invalid character {0}")] DecodingError(char), + #[error("The address checksum is invalid (must be exactly 8 bytes)")] + BadChecksumSize, + #[error("The address checksum is invalid")] BadChecksum, + #[error("The address payload invalid")] + BadPayload, + #[error("The address is invalid")] InvalidAddress, From f064ea7d19c7d4f46d1c13fab3a8377121b0866b Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 15 Jun 2024 03:28:39 +0300 Subject: [PATCH 018/158] update error messaging --- crypto/addresses/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index 66e3b15c67..ac0170b1f0 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -34,7 +34,7 @@ pub enum AddressError { #[error("The address checksum is invalid")] BadChecksum, - #[error("The address payload invalid")] + #[error("The address payload is invalid")] BadPayload, #[error("The address is invalid")] From 528eeae124b4e337e1caf65e7618f4a2226d1aee Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 17 Jun 2024 09:37:16 +0300 Subject: [PATCH 019/158] Update for bind/listen compatibility with WRS --- rpc/wrpc/proxy/src/main.rs | 3 ++- rpc/wrpc/server/src/service.rs | 13 +++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/rpc/wrpc/proxy/src/main.rs b/rpc/wrpc/proxy/src/main.rs index 5450463e69..1cb9ad5c60 100644 --- a/rpc/wrpc/proxy/src/main.rs +++ b/rpc/wrpc/proxy/src/main.rs @@ -97,7 +97,8 @@ async fn main() -> Result<()> { log_info!("Using `{encoding}` protocol encoding"); let config = WebSocketConfig { max_message_size: Some(1024 * 1024 * 1024), ..Default::default() }; - server.listen(&options.listen_address, Some(config)).await?; + let listener = server.bind(&options.listen_address).await?; + server.listen(listener, Some(config)).await?; Ok(()) } diff --git a/rpc/wrpc/server/src/service.rs b/rpc/wrpc/server/src/service.rs index 866c4cdd64..72d09f6e67 100644 --- a/rpc/wrpc/server/src/service.rs +++ b/rpc/wrpc/server/src/service.rs @@ -147,10 +147,15 @@ impl WrpcService { info!("WRPC Server starting on: {}", listen_address); tokio::spawn(async move { let config = WebSocketConfig { max_message_size: Some(MAX_WRPC_MESSAGE_SIZE), ..Default::default() }; - let serve_result = self.server.listen(&listen_address, Some(config)).await; - match serve_result { - Ok(_) => info!("WRPC Server stopped on: {}", listen_address), - Err(err) => panic!("WRPC Server {listen_address} stopped with error: {err:?}"), + match self.server.bind(&listen_address).await { + Ok(listener) => { + let serve_result = self.server.listen(listener, Some(config)).await; + match serve_result { + Ok(_) => info!("WRPC Server stopped on: {}", listen_address), + Err(err) => panic!("WRPC Server {listen_address} stopped with error: {err:?}"), + } + } + Err(err) => panic!("WRPC Server bind error on {listen_address}: {err:?}"), } }); From 4613314adddba08deeeb3de31fffb1c8be2a35cc Mon Sep 17 00:00:00 2001 From: surinder singh Date: Mon, 17 Jun 2024 12:11:28 +0530 Subject: [PATCH 020/158] range support added for transactions pagination (#49) * range support added for transactions pagination * cargo fmt/clippy --- .../src/storage/local/transaction/indexdb.rs | 61 ++++++++++++++++--- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/wallet/core/src/storage/local/transaction/indexdb.rs b/wallet/core/src/storage/local/transaction/indexdb.rs index 55d3ee97be..1226c34806 100644 --- a/wallet/core/src/storage/local/transaction/indexdb.rs +++ b/wallet/core/src/storage/local/transaction/indexdb.rs @@ -218,9 +218,9 @@ impl TransactionRecordStore for TransactionStore { binding: &Binding, network_id: &NetworkId, _filter: Option>, - _range: std::ops::Range, + range: std::ops::Range, ) -> Result { - // log_info!("DEBUG IDB: Loading transaction records for range {:?}", _range); + log_info!("DEBUG IDB: Loading transaction records for range {:?}", range); let binding_str = binding.to_hex(); let network_id_str = network_id.to_string(); @@ -239,13 +239,56 @@ impl TransactionRecordStore for TransactionStore { .object_store(TRANSACTIONS_STORE_NAME) .map_err(|err| Error::Custom(format!("Failed to open indexdb object store for reading {:?}", err)))?; - let array = store - .get_all() - .map_err(|err| Error::Custom(format!("Failed to get transaction record from indexdb {:?}", err)))? + let total = store + .count() + .map_err(|err| Error::Custom(format!("Failed to count indexdb records {:?}", err)))? .await - .map_err(|err| Error::Custom(format!("Failed to get transaction record from indexdb {:?}", err)))?; + .map_err(|err| Error::Custom(format!("Failed to count indexdb records from future {:?}", err)))?; + let cursor = store + .open_cursor() + .map_err(|err| Error::Custom(format!("Failed to open indexdb store cursor for reading {:?}", err)))?; + let mut records = vec![]; + let cursor = cursor.await.map_err(|err| Error::Custom(format!("Failed to open indexdb store cursor {:?}", err)))?; + + if let Some(cursor) = cursor { + if range.start > 0 { + let res = cursor + .advance(range.start as u32) + .map_err(|err| Error::Custom(format!("Unable to advance indexdb cursor {:?}", err)))? + .await; + let _res = res.map_err(|err| Error::Custom(format!("Unable to advance indexdb cursor future {:?}", err)))?; + // if !res { + // //return Err(Error::Custom(format!("Unable to advance indexdb cursor future {:?}", err))); + // } + } + + let count = range.end - range.start; + + loop { + if records.len() < count { + records.push(cursor.value()); + if let Ok(b) = cursor.continue_cursor() { + match b.await { + Ok(b) => { + if !b { + break; + } + } + Err(err) => { + log_info!("DEBUG IDB: Loading transaction error, cursor.continue_cursor() {:?}", err); + break; + } + } + } else { + break; + } + } else { + break; + } + } + } - let transactions = array + let transactions = records .iter() .filter_map(|js_value| match transaction_record_from_js_value(&js_value, None) { Ok(transaction_record) => Some(Arc::new(transaction_record)), @@ -256,8 +299,8 @@ impl TransactionRecordStore for TransactionStore { }) .collect::>(); - let total = transactions.len() as u64; - Ok(TransactionRangeResult { transactions, total }) + //let total = transactions.len() as u64; + Ok(TransactionRangeResult { transactions, total: total.into() }) }) } From 423b7a1b05cc0c448bca9f8425408cc86d3347d8 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 17 Jun 2024 09:43:55 +0300 Subject: [PATCH 021/158] lints --- wallet/core/src/storage/local/transaction/indexdb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/core/src/storage/local/transaction/indexdb.rs b/wallet/core/src/storage/local/transaction/indexdb.rs index 1226c34806..888d86340a 100644 --- a/wallet/core/src/storage/local/transaction/indexdb.rs +++ b/wallet/core/src/storage/local/transaction/indexdb.rs @@ -290,7 +290,7 @@ impl TransactionRecordStore for TransactionStore { let transactions = records .iter() - .filter_map(|js_value| match transaction_record_from_js_value(&js_value, None) { + .filter_map(|js_value| match transaction_record_from_js_value(js_value, None) { Ok(transaction_record) => Some(Arc::new(transaction_record)), Err(err) => { log_error!("Failed to deserialize transaction record from indexdb {:?}", err); From 7906a58ffdd4d5ad787c5ec29761315c97b2cf5c Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 17 Jun 2024 11:27:31 +0300 Subject: [PATCH 022/158] WRS 0.13.0 --- Cargo.toml | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc0d81acc0..527dd55800 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -270,34 +270,34 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -# workflow-core = { version = "0.12.1" } -# workflow-d3 = { version = "0.12.1" } -# workflow-dom = { version = "0.12.1" } -# workflow-http = { version = "0.12.1" } -# workflow-log = { version = "0.12.1" } -# workflow-node = { version = "0.12.1" } -# workflow-nw = { version = "0.12.1" } -# workflow-rpc = { version = "0.12.1" } -# workflow-serializer = { version = "0.12.1" } -# workflow-store = { version = "0.12.1" } -# workflow-terminal = { version = "0.12.1" } -# workflow-wasm = { version = "0.12.1" } +workflow-core = { version = "0.13.0" } +workflow-d3 = { version = "0.13.0" } +workflow-dom = { version = "0.13.0" } +workflow-http = { version = "0.13.0" } +workflow-log = { version = "0.13.0" } +workflow-node = { version = "0.13.0" } +workflow-nw = { version = "0.13.0" } +workflow-rpc = { version = "0.13.0" } +workflow-serializer = { version = "0.13.0" } +workflow-store = { version = "0.13.0" } +workflow-terminal = { version = "0.13.0" } +workflow-wasm = { version = "0.13.0" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs # into a sibling folder from https://github.com/workflow-rs/workflow-rs -workflow-core = { path = "../workflow-rs/core" } -workflow-d3 = { path = "../workflow-rs/d3" } -workflow-dom = { path = "../workflow-rs/dom" } -workflow-http = { path = "../workflow-rs/http" } -workflow-log = { path = "../workflow-rs/log" } -workflow-node = { path = "../workflow-rs/node" } -workflow-nw = { path = "../workflow-rs/nw" } -workflow-rpc = { path = "../workflow-rs/rpc" } -workflow-serializer = { path = "../workflow-rs/serializer" } -workflow-store = { path = "../workflow-rs/store" } -workflow-terminal = { path = "../workflow-rs/terminal" } -workflow-wasm = { path = "../workflow-rs/wasm" } +# workflow-core = { path = "../workflow-rs/core" } +# workflow-d3 = { path = "../workflow-rs/d3" } +# workflow-dom = { path = "../workflow-rs/dom" } +# workflow-http = { path = "../workflow-rs/http" } +# workflow-log = { path = "../workflow-rs/log" } +# workflow-node = { path = "../workflow-rs/node" } +# workflow-nw = { path = "../workflow-rs/nw" } +# workflow-rpc = { path = "../workflow-rs/rpc" } +# workflow-serializer = { path = "../workflow-rs/serializer" } +# workflow-store = { path = "../workflow-rs/store" } +# workflow-terminal = { path = "../workflow-rs/terminal" } +# workflow-wasm = { path = "../workflow-rs/wasm" } # --- # workflow-core = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } From 75194b904a6cbfbebd0a61f2b5829e2974da96db Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 18 Jun 2024 05:01:56 +0300 Subject: [PATCH 023/158] wallet ergonomics helper functions --- Cargo.lock | 42 ++++++++++++++++++++++++++++++ wallet/core/src/wallet/mod.rs | 48 +++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78d9494c4f..e779f91bd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6836,6 +6836,8 @@ dependencies = [ [[package]] name = "workflow-chrome" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d9071998d28aba6399cd7627febaf1a43661c392d9f1c3248ef4fd9b7b6777" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6849,6 +6851,8 @@ dependencies = [ [[package]] name = "workflow-core" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb2cf53ab23f5c92f010aeb141fcc458edf450ea68deaa9ad0eb98e6d8cce5f" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6879,6 +6883,8 @@ dependencies = [ [[package]] name = "workflow-core-macros" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccbd182d16e43e04da1bd2bd33877d6c58dc84da9fe6675bea83b37d55558862" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6894,6 +6900,8 @@ dependencies = [ [[package]] name = "workflow-dom" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b53bee7985c0feed6534aacf14f53cc42ea1803b29ad286b887daa4ab47cc36" dependencies = [ "futures", "js-sys", @@ -6910,6 +6918,8 @@ dependencies = [ [[package]] name = "workflow-http" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f26f77f7857e47a9356c953ef776600fabfdb334c6af0f43d91006d4e46d86" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6924,6 +6934,8 @@ dependencies = [ [[package]] name = "workflow-log" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bffdd5581af954a6c27f1e36361aa4628cdc50aec0a0ea9a2319b804ff824a" dependencies = [ "cfg-if 1.0.0", "console", @@ -6938,6 +6950,8 @@ dependencies = [ [[package]] name = "workflow-macro-tools" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74953849ead8e3d664f194b8b92f6847d30010d54e300936e99571f33a856a" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6949,6 +6963,8 @@ dependencies = [ [[package]] name = "workflow-node" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5818cae741747cdb1d91ae5dcb32cec7c6b049d4a2c5b0a88206f1b7c8f838" dependencies = [ "borsh", "futures", @@ -6968,6 +6984,8 @@ dependencies = [ [[package]] name = "workflow-nw" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b273ca51c258ebe4249157a301e76a365af0ac968319d7c517d997e98c375db" dependencies = [ "ahash", "async-trait", @@ -6990,6 +7008,8 @@ dependencies = [ [[package]] name = "workflow-panic-hook" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b3a81fd6cec4e61d7210037604bdf9777e4db9110f4e49ba284b41dc8f8f49" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7013,6 +7033,8 @@ dependencies = [ [[package]] name = "workflow-rpc" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edece974143ddc3d3aa3e8ec6bc5f75689922e758378c61ceeca2dab5c50e31" dependencies = [ "ahash", "async-std", @@ -7041,6 +7063,8 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a208e54c43414b7fc48d15fa99a6bec60b0ce16cab5c2fc0fc682afa06cb7e" dependencies = [ "parse-variants", "proc-macro-error", @@ -7052,6 +7076,8 @@ dependencies = [ [[package]] name = "workflow-serializer" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e420ffb8ab865d8072ba034a72ad1128077529d38623ab8c8533e0ccb72d2f" dependencies = [ "ahash", "borsh", @@ -7061,6 +7087,8 @@ dependencies = [ [[package]] name = "workflow-store" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05d8c0b4444de383bef68abbed7b28d71be77e1712ff24ca6814e24213ce385" dependencies = [ "async-std", "base64 0.21.7", @@ -7087,6 +7115,8 @@ dependencies = [ [[package]] name = "workflow-task" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce39eb639b8d799e868a746b017817ddddc1158063d20cde7d165f8935be22c9" dependencies = [ "futures", "thiserror", @@ -7097,6 +7127,8 @@ dependencies = [ [[package]] name = "workflow-task-macros" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35be4886c67ac6a03394ce78c82b6504fc980aa9d4e87e56c67347822a7e1371" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7111,6 +7143,8 @@ dependencies = [ [[package]] name = "workflow-terminal" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343b71e007578706d9600ae121505c4fb0dc93005f5afcd9c963b3b459dc9ff3" dependencies = [ "async-std", "async-trait", @@ -7138,6 +7172,8 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "698264d025bc77ecc8d64f40967cef13116653de0709173c3ed9074fc1f98a57" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7152,6 +7188,8 @@ dependencies = [ [[package]] name = "workflow-wasm" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44a962dd16fa3b6070bb45a1fbb5b58b07b35cc65438eb23c4de0cd9b5e3b136" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7171,6 +7209,8 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a20b3b0217643a4b5b7f68c0edadb0a5cfb82861fc209568de676adc7cc644" dependencies = [ "js-sys", "proc-macro-error", @@ -7183,6 +7223,8 @@ dependencies = [ [[package]] name = "workflow-websocket" version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e6546ada08f288919e312363c35f87ebc60f07991543469570a5da2ea6f0c2" dependencies = [ "ahash", "async-channel 2.2.1", diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index d20f041adb..e1b9b29e09 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -7,6 +7,7 @@ pub mod maps; pub use args::*; use crate::account::ScanNotifier; +use crate::api::traits::WalletApi; use crate::compat::gen1::decrypt_mnemonic; use crate::error::Error::Custom; use crate::factory::try_load_account; @@ -104,6 +105,13 @@ pub struct Wallet { inner: Arc, } +impl Default for Wallet { + fn default() -> Self { + let storage = Wallet::local_store().expect("Unable to initialize local storage"); + Wallet::try_new(storage, None, None).unwrap() + } +} + impl Wallet { pub fn local_store() -> Result> { Ok(Arc::new(LocalStore::try_new(false)?)) @@ -121,14 +129,6 @@ impl Wallet { let rpc_client = Arc::new(KaspaRpcClient::new_with_args(WrpcEncoding::Borsh, Some("wrpc://127.0.0.1:17110"), resolver, network_id, None)?); - // pub fn try_with_wrpc(store: Arc, network_id: Option) -> Result { - // let rpc_client = Arc::new(KaspaRpcClient::new_with_args( - // WrpcEncoding::Borsh, - // NotificationMode::MultiListeners, - // "wrpc://127.0.0.1:17110", - // None, - // )?); - let rpc_ctl = rpc_client.ctl().clone(); let rpc_api: Arc = rpc_client; let rpc = Rpc::new(rpc_api, rpc_ctl); @@ -161,6 +161,26 @@ impl Wallet { Ok(wallet) } + pub fn to_arc(self) -> Arc { + Arc::new(self) + } + + /// Helper fn for creating the wallet using a builder pattern. + pub fn with_network_id(self, network_id: NetworkId) -> Self { + self.set_network_id(&network_id).expect("Unable to set network id"); + self + } + + pub fn with_resolver(self, resolver: Resolver) -> Self { + self.wrpc_client().set_resolver(resolver).expect("Unable to set resolver"); + self + } + + pub fn with_url(self, url: Option<&str>) -> Self { + self.wrpc_client().set_url(url).expect("Unable to set url"); + self + } + pub fn inner(&self) -> &Arc { &self.inner } @@ -462,6 +482,10 @@ impl Wallet { self.try_rpc_api().and_then(|api| api.clone().downcast_arc::().ok()) } + pub fn wrpc_client(&self) -> Arc { + self.try_rpc_api().and_then(|api| api.clone().downcast_arc::().ok()).unwrap() + } + pub fn rpc_api(&self) -> Arc { self.utxo_processor().rpc_api() } @@ -487,6 +511,14 @@ impl Wallet { Ok(()) } + pub fn as_api(self: &Arc) -> Arc { + self.clone() + } + + pub fn to_api(self) -> Arc { + Arc::new(self) + } + pub fn multiplexer(&self) -> &Multiplexer> { &self.inner.multiplexer } From a0cbd2c6c6084e49e56281f076917cfd14a1c878 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 18 Jun 2024 05:31:24 +0300 Subject: [PATCH 024/158] WRS 0.13.1 --- Cargo.toml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 527dd55800..232be39c7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -270,18 +270,18 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.13.0" } -workflow-d3 = { version = "0.13.0" } -workflow-dom = { version = "0.13.0" } -workflow-http = { version = "0.13.0" } -workflow-log = { version = "0.13.0" } -workflow-node = { version = "0.13.0" } -workflow-nw = { version = "0.13.0" } -workflow-rpc = { version = "0.13.0" } -workflow-serializer = { version = "0.13.0" } -workflow-store = { version = "0.13.0" } -workflow-terminal = { version = "0.13.0" } -workflow-wasm = { version = "0.13.0" } +workflow-core = { version = "0.13.1" } +workflow-d3 = { version = "0.13.1" } +workflow-dom = { version = "0.13.1" } +workflow-http = { version = "0.13.1" } +workflow-log = { version = "0.13.1" } +workflow-node = { version = "0.13.1" } +workflow-nw = { version = "0.13.1" } +workflow-rpc = { version = "0.13.1" } +workflow-serializer = { version = "0.13.1" } +workflow-store = { version = "0.13.1" } +workflow-terminal = { version = "0.13.1" } +workflow-wasm = { version = "0.13.1" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs From cf53be9b935d2d3544f98301abf52aa54acba066 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 18 Jun 2024 16:20:16 +0300 Subject: [PATCH 025/158] WRS 0.13.1 lock --- Cargo.lock | 84 +++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e779f91bd1..d5213265db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6835,9 +6835,9 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d9071998d28aba6399cd7627febaf1a43661c392d9f1c3248ef4fd9b7b6777" +checksum = "cbca78ed1a9353795737f02ee2a789c37f615142e98a93efea226b0a2d23d1c3" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6850,9 +6850,9 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb2cf53ab23f5c92f010aeb141fcc458edf450ea68deaa9ad0eb98e6d8cce5f" +checksum = "8f37e9dd70a07ec5a5b5a690c6314e717175b542d86bbe7b53ea184ad2b32099" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6882,9 +6882,9 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccbd182d16e43e04da1bd2bd33877d6c58dc84da9fe6675bea83b37d55558862" +checksum = "7b6636289adf08a3589c402b8d412c417ce97bf409068e5fe25fcf6bd345eb2c" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6899,9 +6899,9 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b53bee7985c0feed6534aacf14f53cc42ea1803b29ad286b887daa4ab47cc36" +checksum = "4f8624afca0958da6cab8445df720b8f24f2b3998458d0d72ad12cde2123f70a" dependencies = [ "futures", "js-sys", @@ -6917,9 +6917,9 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f26f77f7857e47a9356c953ef776600fabfdb334c6af0f43d91006d4e46d86" +checksum = "1c71ae40b4fc516f79c2f1535a30f5c8c32248b0bad1992c80eb7d8c5035b59b" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6933,9 +6933,9 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bffdd5581af954a6c27f1e36361aa4628cdc50aec0a0ea9a2319b804ff824a" +checksum = "a236ea5f11a586df2118076a187e781ff1968c24087c7afff4968d89f313626d" dependencies = [ "cfg-if 1.0.0", "console", @@ -6949,9 +6949,9 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74953849ead8e3d664f194b8b92f6847d30010d54e300936e99571f33a856a" +checksum = "8c915eff417eaceead109c27ebb8bd07ada056fb2637620cb2030261ddaca880" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6962,9 +6962,9 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5818cae741747cdb1d91ae5dcb32cec7c6b049d4a2c5b0a88206f1b7c8f838" +checksum = "0ab160ef28b0564eca8fb720529be87ce8e8c93ea33257a481f1a0d58ad322db" dependencies = [ "borsh", "futures", @@ -6983,9 +6983,9 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b273ca51c258ebe4249157a301e76a365af0ac968319d7c517d997e98c375db" +checksum = "709cd750bcde559c26af294cadc91aea5142fa73fd7b567b61d0ad2f01474ae5" dependencies = [ "ahash", "async-trait", @@ -7007,9 +7007,9 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b3a81fd6cec4e61d7210037604bdf9777e4db9110f4e49ba284b41dc8f8f49" +checksum = "b93bf9cf8e442935d357a9457355012a6753986071fdcda2924fdf24ecd617dd" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7032,9 +7032,9 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edece974143ddc3d3aa3e8ec6bc5f75689922e758378c61ceeca2dab5c50e31" +checksum = "f103543561d4c8be736a30a70ca1bebdd1c3a4f2cef101c265da97d1a57bf88c" dependencies = [ "ahash", "async-std", @@ -7062,9 +7062,9 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a208e54c43414b7fc48d15fa99a6bec60b0ce16cab5c2fc0fc682afa06cb7e" +checksum = "7c1a76618e22c237260c1e89872652c42515d433649882f63c0c54939c4a52a8" dependencies = [ "parse-variants", "proc-macro-error", @@ -7075,9 +7075,9 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e420ffb8ab865d8072ba034a72ad1128077529d38623ab8c8533e0ccb72d2f" +checksum = "93efad71dc66bb68c3afb332d8835d020779aed7a9f67429ce3dca837f040564" dependencies = [ "ahash", "borsh", @@ -7086,9 +7086,9 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05d8c0b4444de383bef68abbed7b28d71be77e1712ff24ca6814e24213ce385" +checksum = "0cf7f06cc779c10419ec983a99ab11578262dff945554734d1a2ec8b221549fd" dependencies = [ "async-std", "base64 0.21.7", @@ -7114,9 +7114,9 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce39eb639b8d799e868a746b017817ddddc1158063d20cde7d165f8935be22c9" +checksum = "13da44b9cfa0fdef9ad7cdeb3d9425f43faf7c9d5681058bb47c6886e70de01a" dependencies = [ "futures", "thiserror", @@ -7126,9 +7126,9 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35be4886c67ac6a03394ce78c82b6504fc980aa9d4e87e56c67347822a7e1371" +checksum = "b221a893e0ee67cf7e20e6177a083e8cb4262c2e343c65b61e46a81ba84dd459" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7142,9 +7142,9 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343b71e007578706d9600ae121505c4fb0dc93005f5afcd9c963b3b459dc9ff3" +checksum = "c812e0e207be43b9387d47333b614c210c5f2bc074d7a8b125086bffc5fca9f9" dependencies = [ "async-std", "async-trait", @@ -7171,9 +7171,9 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "698264d025bc77ecc8d64f40967cef13116653de0709173c3ed9074fc1f98a57" +checksum = "c2dcdd1d269d83afc908bca8bcac8cc26dc170772f1e2abe938d94da367335f6" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7187,9 +7187,9 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44a962dd16fa3b6070bb45a1fbb5b58b07b35cc65438eb23c4de0cd9b5e3b136" +checksum = "e0120884d7a35c598972f67651144d9d09afec93c88e62becca9ba9e7b34c0dd" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7208,9 +7208,9 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a20b3b0217643a4b5b7f68c0edadb0a5cfb82861fc209568de676adc7cc644" +checksum = "c08251c28898bce81f0697827d4d4238ea6a2c52fc58cc21af151158841e087e" dependencies = [ "js-sys", "proc-macro-error", @@ -7222,9 +7222,9 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88e6546ada08f288919e312363c35f87ebc60f07991543469570a5da2ea6f0c2" +checksum = "df3c9cc4c8ab618ff8d8905f45d5c69d6629afe8ebce477ce01d949001302d31" dependencies = [ "ahash", "async-channel 2.2.1", From 55edd8ca55e0c3933264c85061b35fb69837589d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 18 Jun 2024 19:18:02 +0300 Subject: [PATCH 026/158] WRS 0.13.2 --- Cargo.lock | 84 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 24 ++++++++-------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5213265db..e97af1b8a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6835,9 +6835,9 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbca78ed1a9353795737f02ee2a789c37f615142e98a93efea226b0a2d23d1c3" +checksum = "e69fe24347c0f2af138170b09f69b977258a871dc7987a60030a21773a96f582" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6850,9 +6850,9 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f37e9dd70a07ec5a5b5a690c6314e717175b542d86bbe7b53ea184ad2b32099" +checksum = "7685fc9a612e1cd8da852b5cea8f7b978121549ae239e7bc1e191302b5c4033f" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6882,9 +6882,9 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b6636289adf08a3589c402b8d412c417ce97bf409068e5fe25fcf6bd345eb2c" +checksum = "e05d272b45174884602afa82dc26e97f4bbb1392ef169c0b096300a481c0d890" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6899,9 +6899,9 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f8624afca0958da6cab8445df720b8f24f2b3998458d0d72ad12cde2123f70a" +checksum = "537841fcd3e3e80e13eccd8275520f2994d5002a36ee954c6d685d690babe334" dependencies = [ "futures", "js-sys", @@ -6917,9 +6917,9 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c71ae40b4fc516f79c2f1535a30f5c8c32248b0bad1992c80eb7d8c5035b59b" +checksum = "60f358e9976f6fad2448301a5656d5d4d53f8081f69faa19f61415e80083d94a" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6933,9 +6933,9 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a236ea5f11a586df2118076a187e781ff1968c24087c7afff4968d89f313626d" +checksum = "a74c615c6fe85ec1a2fe842763520be781256fa13e9710dabb0e067777f03c7e" dependencies = [ "cfg-if 1.0.0", "console", @@ -6949,9 +6949,9 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c915eff417eaceead109c27ebb8bd07ada056fb2637620cb2030261ddaca880" +checksum = "2263943df045dc8007685e3b710a606ac1ed5f066fdab24c1674674ef1835c87" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6962,9 +6962,9 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab160ef28b0564eca8fb720529be87ce8e8c93ea33257a481f1a0d58ad322db" +checksum = "60222ed88d27300d28176f2a6c1e454c32d26c9264f0a1bc710439fc570e38ab" dependencies = [ "borsh", "futures", @@ -6983,9 +6983,9 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709cd750bcde559c26af294cadc91aea5142fa73fd7b567b61d0ad2f01474ae5" +checksum = "97d7d555f20dff92855e2c0bb8ae4c48d5a9a85d729e46a3bd13068f823562f6" dependencies = [ "ahash", "async-trait", @@ -7007,9 +7007,9 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93bf9cf8e442935d357a9457355012a6753986071fdcda2924fdf24ecd617dd" +checksum = "28c7e88e74cbe7504e8622f9b46f1880eef722e842bde368fc8c4cb8ba51be84" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7032,9 +7032,9 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f103543561d4c8be736a30a70ca1bebdd1c3a4f2cef101c265da97d1a57bf88c" +checksum = "01b61f8e569d6a7ede17e9c44c9030c322fe2fd86486ec5e3ef29d6c7a468486" dependencies = [ "ahash", "async-std", @@ -7062,9 +7062,9 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c1a76618e22c237260c1e89872652c42515d433649882f63c0c54939c4a52a8" +checksum = "6e12859e5869e643faefb7c1051cd54830d6686df791486c0e24dc243fd772de" dependencies = [ "parse-variants", "proc-macro-error", @@ -7075,9 +7075,9 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93efad71dc66bb68c3afb332d8835d020779aed7a9f67429ce3dca837f040564" +checksum = "e1acc7bb2af256394f4468871bad06192899e05b0b3eac6e0b86f2419b26fffa" dependencies = [ "ahash", "borsh", @@ -7086,9 +7086,9 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cf7f06cc779c10419ec983a99ab11578262dff945554734d1a2ec8b221549fd" +checksum = "4e50629a28c063226ad832610ef068645094785dc21dcfc09d73a7fd73e3bb59" dependencies = [ "async-std", "base64 0.21.7", @@ -7114,9 +7114,9 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13da44b9cfa0fdef9ad7cdeb3d9425f43faf7c9d5681058bb47c6886e70de01a" +checksum = "6eb2273998932ac249227779a619b205b55d579ccd54bb492593b39e2341f164" dependencies = [ "futures", "thiserror", @@ -7126,9 +7126,9 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b221a893e0ee67cf7e20e6177a083e8cb4262c2e343c65b61e46a81ba84dd459" +checksum = "d2682bb5ea1dbfb6a5e1c624e6280741e0eb0ed39a5d3c7b738157ca64a5cd42" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7142,9 +7142,9 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c812e0e207be43b9387d47333b614c210c5f2bc074d7a8b125086bffc5fca9f9" +checksum = "3d53f3d0a4e1afd5d7336d41328931e297951e7811e7c74e950255afe0d73076" dependencies = [ "async-std", "async-trait", @@ -7171,9 +7171,9 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dcdd1d269d83afc908bca8bcac8cc26dc170772f1e2abe938d94da367335f6" +checksum = "e5fb04baa73f6aa40e38174dda63d26b5717e0fed2a27345c7409ae1faa73387" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7187,9 +7187,9 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0120884d7a35c598972f67651144d9d09afec93c88e62becca9ba9e7b34c0dd" +checksum = "770586ce7cd1084e0033b785a22258b63917b4ba703cf80ba3f97053b83ccb10" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7208,9 +7208,9 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08251c28898bce81f0697827d4d4238ea6a2c52fc58cc21af151158841e087e" +checksum = "6dca209b50904b71b415281c00be817e6fa779b43cc373d70283711195f47c09" dependencies = [ "js-sys", "proc-macro-error", @@ -7222,9 +7222,9 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3c9cc4c8ab618ff8d8905f45d5c69d6629afe8ebce477ce01d949001302d31" +checksum = "ab64d7729b0dbcc1185c48191180548a69a71d3cd199929c93c36f63fdbec276" dependencies = [ "ahash", "async-channel 2.2.1", diff --git a/Cargo.toml b/Cargo.toml index 232be39c7c..eb3937c2f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -270,18 +270,18 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.13.1" } -workflow-d3 = { version = "0.13.1" } -workflow-dom = { version = "0.13.1" } -workflow-http = { version = "0.13.1" } -workflow-log = { version = "0.13.1" } -workflow-node = { version = "0.13.1" } -workflow-nw = { version = "0.13.1" } -workflow-rpc = { version = "0.13.1" } -workflow-serializer = { version = "0.13.1" } -workflow-store = { version = "0.13.1" } -workflow-terminal = { version = "0.13.1" } -workflow-wasm = { version = "0.13.1" } +workflow-core = { version = "0.13.2" } +workflow-d3 = { version = "0.13.2" } +workflow-dom = { version = "0.13.2" } +workflow-http = { version = "0.13.2" } +workflow-log = { version = "0.13.2" } +workflow-node = { version = "0.13.2" } +workflow-nw = { version = "0.13.2" } +workflow-rpc = { version = "0.13.2" } +workflow-serializer = { version = "0.13.2" } +workflow-store = { version = "0.13.2" } +workflow-terminal = { version = "0.13.2" } +workflow-wasm = { version = "0.13.2" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs From 82071b5566f2456b2054e0eb1b4add4759047bac Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 18 Jun 2024 22:21:14 +0300 Subject: [PATCH 027/158] kaspa-wallet (cli) updates (using latest resolver APIs + guide cleanup) --- cli/src/cli.rs | 4 ++-- cli/src/modules/connect.rs | 24 +++++++------------ cli/src/modules/guide.txt | 44 ++++------------------------------- rpc/wrpc/client/src/client.rs | 6 ++--- 4 files changed, 18 insertions(+), 60 deletions(-) diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 4f562e7494..c5c8faa162 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -8,7 +8,7 @@ use crate::result::Result; use kaspa_daemon::{DaemonEvent, DaemonKind, Daemons}; use kaspa_wallet_core::rpc::DynRpcApi; use kaspa_wallet_core::storage::{IdT, PrvKeyDataInfo}; -use kaspa_wrpc_client::KaspaRpcClient; +use kaspa_wrpc_client::{KaspaRpcClient, Resolver}; use workflow_core::channel::*; use workflow_core::time::Instant; use workflow_log::*; @@ -102,7 +102,7 @@ impl KaspaCli { } pub async fn try_new_arc(options: Options) -> Result> { - let wallet = Arc::new(Wallet::try_new(Wallet::local_store()?, None, None)?); + let wallet = Arc::new(Wallet::try_new(Wallet::local_store()?, Some(Resolver::default()), None)?); let kaspa_cli = Arc::new(KaspaCli { term: Arc::new(Mutex::new(None)), diff --git a/cli/src/modules/connect.rs b/cli/src/modules/connect.rs index 26173256b4..826956f0f6 100644 --- a/cli/src/modules/connect.rs +++ b/cli/src/modules/connect.rs @@ -1,5 +1,4 @@ use crate::imports::*; -use kaspa_wrpc_client::Resolver; #[derive(Default, Handler)] #[help("Connect to a Kaspa network")] @@ -12,36 +11,31 @@ impl Connect { let network_id = ctx.wallet().network_id()?; let arg_or_server_address = argv.first().cloned().or_else(|| ctx.wallet().settings().get(WalletSettings::Server)); - let (is_public, url) = match arg_or_server_address.as_deref() { - Some("public") => { + let url = match arg_or_server_address.as_deref() { + Some("public") | Some("resolver") => { tprintln!(ctx, "Connecting to a public node"); - (true, Resolver::default().fetch(WrpcEncoding::Borsh, network_id).await.map_err(|e| e.to_string())?.url) + None } None => { tprintln!(ctx, "No server set, connecting to a public node"); - (true, Resolver::default().fetch(WrpcEncoding::Borsh, network_id).await.map_err(|e| e.to_string())?.url) + None } Some(url) => { - (false, wrpc_client.parse_url_with_network_type(url.to_string(), network_id.into()).map_err(|e| e.to_string())?) + Some(wrpc_client.parse_url_with_network_type(url.to_string(), network_id.into()).map_err(|e| e.to_string())?) } }; - if is_public { + if url.is_none() { tpara!( ctx, - "Please note that default public nodes are community-operated and \ - accessing them may expose your IP address to different node providers. \ + "Please note that public node infrastructure is community-operated and \ + accessing it may expose your IP address to different node providers. \ Consider running your own node for better privacy. \ ", ); } - let options = ConnectOptions { - block_async_connect: true, - strategy: ConnectStrategy::Fallback, - url: Some(url), - ..Default::default() - }; + let options = ConnectOptions { block_async_connect: true, strategy: ConnectStrategy::Fallback, url, ..Default::default() }; wrpc_client.connect(Some(options)).await.map_err(|e| e.to_string())?; } else { terrorln!(ctx, "Unable to connect with non-wRPC client"); diff --git a/cli/src/modules/guide.txt b/cli/src/modules/guide.txt index ecc0f8d968..637a3cab4e 100644 --- a/cli/src/modules/guide.txt +++ b/cli/src/modules/guide.txt @@ -1,43 +1,17 @@ -Please note - this is an alpha version of the softeware, not all features are currently functional. - -If using a dekstop or a web version of this software, you can use Ctrl+'+' or Ctrl+'-' (Command on MacOS) to -change the terminal font size. - -If using a desktop version, you can use Ctrl+M (Command on MacOS) to bring up metrics. - -Type `help` to see the complete list of commands. `exit` to exit this application. -On Windows you can use `Alt+F4` and on MacOS `Command+Q` to exit. - ---- - Before you start, you must configure the default network setting. There are currently -3 networks available. `mainnet`, `testnet-10` and `testnet-11`. While this software -is in alpha stage, you should not use it on the mainnet. If you wish to experiment, -you should select `testnet-10` by entering `network testnet-10` +3 networks available. `mainnet`, `testnet-10` and `testnet-11`. If you wish to experiment, +you should select `testnet-11` by entering `network testnet-11` The `server` command configures the target server. You can connect to any Rusty Kaspa -node that has User RPC enabled with `--rpclisten-borsh=public`. If you are running the node -from within KOS, it is locked to listen to a local IP address. +node that has wRPC enabled with `--rpclisten-borsh=0.0.0.0`. If the server setting +is set to 'public' the node will connect to the public node infrastructure. Both network and server values are stored in the application settings and are used when running a local node or connecting to a remote node. --- -You can use `node start` to start the node. Type `node` to see an overview of commands. -`node mute` toggles node log output (you can also use `node logs`). `node select` allows -you to choose between locally installed flavors (if running in the development environment). -You can also specify an absolute path by typing `node select `. - -For developers: `node select` scans 'target' folder for the debug and release builds -so you can switch between builds at runtime using the `node select` command. - -Once you node is running, you can connect to it using the `connect` command. - -When starting the node and the `server` setting is configured to your local host, -the `connect` action will occure automatically. - -`wallet create []` Use theis command to create a local wallet. The argument +`wallet create []` Use this command to create a local wallet. The argument is optional (the default wallet name is "kaspa") and allows you to create multiple named wallets. Only one wallet can be opened at a time. Keep in mind that a wallet can have multiple accounts, as such you only need one wallet, unless, for example, you want to separate wallets for @@ -56,9 +30,6 @@ testnet KAS. `address` - shows your selected account address -Note - you can click on the address to copy it to the clipboard. (When on mainnet, Ctrl+Click on addresses, transactions and -block hashes will open a new browser window with an explorer.) - Before you transact: `mute` option (enabled by default) toggles mute on/off. Mute enables terminal output of internal framework events. Rust and JavaScript/TypeScript applications integrating with this platform are meant to update their state by monitoring event notifications. Mute allows you to see these events in @@ -78,11 +49,6 @@ the selected account to an account named 'pete' (starts with a 'p' letter) `history details` - Show previous account transactions with extended information. -Once your node is synced, you can start the CPU miner. - -`miner start` - Starts the miner. The miner will mine to your currently selected account. (So you need to have a wallet open and an -account selected to start the miner) - `monitor` - A test screen environment that periodically updates account balances. `rpc` - Allows you to execute RPC methods against the node (not all methods are currently available) diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index 4e9fffc0c5..a815c58e6b 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -222,7 +222,7 @@ impl RpcResolver for Inner { self.node_descriptor.lock().unwrap().replace(Arc::new(node)); url } else { - panic!("RpcClient resolver configuration error (expecting Some(Resolver))") + panic!("RpcClient resolver configuration error (expecting `url` or `resolver` as `Some(Resolver))`") }; self.rpc_ctl.set_descriptor(Some(url.clone())); @@ -406,9 +406,7 @@ impl KaspaRpcClient { let mut options = options.unwrap_or_default(); let strategy = options.strategy; - if let Some(url) = options.url.take() { - self.set_url(Some(&url))?; - } + self.set_url(options.url.take().as_deref())?; // 1Gb message and frame size limits (on native and NodeJs platforms) let ws_config = WebSocketConfig { From 738ca07e1d60d9c466998e282004c2a98669c7ed Mon Sep 17 00:00:00 2001 From: surinder singh Date: Thu, 20 Jun 2024 03:38:01 +0530 Subject: [PATCH 028/158] range support for indexdb store load_range function (#52) --- .../src/storage/local/transaction/indexdb.rs | 74 ++++++++++++++----- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/wallet/core/src/storage/local/transaction/indexdb.rs b/wallet/core/src/storage/local/transaction/indexdb.rs index e4190aa1ff..16f89a0906 100644 --- a/wallet/core/src/storage/local/transaction/indexdb.rs +++ b/wallet/core/src/storage/local/transaction/indexdb.rs @@ -218,36 +218,73 @@ impl TransactionRecordStore for TransactionStore { binding: &Binding, network_id: &NetworkId, _filter: Option>, - _range: std::ops::Range, + range: std::ops::Range, ) -> Result { - // log_info!("DEBUG IDB: Loading transaction records for range {:?}", _range); - + log_info!("DEBUG IDB: Loading transaction records for range {:?}", range); let binding_str = binding.to_hex(); let network_id_str = network_id.to_string(); let db_name = self.make_db_name(&binding_str, &network_id_str); - let inner = self.inner().clone(); - call_async_no_send!(async move { let db = inner.open_db(db_name).await?; - let idb_tx = db .transaction_on_one_with_mode(TRANSACTIONS_STORE_NAME, IdbTransactionMode::Readonly) .map_err(|err| Error::Custom(format!("Failed to open indexdb transaction for reading {:?}", err)))?; - let store = idb_tx .object_store(TRANSACTIONS_STORE_NAME) .map_err(|err| Error::Custom(format!("Failed to open indexdb object store for reading {:?}", err)))?; - - let array = store - .get_all() - .map_err(|err| Error::Custom(format!("Failed to get transaction record from indexdb {:?}", err)))? + let total = store + .count() + .map_err(|err| Error::Custom(format!("Failed to count indexdb records {:?}", err)))? .await - .map_err(|err| Error::Custom(format!("Failed to get transaction record from indexdb {:?}", err)))?; - - let transactions = array + .map_err(|err| Error::Custom(format!("Failed to count indexdb records from future {:?}", err)))?; + + let binding = store + .index(TRANSACTIONS_STORE_TIMESTAMP_INDEX) + .map_err(|err| Error::Custom(format!("Failed to open indexdb indexed store cursor {:?}", err)))?; + let cursor = binding + .open_cursor_with_range_and_direction(&JsValue::NULL, web_sys::IdbCursorDirection::Prev) + .map_err(|err| Error::Custom(format!("Failed to open indexdb store cursor for reading {:?}", err)))?; + let mut records = vec![]; + let cursor = cursor.await.map_err(|err| Error::Custom(format!("Failed to open indexdb store cursor {:?}", err)))?; + if let Some(cursor) = cursor { + if range.start > 0 { + let res = cursor + .advance(range.start as u32) + .map_err(|err| Error::Custom(format!("Unable to advance indexdb cursor {:?}", err)))? + .await; + let _res = res.map_err(|err| Error::Custom(format!("Unable to advance indexdb cursor future {:?}", err)))?; + // if !res { + // //return Err(Error::Custom(format!("Unable to advance indexdb cursor future {:?}", err))); + // } + } + let count = range.end - range.start; + loop { + if records.len() < count { + records.push(cursor.value()); + if let Ok(b) = cursor.continue_cursor() { + match b.await { + Ok(b) => { + if !b { + break; + } + } + Err(err) => { + log_info!("DEBUG IDB: Loading transaction error, cursor.continue_cursor() {:?}", err); + break; + } + } + } else { + break; + } + } else { + break; + } + } + } + let transactions = records .iter() - .filter_map(|js_value| match transaction_record_from_js_value(&js_value, None) { + .filter_map(|js_value| match transaction_record_from_js_value(js_value, None) { Ok(transaction_record) => Some(Arc::new(transaction_record)), Err(err) => { log_error!("Failed to deserialize transaction record from indexdb {:?}", err); @@ -256,8 +293,7 @@ impl TransactionRecordStore for TransactionStore { }) .collect::>(); - let total = transactions.len() as u64; - Ok(TransactionRangeResult { transactions, total }) + Ok(TransactionRangeResult { transactions, total: total.into() }) }) } @@ -480,10 +516,10 @@ fn transaction_record_to_js_value( let id_js_value = JsValue::from_str(&id); let timestamp_js_value = match unixtime_msec { Some(unixtime_msec) => { - let unixtime_sec = (unixtime_msec / 1000) as u32; + //let unixtime_sec = (unixtime_msec / 1000) as u32; let date = Date::new_0(); - date.set_utc_seconds(unixtime_sec); + date.set_time(unixtime_msec as f64); date.into() } None => JsValue::NULL, From 7248b43ea2c27b8f9b53598668287cb0d9765451 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 20 Jun 2024 16:10:52 +0300 Subject: [PATCH 029/158] add balance info to account descriptors, additional wallet connectivity utilities and connect api method changes, export wallet utils in wallet prelude, --- cli/src/imports.rs | 1 - wallet/core/src/account/descriptor.rs | 14 +++++- wallet/core/src/account/variants/bip32.rs | 1 + wallet/core/src/account/variants/keypair.rs | 1 + wallet/core/src/account/variants/legacy.rs | 1 + wallet/core/src/account/variants/multisig.rs | 1 + wallet/core/src/account/variants/resident.rs | 1 + wallet/core/src/api/message.rs | 46 ++++++++++++++++++++ wallet/core/src/api/traits.rs | 8 +++- wallet/core/src/error.rs | 20 ++++++++- wallet/core/src/prelude.rs | 4 ++ wallet/core/src/utxo/processor.rs | 43 +++++++++++++----- wallet/core/src/wallet/api.rs | 25 ++++++++--- wallet/core/src/wasm/api/message.rs | 15 ++++++- 14 files changed, 156 insertions(+), 25 deletions(-) diff --git a/cli/src/imports.rs b/cli/src/imports.rs index a158128046..17a4169c51 100644 --- a/cli/src/imports.rs +++ b/cli/src/imports.rs @@ -14,7 +14,6 @@ pub use kaspa_utils::hex::*; pub use kaspa_wallet_core::compat::*; pub use kaspa_wallet_core::prelude::*; pub use kaspa_wallet_core::settings::{DefaultSettings, SettingsStore, WalletSettings}; -pub use kaspa_wallet_core::utils::*; pub use pad::PadStr; pub use regex::Regex; pub use separator::Separatable; diff --git a/wallet/core/src/account/descriptor.rs b/wallet/core/src/account/descriptor.rs index d433b7bf8f..fd9c37c613 100644 --- a/wallet/core/src/account/descriptor.rs +++ b/wallet/core/src/account/descriptor.rs @@ -17,6 +17,7 @@ pub struct AccountDescriptor { pub kind: AccountKind, pub account_id: AccountId, pub account_name: Option, + pub balance: Option, pub prv_key_data_ids: AssocPrvKeyDataIds, pub receive_address: Option
, pub change_address: Option
, @@ -29,11 +30,21 @@ impl AccountDescriptor { kind: AccountKind, account_id: AccountId, account_name: Option, + balance: Option, prv_key_data_ids: AssocPrvKeyDataIds, receive_address: Option
, change_address: Option
, ) -> Self { - Self { kind, account_id, account_name, prv_key_data_ids, receive_address, change_address, properties: BTreeMap::default() } + Self { + kind, + account_id, + account_name, + balance, + prv_key_data_ids, + receive_address, + change_address, + properties: BTreeMap::default(), + } } pub fn with_property(mut self, property: AccountDescriptorProperty, value: AccountDescriptorValue) -> Self { @@ -225,6 +236,7 @@ declare! { receiveAddress? : Address, changeAddress? : Address, prvKeyDataIds : HexString[], + // balance? : Balance, [key: string]: any } "#, diff --git a/wallet/core/src/account/variants/bip32.rs b/wallet/core/src/account/variants/bip32.rs index 3a8b3ad40e..61cbfb1af7 100644 --- a/wallet/core/src/account/variants/bip32.rs +++ b/wallet/core/src/account/variants/bip32.rs @@ -217,6 +217,7 @@ impl Account for Bip32 { BIP32_ACCOUNT_KIND.into(), *self.id(), self.name(), + self.balance(), self.prv_key_data_id.into(), self.receive_address().ok(), self.change_address().ok(), diff --git a/wallet/core/src/account/variants/keypair.rs b/wallet/core/src/account/variants/keypair.rs index d0d37f74e8..6381ca046b 100644 --- a/wallet/core/src/account/variants/keypair.rs +++ b/wallet/core/src/account/variants/keypair.rs @@ -179,6 +179,7 @@ impl Account for Keypair { KEYPAIR_ACCOUNT_KIND.into(), *self.id(), self.name(), + self.balance(), self.prv_key_data_id.into(), self.receive_address().ok(), self.change_address().ok(), diff --git a/wallet/core/src/account/variants/legacy.rs b/wallet/core/src/account/variants/legacy.rs index 0ea69554df..cf05acf681 100644 --- a/wallet/core/src/account/variants/legacy.rs +++ b/wallet/core/src/account/variants/legacy.rs @@ -192,6 +192,7 @@ impl Account for Legacy { LEGACY_ACCOUNT_KIND.into(), *self.id(), self.name(), + self.balance(), self.prv_key_data_id.into(), self.receive_address().ok(), self.change_address().ok(), diff --git a/wallet/core/src/account/variants/multisig.rs b/wallet/core/src/account/variants/multisig.rs index 253464543d..b5d7db6786 100644 --- a/wallet/core/src/account/variants/multisig.rs +++ b/wallet/core/src/account/variants/multisig.rs @@ -222,6 +222,7 @@ impl Account for MultiSig { MULTISIG_ACCOUNT_KIND.into(), *self.id(), self.name(), + self.balance(), self.prv_key_data_ids.clone().try_into()?, self.receive_address().ok(), self.change_address().ok(), diff --git a/wallet/core/src/account/variants/resident.rs b/wallet/core/src/account/variants/resident.rs index 74f3868965..c7e56d3d6c 100644 --- a/wallet/core/src/account/variants/resident.rs +++ b/wallet/core/src/account/variants/resident.rs @@ -77,6 +77,7 @@ impl Account for Resident { RESIDENT_ACCOUNT_KIND.into(), *self.id(), self.name(), + self.balance(), AssocPrvKeyDataIds::None, self.receive_address().ok(), self.change_address().ok(), diff --git a/wallet/core/src/api/message.rs b/wallet/core/src/api/message.rs index 9cd2f830cd..9eebba29f8 100644 --- a/wallet/core/src/api/message.rs +++ b/wallet/core/src/api/message.rs @@ -44,6 +44,52 @@ pub struct FlushResponse {} pub struct ConnectRequest { pub url: Option, pub network_id: NetworkId, + // retry on error, otherwise give up + pub retry_on_error: bool, + // block async call until connected, otherwise return immediately + // and continue attempting to connect in the background + pub block_async_connect: bool, + // require node to be synced, fail otherwise + pub require_sync: bool, +} + +impl Default for ConnectRequest { + fn default() -> Self { + Self { + url: None, + network_id: NetworkId::new(NetworkType::Mainnet), + retry_on_error: true, + block_async_connect: true, + require_sync: true, + } + } +} + +impl ConnectRequest { + pub fn with_url(mut self, url: Option) -> Self { + self.url = url; + self + } + + pub fn with_network_id(mut self, network_id: &NetworkId) -> Self { + self.network_id = *network_id; + self + } + + pub fn with_retry_on_error(mut self, retry_on_error: bool) -> Self { + self.retry_on_error = retry_on_error; + self + } + + pub fn with_block_async_connect(mut self, block_async_connect: bool) -> Self { + self.block_async_connect = block_async_connect; + self + } + + pub fn with_require_sync(mut self, require_sync: bool) -> Self { + self.require_sync = require_sync; + self + } } #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] diff --git a/wallet/core/src/api/traits.rs b/wallet/core/src/api/traits.rs index 2eb12907a1..08ebd65f67 100644 --- a/wallet/core/src/api/traits.rs +++ b/wallet/core/src/api/traits.rs @@ -42,8 +42,12 @@ pub trait WalletApi: Send + Sync + AnySync { /// - `is_wrpc_client` - whether the wallet is connected to a node via wRPC async fn get_status_call(self: Arc, request: GetStatusRequest) -> Result; - async fn connect(self: Arc, url: Option, network_id: NetworkId) -> Result<()> { - self.connect_call(ConnectRequest { url, network_id }).await?; + /// Synchronous connect call (blocking, single attempt, requires sync). + async fn connect(self: Arc, url: Option, network_id: &NetworkId) -> Result<()> { + let retry_on_error = false; + let block_async_connect = true; + let require_sync = true; + self.connect_call(ConnectRequest { url, network_id: *network_id, retry_on_error, block_async_connect, require_sync }).await?; Ok(()) } diff --git a/wallet/core/src/error.rs b/wallet/core/src/error.rs index a89b1dcf00..a70e87f9d7 100644 --- a/wallet/core/src/error.rs +++ b/wallet/core/src/error.rs @@ -13,6 +13,7 @@ use std::sync::PoisonError; use thiserror::Error; use wasm_bindgen::JsValue; use workflow_core::abortable::Aborted; +use workflow_core::channel::{RecvError, SendError, TrySendError}; use workflow_core::sendable::*; use workflow_rpc::client::error::Error as RpcError; use workflow_wasm::jserror::*; @@ -326,6 +327,9 @@ pub enum Error { #[error(transparent)] Metrics(#[from] kaspa_metrics_core::error::Error), + + #[error("Connected node is not synced")] + NotSynced, } impl From for Error { @@ -409,8 +413,20 @@ impl From> for Error { } } -impl From> for Error { - fn from(e: workflow_core::channel::SendError) -> Self { +impl From> for Error { + fn from(e: SendError) -> Self { + Error::Custom(e.to_string()) + } +} + +impl From for Error { + fn from(e: RecvError) -> Self { + Error::Custom(e.to_string()) + } +} + +impl From> for Error { + fn from(e: TrySendError) -> Self { Error::Custom(e.to_string()) } } diff --git a/wallet/core/src/prelude.rs b/wallet/core/src/prelude.rs index eb9bb2b1e9..f622a53654 100644 --- a/wallet/core/src/prelude.rs +++ b/wallet/core/src/prelude.rs @@ -14,6 +14,10 @@ pub use crate::rpc::{ConnectOptions, ConnectStrategy, DynRpcApi}; pub use crate::settings::WalletSettings; pub use crate::storage::{IdT, Interface, PrvKeyDataId, PrvKeyDataInfo, TransactionId, TransactionRecord, WalletDescriptor}; pub use crate::tx::{Fees, PaymentDestination, PaymentOutput, PaymentOutputs}; +pub use crate::utils::{ + kaspa_suffix, kaspa_to_sompi, sompi_to_kaspa, sompi_to_kaspa_string, sompi_to_kaspa_string_with_suffix, try_kaspa_str_to_sompi, + try_kaspa_str_to_sompi_i64, +}; pub use crate::utxo::balance::{Balance, BalanceStrings}; pub use crate::wallet::args::*; pub use crate::wallet::Wallet; diff --git a/wallet/core/src/utxo/processor.rs b/wallet/core/src/utxo/processor.rs index e788272f23..a27603c465 100644 --- a/wallet/core/src/utxo/processor.rs +++ b/wallet/core/src/utxo/processor.rs @@ -20,7 +20,7 @@ use kaspa_rpc_core::{ GetServerInfoResponse, }; use kaspa_wrpc_client::KaspaRpcClient; -use workflow_core::channel::{Channel, DuplexChannel}; +use workflow_core::channel::{Channel, DuplexChannel, Sender}; use workflow_core::task::spawn; use crate::events::Events; @@ -62,6 +62,7 @@ pub struct Inner { connect_disconnect_guard: AsyncMutex<()>, metrics: Arc, metrics_kinds: Mutex>, + connection_signaler: Mutex>>>, } impl Inner { @@ -91,6 +92,7 @@ impl Inner { connect_disconnect_guard: Default::default(), metrics: Arc::new(Metrics::default()), metrics_kinds: Mutex::new(vec![]), + connection_signaler: Mutex::new(None), } } } @@ -487,12 +489,30 @@ impl UtxoProcessor { Ok(()) } + /// Allows use to supply a channel Sender that will + /// receive the result of the wRPC connection attempt. + pub fn set_connection_signaler(&self, signal: Sender>) { + *self.inner.connection_signaler.lock().unwrap() = Some(signal); + } + + fn signal_connection(&self, result: std::result::Result<(), String>) -> bool { + let signal = self.inner.connection_signaler.lock().unwrap().take(); + if let Some(signal) = signal.as_ref() { + let _ = signal.try_send(result); + true + } else { + false + } + } + pub async fn handle_connect(&self) -> Result<()> { let _ = self.inner.connect_disconnect_guard.lock().await; match self.handle_connect_impl().await { Err(err) => { - log_error!("UtxoProcessor: error while connecting to node: {err}"); + if !self.signal_connection(Err(err.to_string())) { + log_error!("UtxoProcessor: error while connecting to node: {err}"); + } self.notify(Events::UtxoProcError { message: err.to_string() }).await?; if let Some(client) = self.rpc_client() { // try force disconnect the client if we have failed @@ -501,7 +521,10 @@ impl UtxoProcessor { } Err(err) } - Ok(_) => Ok(()), + Ok(_) => { + self.signal_connection(Ok(())); + Ok(()) + } } } @@ -631,15 +654,11 @@ impl UtxoProcessor { // handle RPC channel connection and disconnection events match msg { RpcState::Connected => { - if !this.is_connected() { - if let Err(err) = this.handle_connect().await { - log_error!("UtxoProcessor error: {err}"); - } else { - this.inner.multiplexer.try_broadcast(Box::new(Events::Connect { - network_id : this.network_id().expect("network id expected during connection"), - url : this.rpc_url() - })).unwrap_or_else(|err| log_error!("{err}")); - } + if !this.is_connected() && this.handle_connect().await.is_ok() { + this.inner.multiplexer.try_broadcast(Box::new(Events::Connect { + network_id : this.network_id().expect("network id expected during connection"), + url : this.rpc_url() + })).unwrap_or_else(|err| log_error!("{err}")); } }, RpcState::Disconnected => { diff --git a/wallet/core/src/wallet/api.rs b/wallet/core/src/wallet/api.rs index 313759ba72..c8e639def7 100644 --- a/wallet/core/src/wallet/api.rs +++ b/wallet/core/src/wallet/api.rs @@ -75,22 +75,37 @@ impl WalletApi for super::Wallet { async fn connect_call(self: Arc, request: ConnectRequest) -> Result { use workflow_rpc::client::{ConnectOptions, ConnectStrategy}; - let ConnectRequest { url, network_id } = request; + let ConnectRequest { url, network_id, retry_on_error, block_async_connect, require_sync } = request; if let Some(wrpc_client) = self.try_wrpc_client().as_ref() { - // self.set_network_id(network_id)?; + let strategy = if retry_on_error { ConnectStrategy::Retry } else { ConnectStrategy::Fallback }; - // let network_type = NetworkType::from(network_id); let url = url .map(|url| wrpc_client.parse_url_with_network_type(url, network_id.into()).map_err(|e| e.to_string())) .transpose()?; - let options = ConnectOptions { block_async_connect: false, strategy: ConnectStrategy::Retry, url, ..Default::default() }; + let options = ConnectOptions { block_async_connect, strategy, url, ..Default::default() }; wrpc_client.disconnect().await?; self.set_network_id(&network_id)?; + let processor = self.utxo_processor().clone(); + let (sender, receiver) = oneshot(); + + // set connection signaler that gets triggered + // by utxo processor when connection occurs + processor.set_connection_signaler(sender); + + // connect rpc wrpc_client.connect(Some(options)).await.map_err(|e| e.to_string())?; - Ok(ConnectResponse {}) + + // wait for connection signal, cascade if error + receiver.recv().await?.map_err(Error::custom)?; + + if require_sync && !self.is_synced() { + Err(Error::NotSynced) + } else { + Ok(ConnectResponse {}) + } } else { Err(Error::NotWrpcClient) } diff --git a/wallet/core/src/wasm/api/message.rs b/wallet/core/src/wasm/api/message.rs index 33bf5e00dd..801032eeb8 100644 --- a/wallet/core/src/wasm/api/message.rs +++ b/wallet/core/src/wasm/api/message.rs @@ -153,8 +153,16 @@ declare! { * @category Wallet API */ export interface IConnectRequest { - url : string; + // destination wRPC node URL (if omitted, the resolver is used) + url? : string; + // network identifier networkId : NetworkId | string; + // retry on error + retryOnError? : boolean; + // block async connect (method will not return until the connection is established) + block? : boolean; + // require node to be synced (fail otherwise) + requireSync? : boolean; } "#, } @@ -162,7 +170,10 @@ declare! { try_from! ( args: IConnectRequest, ConnectRequest, { let url = args.try_get_string("url")?; let network_id = args.get_network_id("networkId")?; - Ok(ConnectRequest { url, network_id }) + let retry_on_error = args.try_get_bool("retryOnError")?.unwrap_or(true); + let block_async_connect = args.try_get_bool("block")?.unwrap_or(false); + let require_sync = args.try_get_bool("requireSync")?.unwrap_or(true); + Ok(ConnectRequest { url, network_id, retry_on_error, block_async_connect, require_sync }) }); declare! { From 8c718a649b80604fa05c97907cf13dc8d705f448 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 21 Jun 2024 02:05:33 +0300 Subject: [PATCH 030/158] Improve ConnectRequest builder interface --- Cargo.toml | 24 ++++++++++++------------ wallet/core/src/api/message.rs | 25 ++++++++++--------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 82b751afc1..5a7a330108 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,18 +274,18 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.13.2" } -workflow-d3 = { version = "0.13.2" } -workflow-dom = { version = "0.13.2" } -workflow-http = { version = "0.13.2" } -workflow-log = { version = "0.13.2" } -workflow-node = { version = "0.13.2" } -workflow-nw = { version = "0.13.2" } -workflow-rpc = { version = "0.13.2" } -workflow-serializer = { version = "0.13.2" } -workflow-store = { version = "0.13.2" } -workflow-terminal = { version = "0.13.2" } -workflow-wasm = { version = "0.13.2" } +workflow-core = { version = "0.13.3" } +workflow-d3 = { version = "0.13.3" } +workflow-dom = { version = "0.13.3" } +workflow-http = { version = "0.13.3" } +workflow-log = { version = "0.13.3" } +workflow-node = { version = "0.13.3" } +workflow-nw = { version = "0.13.3" } +workflow-rpc = { version = "0.13.3" } +workflow-serializer = { version = "0.13.3" } +workflow-store = { version = "0.13.3" } +workflow-terminal = { version = "0.13.3" } +workflow-wasm = { version = "0.13.3" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs diff --git a/wallet/core/src/api/message.rs b/wallet/core/src/api/message.rs index 9eebba29f8..3b96abd1a5 100644 --- a/wallet/core/src/api/message.rs +++ b/wallet/core/src/api/message.rs @@ -66,29 +66,24 @@ impl Default for ConnectRequest { } impl ConnectRequest { - pub fn with_url(mut self, url: Option) -> Self { - self.url = url; - self + pub fn with_url(self, url: Option) -> Self { + ConnectRequest { url, ..self } } - pub fn with_network_id(mut self, network_id: &NetworkId) -> Self { - self.network_id = *network_id; - self + pub fn with_network_id(self, network_id: &NetworkId) -> Self { + ConnectRequest { network_id: *network_id, ..self } } - pub fn with_retry_on_error(mut self, retry_on_error: bool) -> Self { - self.retry_on_error = retry_on_error; - self + pub fn with_retry_on_error(self, retry_on_error: bool) -> Self { + ConnectRequest { retry_on_error, ..self } } - pub fn with_block_async_connect(mut self, block_async_connect: bool) -> Self { - self.block_async_connect = block_async_connect; - self + pub fn with_block_async_connect(self, block_async_connect: bool) -> Self { + ConnectRequest { block_async_connect, ..self } } - pub fn with_require_sync(mut self, require_sync: bool) -> Self { - self.require_sync = require_sync; - self + pub fn with_require_sync(self, require_sync: bool) -> Self { + ConnectRequest { require_sync, ..self } } } From 56972d002f06d7078c350203f6eb6bf83019dc93 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 21 Jun 2024 10:59:23 +0300 Subject: [PATCH 031/158] Allow UtxoEntryReference to be re-integrated from a flat object structure. --- consensus/client/src/utxo.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/consensus/client/src/utxo.rs b/consensus/client/src/utxo.rs index ffa7f7a49c..8c248a6d35 100644 --- a/consensus/client/src/utxo.rs +++ b/consensus/client/src/utxo.rs @@ -380,13 +380,34 @@ impl TryCastFromJs for UtxoEntryReference { let address = object.get_cast::
("address")?.into_owned(); let outpoint = TransactionOutpoint::try_from(object.get_value("outpoint")?.as_ref())?; let utxo_entry = Object::from(object.get_value("utxoEntry")?); - let amount = utxo_entry.get_u64("amount")?; - let script_public_key = ScriptPublicKey::try_owned_from(utxo_entry.get_value("scriptPublicKey")?)?; - let block_daa_score = utxo_entry.get_u64("blockDaaScore")?; - let is_coinbase = utxo_entry.get_bool("isCoinbase")?; - let utxo_entry = - UtxoEntry { address: Some(address), outpoint, amount, script_public_key, block_daa_score, is_coinbase }; + let utxo_entry = if !utxo_entry.is_undefined() { + let amount = utxo_entry.get_u64("amount").map_err(|_| { + Error::custom("Supplied object does not contain `utxoEntry.amount` property (or it is not a numerical value)") + })?; + let script_public_key = ScriptPublicKey::try_owned_from(utxo_entry.get_value("scriptPublicKey")?) + .map_err(|_|Error::custom("Supplied object does not contain `utxoEntry.scriptPublicKey` property (or it is not a hex string or a ScriptPublicKey class)"))?; + let block_daa_score = utxo_entry.get_u64("blockDaaScore").map_err(|_| { + Error::custom( + "Supplied object does not contain `utxoEntry.blockDaaScore` property (or it is not a numerical value)", + ) + })?; + let is_coinbase = utxo_entry.get_bool("isCoinbase")?; + + UtxoEntry { address: Some(address), outpoint, amount, script_public_key, block_daa_score, is_coinbase } + } else { + let amount = object.get_u64("amount").map_err(|_| { + Error::custom("Supplied object does not contain `amount` property (or it is not a numerical value)") + })?; + let script_public_key = ScriptPublicKey::try_owned_from(object.get_value("scriptPublicKey")?) + .map_err(|_|Error::custom("Supplied object does not contain `scriptPublicKey` property (or it is not a hex string or a ScriptPublicKey class)"))?; + let block_daa_score = object.get_u64("blockDaaScore").map_err(|_| { + Error::custom("Supplied object does not contain `blockDaaScore` property (or it is not a numerical value)") + })?; + let is_coinbase = object.try_get_bool("isCoinbase")?.unwrap_or(false); + + UtxoEntry { address: Some(address), outpoint, amount, script_public_key, block_daa_score, is_coinbase } + }; Ok(UtxoEntryReference::from(utxo_entry)) } else { From 339421f05ac0fef51a6d86dbef9807616e40b572 Mon Sep 17 00:00:00 2001 From: surinder singh Date: Sat, 22 Jun 2024 06:10:07 +0530 Subject: [PATCH 032/158] indexdb data maintenance: timestamp fix (#53) * range support for indexdb store load_range function * indexdb data maintenance: timestamp fix * removing unused * Fix incorrect balance when sending to self --- .../src/storage/local/transaction/indexdb.rs | 85 ++++++++++++++++--- wallet/core/src/utxo/context.rs | 8 +- 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/wallet/core/src/storage/local/transaction/indexdb.rs b/wallet/core/src/storage/local/transaction/indexdb.rs index 16f89a0906..ed9f17bae6 100644 --- a/wallet/core/src/storage/local/transaction/indexdb.rs +++ b/wallet/core/src/storage/local/transaction/indexdb.rs @@ -26,12 +26,13 @@ pub struct Inner { impl Inner { async fn open_db(&self, db_name: String) -> Result { call_async_no_send!(async move { - let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&db_name, 1) + let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&db_name, 2) .map_err(|err| Error::Custom(format!("Failed to open indexdb database {:?}", err)))?; - - fn on_upgrade_needed(evt: &IdbVersionChangeEvent) -> Result<(), JsValue> { - // Check if the object store exists; create it if it doesn't - if !evt.db().object_store_names().any(|n| n == TRANSACTIONS_STORE_NAME) { + let fix_timestamp = Arc::new(Mutex::new(false)); + let fix_timestamp_clone = fix_timestamp.clone(); + let on_upgrade_needed = move |evt: &IdbVersionChangeEvent| -> Result<(), JsValue> { + let old_version = evt.old_version(); + if old_version < 1.0 { let object_store = evt.db().create_object_store(TRANSACTIONS_STORE_NAME)?; object_store.create_index_with_params( TRANSACTIONS_STORE_ID_INDEX, @@ -48,13 +49,79 @@ impl Inner { &IdbKeyPath::str(TRANSACTIONS_STORE_DATA_INDEX), IdbIndexParameters::new().unique(false), )?; + + // these changes are not required for new db + } else if old_version < 2.0 { + *fix_timestamp_clone.lock().unwrap() = true; } + // // Check if the object store exists; create it if it doesn't + // if !evt.db().object_store_names().any(|n| n == TRANSACTIONS_STORE_NAME) { + + // } Ok(()) - } + }; db_req.set_on_upgrade_needed(Some(on_upgrade_needed)); - db_req.await.map_err(|err| Error::Custom(format!("Open database request failed for indexdb database {:?}", err))) + let db = + db_req.await.map_err(|err| Error::Custom(format!("Open database request failed for indexdb database {:?}", err)))?; + + if *fix_timestamp.lock().unwrap() { + log_info!("DEBUG: fixing timestamp"); + let idb_tx = db + .transaction_on_one_with_mode(TRANSACTIONS_STORE_NAME, IdbTransactionMode::Readwrite) + .map_err(|err| Error::Custom(format!("Failed to open indexdb transaction for reading {:?}", err)))?; + let store = idb_tx + .object_store(TRANSACTIONS_STORE_NAME) + .map_err(|err| Error::Custom(format!("Failed to open indexdb object store for reading {:?}", err)))?; + let binding = store + .index(TRANSACTIONS_STORE_TIMESTAMP_INDEX) + .map_err(|err| Error::Custom(format!("Failed to open indexdb indexed store cursor {:?}", err)))?; + let cursor = binding + .open_cursor_with_range_and_direction(&JsValue::NULL, web_sys::IdbCursorDirection::Prev) + .map_err(|err| Error::Custom(format!("Failed to open indexdb store cursor for reading {:?}", err)))?; + let cursor = cursor.await.map_err(|err| Error::Custom(format!("Failed to open indexdb store cursor {:?}", err)))?; + + // let next_year_date = Date::new_0(); + // next_year_date.set_full_year(next_year_date.get_full_year() + 1); + // let next_year_ts = next_year_date.get_time(); + + if let Some(cursor) = cursor { + loop { + let js_value = cursor.value(); + if let Ok(record) = transaction_record_from_js_value(&js_value, None) { + if record.unixtime_msec.is_some() { + let new_js_value = transaction_record_to_js_value(&record, None, ENCRYPTION_KIND)?; + + //log_info!("DEBUG: new_js_value: {:?}", new_js_value); + + cursor + .update(&new_js_value) + .map_err(|err| Error::Custom(format!("Failed to update record timestamp {:?}", err)))? + .await + .map_err(|err| Error::Custom(format!("Failed to update record timestamp {:?}", err)))?; + } + } + if let Ok(b) = cursor.continue_cursor() { + match b.await { + Ok(b) => { + if !b { + break; + } + } + Err(err) => { + log_info!("DEBUG IDB: Loading transaction error, cursor.continue_cursor() {:?}", err); + break; + } + } + } else { + break; + } + } + } + } + + Ok(db) }) } } @@ -510,8 +577,6 @@ fn transaction_record_to_js_value( ) -> Result { let id = transaction_record.id.to_string(); let unixtime_msec = transaction_record.unixtime_msec; - let mut borsh_data = vec![]; - ::serialize(transaction_record, &mut borsh_data)?; let id_js_value = JsValue::from_str(&id); let timestamp_js_value = match unixtime_msec { @@ -555,6 +620,6 @@ fn transaction_record_from_js_value(js_value: &JsValue, secret: Option<&Secret>) Ok(transaction_record.0) } else { - Err(Error::Custom("supplied argument must be an object".to_string())) + Err(Error::Custom("supplied argument must be an object, found ({js_value:?})".to_string())) } } diff --git a/wallet/core/src/utxo/context.rs b/wallet/core/src/utxo/context.rs index 51ef0e5ea9..3a74140379 100644 --- a/wallet/core/src/utxo/context.rs +++ b/wallet/core/src/utxo/context.rs @@ -564,21 +564,19 @@ impl UtxoContext { Ok(()) } - pub(crate) async fn handle_utxo_removed(&self, mut utxos: Vec, current_daa_score: u64) -> Result<()> { + pub(crate) async fn handle_utxo_removed(&self, utxos: Vec, current_daa_score: u64) -> Result<()> { // remove UTXOs from account set let outgoing_transactions = self.processor().outgoing(); let mut accepted_outgoing_transactions = HashSet::::new(); - utxos.retain(|utxo| { + for utxo in &utxos { for outgoing_transaction in outgoing_transactions.iter() { if outgoing_transaction.utxo_entries().contains_key(&utxo.id()) { accepted_outgoing_transactions.insert((*outgoing_transaction).clone()); - return false; } } - true - }); + } for accepted_outgoing_transaction in accepted_outgoing_transactions.into_iter() { if accepted_outgoing_transaction.is_batch() { From 08636667731442a531d2514796353eea39a07ae0 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 22 Jun 2024 03:56:05 +0300 Subject: [PATCH 033/158] fix child_number typo --- wallet/keys/src/xprv.rs | 6 +++--- wallet/keys/src/xpub.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wallet/keys/src/xprv.rs b/wallet/keys/src/xprv.rs index bc4b681cf8..e431a5ee34 100644 --- a/wallet/keys/src/xprv.rs +++ b/wallet/keys/src/xprv.rs @@ -41,9 +41,9 @@ impl XPrv { } #[wasm_bindgen(js_name=deriveChild)] - pub fn derive_child(&self, chile_number: u32, hardened: Option) -> Result { - let chile_number = ChildNumber::new(chile_number, hardened.unwrap_or(false))?; - let inner = self.inner.derive_child(chile_number)?; + pub fn derive_child(&self, child_number: u32, hardened: Option) -> Result { + let child_number = ChildNumber::new(child_number, hardened.unwrap_or(false))?; + let inner = self.inner.derive_child(child_number)?; Ok(Self { inner }) } diff --git a/wallet/keys/src/xpub.rs b/wallet/keys/src/xpub.rs index ded247edf0..0ef461f63e 100644 --- a/wallet/keys/src/xpub.rs +++ b/wallet/keys/src/xpub.rs @@ -32,9 +32,9 @@ impl XPub { } #[wasm_bindgen(js_name=deriveChild)] - pub fn derive_child(&self, chile_number: u32, hardened: Option) -> Result { - let chile_number = ChildNumber::new(chile_number, hardened.unwrap_or(false))?; - let inner = self.inner.derive_child(chile_number)?; + pub fn derive_child(&self, child_number: u32, hardened: Option) -> Result { + let child_number = ChildNumber::new(child_number, hardened.unwrap_or(false))?; + let inner = self.inner.derive_child(child_number)?; Ok(Self { inner }) } From c178580a3232221f7731ad6cf9b371aacde5f568 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 23 Jun 2024 08:42:41 +0300 Subject: [PATCH 034/158] allow setting custom transaction maturity periods --- Cargo.lock | 1 + Cargo.toml | 1 + wallet/core/Cargo.toml | 1 + wallet/core/src/storage/transaction/record.rs | 8 +- wallet/core/src/tx/generator/generator.rs | 8 +- wallet/core/src/tx/generator/test.rs | 4 +- wallet/core/src/tx/mass.rs | 2 +- wallet/core/src/utxo/processor.rs | 9 +- wallet/core/src/utxo/reference.rs | 6 +- wallet/core/src/utxo/scan.rs | 4 +- wallet/core/src/utxo/settings.rs | 191 ++++++++++++------ wallet/core/src/wasm/utxo/processor.rs | 34 ++++ 12 files changed, 188 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22cd64f4f9..1d1852978a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3510,6 +3510,7 @@ dependencies = [ "kaspa-wasm-core", "kaspa-wrpc-client", "kaspa-wrpc-wasm", + "lazy_static", "md-5", "pad", "pbkdf2", diff --git a/Cargo.toml b/Cargo.toml index d96b33f175..5abf389084 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -192,6 +192,7 @@ ipnet = "2.9.0" itertools = "0.11.0" js-sys = "0.3.67" keccak = "0.1.4" +lazy_static = "1.4.0" local-ip-address = "0.5.6" log = "0.4.20" log4rs = "1.2.0" diff --git a/wallet/core/Cargo.toml b/wallet/core/Cargo.toml index 9a6c4cfa6a..1e528e3692 100644 --- a/wallet/core/Cargo.toml +++ b/wallet/core/Cargo.toml @@ -74,6 +74,7 @@ kaspa-wallet-macros.workspace = true kaspa-wasm-core.workspace = true kaspa-wrpc-client.workspace = true kaspa-wrpc-wasm.workspace = true +lazy_static.workspace = true md-5.workspace = true pad.workspace = true pbkdf2.workspace = true diff --git a/wallet/core/src/storage/transaction/record.rs b/wallet/core/src/storage/transaction/record.rs index fc6613361d..81fd6ae0f1 100644 --- a/wallet/core/src/storage/transaction/record.rs +++ b/wallet/core/src/storage/transaction/record.rs @@ -381,9 +381,9 @@ impl TransactionRecord { let params = NetworkParams::from(self.network_id); let maturity = if self.is_coinbase() { - params.coinbase_transaction_maturity_period_daa + params.coinbase_transaction_maturity_period_daa() } else { - params.user_transaction_maturity_period_daa + params.user_transaction_maturity_period_daa() }; if current_daa_score < self.block_daa_score() + maturity { @@ -434,9 +434,9 @@ impl TransactionRecord { pub fn maturity_progress(&self, current_daa_score: u64) -> Option { let params = NetworkParams::from(self.network_id); let maturity = if self.is_coinbase() { - params.coinbase_transaction_maturity_period_daa + params.coinbase_transaction_maturity_period_daa() } else { - params.user_transaction_maturity_period_daa + params.user_transaction_maturity_period_daa() }; if current_daa_score < self.block_daa_score + maturity { diff --git a/wallet/core/src/tx/generator/generator.rs b/wallet/core/src/tx/generator/generator.rs index 533f2e0464..f56af9ffdf 100644 --- a/wallet/core/src/tx/generator/generator.rs +++ b/wallet/core/src/tx/generator/generator.rs @@ -640,7 +640,7 @@ impl Generator { if data.aggregate_mass + input_compute_mass + self.inner.standard_change_output_compute_mass - + self.inner.network_params.additional_compound_transaction_mass + + self.inner.network_params.additional_compound_transaction_mass() > MAXIMUM_STANDARD_TRANSACTION_MASS { // note, we've used input for mass boundary calc and now abandon it @@ -648,7 +648,7 @@ impl Generator { context.utxo_stash.push_back(utxo_entry_reference); data.aggregate_mass += - self.inner.standard_change_output_compute_mass + self.inner.network_params.additional_compound_transaction_mass; + self.inner.standard_change_output_compute_mass + self.inner.network_params.additional_compound_transaction_mass(); data.transaction_fees = self.calc_relay_transaction_compute_fees(data); stage.aggregate_fees += data.transaction_fees; context.aggregate_fees += data.transaction_fees; @@ -837,7 +837,7 @@ impl Generator { let storage_mass_with_change = self.calc_storage_mass(data, output_harmonic_with_change); if storage_mass_with_change == 0 - || (self.inner.network_params.mass_combination_strategy == MassCombinationStrategy::Max + || (self.inner.network_params.mass_combination_strategy() == MassCombinationStrategy::Max && storage_mass_with_change < compute_mass_with_change) { 0 @@ -878,7 +878,7 @@ impl Generator { let compute_mass = data.aggregate_mass + self.inner.standard_change_output_compute_mass - + self.inner.network_params.additional_compound_transaction_mass; + + self.inner.network_params.additional_compound_transaction_mass(); let compute_fees = calc.calc_minimum_transaction_fee_from_mass(compute_mass); // TODO - consider removing this as calculated storage mass should produce `0` value diff --git a/wallet/core/src/tx/generator/test.rs b/wallet/core/src/tx/generator/test.rs index 1368c51f2b..4cce76ea11 100644 --- a/wallet/core/src/tx/generator/test.rs +++ b/wallet/core/src/tx/generator/test.rs @@ -169,7 +169,7 @@ fn validate(pt: &PendingTransaction) { ); let calc = MassCalculator::new(&pt.network_type().into(), network_params); - let additional_mass = if pt.is_final() { 0 } else { network_params.additional_compound_transaction_mass }; + let additional_mass = if pt.is_final() { 0 } else { network_params.additional_compound_transaction_mass() }; let compute_mass = calc.calc_mass_for_signed_transaction(&tx, 1); let utxo_entries = pt.utxo_entries().values().cloned().collect::>(); @@ -199,7 +199,7 @@ where let pt_fees = pt.fees(); let calc = MassCalculator::new(&pt.network_type().into(), network_params); - let additional_mass = if pt.is_final() { 0 } else { network_params.additional_compound_transaction_mass }; + let additional_mass = if pt.is_final() { 0 } else { network_params.additional_compound_transaction_mass() }; let compute_mass = calc.calc_mass_for_signed_transaction(&tx, 1); diff --git a/wallet/core/src/tx/mass.rs b/wallet/core/src/tx/mass.rs index b5583ddf34..c76d109050 100644 --- a/wallet/core/src/tx/mass.rs +++ b/wallet/core/src/tx/mass.rs @@ -232,7 +232,7 @@ impl MassCalculator { mass_per_script_pub_key_byte: consensus_params.mass_per_script_pub_key_byte, mass_per_sig_op: consensus_params.mass_per_sig_op, storage_mass_parameter: consensus_params.storage_mass_parameter, - mass_combination_strategy: network_params.mass_combination_strategy, + mass_combination_strategy: network_params.mass_combination_strategy(), } } diff --git a/wallet/core/src/utxo/processor.rs b/wallet/core/src/utxo/processor.rs index e788272f23..9e5acc926b 100644 --- a/wallet/core/src/utxo/processor.rs +++ b/wallet/core/src/utxo/processor.rs @@ -179,7 +179,8 @@ impl UtxoProcessor { (*self.inner.network_id.lock().unwrap()).ok_or(Error::MissingNetworkId) } - pub fn network_params(&self) -> Result<&'static NetworkParams> { + // pub fn network_params(&self) -> Result<&'static NetworkParams> { + pub fn network_params(&self) -> Result { let network_id = (*self.inner.network_id.lock().unwrap()).ok_or(Error::MissingNetworkId)?; Ok(network_id.into()) } @@ -271,7 +272,7 @@ impl UtxoProcessor { // scan and remove any pending entries that gained maturity let mut mature_entries = vec![]; let pending_entries = &self.inner.pending; - pending_entries.retain(|_, pending_entry| match pending_entry.maturity(params, current_daa_score) { + pending_entries.retain(|_, pending_entry| match pending_entry.maturity(¶ms, current_daa_score) { Maturity::Confirmed => { mature_entries.push(pending_entry.clone()); false @@ -284,7 +285,7 @@ impl UtxoProcessor { let mut revived_entries = vec![]; let stasis_entries = &self.inner.stasis; stasis_entries.retain(|_, stasis_entry| { - match stasis_entry.maturity(params, current_daa_score) { + match stasis_entry.maturity(¶ms, current_daa_score) { Maturity::Confirmed => { mature_entries.push(stasis_entry.clone()); false @@ -329,7 +330,7 @@ impl UtxoProcessor { } async fn handle_outgoing(&self, current_daa_score: u64) -> Result<()> { - let longevity = self.network_params()?.user_transaction_maturity_period_daa; + let longevity = self.network_params()?.user_transaction_maturity_period_daa(); self.inner.outgoing.retain(|_, outgoing| { if outgoing.acceptance_daa_score() != 0 && (outgoing.acceptance_daa_score() + longevity) < current_daa_score { diff --git a/wallet/core/src/utxo/reference.rs b/wallet/core/src/utxo/reference.rs index 5e35f9e8b7..7bc0ec287c 100644 --- a/wallet/core/src/utxo/reference.rs +++ b/wallet/core/src/utxo/reference.rs @@ -34,14 +34,14 @@ pub trait UtxoEntryReferenceExtension { impl UtxoEntryReferenceExtension for UtxoEntryReference { fn maturity(&self, params: &NetworkParams, current_daa_score: u64) -> Maturity { if self.is_coinbase() { - if self.block_daa_score() + params.coinbase_transaction_stasis_period_daa > current_daa_score { + if self.block_daa_score() + params.coinbase_transaction_stasis_period_daa() > current_daa_score { Maturity::Stasis - } else if self.block_daa_score() + params.coinbase_transaction_maturity_period_daa > current_daa_score { + } else if self.block_daa_score() + params.coinbase_transaction_maturity_period_daa() > current_daa_score { Maturity::Pending } else { Maturity::Confirmed } - } else if self.block_daa_score() + params.user_transaction_maturity_period_daa > current_daa_score { + } else if self.block_daa_score() + params.user_transaction_maturity_period_daa() > current_daa_score { Maturity::Pending } else { Maturity::Confirmed diff --git a/wallet/core/src/utxo/scan.rs b/wallet/core/src/utxo/scan.rs index f01257c965..fda8ea67cf 100644 --- a/wallet/core/src/utxo/scan.rs +++ b/wallet/core/src/utxo/scan.rs @@ -107,7 +107,7 @@ impl Scan { } let balance: Balance = refs.iter().fold(Balance::default(), |mut balance, r| { - let entry_balance = r.balance(params, self.current_daa_score); + let entry_balance = r.balance(¶ms, self.current_daa_score); balance.mature += entry_balance.mature; balance.pending += entry_balance.pending; balance.mature_utxo_count += entry_balance.mature_utxo_count; @@ -151,7 +151,7 @@ impl Scan { let refs: Vec = resp.into_iter().map(UtxoEntryReference::from).collect(); let balance: Balance = refs.iter().fold(Balance::default(), |mut balance, r| { - let entry_balance = r.balance(params, self.current_daa_score); + let entry_balance = r.balance(¶ms, self.current_daa_score); balance.mature += entry_balance.mature; balance.pending += entry_balance.pending; balance.mature_utxo_count += entry_balance.mature_utxo_count; diff --git a/wallet/core/src/utxo/settings.rs b/wallet/core/src/utxo/settings.rs index 3890263be0..caa8032eb4 100644 --- a/wallet/core/src/utxo/settings.rs +++ b/wallet/core/src/utxo/settings.rs @@ -6,82 +6,151 @@ use crate::imports::*; #[derive(Debug)] -pub struct NetworkParams { - pub coinbase_transaction_maturity_period_daa: u64, +pub struct Inner { + pub coinbase_transaction_maturity_period_daa: AtomicU64, pub coinbase_transaction_stasis_period_daa: u64, - pub user_transaction_maturity_period_daa: u64, + pub user_transaction_maturity_period_daa: AtomicU64, pub mass_combination_strategy: MassCombinationStrategy, pub additional_compound_transaction_mass: u64, } -pub const MAINNET_NETWORK_PARAMS: NetworkParams = NetworkParams { - coinbase_transaction_maturity_period_daa: 100, - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: 10, - mass_combination_strategy: MassCombinationStrategy::Add, - additional_compound_transaction_mass: 0, -}; - -pub const TESTNET10_NETWORK_PARAMS: NetworkParams = NetworkParams { - coinbase_transaction_maturity_period_daa: 100, - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: 10, - mass_combination_strategy: MassCombinationStrategy::Add, - additional_compound_transaction_mass: 100, -}; - -pub const TESTNET11_NETWORK_PARAMS: NetworkParams = NetworkParams { - coinbase_transaction_maturity_period_daa: 1_000, - coinbase_transaction_stasis_period_daa: 500, - user_transaction_maturity_period_daa: 100, - mass_combination_strategy: MassCombinationStrategy::Add, - additional_compound_transaction_mass: 100, -}; - -pub const DEVNET_NETWORK_PARAMS: NetworkParams = NetworkParams { - coinbase_transaction_maturity_period_daa: 100, - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: 10, - mass_combination_strategy: MassCombinationStrategy::Add, - additional_compound_transaction_mass: 0, -}; - -pub const SIMNET_NETWORK_PARAMS: NetworkParams = NetworkParams { - coinbase_transaction_maturity_period_daa: 100, - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: 10, - mass_combination_strategy: MassCombinationStrategy::Add, - additional_compound_transaction_mass: 0, -}; - -impl From for &'static NetworkParams { - fn from(value: NetworkId) -> Self { - match value.network_type { - NetworkType::Mainnet => &MAINNET_NETWORK_PARAMS, - NetworkType::Testnet => match value.suffix { - Some(10) => &TESTNET10_NETWORK_PARAMS, - Some(11) => &TESTNET11_NETWORK_PARAMS, - Some(x) => panic!("Testnet suffix {} is not supported", x), - None => panic!("Testnet suffix not provided"), - }, - NetworkType::Devnet => &DEVNET_NETWORK_PARAMS, - NetworkType::Simnet => &SIMNET_NETWORK_PARAMS, - } +#[derive(Debug, Clone)] +pub struct NetworkParams { + inner: Arc, +} + +impl NetworkParams { + #[inline] + pub fn coinbase_transaction_maturity_period_daa(&self) -> u64 { + self.inner.coinbase_transaction_maturity_period_daa.load(Ordering::Relaxed) + } + + #[inline] + pub fn coinbase_transaction_stasis_period_daa(&self) -> u64 { + self.inner.coinbase_transaction_stasis_period_daa + } + + #[inline] + pub fn user_transaction_maturity_period_daa(&self) -> u64 { + self.inner.user_transaction_maturity_period_daa.load(Ordering::Relaxed) } + + #[inline] + pub fn mass_combination_strategy(&self) -> MassCombinationStrategy { + self.inner.mass_combination_strategy + } + + #[inline] + pub fn additional_compound_transaction_mass(&self) -> u64 { + self.inner.additional_compound_transaction_mass + } + + pub fn set_coinbase_transaction_maturity_period_daa(&self, value: u64) { + self.inner.coinbase_transaction_maturity_period_daa.store(value, Ordering::Relaxed); + } + + pub fn set_user_transaction_maturity_period_daa(&self, value: u64) { + self.inner.user_transaction_maturity_period_daa.store(value, Ordering::Relaxed); + } +} + +lazy_static::lazy_static! { + pub static ref MAINNET_NETWORK_PARAMS: NetworkParams = NetworkParams { + inner: Arc::new(Inner { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + mass_combination_strategy: MassCombinationStrategy::Max, + additional_compound_transaction_mass: 0, + }), + }; +} + +lazy_static::lazy_static! { + pub static ref TESTNET10_NETWORK_PARAMS: NetworkParams = NetworkParams { + inner: Arc::new(Inner { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + mass_combination_strategy: MassCombinationStrategy::Max, + additional_compound_transaction_mass: 0, + }), + }; +} + +lazy_static::lazy_static! { + pub static ref TESTNET11_NETWORK_PARAMS: NetworkParams = NetworkParams { + inner: Arc::new(Inner { + coinbase_transaction_maturity_period_daa: AtomicU64::new(1_000), + coinbase_transaction_stasis_period_daa: 500, + user_transaction_maturity_period_daa: AtomicU64::new(100), + mass_combination_strategy: MassCombinationStrategy::Max, + additional_compound_transaction_mass: 100, + }), + }; +} + +lazy_static::lazy_static! { + pub static ref SIMNET_NETWORK_PARAMS: NetworkParams = NetworkParams { + inner: Arc::new(Inner { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + mass_combination_strategy: MassCombinationStrategy::Max, + additional_compound_transaction_mass: 0, + }), + }; +} + +lazy_static::lazy_static! { + pub static ref DEVNET_NETWORK_PARAMS: NetworkParams = NetworkParams { + inner: Arc::new(Inner { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + mass_combination_strategy: MassCombinationStrategy::Max, + additional_compound_transaction_mass: 0, + }), + }; } impl From for NetworkParams { fn from(value: NetworkId) -> Self { match value.network_type { - NetworkType::Mainnet => MAINNET_NETWORK_PARAMS, + NetworkType::Mainnet => MAINNET_NETWORK_PARAMS.clone(), NetworkType::Testnet => match value.suffix { - Some(10) => TESTNET10_NETWORK_PARAMS, - Some(11) => TESTNET11_NETWORK_PARAMS, + Some(10) => TESTNET10_NETWORK_PARAMS.clone(), + Some(11) => TESTNET11_NETWORK_PARAMS.clone(), Some(x) => panic!("Testnet suffix {} is not supported", x), None => panic!("Testnet suffix not provided"), }, - NetworkType::Devnet => DEVNET_NETWORK_PARAMS, - NetworkType::Simnet => SIMNET_NETWORK_PARAMS, + NetworkType::Devnet => DEVNET_NETWORK_PARAMS.clone(), + NetworkType::Simnet => SIMNET_NETWORK_PARAMS.clone(), } } } + +/// Set the coinbase transaction maturity period DAA score for a given network. +/// This controls the DAA period after which the user transactions are considered mature +/// and the wallet subsystem emits the transaction maturity event. +pub fn set_coinbase_transaction_maturity_period_daa(network_id: &NetworkId, value: u64) { + let network_params = NetworkParams::from(*network_id); + if value <= network_params.coinbase_transaction_stasis_period_daa() { + panic!( + "Coinbase transaction maturity period must be greater than the stasis period of {} DAA", + network_params.coinbase_transaction_stasis_period_daa() + ); + } + network_params.set_coinbase_transaction_maturity_period_daa(value); +} + +/// Set the user transaction maturity period DAA score for a given network. +/// This controls the DAA period after which the user transactions are considered mature +/// and the wallet subsystem emits the transaction maturity event. +pub fn set_user_transaction_maturity_period_daa(network_id: &NetworkId, value: u64) { + let network_params = NetworkParams::from(*network_id); + if value == 0 { + panic!("User transaction maturity period must be greater than 0"); + } + network_params.set_user_transaction_maturity_period_daa(value); +} diff --git a/wallet/core/src/wasm/utxo/processor.rs b/wallet/core/src/wasm/utxo/processor.rs index 2d35a4a9a8..9d33b43434 100644 --- a/wallet/core/src/wasm/utxo/processor.rs +++ b/wallet/core/src/wasm/utxo/processor.rs @@ -159,6 +159,40 @@ impl UtxoProcessor { let processor = &self.inner.processor; processor.try_rpc_ctl().map(|ctl| ctl.is_connected()).unwrap_or(false) && processor.is_connected() && processor.is_running() } + + /// + /// Set the coinbase transaction maturity period DAA score for a given network. + /// This controls the DAA period after which the user transactions are considered mature + /// and the wallet subsystem emits the transaction maturity event. + /// + /// @see {@link TransactionRecord} + /// @see {@link Events} + /// + /// @category Wallet SDK + /// + #[wasm_bindgen(js_name = "setCoinbaseTransactionMaturityDAA")] + pub fn set_coinbase_transaction_maturity_period_daa_js(network_id: NetworkIdT, value: u64) -> Result<()> { + let network_id = NetworkId::try_cast_from(network_id)?.into_owned(); + crate::utxo::set_coinbase_transaction_maturity_period_daa(&network_id, value); + Ok(()) + } + + /// + /// Set the user transaction maturity period DAA score for a given network. + /// This controls the DAA period after which the user transactions are considered mature + /// and the wallet subsystem emits the transaction maturity event. + /// + /// @see {@link TransactionRecord} + /// @see {@link Events} + /// + /// @category Wallet SDK + /// + #[wasm_bindgen(js_name = "setUserTransactionMaturityDAA")] + pub fn set_user_transaction_maturity_period_daa_js(network_id: NetworkIdT, value: u64) -> Result<()> { + let network_id = NetworkId::try_cast_from(network_id)?.into_owned(); + crate::utxo::set_user_transaction_maturity_period_daa(&network_id, value); + Ok(()) + } } impl TryCastFromJs for UtxoProcessor { From 381bacd31e5a6bdbcc89495acb572b39c65a66cc Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 23 Jun 2024 08:43:14 +0300 Subject: [PATCH 035/158] fix missing renaming for record.value --- wallet/core/src/storage/transaction/record.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/core/src/storage/transaction/record.rs b/wallet/core/src/storage/transaction/record.rs index 81fd6ae0f1..1afd463618 100644 --- a/wallet/core/src/storage/transaction/record.rs +++ b/wallet/core/src/storage/transaction/record.rs @@ -787,7 +787,7 @@ impl TransactionRecord { #[wasm_bindgen] impl TransactionRecord { - #[wasm_bindgen(getter)] + #[wasm_bindgen(getter, js_name = "value")] pub fn value_as_js_bigint(&self) -> BigInt { self.value.into() } From bd197da7b9439c67d44def5b71ddf46427ac1c39 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 23 Jun 2024 10:20:09 +0300 Subject: [PATCH 036/158] fix typedoc references --- wallet/core/src/wasm/utxo/processor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wallet/core/src/wasm/utxo/processor.rs b/wallet/core/src/wasm/utxo/processor.rs index 9d33b43434..09296a2a6f 100644 --- a/wallet/core/src/wasm/utxo/processor.rs +++ b/wallet/core/src/wasm/utxo/processor.rs @@ -166,7 +166,7 @@ impl UtxoProcessor { /// and the wallet subsystem emits the transaction maturity event. /// /// @see {@link TransactionRecord} - /// @see {@link Events} + /// @see {@link IUtxoProcessorEvent} /// /// @category Wallet SDK /// @@ -183,7 +183,7 @@ impl UtxoProcessor { /// and the wallet subsystem emits the transaction maturity event. /// /// @see {@link TransactionRecord} - /// @see {@link Events} + /// @see {@link IUtxoProcessorEvent} /// /// @category Wallet SDK /// From 67d80461deb29233c305e37d41af3be90f1c94a8 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 23 Jun 2024 18:11:53 +0300 Subject: [PATCH 037/158] pre-borsh-1-5-1 compat --- Cargo.lock | 386 +++++++++++++++++++++++++++++++++++++---------------- Cargo.toml | 34 +++-- 2 files changed, 291 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d1852978a..6289f02c21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -695,10 +695,20 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" dependencies = [ - "borsh-derive", + "borsh-derive 0.9.3", "hashbrown 0.11.2", ] +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive 1.5.1", + "cfg_aliases 0.2.1", +] + [[package]] name = "borsh-derive" version = "0.9.3" @@ -707,11 +717,25 @@ checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", - "proc-macro-crate", + "proc-macro-crate 0.1.5", "proc-macro2", "syn 1.0.109", ] +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.60", + "syn_derive", +] + [[package]] name = "borsh-derive-internal" version = "0.9.3" @@ -858,6 +882,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chacha20" version = "0.9.1" @@ -1911,6 +1941,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -2121,7 +2170,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -2144,6 +2193,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -2152,6 +2202,24 @@ dependencies = [ "pin-project-lite", "smallvec", "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls 0.23.10", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", ] [[package]] @@ -2168,15 +2236,18 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.28", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -2186,6 +2257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", + "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.0", @@ -2193,6 +2265,9 @@ dependencies = [ "pin-project-lite", "socket2 0.5.7", "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -2425,7 +2500,7 @@ dependencies = [ name = "kaspa-addresses" version = "0.14.1" dependencies = [ - "borsh", + "borsh 0.9.3", "criterion", "js-sys", "serde", @@ -2442,7 +2517,7 @@ dependencies = [ name = "kaspa-addressmanager" version = "0.14.1" dependencies = [ - "borsh", + "borsh 0.9.3", "igd-next", "itertools 0.11.0", "kaspa-consensus-core", @@ -2472,7 +2547,7 @@ dependencies = [ name = "kaspa-bip32" version = "0.14.1" dependencies = [ - "borsh", + "borsh 0.9.3", "bs58", "faster-hex 0.6.1", "getrandom 0.2.14", @@ -2499,7 +2574,7 @@ name = "kaspa-cli" version = "0.14.1" dependencies = [ "async-trait", - "borsh", + "borsh 0.9.3", "cfg-if 1.0.0", "convert_case 0.6.0", "dashmap", @@ -2633,7 +2708,7 @@ version = "0.14.1" dependencies = [ "async-trait", "bincode", - "borsh", + "borsh 0.9.3", "cfg-if 1.0.0", "criterion", "faster-hex 0.6.1", @@ -2748,7 +2823,7 @@ name = "kaspa-daemon" version = "0.14.1" dependencies = [ "async-trait", - "borsh", + "borsh 0.9.3", "cfg-if 1.0.0", "downcast-rs", "futures", @@ -2797,7 +2872,7 @@ dependencies = [ "faster-hex 0.6.1", "futures", "futures-util", - "h2", + "h2 0.3.26", "itertools 0.11.0", "kaspa-core", "kaspa-grpc-core", @@ -2827,7 +2902,7 @@ dependencies = [ "async-trait", "faster-hex 0.6.1", "futures", - "h2", + "h2 0.3.26", "kaspa-consensus-core", "kaspa-core", "kaspa-notify", @@ -2856,7 +2931,7 @@ dependencies = [ "async-trait", "faster-hex 0.6.1", "futures", - "h2", + "h2 0.3.26", "itertools 0.11.0", "kaspa-consensus-core", "kaspa-core", @@ -2887,7 +2962,7 @@ name = "kaspa-hashes" version = "0.14.1" dependencies = [ "blake2b_simd", - "borsh", + "borsh 0.9.3", "cc", "criterion", "faster-hex 0.6.1", @@ -2954,7 +3029,7 @@ dependencies = [ name = "kaspa-math" version = "0.14.1" dependencies = [ - "borsh", + "borsh 0.9.3", "criterion", "faster-hex 0.6.1", "js-sys", @@ -2983,7 +3058,7 @@ name = "kaspa-metrics-core" version = "0.14.1" dependencies = [ "async-trait", - "borsh", + "borsh 0.9.3", "futures", "kaspa-core", "kaspa-rpc-core", @@ -3047,7 +3122,7 @@ version = "0.14.1" dependencies = [ "async-channel 2.2.1", "async-trait", - "borsh", + "borsh 0.9.3", "criterion", "derive_more", "futures", @@ -3111,10 +3186,10 @@ dependencies = [ name = "kaspa-p2p-lib" version = "0.14.1" dependencies = [ - "borsh", + "borsh 0.9.3", "ctrlc", "futures", - "h2", + "h2 0.3.26", "hex", "itertools 0.11.0", "kaspa-consensus-core", @@ -3202,7 +3277,7 @@ version = "0.14.1" dependencies = [ "async-channel 2.2.1", "async-trait", - "borsh", + "borsh 0.9.3", "cfg-if 1.0.0", "derive_more", "downcast", @@ -3342,7 +3417,7 @@ name = "kaspa-txscript" version = "0.14.1" dependencies = [ "blake2b_simd", - "borsh", + "borsh 0.9.3", "criterion", "hex", "indexmap 2.2.6", @@ -3378,7 +3453,7 @@ dependencies = [ "async-channel 2.2.1", "async-trait", "bincode", - "borsh", + "borsh 0.9.3", "cfg-if 1.0.0", "criterion", "event-listener 2.5.3", @@ -3472,7 +3547,7 @@ dependencies = [ "async-std", "async-trait", "base64 0.21.7", - "borsh", + "borsh 0.9.3", "cfb-mode", "cfg-if 1.0.0", "chacha20poly1305", @@ -3547,7 +3622,7 @@ name = "kaspa-wallet-keys" version = "0.14.1" dependencies = [ "async-trait", - "borsh", + "borsh 0.9.3", "downcast", "faster-hex 0.6.1", "hmac", @@ -3629,7 +3704,7 @@ version = "0.14.1" dependencies = [ "async-std", "async-trait", - "borsh", + "borsh 0.9.3", "cfg-if 1.0.0", "futures", "js-sys", @@ -3696,7 +3771,7 @@ name = "kaspa-wrpc-server" version = "0.14.1" dependencies = [ "async-trait", - "borsh", + "borsh 0.9.3", "futures", "kaspa-consensus-core", "kaspa-core", @@ -4264,7 +4339,7 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.5.0", "cfg-if 1.0.0", - "cfg_aliases", + "cfg_aliases 0.1.1", "libc", ] @@ -4785,6 +4860,15 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5055,20 +5139,23 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-rustls", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -5077,11 +5164,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pemfile 2.1.2", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "system-configuration", "tokio", "tokio-native-tls", @@ -5216,10 +5303,23 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -5229,6 +5329,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -5239,6 +5355,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.15" @@ -5752,6 +5879,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "sync_wrapper" version = "0.1.2" @@ -5985,7 +6124,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.10", + "rustls-pki-types", "tokio", ] @@ -6002,9 +6152,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", @@ -6045,7 +6195,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.12", ] [[package]] @@ -6057,6 +6207,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.12" @@ -6067,7 +6228,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.7", ] [[package]] @@ -6082,7 +6243,7 @@ dependencies = [ "base64 0.21.7", "bytes", "flate2", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", @@ -6090,10 +6251,10 @@ dependencies = [ "percent-encoding", "pin-project", "prost", - "rustls", - "rustls-pemfile", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-stream", "tower", "tower-layer", @@ -6252,9 +6413,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" dependencies = [ "byteorder", "bytes", @@ -6266,7 +6427,6 @@ dependencies = [ "rand 0.8.5", "sha1", "thiserror", - "url", "utf-8", ] @@ -6775,6 +6935,15 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.6.7" @@ -6786,9 +6955,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.50.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if 1.0.0", "windows-sys 0.48.0", @@ -6796,9 +6965,8 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109b6289f65b3e1cdfa6f2d9e8eb454453d5763c5061350e2300473c48d91b99" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6811,13 +6979,12 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcea01cb6122ac3f20dc14f8e4104e2c0cd9c718c17ddb3fc115f9b2ed99f9ae" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "async-channel 2.2.1", "async-std", - "borsh", + "borsh 1.5.1", "bs58", "cfg-if 1.0.0", "chrono", @@ -6838,13 +7005,13 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "workflow-core-macros", + "workflow-log", ] [[package]] name = "workflow-core-macros" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe24820a62e2b544c75c000cff72781383495a0e05157ec3e29b2abafe1ca2cb" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6859,9 +7026,8 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91264d4e789f23c6730c2f3adede04a24b6a9eb9797f9d4ab23de370ba04c27f" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "futures", "js-sys", @@ -6877,9 +7043,8 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b191def1625c3aa5e7d62d1ebbbb3e639113a4a2f122418e4cf8d3379374f8" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6893,9 +7058,8 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "077a8f720aa45c8cd867de1ccc73e068c4084d9fea46d11be7697a108e6a00ba" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "cfg-if 1.0.0", "console", @@ -6909,9 +7073,8 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a8af8b8951fa0cf94df4057b8cf583e067a525d3d997370db7797f33ba201f" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6922,11 +7085,10 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7748eb6c76779993ed7f4457356d6b57f48f97f9e264c64c3405098330bcb8c7" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ - "borsh", + "borsh 1.5.1", "futures", "js-sys", "lazy_static", @@ -6943,13 +7105,12 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010fff3468303b39fb0d5d267847a3d293ed083afbf83f4184fb1a749be56010" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "ahash 0.8.11", "async-trait", - "borsh", + "borsh 1.5.1", "futures", "js-sys", "nw-sys", @@ -6967,9 +7128,8 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c1ed51290daf255e5fd83dfe6bd754b108e371b971afbb5c5fd1ea8fe148af" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -6992,14 +7152,13 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14784fbad27d0403fc752d835c4c4683cfc6af970a484ea83f40ce7ad6dc7745" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "ahash 0.8.11", "async-std", "async-trait", - "borsh", + "borsh 1.5.1", "downcast-rs", "futures", "futures-util", @@ -7022,9 +7181,8 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c372e99d1336a137b907274a3c50fc195e30141c87fc6da4dba54e7d4b09b8ec" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "parse-variants", "proc-macro-error", @@ -7035,12 +7193,11 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "762861614298160b9205302bec4f2b7eb45853413d10a90ad8edca44bafc324b" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "async-std", - "base64 0.21.7", + "base64 0.22.1", "cfg-if 1.0.0", "chrome-sys", "faster-hex 0.9.0", @@ -7063,9 +7220,8 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4023e2598734e04aa4e968a4dd1cd2b5d0c344edc38b40970926d5742f5afa0" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "futures", "thiserror", @@ -7075,9 +7231,8 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057801365ce04c520a2a694bc5bfdf1784f1a33fff97af4cd735f94eb12947b1" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7091,9 +7246,8 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "895c236dd5cf493e01fc31733c4687b3e67032f610d594ce3b8e5cafd14eaf33" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "async-std", "async-trait", @@ -7120,9 +7274,8 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1fe67beb12d31f2e69715898aa32abd2349ffc8fe0555617f0d77500cebc56" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7136,9 +7289,8 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ffbd1de665304ba6040a1ab4e0867fd9174446491d257bc6a1474ae25d4a6c" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7157,9 +7309,8 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "082644f52215ecc86b4b8a20a763e482adee52c338208ade268f47fe25eb07ca" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "js-sys", "proc-macro-error", @@ -7171,9 +7322,8 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6967baf2bd85deb2a014a32d34c1664ded9333e10d11d43ffc179fa09cc55db8" +version = "0.13.3" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" dependencies = [ "ahash 0.8.11", "async-channel 2.2.1", diff --git a/Cargo.toml b/Cargo.toml index 5abf389084..7b5e2970d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -271,17 +271,17 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.12.1" } -workflow-d3 = { version = "0.12.1" } -workflow-dom = { version = "0.12.1" } -workflow-http = { version = "0.12.1" } -workflow-log = { version = "0.12.1" } -workflow-node = { version = "0.12.1" } -workflow-nw = { version = "0.12.1" } -workflow-rpc = { version = "0.12.1" } -workflow-store = { version = "0.12.1" } -workflow-terminal = { version = "0.12.1" } -workflow-wasm = { version = "0.12.1" } +# workflow-core = { version = "0.12.1" } +# workflow-d3 = { version = "0.12.1" } +# workflow-dom = { version = "0.12.1" } +# workflow-http = { version = "0.12.1" } +# workflow-log = { version = "0.12.1" } +# workflow-node = { version = "0.12.1" } +# workflow-nw = { version = "0.12.1" } +# workflow-rpc = { version = "0.12.1" } +# workflow-store = { version = "0.12.1" } +# workflow-terminal = { version = "0.12.1" } +# workflow-wasm = { version = "0.12.1" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs @@ -299,6 +299,18 @@ workflow-wasm = { version = "0.12.1" } # workflow-wasm = { path = "../workflow-rs/wasm" } # --- +workflow-core = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-d3 = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-dom = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-http = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-log = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-node = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-nw = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-rpc = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-store = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-terminal = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } +workflow-wasm = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "pre-borsh-1-5-1" } + # workflow-core = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } # workflow-d3 = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } # workflow-dom = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } From 51f9165c6bde1316503b2c9610b34a0b5f54c87f Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 23 Jun 2024 18:29:46 +0300 Subject: [PATCH 038/158] testing typedoc --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 7b5e2970d3..5ad3e0b957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -337,3 +337,4 @@ strip = false [workspace.lints.clippy] empty_docs = "allow" + From 852a05acb51ec7aa0cacaab79a170d55bfbffc42 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 23 Jun 2024 18:34:13 +0300 Subject: [PATCH 039/158] testing typedoc --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5ad3e0b957..7b5e2970d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -337,4 +337,3 @@ strip = false [workspace.lints.clippy] empty_docs = "allow" - From 1892067a0a82ef109d909f26555743e0591874d8 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 23 Jun 2024 19:09:00 +0300 Subject: [PATCH 040/158] lock typedoc to 0.25.8 --- .github/workflows/ci.yaml | 2 +- .github/workflows/deploy.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8a412c054c..f021be36a4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -323,7 +323,7 @@ jobs: node-version: '20' - name: Install NodeJS dependencies - run: npm install --global typedoc typescript + run: npm install --global typedoc@0.25.8 typescript - name: Cache uses: actions/cache@v4 diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 567adb557f..31daf3a425 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -159,7 +159,7 @@ jobs: node-version: '20' - name: Install NodeJS dependencies - run: npm install --global typedoc typescript + run: npm install --global typedoc@0.25.8 typescript - name: Cache uses: actions/cache@v3 From 5adffba6cb66c96a81aa64f72953719fc1ea90c9 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 23 Jun 2024 19:11:47 +0300 Subject: [PATCH 041/158] disable typedoc treatWarningsAsErrors --- .github/workflows/ci.yaml | 2 +- .github/workflows/deploy.yaml | 2 +- wasm/build/docs/typedoc.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f021be36a4..8a412c054c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -323,7 +323,7 @@ jobs: node-version: '20' - name: Install NodeJS dependencies - run: npm install --global typedoc@0.25.8 typescript + run: npm install --global typedoc typescript - name: Cache uses: actions/cache@v4 diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 31daf3a425..567adb557f 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -159,7 +159,7 @@ jobs: node-version: '20' - name: Install NodeJS dependencies - run: npm install --global typedoc@0.25.8 typescript + run: npm install --global typedoc typescript - name: Cache uses: actions/cache@v3 diff --git a/wasm/build/docs/typedoc.json b/wasm/build/docs/typedoc.json index b89af0882c..1092872b64 100644 --- a/wasm/build/docs/typedoc.json +++ b/wasm/build/docs/typedoc.json @@ -1,6 +1,6 @@ { "$schema": "https://typedoc.org/schema.json", - "treatWarningsAsErrors": true, + "treatWarningsAsErrors": false, "cleanOutputDir": true, "disableSources": true, "categoryOrder": ["*", "Other"], From 66849f33ed5a0045aab77e47e9b183c8ec6b9212 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 24 Jun 2024 05:25:20 +0300 Subject: [PATCH 042/158] deps --- Cargo.lock | 95 +++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 389ef78d82..7ff8dd3837 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6080,9 +6080,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", @@ -6332,9 +6332,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" dependencies = [ "byteorder", "bytes", @@ -6346,7 +6346,6 @@ dependencies = [ "rand 0.8.5", "sha1", "thiserror", - "url", "utf-8", ] @@ -6885,9 +6884,9 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69fe24347c0f2af138170b09f69b977258a871dc7987a60030a21773a96f582" +checksum = "b75e2d6bea942b674215703772ba7a45f430e90994b0cc4c460956a7cd1508b0" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6900,9 +6899,9 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7685fc9a612e1cd8da852b5cea8f7b978121549ae239e7bc1e191302b5c4033f" +checksum = "643260321c3a2a986c9bac6ed5ef08f6f4962fb421ea8bd7a6aa183b4aa99ea1" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6932,9 +6931,9 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05d272b45174884602afa82dc26e97f4bbb1392ef169c0b096300a481c0d890" +checksum = "7506504cf89f1737eb1eb485c20a865123e21b57354749058415cc6f47b2e918" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6949,9 +6948,9 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537841fcd3e3e80e13eccd8275520f2994d5002a36ee954c6d685d690babe334" +checksum = "21ffe3726174f58d5c02b4150c23ca07caf3bdddb79e74249d0aba8a9c2504a2" dependencies = [ "futures", "js-sys", @@ -6967,9 +6966,9 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f358e9976f6fad2448301a5656d5d4d53f8081f69faa19f61415e80083d94a" +checksum = "27ee1dee7f86f10dd55f157227119c248e9d263a4edc7350d191224aa60754cd" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6983,9 +6982,9 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74c615c6fe85ec1a2fe842763520be781256fa13e9710dabb0e067777f03c7e" +checksum = "2b371e9c84ce1c7f7f9eb495c8afd97682a56e5b359960f0410d466d370bd709" dependencies = [ "cfg-if 1.0.0", "console", @@ -6999,9 +6998,9 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2263943df045dc8007685e3b710a606ac1ed5f066fdab24c1674674ef1835c87" +checksum = "e98324f2895e3e4e6675e3cecd7fca7615699dd36942c05cb8f4665a1a03ed7e" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7012,9 +7011,9 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60222ed88d27300d28176f2a6c1e454c32d26c9264f0a1bc710439fc570e38ab" +checksum = "eb25cd690967d5eef54cf55ea942ca806af4478802c199276608c8f9112e48f8" dependencies = [ "borsh", "futures", @@ -7033,9 +7032,9 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d7d555f20dff92855e2c0bb8ae4c48d5a9a85d729e46a3bd13068f823562f6" +checksum = "9a8f6660fa61ab6274b653e2d70e09568a0e702ca6cbae267a046aa7f2d723d3" dependencies = [ "ahash", "async-trait", @@ -7057,9 +7056,9 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c7e88e74cbe7504e8622f9b46f1880eef722e842bde368fc8c4cb8ba51be84" +checksum = "95179cc0d8523774610da286144c21e19c9951d51e4c215d4d20ce9cd141ae98" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7082,9 +7081,9 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b61f8e569d6a7ede17e9c44c9030c322fe2fd86486ec5e3ef29d6c7a468486" +checksum = "ceb2e6e00c99063f9c58519207ffadd936cfb87d683add3acecccf0fb581c02b" dependencies = [ "ahash", "async-std", @@ -7112,9 +7111,9 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e12859e5869e643faefb7c1051cd54830d6686df791486c0e24dc243fd772de" +checksum = "73d4eeb46a411b487485c63787c3cb4c2dfbed3bedc189e004970ff0d4c433c5" dependencies = [ "parse-variants", "proc-macro-error", @@ -7125,9 +7124,9 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1acc7bb2af256394f4468871bad06192899e05b0b3eac6e0b86f2419b26fffa" +checksum = "4d314b7df2a10837be1b756c177531eef5de89b434e8a4599973a521a7b26fcc" dependencies = [ "ahash", "borsh", @@ -7136,12 +7135,12 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e50629a28c063226ad832610ef068645094785dc21dcfc09d73a7fd73e3bb59" +checksum = "96c4ea179eab3a95708506609e61bcda06facecec3cdfe42c9321d3d4a8c7174" dependencies = [ "async-std", - "base64 0.21.7", + "base64 0.22.1", "cfg-if 1.0.0", "chrome-sys", "faster-hex 0.9.0", @@ -7164,9 +7163,9 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eb2273998932ac249227779a619b205b55d579ccd54bb492593b39e2341f164" +checksum = "68048ecc70615b0939463b80f514ff31e5744d09640c27adfea9dcc86c229bcb" dependencies = [ "futures", "thiserror", @@ -7176,9 +7175,9 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2682bb5ea1dbfb6a5e1c624e6280741e0eb0ed39a5d3c7b738157ca64a5cd42" +checksum = "d9c5b7dc18144a39638f80f16cd990e808e36d65c54b6f30de8867509e18f941" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7192,9 +7191,9 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d53f3d0a4e1afd5d7336d41328931e297951e7811e7c74e950255afe0d73076" +checksum = "3277346b19136e6cb96b1739cfb5b5a4f2674c037d8079d8931fc8d7dd87165d" dependencies = [ "async-std", "async-trait", @@ -7221,9 +7220,9 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5fb04baa73f6aa40e38174dda63d26b5717e0fed2a27345c7409ae1faa73387" +checksum = "da39f28a2bff7b278d158f0c7e36928e0bb4e098878e50deb4d94869a0ab7c20" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7237,9 +7236,9 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770586ce7cd1084e0033b785a22258b63917b4ba703cf80ba3f97053b83ccb10" +checksum = "34fc060edd858c40bd4b7fa988b6a249c80f4605be48feba06b240adfd00b7dd" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7258,9 +7257,9 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dca209b50904b71b415281c00be817e6fa779b43cc373d70283711195f47c09" +checksum = "131d22befa5a7d4fcddc3d159d58dcbe22dffcf642e3ee92b66f41a31cbac26b" dependencies = [ "js-sys", "proc-macro-error", @@ -7272,9 +7271,9 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab64d7729b0dbcc1185c48191180548a69a71d3cd199929c93c36f63fdbec276" +checksum = "cb53088fdc3b5ad61797705136d4b538e25673053964cfea723b88ddeaf03e19" dependencies = [ "ahash", "async-channel 2.2.1", From 5945bd12fea3e1762e76990178f820f04c147aaa Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 25 Jun 2024 10:44:04 +0300 Subject: [PATCH 043/158] WIP SPK WASM --- consensus/core/src/tx/script_public_key.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/consensus/core/src/tx/script_public_key.rs b/consensus/core/src/tx/script_public_key.rs index 459be3f4d2..64c90b1dc4 100644 --- a/consensus/core/src/tx/script_public_key.rs +++ b/consensus/core/src/tx/script_public_key.rs @@ -14,6 +14,7 @@ use std::{ collections::HashSet, str::{self, FromStr}, }; +use js_sys::Object; use wasm_bindgen::prelude::*; use workflow_wasm::prelude::*; @@ -41,6 +42,7 @@ const TS_SCRIPT_PUBLIC_KEY: &'static str = r#" * @category Consensus */ export interface IScriptPublicKey { + version : number; script: HexString; } "#; @@ -376,6 +378,12 @@ impl TryCastFromJs for ScriptPublicKey { Self::resolve(&value, || { if let Some(hex_str) = value.as_ref().as_string() { Ok(Self::from_str(&hex_str).map_err(CastError::custom)?) + // } else if let Some(object) = Object::try_from(value.as_ref()) { + // let version = object.get("version").ok_or_else(|| CastError::custom("Missing version"))?; + // let script = object.get("script").ok_or_else(|| CastError::custom("Missing script"))?; + // let version = version.as_f64().ok_or_else(|| CastError::custom("Invalid version"))? as u16; + // let script = script.as_string().ok_or_else(|| CastError::custom("Invalid script"))?; + // Ok(ScriptPublicKey::constructor(version, JsValue::from_str(script)).map_err(|_| CastError::custom("Invalid script"))?) } else { Err(CastError::custom(format!("Unable to convert ScriptPublicKey from: {:?}", value.as_ref()))) } From 4323f969f942e201a7081c46165a5640ea4574fb Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 25 Jun 2024 10:44:28 +0300 Subject: [PATCH 044/158] Cargo.lock --- Cargo.lock | 365 ++++++++++++++--------------------------------------- 1 file changed, 98 insertions(+), 267 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6289f02c21..b0910a98e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -695,20 +695,10 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" dependencies = [ - "borsh-derive 0.9.3", + "borsh-derive", "hashbrown 0.11.2", ] -[[package]] -name = "borsh" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" -dependencies = [ - "borsh-derive 1.5.1", - "cfg_aliases 0.2.1", -] - [[package]] name = "borsh-derive" version = "0.9.3" @@ -717,25 +707,11 @@ checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", - "proc-macro-crate 0.1.5", + "proc-macro-crate", "proc-macro2", "syn 1.0.109", ] -[[package]] -name = "borsh-derive" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" -dependencies = [ - "once_cell", - "proc-macro-crate 3.1.0", - "proc-macro2", - "quote", - "syn 2.0.60", - "syn_derive", -] - [[package]] name = "borsh-derive-internal" version = "0.9.3" @@ -882,12 +858,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chacha20" version = "0.9.1" @@ -1941,25 +1911,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "h2" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.1.0", - "indexmap 2.2.6", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "half" version = "2.4.1" @@ -2170,7 +2121,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -2193,7 +2144,6 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -2202,24 +2152,6 @@ dependencies = [ "pin-project-lite", "smallvec", "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" -dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.3.1", - "hyper-util", - "rustls 0.23.10", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.26.0", - "tower-service", ] [[package]] @@ -2236,18 +2168,15 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "http-body-util", - "hyper 1.3.1", - "hyper-util", + "hyper 0.14.28", "native-tls", "tokio", "tokio-native-tls", - "tower-service", ] [[package]] @@ -2257,7 +2186,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", - "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.0", @@ -2265,9 +2193,6 @@ dependencies = [ "pin-project-lite", "socket2 0.5.7", "tokio", - "tower", - "tower-service", - "tracing", ] [[package]] @@ -2500,7 +2425,7 @@ dependencies = [ name = "kaspa-addresses" version = "0.14.1" dependencies = [ - "borsh 0.9.3", + "borsh", "criterion", "js-sys", "serde", @@ -2517,7 +2442,7 @@ dependencies = [ name = "kaspa-addressmanager" version = "0.14.1" dependencies = [ - "borsh 0.9.3", + "borsh", "igd-next", "itertools 0.11.0", "kaspa-consensus-core", @@ -2547,7 +2472,7 @@ dependencies = [ name = "kaspa-bip32" version = "0.14.1" dependencies = [ - "borsh 0.9.3", + "borsh", "bs58", "faster-hex 0.6.1", "getrandom 0.2.14", @@ -2574,7 +2499,7 @@ name = "kaspa-cli" version = "0.14.1" dependencies = [ "async-trait", - "borsh 0.9.3", + "borsh", "cfg-if 1.0.0", "convert_case 0.6.0", "dashmap", @@ -2708,7 +2633,7 @@ version = "0.14.1" dependencies = [ "async-trait", "bincode", - "borsh 0.9.3", + "borsh", "cfg-if 1.0.0", "criterion", "faster-hex 0.6.1", @@ -2823,7 +2748,7 @@ name = "kaspa-daemon" version = "0.14.1" dependencies = [ "async-trait", - "borsh 0.9.3", + "borsh", "cfg-if 1.0.0", "downcast-rs", "futures", @@ -2872,7 +2797,7 @@ dependencies = [ "faster-hex 0.6.1", "futures", "futures-util", - "h2 0.3.26", + "h2", "itertools 0.11.0", "kaspa-core", "kaspa-grpc-core", @@ -2902,7 +2827,7 @@ dependencies = [ "async-trait", "faster-hex 0.6.1", "futures", - "h2 0.3.26", + "h2", "kaspa-consensus-core", "kaspa-core", "kaspa-notify", @@ -2931,7 +2856,7 @@ dependencies = [ "async-trait", "faster-hex 0.6.1", "futures", - "h2 0.3.26", + "h2", "itertools 0.11.0", "kaspa-consensus-core", "kaspa-core", @@ -2962,7 +2887,7 @@ name = "kaspa-hashes" version = "0.14.1" dependencies = [ "blake2b_simd", - "borsh 0.9.3", + "borsh", "cc", "criterion", "faster-hex 0.6.1", @@ -3029,7 +2954,7 @@ dependencies = [ name = "kaspa-math" version = "0.14.1" dependencies = [ - "borsh 0.9.3", + "borsh", "criterion", "faster-hex 0.6.1", "js-sys", @@ -3058,7 +2983,7 @@ name = "kaspa-metrics-core" version = "0.14.1" dependencies = [ "async-trait", - "borsh 0.9.3", + "borsh", "futures", "kaspa-core", "kaspa-rpc-core", @@ -3122,7 +3047,7 @@ version = "0.14.1" dependencies = [ "async-channel 2.2.1", "async-trait", - "borsh 0.9.3", + "borsh", "criterion", "derive_more", "futures", @@ -3186,10 +3111,10 @@ dependencies = [ name = "kaspa-p2p-lib" version = "0.14.1" dependencies = [ - "borsh 0.9.3", + "borsh", "ctrlc", "futures", - "h2 0.3.26", + "h2", "hex", "itertools 0.11.0", "kaspa-consensus-core", @@ -3277,7 +3202,7 @@ version = "0.14.1" dependencies = [ "async-channel 2.2.1", "async-trait", - "borsh 0.9.3", + "borsh", "cfg-if 1.0.0", "derive_more", "downcast", @@ -3417,7 +3342,7 @@ name = "kaspa-txscript" version = "0.14.1" dependencies = [ "blake2b_simd", - "borsh 0.9.3", + "borsh", "criterion", "hex", "indexmap 2.2.6", @@ -3453,7 +3378,7 @@ dependencies = [ "async-channel 2.2.1", "async-trait", "bincode", - "borsh 0.9.3", + "borsh", "cfg-if 1.0.0", "criterion", "event-listener 2.5.3", @@ -3547,7 +3472,7 @@ dependencies = [ "async-std", "async-trait", "base64 0.21.7", - "borsh 0.9.3", + "borsh", "cfb-mode", "cfg-if 1.0.0", "chacha20poly1305", @@ -3622,7 +3547,7 @@ name = "kaspa-wallet-keys" version = "0.14.1" dependencies = [ "async-trait", - "borsh 0.9.3", + "borsh", "downcast", "faster-hex 0.6.1", "hmac", @@ -3704,7 +3629,7 @@ version = "0.14.1" dependencies = [ "async-std", "async-trait", - "borsh 0.9.3", + "borsh", "cfg-if 1.0.0", "futures", "js-sys", @@ -3771,7 +3696,7 @@ name = "kaspa-wrpc-server" version = "0.14.1" dependencies = [ "async-trait", - "borsh 0.9.3", + "borsh", "futures", "kaspa-consensus-core", "kaspa-core", @@ -4339,7 +4264,7 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.5.0", "cfg-if 1.0.0", - "cfg_aliases 0.1.1", + "cfg_aliases", "libc", ] @@ -4860,15 +4785,6 @@ dependencies = [ "toml 0.5.11", ] -[[package]] -name = "proc-macro-crate" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" -dependencies = [ - "toml_edit 0.21.1", -] - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5139,23 +5055,20 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.12.5" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "hyper 1.3.1", - "hyper-rustls", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-tls", - "hyper-util", "ipnet", "js-sys", "log", @@ -5164,11 +5077,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 2.1.2", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -5303,23 +5216,10 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki 0.101.7", + "rustls-webpki", "sct", ] -[[package]] -name = "rustls" -version = "0.23.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" -dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki 0.102.4", - "subtle", - "zeroize", -] - [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -5329,22 +5229,6 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pemfile" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" -dependencies = [ - "base64 0.22.1", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" - [[package]] name = "rustls-webpki" version = "0.101.7" @@ -5355,17 +5239,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "rustls-webpki" -version = "0.102.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.15" @@ -5879,18 +5752,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.60", -] - [[package]] name = "sync_wrapper" version = "0.1.2" @@ -6124,18 +5985,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.12", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls 0.23.10", - "rustls-pki-types", + "rustls", "tokio", ] @@ -6152,9 +6002,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.23.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", @@ -6195,7 +6045,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit", ] [[package]] @@ -6207,17 +6057,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.12" @@ -6228,7 +6067,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.7", + "winnow", ] [[package]] @@ -6243,7 +6082,7 @@ dependencies = [ "base64 0.21.7", "bytes", "flate2", - "h2 0.3.26", + "h2", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", @@ -6251,10 +6090,10 @@ dependencies = [ "percent-encoding", "pin-project", "prost", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "rustls", + "rustls-pemfile", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls", "tokio-stream", "tower", "tower-layer", @@ -6413,9 +6252,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.23.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", @@ -6427,6 +6266,7 @@ dependencies = [ "rand 0.8.5", "sha1", "thiserror", + "url", "utf-8", ] @@ -6935,15 +6775,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.6.7" @@ -6955,9 +6786,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.52.0" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if 1.0.0", "windows-sys 0.48.0", @@ -6965,8 +6796,8 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6979,12 +6810,12 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "async-channel 2.2.1", "async-std", - "borsh 1.5.1", + "borsh", "bs58", "cfg-if 1.0.0", "chrono", @@ -7010,8 +6841,8 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7026,8 +6857,8 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "futures", "js-sys", @@ -7043,8 +6874,8 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -7058,8 +6889,8 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "cfg-if 1.0.0", "console", @@ -7073,8 +6904,8 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7085,10 +6916,10 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ - "borsh 1.5.1", + "borsh", "futures", "js-sys", "lazy_static", @@ -7105,12 +6936,12 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "ahash 0.8.11", "async-trait", - "borsh 1.5.1", + "borsh", "futures", "js-sys", "nw-sys", @@ -7128,8 +6959,8 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7152,13 +6983,13 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "ahash 0.8.11", "async-std", "async-trait", - "borsh 1.5.1", + "borsh", "downcast-rs", "futures", "futures-util", @@ -7181,8 +7012,8 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "parse-variants", "proc-macro-error", @@ -7193,11 +7024,11 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "async-std", - "base64 0.22.1", + "base64 0.21.7", "cfg-if 1.0.0", "chrome-sys", "faster-hex 0.9.0", @@ -7220,8 +7051,8 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "futures", "thiserror", @@ -7231,8 +7062,8 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7246,8 +7077,8 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "async-std", "async-trait", @@ -7274,8 +7105,8 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7289,8 +7120,8 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7309,8 +7140,8 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "js-sys", "proc-macro-error", @@ -7322,8 +7153,8 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.13.3" -source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=master#402fba4dae12a7453787cabad8e9ebb47520ed77" +version = "0.12.1" +source = "git+https://github.com/workflow-rs/workflow-rs.git?branch=pre-borsh-1-5-1#ccb19a89e4552df45fe5278331a854a4343dda45" dependencies = [ "ahash 0.8.11", "async-channel 2.2.1", From b0479d340436b7f26358446d25711fc2d5d5878a Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 25 Jun 2024 12:12:42 +0300 Subject: [PATCH 045/158] SPK raw object cast + fix return of ISerializableTransaction --- consensus/client/src/serializable/mod.rs | 6 +++++ consensus/client/src/transaction.rs | 4 +-- consensus/core/src/tx/script_public_key.rs | 31 +++++++++++++++++----- rpc/wrpc/proxy/src/main.rs | 1 + rpc/wrpc/server/src/service.rs | 1 + 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/consensus/client/src/serializable/mod.rs b/consensus/client/src/serializable/mod.rs index 5855e26dfb..6099924e7c 100644 --- a/consensus/client/src/serializable/mod.rs +++ b/consensus/client/src/serializable/mod.rs @@ -77,3 +77,9 @@ export interface ISerializableTransaction { } "#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = js_sys::Array, typescript_type = "ISerializableTransaction")] + pub type SerializableTransactionT; +} diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index 3293497149..ef4cdc7919 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -5,7 +5,7 @@ use crate::input::TransactionInput; use crate::outpoint::TransactionOutpoint; use crate::output::TransactionOutput; use crate::result::Result; -use crate::serializable::{numeric, string}; +use crate::serializable::{numeric, string, SerializableTransactionT}; use crate::utxo::{UtxoEntryId, UtxoEntryReference}; use ahash::AHashMap; use kaspa_consensus_core::network::NetworkType; @@ -401,7 +401,7 @@ impl Transaction { /// The schema of the JavaScript object is defined by {@link ISerializableTransaction}. /// @see {@link ISerializableTransaction} #[wasm_bindgen(js_name = "serializeToObject")] - pub fn serialize_to_object(&self) -> Result { + pub fn serialize_to_object(&self) -> Result { Ok(numeric::SerializableTransaction::from_client_transaction(self)?.serialize_to_object()?.into()) } diff --git a/consensus/core/src/tx/script_public_key.rs b/consensus/core/src/tx/script_public_key.rs index 64c90b1dc4..a02fe702a1 100644 --- a/consensus/core/src/tx/script_public_key.rs +++ b/consensus/core/src/tx/script_public_key.rs @@ -1,6 +1,7 @@ use alloc::borrow::Cow; use borsh::{BorshDeserialize, BorshSerialize}; use core::fmt::Formatter; +use js_sys::Object; use kaspa_utils::{ hex::{FromHex, ToHex}, serde_bytes::FromHexVisitor, @@ -14,7 +15,6 @@ use std::{ collections::HashSet, str::{self, FromStr}, }; -use js_sys::Object; use wasm_bindgen::prelude::*; use workflow_wasm::prelude::*; @@ -378,12 +378,29 @@ impl TryCastFromJs for ScriptPublicKey { Self::resolve(&value, || { if let Some(hex_str) = value.as_ref().as_string() { Ok(Self::from_str(&hex_str).map_err(CastError::custom)?) - // } else if let Some(object) = Object::try_from(value.as_ref()) { - // let version = object.get("version").ok_or_else(|| CastError::custom("Missing version"))?; - // let script = object.get("script").ok_or_else(|| CastError::custom("Missing script"))?; - // let version = version.as_f64().ok_or_else(|| CastError::custom("Invalid version"))? as u16; - // let script = script.as_string().ok_or_else(|| CastError::custom("Invalid script"))?; - // Ok(ScriptPublicKey::constructor(version, JsValue::from_str(script)).map_err(|_| CastError::custom("Invalid script"))?) + } else if let Some(object) = Object::try_from(value.as_ref()) { + let version = object.try_get_value("version")?.ok_or(CastError::custom( + "ScriptPublicKey must be a hex string or an object with 'version' and 'script' properties", + ))?; + + let version = if let Ok(version) = version.try_as_u16() { + version + } else if let Some(version) = version.as_string() { + if version.len() != 2 { + return Err(CastError::custom("Invalid version data: must be a number or a 2-character hex string")); + } + + let mut version_bytes = vec![0u8; 2]; + faster_hex::hex_decode(version.as_bytes(), version_bytes.as_mut_slice()) + .map_err(|err| CastError::custom(format!("Error decoding version: {err}")))?; + u16::from_le_bytes(version_bytes.try_into().unwrap()) + } else { + return Err(CastError::custom("Invalid version value '{version:?}'")); + }; + + let script = object.get_vec_u8("script")?; + + Ok(ScriptPublicKey::from_vec(version, script)) } else { Err(CastError::custom(format!("Unable to convert ScriptPublicKey from: {:?}", value.as_ref()))) } diff --git a/rpc/wrpc/proxy/src/main.rs b/rpc/wrpc/proxy/src/main.rs index 43b3938c97..5450463e69 100644 --- a/rpc/wrpc/proxy/src/main.rs +++ b/rpc/wrpc/proxy/src/main.rs @@ -90,6 +90,7 @@ async fn main() -> Result<()> { rpc_handler.clone(), router.interface.clone(), Some(counters), + false, ); log_info!("Kaspa wRPC server is listening on {}", options.listen_address); diff --git a/rpc/wrpc/server/src/service.rs b/rpc/wrpc/server/src/service.rs index 898ac5e295..866c4cdd64 100644 --- a/rpc/wrpc/server/src/service.rs +++ b/rpc/wrpc/server/src/service.rs @@ -123,6 +123,7 @@ impl WrpcService { rpc_handler.clone(), router.interface.clone(), Some(counters), + false, ); WrpcService { options, server, rpc_handler, shutdown: SingleTrigger::default() } From b25dd2915a0d6fcfbcad07952e9183a585a74ab5 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 25 Jun 2024 12:35:04 +0300 Subject: [PATCH 046/158] fix le to be when parsing SPK hex version --- consensus/core/src/tx/script_public_key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/core/src/tx/script_public_key.rs b/consensus/core/src/tx/script_public_key.rs index a02fe702a1..bafb060095 100644 --- a/consensus/core/src/tx/script_public_key.rs +++ b/consensus/core/src/tx/script_public_key.rs @@ -393,7 +393,7 @@ impl TryCastFromJs for ScriptPublicKey { let mut version_bytes = vec![0u8; 2]; faster_hex::hex_decode(version.as_bytes(), version_bytes.as_mut_slice()) .map_err(|err| CastError::custom(format!("Error decoding version: {err}")))?; - u16::from_le_bytes(version_bytes.try_into().unwrap()) + u16::from_be_bytes(version_bytes.try_into().unwrap()) } else { return Err(CastError::custom("Invalid version value '{version:?}'")); }; From 141f82f25ff1c3d882dd536b2e554e462a680a38 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 26 Jun 2024 02:48:50 +0300 Subject: [PATCH 047/158] remove string version parsing from SPK --- consensus/core/src/tx/script_public_key.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/consensus/core/src/tx/script_public_key.rs b/consensus/core/src/tx/script_public_key.rs index bafb060095..8232bb1264 100644 --- a/consensus/core/src/tx/script_public_key.rs +++ b/consensus/core/src/tx/script_public_key.rs @@ -385,15 +385,6 @@ impl TryCastFromJs for ScriptPublicKey { let version = if let Ok(version) = version.try_as_u16() { version - } else if let Some(version) = version.as_string() { - if version.len() != 2 { - return Err(CastError::custom("Invalid version data: must be a number or a 2-character hex string")); - } - - let mut version_bytes = vec![0u8; 2]; - faster_hex::hex_decode(version.as_bytes(), version_bytes.as_mut_slice()) - .map_err(|err| CastError::custom(format!("Error decoding version: {err}")))?; - u16::from_be_bytes(version_bytes.try_into().unwrap()) } else { return Err(CastError::custom("Invalid version value '{version:?}'")); }; From 334a56c511993ed7642d281892c8388698b7e77a Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 26 Jun 2024 03:39:30 +0300 Subject: [PATCH 048/158] update WASM SDK changelog --- wasm/CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/wasm/CHANGELOG.md b/wasm/CHANGELOG.md index 1534eca76d..71b6375d20 100644 --- a/wasm/CHANGELOG.md +++ b/wasm/CHANGELOG.md @@ -1,5 +1,13 @@ Latest online documentation available at: https://kaspa.aspectron.org/docs/ +### Latest Release + +- Fix issues with deserializing manually-created objects matching `IUtxoEntry` interface. +- Allow arguments expecting ScriptPublicKey to receive `{ version, script }` object or a hex string. +- Fix `Transaction::serializeToObject()` return type (now returning `ISerializeTransaction` interface). +- Adding `setUserTransactionMaturityDAA()` and `setCoinbaseTransactionMaturityDAA()` that allow customizing +the maturity DAA periods for user and coinbase transactions. + ### Release 2024-06-12 - Fix `PublicKeyGenerator::change_address_as_string()` that was returning the receive address. @@ -10,7 +18,7 @@ Latest online documentation available at: https://kaspa.aspectron.org/docs/ ### Release 2024-05-26 -- Adding utility functions: `payToAddressScript`, `payToScriptHashScript`, `payToScriptHashSignatureScript`, `addressFromScriptPublicKey`, `isScriptPayToPubkey`, `isScriptPayToPubkeyECDSA`, `isScriptPayToScriptHash`. +- Adding utility functions: `payToAddressScript()`, `payToScriptHashScript()`, `payToScriptHashSignatureScript()`, `addressFromScriptPublicKey()`, `isScriptPayToPubkey()`, `isScriptPayToPubkeyECDSA()`, `isScriptPayToScriptHash()`. - Adding `UtxoProcessor::isActive` property to check if the processor is in active state (connected and running). This property can be used to validate the processor state before invoking it's functions (that can throw is the UtxoProcessor is offline). - Rename `UtxoContext::active` to `UtxoContext::isActive` for consistency. From 8875f83b7de4414d722c2736943eb8662ed30e55 Mon Sep 17 00:00:00 2001 From: surinder singh Date: Thu, 27 Jun 2024 17:12:41 +0530 Subject: [PATCH 049/158] incorrect balance issue (#55) --- wallet/core/src/utxo/context.rs | 45 +++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/wallet/core/src/utxo/context.rs b/wallet/core/src/utxo/context.rs index 3a74140379..47a5e9f323 100644 --- a/wallet/core/src/utxo/context.rs +++ b/wallet/core/src/utxo/context.rs @@ -346,7 +346,7 @@ impl UtxoContext { } else { remove_mature_ids.push(id); } - } else { + } else if context.outgoing.get(&utxo.transaction_id()).is_none() { log_error!("Error: UTXO not found in UtxoContext map!"); } } @@ -482,19 +482,25 @@ impl UtxoContext { // the final payments (not compound transactions) // and outgoing transactions that have not yet // been accepted + let mut outgoing_without_batch_tx = 0; let mut outgoing: u64 = 0; let mut consumed: u64 = 0; - for tx in context.outgoing.values() { - if !tx.is_accepted() { - if let Some(payment_value) = tx.payment_value() { + + let transactions = context.outgoing.values().filter(|tx| !tx.is_accepted()); + for tx in transactions { + if let Some(payment_value) = tx.payment_value() { + consumed += tx.aggregate_input_value(); + if tx.is_batch() { + outgoing += tx.fees() + tx.aggregate_output_value(); + } else { // final tx outgoing += tx.fees() + payment_value; - consumed += tx.aggregate_input_value(); - } else { - // compound tx has no payment value - outgoing += tx.fees() + tx.aggregate_output_value(); - consumed += tx.aggregate_input_value() + outgoing_without_batch_tx += payment_value; } + } else { + // compound tx has no payment value + outgoing += tx.fees() + tx.aggregate_output_value(); + consumed += tx.aggregate_input_value(); } } @@ -502,13 +508,12 @@ impl UtxoContext { // this condition does not occur. This is a temporary // log for a fixed bug, but we want to keep the check // just in case. - if mature + consumed < outgoing { - log_error!("Error: outgoing transaction value exceeds available balance"); + if consumed < outgoing { + log_error!("Error: outgoing transaction value exceeds available balance, mature: {mature}, consumed: {consumed}, outgoing: {outgoing}"); } let mature = (mature + consumed).saturating_sub(outgoing); - - Balance::new(mature, pending, outgoing, context.mature.len(), context.pending.len(), context.stasis.len()) + Balance::new(mature, pending, outgoing_without_batch_tx, context.mature.len(), context.pending.len(), context.stasis.len()) } pub(crate) async fn handle_utxo_added(&self, utxos: Vec, current_daa_score: u64) -> Result<()> { @@ -527,12 +532,14 @@ impl UtxoContext { let force_maturity_if_outgoing = outgoing_transaction.is_some(); let is_coinbase_stasis = utxos.first().map(|utxo| matches!(utxo.maturity(¶ms, current_daa_score), Maturity::Stasis)).unwrap_or_default(); - - for utxo in utxos.iter() { - if let Err(err) = self.insert(utxo.clone(), current_daa_score, force_maturity_if_outgoing).await { - // TODO - remove `Result<>` from insert at a later date once - // we are confident that the insert will never result in an error. - log_error!("{}", err); + let is_batch = outgoing_transaction.as_ref().map_or_else(|| false, |tx| tx.is_batch()); + if !is_batch { + for utxo in utxos.iter() { + if let Err(err) = self.insert(utxo.clone(), current_daa_score, force_maturity_if_outgoing).await { + // TODO - remove `Result<>` from insert at a later date once + // we are confident that the insert will never result in an error. + log_error!("{}", err); + } } } From 62a91b43deb45be9d08b2de9d892f602da04acd7 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 28 Jun 2024 16:21:38 +0300 Subject: [PATCH 050/158] TransactionInput signature_script as Option, add associated TS types --- consensus/client/src/input.rs | 23 ++++++------ consensus/client/src/lib.rs | 13 ++++--- consensus/client/src/outpoint.rs | 9 +++++ consensus/client/src/output.rs | 38 ++++++++++---------- consensus/client/src/serializable/mod.rs | 2 +- consensus/client/src/serializable/numeric.rs | 19 ++++++---- consensus/client/src/serializable/string.rs | 6 ++-- consensus/client/src/transaction.rs | 12 +++++-- rpc/core/src/wasm/convert.rs | 2 +- wallet/core/src/wasm/tx/mass.rs | 19 +++++----- wallet/core/src/wasm/tx/utils.rs | 2 +- 11 files changed, 86 insertions(+), 59 deletions(-) diff --git a/consensus/client/src/input.rs b/consensus/client/src/input.rs index 8b48c2d942..33db7a829b 100644 --- a/consensus/client/src/input.rs +++ b/consensus/client/src/input.rs @@ -13,7 +13,7 @@ const TS_TRANSACTION: &'static str = r#" */ export interface ITransactionInput { previousOutpoint: ITransactionOutpoint; - signatureScript: HexString; + signatureScript?: HexString; sequence: bigint; sigOpCount: number; utxo?: UtxoEntryReference; @@ -35,13 +35,15 @@ export interface ITransactionInputVerboseData { } extern "C" { #[wasm_bindgen(typescript_type = "ITransactionInput")] pub type ITransactionInput; + #[wasm_bindgen(extends = js_sys::Array, typescript_type = "ITransactionInput[]")] + pub type ITransactionInputArray; } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionInputInner { pub previous_outpoint: TransactionOutpoint, - pub signature_script: Vec, + pub signature_script: Option>, pub sequence: u64, pub sig_op_count: u8, pub utxo: Option, @@ -50,7 +52,7 @@ pub struct TransactionInputInner { impl TransactionInputInner { pub fn new( previous_outpoint: TransactionOutpoint, - signature_script: Vec, + signature_script: Option>, sequence: u64, sig_op_count: u8, utxo: Option, @@ -70,7 +72,7 @@ pub struct TransactionInput { impl TransactionInput { pub fn new( previous_outpoint: TransactionOutpoint, - signature_script: Vec, + signature_script: Option>, sequence: u64, sig_op_count: u8, utxo: Option, @@ -120,8 +122,8 @@ impl TransactionInput { } #[wasm_bindgen(getter = signatureScript)] - pub fn get_signature_script_as_hex(&self) -> String { - self.inner().signature_script.to_hex() + pub fn get_signature_script_as_hex(&self) -> Option { + self.inner().signature_script.as_ref().map(|script| script.to_hex()) } #[wasm_bindgen(setter = signatureScript)] @@ -163,7 +165,7 @@ impl TransactionInput { impl TransactionInput { pub fn set_signature_script(&self, signature_script: Vec) { - self.inner().signature_script = signature_script; + self.inner().signature_script.replace(signature_script); } pub fn script_public_key(&self) -> Option { @@ -183,7 +185,7 @@ impl TryCastFromJs for TransactionInput { Self::resolve_cast(&value, || { if let Some(object) = Object::try_from(value.as_ref()) { let previous_outpoint: TransactionOutpoint = object.get_value("previousOutpoint")?.as_ref().try_into()?; - let signature_script = object.get_vec_u8("signatureScript")?; + let signature_script = object.get_vec_u8("signatureScript").ok(); let sequence = object.get_u64("sequence")?; let sig_op_count = object.get_u8("sigOpCount")?; let utxo = object.try_get_cast::("utxo")?.map(Cast::into_owned); @@ -199,7 +201,7 @@ impl From for TransactionInput { fn from(tx_input: cctx::TransactionInput) -> Self { TransactionInput::new( tx_input.previous_outpoint.into(), - tx_input.signature_script, + Some(tx_input.signature_script), tx_input.sequence, tx_input.sig_op_count, None, @@ -212,7 +214,8 @@ impl From<&TransactionInput> for cctx::TransactionInput { let inner = tx_input.inner(); cctx::TransactionInput::new( inner.previous_outpoint.clone().into(), - inner.signature_script.clone(), + // TODO - discuss: should this unwrap_or_default or return an error? + inner.signature_script.clone().unwrap_or_default(), inner.sequence, inner.sig_op_count, ) diff --git a/consensus/client/src/lib.rs b/consensus/client/src/lib.rs index 4935b16f76..ec202ff809 100644 --- a/consensus/client/src/lib.rs +++ b/consensus/client/src/lib.rs @@ -1,29 +1,28 @@ pub mod error; mod imports; +mod input; mod outpoint; mod output; pub mod result; +mod serializable; +mod transaction; mod utxo; +pub use input::*; pub use outpoint::*; pub use output::*; +pub use serializable::*; +pub use transaction::*; pub use utxo::*; cfg_if::cfg_if! { if #[cfg(feature = "wasm32-sdk")] { mod header; - mod input; - mod transaction; mod vtx; mod hash; mod sign; mod script; - mod serializable; - pub use header::*; - pub use input::*; - pub use transaction::*; - pub use serializable::*; pub use vtx::*; pub use hash::*; // pub use signing::*; diff --git a/consensus/client/src/outpoint.rs b/consensus/client/src/outpoint.rs index 77e17d542e..06be53f6aa 100644 --- a/consensus/client/src/outpoint.rs +++ b/consensus/client/src/outpoint.rs @@ -165,6 +165,15 @@ impl From for cctx::TransactionOutpoint { } } +impl From<&TransactionOutpoint> for cctx::TransactionOutpoint { + fn from(outpoint: &TransactionOutpoint) -> Self { + let inner = outpoint.inner(); + let transaction_id = inner.transaction_id; + let index = inner.index; + cctx::TransactionOutpoint::new(transaction_id, index) + } +} + impl TransactionOutpoint { pub fn simulated() -> Self { Self::new(TransactionId::from_slice(&rand::random::<[u8; kaspa_hashes::HASH_SIZE]>()), 0) diff --git a/consensus/client/src/output.rs b/consensus/client/src/output.rs index 99fe38ec3a..15c94a5b4f 100644 --- a/consensus/client/src/output.rs +++ b/consensus/client/src/output.rs @@ -26,6 +26,14 @@ export interface ITransactionOutputVerboseData { } "#; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "ITransactionOutput")] + pub type ITransactionOutput; + #[wasm_bindgen(extends = js_sys::Array, typescript_type = "ITransactionOutput[]")] + pub type ITransactionOutputArray; +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionOutputInner { @@ -114,25 +122,17 @@ impl From<&TransactionOutput> for cctx::TransactionOutput { } } -impl TryFrom<&JsValue> for TransactionOutput { - type Error = Error; - fn try_from(js_value: &JsValue) -> Result { - // workflow_log::log_trace!("js_value->TransactionOutput: {js_value:?}"); - if let Some(object) = Object::try_from(js_value) { - let has_address = Object::has_own(object, &JsValue::from("address")); - workflow_log::log_trace!("js_value->TransactionOutput: has_address:{has_address:?}"); - let value = object.get_u64("value")?; - let script_public_key = ScriptPublicKey::try_cast_from(object.get_value("scriptPublicKey")?)?; - Ok(TransactionOutput::new(value, script_public_key.into_owned())) - } else { - Err("TransactionInput must be an object".into()) - } - } -} - -impl TryFrom for TransactionOutput { +impl TryCastFromJs for TransactionOutput { type Error = Error; - fn try_from(js_value: JsValue) -> Result { - Self::try_from(&js_value) + fn try_cast_from(value: impl AsRef) -> std::result::Result, Self::Error> { + Self::resolve_cast(&value, || { + if let Some(object) = Object::try_from(value.as_ref()) { + let value = object.get_u64("value")?; + let script_public_key = ScriptPublicKey::try_cast_from(object.get_value("scriptPublicKey")?)?; + Ok(TransactionOutput::new(value, script_public_key.into_owned()).into()) + } else { + Err("TransactionInput must be an object".into()) + } + }) } } diff --git a/consensus/client/src/serializable/mod.rs b/consensus/client/src/serializable/mod.rs index 5855e26dfb..856adbc4b2 100644 --- a/consensus/client/src/serializable/mod.rs +++ b/consensus/client/src/serializable/mod.rs @@ -30,7 +30,7 @@ export interface ISerializableTransactionInput { index: number; sequence: bigint; sigOpCount: number; - signatureScript: HexString; + signatureScript?: HexString; utxo: ISerializableUtxoEntry; } diff --git a/consensus/client/src/serializable/numeric.rs b/consensus/client/src/serializable/numeric.rs index 0c413fdcfe..d4c145a1bc 100644 --- a/consensus/client/src/serializable/numeric.rs +++ b/consensus/client/src/serializable/numeric.rs @@ -80,6 +80,7 @@ pub struct SerializableTransactionInput { pub sequence: u64, pub sig_op_count: u8, #[serde(with = "hex::serde")] + // TODO - convert to Option> and use hex serialization over Option pub signature_script: Vec, pub utxo: SerializableUtxoEntry, } @@ -91,6 +92,8 @@ impl SerializableTransactionInput { Self { transaction_id: input.previous_outpoint.transaction_id, index: input.previous_outpoint.index, + // TODO - convert signature_script to Option> + // signature_script: (!input.signature_script.is_empty()).then_some(input.signature_script.clone()), signature_script: input.signature_script.clone(), sequence: input.sequence, sig_op_count: input.sig_op_count, @@ -134,15 +137,16 @@ impl TryFrom for cctx::TransactionInput { impl TryFrom<&SerializableTransactionInput> for TransactionInput { type Error = Error; - fn try_from(signable_input: &SerializableTransactionInput) -> Result { - let utxo = UtxoEntryReference::try_from(signable_input)?; + fn try_from(serializable_input: &SerializableTransactionInput) -> Result { + let utxo = UtxoEntryReference::try_from(serializable_input)?; - let previous_outpoint = TransactionOutpoint::new(signable_input.transaction_id, signable_input.index); + let previous_outpoint = TransactionOutpoint::new(serializable_input.transaction_id, serializable_input.index); let inner = TransactionInputInner { previous_outpoint, - signature_script: signable_input.signature_script.clone(), - sequence: signable_input.sequence, - sig_op_count: signable_input.sig_op_count, + // TODO - convert to Option> and use hex serialization over Option + signature_script: (!serializable_input.signature_script.is_empty()).then_some(serializable_input.signature_script.clone()), + sequence: serializable_input.sequence, + sig_op_count: serializable_input.sig_op_count, utxo: Some(utxo), }; @@ -159,7 +163,8 @@ impl TryFrom<&TransactionInput> for SerializableTransactionInput { Ok(Self { transaction_id: inner.previous_outpoint.transaction_id(), index: inner.previous_outpoint.index(), - signature_script: inner.signature_script.clone(), + // TODO - convert to Option> and use hex serialization over Option + signature_script: inner.signature_script.clone().unwrap_or_default(), sequence: inner.sequence, sig_op_count: inner.sig_op_count, utxo, diff --git a/consensus/client/src/serializable/string.rs b/consensus/client/src/serializable/string.rs index be3981b0ea..33c054fac3 100644 --- a/consensus/client/src/serializable/string.rs +++ b/consensus/client/src/serializable/string.rs @@ -139,7 +139,8 @@ impl TryFrom<&SerializableTransactionInput> for TransactionInput { let previous_outpoint = TransactionOutpoint::new(serializable_input.transaction_id, serializable_input.index); let inner = TransactionInputInner { previous_outpoint, - signature_script: serializable_input.signature_script.clone(), + // TODO - convert to Option> and use hex serialization over Option + signature_script: (!serializable_input.signature_script.is_empty()).then_some(serializable_input.signature_script.clone()), sequence: serializable_input.sequence.parse()?, sig_op_count: serializable_input.sig_op_count, utxo: Some(utxo), @@ -158,7 +159,8 @@ impl TryFrom<&TransactionInput> for SerializableTransactionInput { Ok(Self { transaction_id: inner.previous_outpoint.transaction_id(), index: inner.previous_outpoint.index(), - signature_script: inner.signature_script.clone(), + // TODO - convert to Option> and use hex serialization over Option + signature_script: inner.signature_script.clone().unwrap_or_default(), sequence: inner.sequence.to_string(), sig_op_count: inner.sig_op_count, utxo, diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index 3293497149..1498f88874 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -199,7 +199,7 @@ impl Transaction { pub fn set_outputs_from_js_array(&mut self, js_value: &JsValue) { let outputs = Array::from(js_value) .iter() - .map(|js_value| TransactionOutput::try_from(&js_value).unwrap_or_else(|err| panic!("invalid transaction output: {err}"))) + .map(|js_value| TryCastFromJs::try_owned_from(&js_value).unwrap_or_else(|err| panic!("invalid transaction output: {err}"))) .collect::>(); self.inner().outputs = outputs; } @@ -285,7 +285,7 @@ impl TryCastFromJs for Transaction { let outputs: Vec = object .get_vec("outputs")? .iter() - .map(|jsv| jsv.try_into()) + .map(TryCastFromJs::try_owned_from) .collect::, Error>>()?; Transaction::new(id, version, inputs, outputs, lock_time, subnetwork_id, gas, payload).map(Into::into) } @@ -342,7 +342,13 @@ impl Transaction { .map(|input| { let previous_outpoint: TransactionOutpoint = input.previous_outpoint.into(); let utxo = utxos.get(previous_outpoint.id()).cloned(); - TransactionInput::new(previous_outpoint, input.signature_script.clone(), input.sequence, input.sig_op_count, utxo) + TransactionInput::new( + previous_outpoint, + Some(input.signature_script.clone()), + input.sequence, + input.sig_op_count, + utxo, + ) }) .collect::>(); let outputs: Vec = tx.outputs.iter().map(|output| output.into()).collect::>(); diff --git a/rpc/core/src/wasm/convert.rs b/rpc/core/src/wasm/convert.rs index 0c33cf0ec3..94938f22be 100644 --- a/rpc/core/src/wasm/convert.rs +++ b/rpc/core/src/wasm/convert.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { let inner = tx_input.inner(); RpcTransactionInput { previous_outpoint: inner.previous_outpoint.clone().into(), - signature_script: inner.signature_script.clone(), + signature_script: inner.signature_script.clone().unwrap_or_default(), sequence: inner.sequence, sig_op_count: inner.sig_op_count, verbose_data: None, diff --git a/wallet/core/src/wasm/tx/mass.rs b/wallet/core/src/wasm/tx/mass.rs index cc522fd8e0..82c16fe741 100644 --- a/wallet/core/src/wasm/tx/mass.rs +++ b/wallet/core/src/wasm/tx/mass.rs @@ -1,3 +1,4 @@ +use crate::error::Error; use crate::imports::NetworkParams; use crate::result::Result; use crate::tx::mass; @@ -39,9 +40,9 @@ impl MassCalculator { /// /// It is exposed by `MiningManager` for use by transaction generators and wallets. #[wasm_bindgen(js_name=isTransactionOutputDust)] - pub fn is_transaction_output_dust(transaction_output: &JsValue) -> Result { - let transaction_output = TransactionOutput::try_from(transaction_output)?; - let transaction_output = cctx::TransactionOutput::from(&transaction_output); + pub fn is_transaction_output_dust(transaction_output: &ITransactionOutput) -> Result { + let transaction_output = TransactionOutput::try_cast_from(transaction_output)?; + let transaction_output = cctx::TransactionOutput::from(transaction_output.as_ref()); Ok(mass::is_transaction_output_dust(&transaction_output)) } @@ -93,20 +94,22 @@ impl MassCalculator { } #[wasm_bindgen(js_name=calcMassForOutputs)] - pub fn calc_mass_for_outputs(&self, outputs: JsValue) -> Result { + pub fn calc_mass_for_outputs(&self, outputs: ITransactionOutputArray) -> Result { let outputs = outputs - .dyn_into::()? + .dyn_into::() + .map_err(|_| Error::custom("supplied argument must be an array of outputs"))? .iter() - .map(TransactionOutput::try_from) + .map(TransactionOutput::try_owned_from) .collect::, kaspa_consensus_client::error::Error>>()?; let outputs = outputs.iter().map(|output| self.calc_mass_for_output(output)).collect::>>()?; Ok(outputs.iter().sum()) } #[wasm_bindgen(js_name=calcMassForInputs)] - pub fn calc_mass_for_inputs(&self, inputs: JsValue) -> Result { + pub fn calc_mass_for_inputs(&self, inputs: ITransactionInputArray) -> Result { let inputs = inputs - .dyn_into::()? + .dyn_into::() + .map_err(|_| Error::custom("supplied argument must be an array of inputs"))? .iter() .map(TransactionInput::try_owned_from) .collect::, kaspa_consensus_client::error::Error>>()?; diff --git a/wallet/core/src/wasm/tx/utils.rs b/wallet/core/src/wasm/tx/utils.rs index 0d911af76c..0702fcff6d 100644 --- a/wallet/core/src/wasm/tx/utils.rs +++ b/wallet/core/src/wasm/tx/utils.rs @@ -56,7 +56,7 @@ pub fn create_transaction_js( let UtxoEntryReference { utxo } = reference.as_ref(); total_input_amount += utxo.amount(); entries.push(reference.as_ref().clone()); - TransactionInput::new(utxo.outpoint.clone(), vec![], sequence as u64, sig_op_count, Some(reference.into_owned())) + TransactionInput::new(utxo.outpoint.clone(), None, sequence as u64, sig_op_count, Some(reference.into_owned())) }) .collect::>(); From 70bd17267ca571fd0c646511ded95c6452995fb3 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 28 Jun 2024 16:22:56 +0300 Subject: [PATCH 051/158] restructure PSKT + WASM scaffolding (WIP) --- Cargo.lock | 4 + wallet/pskt/Cargo.toml | 7 +- wallet/pskt/examples/multisig.rs | 4 +- wallet/pskt/src/bundle.rs | 9 + wallet/pskt/src/convert.rs | 55 ++++ wallet/pskt/src/error.rs | 6 + wallet/pskt/src/global.rs | 4 +- wallet/pskt/src/input.rs | 8 +- wallet/pskt/src/lib.rs | 483 ++----------------------------- wallet/pskt/src/output.rs | 2 +- wallet/pskt/src/pskt.rs | 455 +++++++++++++++++++++++++++++ wallet/pskt/src/wasm/bundle.rs | 1 + wallet/pskt/src/wasm/error.rs | 64 ++++ wallet/pskt/src/wasm/input.rs | 1 + wallet/pskt/src/wasm/mod.rs | 6 + wallet/pskt/src/wasm/output.rs | 1 + wallet/pskt/src/wasm/pskt.rs | 310 ++++++++++++++++++++ wallet/pskt/src/wasm/result.rs | 1 + 18 files changed, 957 insertions(+), 464 deletions(-) create mode 100644 wallet/pskt/src/bundle.rs create mode 100644 wallet/pskt/src/convert.rs create mode 100644 wallet/pskt/src/pskt.rs create mode 100644 wallet/pskt/src/wasm/bundle.rs create mode 100644 wallet/pskt/src/wasm/error.rs create mode 100644 wallet/pskt/src/wasm/input.rs create mode 100644 wallet/pskt/src/wasm/mod.rs create mode 100644 wallet/pskt/src/wasm/output.rs create mode 100644 wallet/pskt/src/wasm/pskt.rs create mode 100644 wallet/pskt/src/wasm/result.rs diff --git a/Cargo.lock b/Cargo.lock index 0d4703b8ad..d61da0a651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3623,6 +3623,7 @@ name = "kaspa-wallet-pskt" version = "0.14.1" dependencies = [ "derive_builder", + "js-sys", "kaspa-bip32", "kaspa-consensus-client", "kaspa-consensus-core", @@ -3632,9 +3633,12 @@ dependencies = [ "secp256k1", "serde", "serde-value", + "serde-wasm-bindgen", "serde_json", "serde_repr", "thiserror", + "wasm-bindgen", + "workflow-wasm", ] [[package]] diff --git a/wallet/pskt/Cargo.toml b/wallet/pskt/Cargo.toml index f2d82cf07c..c9376fe061 100644 --- a/wallet/pskt/Cargo.toml +++ b/wallet/pskt/Cargo.toml @@ -27,11 +27,16 @@ kaspa-txscript.workspace = true kaspa-utils.workspace = true derive_builder.workspace = true +js-sys.workspace = true secp256k1.workspace = true +serde_repr.workspace = true serde-value.workspace = true serde.workspace = true -serde_repr.workspace = true thiserror.workspace = true +wasm-bindgen.workspace = true +serde_json.workspace = true +serde-wasm-bindgen.workspace = true +workflow-wasm.workspace = true [dev-dependencies] serde_json.workspace = true diff --git a/wallet/pskt/examples/multisig.rs b/wallet/pskt/examples/multisig.rs index a34bef9b55..fb011402fb 100644 --- a/wallet/pskt/examples/multisig.rs +++ b/wallet/pskt/examples/multisig.rs @@ -3,7 +3,9 @@ use kaspa_consensus_core::{ tx::{TransactionId, TransactionOutpoint, UtxoEntry}, }; use kaspa_txscript::{multisig_redeem_script, opcodes::codes::OpData65, pay_to_script_hash_script, script_builder::ScriptBuilder}; -use kaspa_wallet_pskt::{Combiner, Creator, Extractor, Finalizer, Inner, InputBuilder, SignInputOk, Signature, Signer, Updater, PSKT}; +use kaspa_wallet_pskt::prelude::{ + Combiner, Creator, Extractor, Finalizer, Inner, InputBuilder, SignInputOk, Signature, Signer, Updater, PSKT, +}; use secp256k1::{rand::thread_rng, Keypair}; use std::{iter, str::FromStr}; diff --git a/wallet/pskt/src/bundle.rs b/wallet/pskt/src/bundle.rs new file mode 100644 index 0000000000..1443ad600b --- /dev/null +++ b/wallet/pskt/src/bundle.rs @@ -0,0 +1,9 @@ +// use crate::role::*; +use serde::{Deserialize, Serialize}; +use std::marker::PhantomData; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Bundle { + #[serde(skip_serializing, default)] + role: PhantomData, +} diff --git a/wallet/pskt/src/convert.rs b/wallet/pskt/src/convert.rs new file mode 100644 index 0000000000..17edc0847f --- /dev/null +++ b/wallet/pskt/src/convert.rs @@ -0,0 +1,55 @@ +use crate::error::Error; +use crate::input::{Input, InputBuilder}; +use crate::output::{Output, OutputBuilder}; +use crate::pskt::Inner; +use kaspa_consensus_client::{Transaction, TransactionInput, TransactionInputInner, TransactionOutput, TransactionOutputInner}; + +impl From for Inner { + fn from(_transaction: Transaction) -> Inner { + // Self::Transaction(transaction) + + todo!() + } +} + +impl TryFrom for Input { + type Error = Error; + fn try_from(input: TransactionInput) -> std::result::Result { + let TransactionInputInner { previous_outpoint, signature_script: _, sequence: _, sig_op_count, utxo } = &*input.inner(); + + let input = InputBuilder::default() + .utxo_entry(utxo.as_ref().ok_or(Error::MissingUtxoEntry)?.into()) + .previous_outpoint(previous_outpoint.into()) + // .sequence(*sequence) + // min_time + // partial_sigs + // sighash_type + // redeem_script + .sig_op_count(*sig_op_count) + // bip32_derivations + // final_script_sig + .build()?; + + Ok(input) + } +} + +impl TryFrom for Output { + type Error = Error; + fn try_from(output: TransactionOutput) -> std::result::Result { + // Self::Transaction(transaction) + + let TransactionOutputInner { value, script_public_key } = &*output.inner(); + + let output = OutputBuilder::default() + .amount(*value) + .script_public_key(script_public_key.clone()) + // .redeem_script + // .bip32_derivations + // .proprietaries + // .unknowns + .build()?; + + Ok(output) + } +} diff --git a/wallet/pskt/src/error.rs b/wallet/pskt/src/error.rs index 5041190862..2ce546d230 100644 --- a/wallet/pskt/src/error.rs +++ b/wallet/pskt/src/error.rs @@ -4,6 +4,12 @@ pub enum Error { ConstructorError(#[from] ConstructorError), #[error("OutputNotModifiable")] OutOfBounds, + #[error("Missing UTXO entry")] + MissingUtxoEntry, + #[error(transparent)] + InputBuilder(#[from] crate::input::InputBuilderError), + #[error(transparent)] + OutputBuilder(#[from] crate::output::OutputBuilderError), } #[derive(thiserror::Error, Debug)] diff --git a/wallet/pskt/src/global.rs b/wallet/pskt/src/global.rs index 8e16b832bc..fe0b36fb91 100644 --- a/wallet/pskt/src/global.rs +++ b/wallet/pskt/src/global.rs @@ -1,4 +1,5 @@ -use crate::{utils::combine_if_no_conflicts, KeySource, Version}; +use crate::pskt::{KeySource, Version}; +use crate::utils::combine_if_no_conflicts; use derive_builder::Builder; use kaspa_consensus_core::tx::TransactionId; use serde::{Deserialize, Serialize}; @@ -33,6 +34,7 @@ pub struct Global { /// Proprietary key-value pairs for this output. pub proprietaries: BTreeMap, /// Unknown key-value pairs for this output. + #[serde(flatten)] pub unknowns: BTreeMap, } diff --git a/wallet/pskt/src/input.rs b/wallet/pskt/src/input.rs index 4c25600a1f..8c7102fb86 100644 --- a/wallet/pskt/src/input.rs +++ b/wallet/pskt/src/input.rs @@ -1,7 +1,5 @@ -use crate::{ - utils::{combine_if_no_conflicts, Error as CombineMapErr}, - KeySource, PartialSigs, -}; +use crate::pskt::{KeySource, PartialSigs}; +use crate::utils::{combine_if_no_conflicts, Error as CombineMapErr}; use derive_builder::Builder; use kaspa_consensus_core::{ hashing::sighash_type::{SigHashType, SIG_HASH_ALL}, @@ -47,7 +45,7 @@ pub struct Input { /// scripts necessary for this input to pass validation. pub final_script_sig: Option>, #[serde(skip_serializing, default)] - hidden: PhantomData<()>, // prevents manual filling of fields + pub(crate) hidden: PhantomData<()>, // prevents manual filling of fields #[builder(setter)] /// Proprietary key-value pairs for this output. pub proprietaries: BTreeMap, diff --git a/wallet/pskt/src/lib.rs b/wallet/pskt/src/lib.rs index e26d5c9ea3..a7e423cd47 100644 --- a/wallet/pskt/src/lib.rs +++ b/wallet/pskt/src/lib.rs @@ -1,458 +1,31 @@ -use kaspa_bip32::{secp256k1, DerivationPath, KeyFingerprint}; -use serde::{Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::{collections::BTreeMap, fmt::Display, fmt::Formatter, future::Future, marker::PhantomData, ops::Deref}; - -mod error; -mod global; -mod input; - -mod output; - -mod role; +//! +//! PSKT is a crate for working with Partially Signed Kaspa Transactions (PSKTs). +//! This crate provides following primitives: `PSKT`, `PSKTBuilder` and `Bundle`. +//! The `Bundle` struct is used for PSKT exchange payload serialization and carries +//! multiple `PSKT` instances allowing for exchange of Kaspa sweep transactions. +//! + +pub mod bundle; +pub mod error; +pub mod global; +pub mod input; +pub mod output; +pub mod pskt; +pub mod role; +pub mod wasm; + +mod convert; mod utils; -pub use error::Error; -pub use global::{Global, GlobalBuilder}; -pub use input::{Input, InputBuilder}; -use kaspa_consensus_core::tx::UtxoEntry; -use kaspa_consensus_core::{ - hashing::{sighash::SigHashReusedValues, sighash_type::SigHashType}, - subnets::SUBNETWORK_ID_NATIVE, - tx::{MutableTransaction, SignableTransaction, Transaction, TransactionId, TransactionInput, TransactionOutput}, -}; -use kaspa_txscript::{caches::Cache, TxScriptEngine}; -pub use output::{Output, OutputBuilder}; -pub use role::{Combiner, Constructor, Creator, Extractor, Finalizer, Signer, Updater}; - -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct Inner { - /// The global map. - pub global: Global, - /// The corresponding key-value map for each input in the unsigned transaction. - pub inputs: Vec, - /// The corresponding key-value map for each output in the unsigned transaction. - pub outputs: Vec, -} - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] -#[repr(u8)] -pub enum Version { - #[default] - Zero = 0, -} - -impl Display for Version { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Version::Zero => write!(f, "{}", Version::Zero as u8), - } - } -} - -/// Full information on the used extended public key: fingerprint of the -/// master extended public key and a derivation path from it. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct KeySource { - #[serde(with = "kaspa_utils::serde_bytes_fixed")] - pub key_fingerprint: KeyFingerprint, - pub derivation_path: DerivationPath, -} - -impl KeySource { - pub fn new(key_fingerprint: KeyFingerprint, derivation_path: DerivationPath) -> Self { - Self { key_fingerprint, derivation_path } - } -} - -pub type PartialSigs = BTreeMap; - -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Copy, Clone)] -pub enum Signature { - ECDSA(secp256k1::ecdsa::Signature), - Schnorr(secp256k1::schnorr::Signature), -} - -impl Signature { - pub fn into_bytes(self) -> [u8; 64] { - match self { - Signature::ECDSA(s) => s.serialize_compact(), - Signature::Schnorr(s) => s.serialize(), - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct PSKT { - #[serde(flatten)] - inner_pskt: Inner, - #[serde(skip_serializing, default)] - role: PhantomData, -} - -impl Clone for PSKT { - fn clone(&self) -> Self { - PSKT { inner_pskt: self.inner_pskt.clone(), role: Default::default() } - } -} - -impl Deref for PSKT { - type Target = Inner; - - fn deref(&self) -> &Self::Target { - &self.inner_pskt - } -} - -impl PSKT { - fn unsigned_tx(&self) -> SignableTransaction { - let tx = Transaction::new( - self.global.tx_version, - self.inputs - .iter() - .map(|Input { previous_outpoint, sequence, sig_op_count, .. }| TransactionInput { - previous_outpoint: *previous_outpoint, - signature_script: vec![], - sequence: sequence.unwrap_or(u64::MAX), - sig_op_count: sig_op_count.unwrap_or(0), - }) - .collect(), - self.outputs - .iter() - .map(|Output { amount, script_public_key, .. }: &Output| TransactionOutput { - value: *amount, - script_public_key: script_public_key.clone(), - }) - .collect(), - self.determine_lock_time(), - SUBNETWORK_ID_NATIVE, - 0, - vec![], - ); - let entries = self.inputs.iter().filter_map(|Input { utxo_entry, .. }| utxo_entry.clone()).collect(); - SignableTransaction::with_entries(tx, entries) - } - - fn calculate_id_internal(&self) -> TransactionId { - self.unsigned_tx().tx.id() - } - - fn determine_lock_time(&self) -> u64 { - self.inputs.iter().map(|input: &Input| input.min_time).max().unwrap_or(self.global.fallback_lock_time).unwrap_or(0) - } -} - -impl Default for PSKT { - fn default() -> Self { - PSKT { inner_pskt: Default::default(), role: Default::default() } - } -} - -impl PSKT { - /// Sets the fallback lock time. - pub fn fallback_lock_time(mut self, fallback: u64) -> Self { - self.inner_pskt.global.fallback_lock_time = Some(fallback); - self - } - - // todo generic const - /// Sets the inputs modifiable bit in the transaction modifiable flags. - pub fn inputs_modifiable(mut self) -> Self { - self.inner_pskt.global.inputs_modifiable = true; - self - } - // todo generic const - /// Sets the outputs modifiable bit in the transaction modifiable flags. - pub fn outputs_modifiable(mut self) -> Self { - self.inner_pskt.global.outputs_modifiable = true; - self - } - - pub fn constructor(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -impl PSKT { - // todo generic const - /// Marks that the `PSKT` can not have any more inputs added to it. - pub fn no_more_inputs(mut self) -> Self { - self.inner_pskt.global.inputs_modifiable = false; - self - } - // todo generic const - /// Marks that the `PSKT` can not have any more outputs added to it. - pub fn no_more_outputs(mut self) -> Self { - self.inner_pskt.global.outputs_modifiable = false; - self - } - - /// Adds an input to the PSKT. - pub fn input(mut self, input: Input) -> Self { - self.inner_pskt.inputs.push(input); - self.inner_pskt.global.input_count += 1; - self - } - - /// Adds an output to the PSKT. - pub fn output(mut self, output: Output) -> Self { - self.inner_pskt.outputs.push(output); - self.inner_pskt.global.output_count += 1; - self - } - - /// Returns a PSKT [`Updater`] once construction is completed. - pub fn updater(self) -> PSKT { - let pskt = self.no_more_inputs().no_more_outputs(); - PSKT { inner_pskt: pskt.inner_pskt, role: Default::default() } - } - - pub fn signer(self) -> PSKT { - self.updater().signer() - } - - pub fn combiner(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -impl PSKT { - pub fn set_sequence(mut self, n: u64, input_index: usize) -> Result { - self.inner_pskt.inputs.get_mut(input_index).ok_or(Error::OutOfBounds)?.sequence = Some(n); - Ok(self) - } - - pub fn signer(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } - - pub fn combiner(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -impl PSKT { - // todo use iterator instead of vector - pub fn pass_signature_sync(mut self, sign_fn: SignFn) -> Result - where - E: Display, - SignFn: FnOnce(SignableTransaction, Vec) -> Result, E>, - { - let unsigned_tx = self.unsigned_tx(); - let sighashes = self.inputs.iter().map(|input| input.sighash_type).collect(); - self.inner_pskt.inputs.iter_mut().zip(sign_fn(unsigned_tx, sighashes)?).for_each( - |(input, SignInputOk { signature, pub_key, key_source })| { - input.bip32_derivations.insert(pub_key, key_source); - input.partial_sigs.insert(pub_key, signature); - }, - ); - - Ok(self) - } - // todo use iterator instead of vector - pub async fn pass_signature(mut self, sign_fn: SignFn) -> Result - where - E: Display, - Fut: Future, E>>, - SignFn: FnOnce(SignableTransaction, Vec) -> Fut, - { - let unsigned_tx = self.unsigned_tx(); - let sighashes = self.inputs.iter().map(|input| input.sighash_type).collect(); - self.inner_pskt.inputs.iter_mut().zip(sign_fn(unsigned_tx, sighashes).await?).for_each( - |(input, SignInputOk { signature, pub_key, key_source })| { - input.bip32_derivations.insert(pub_key, key_source); - input.partial_sigs.insert(pub_key, signature); - }, - ); - Ok(self) - } - - pub fn calculate_id(&self) -> TransactionId { - self.calculate_id_internal() - } - - pub fn finalizer(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } - - pub fn combiner(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SignInputOk { - pub signature: Signature, - pub pub_key: secp256k1::PublicKey, - pub key_source: Option, -} - -impl std::ops::Add> for PSKT { - type Output = Result; - - fn add(mut self, mut rhs: PSKT) -> Self::Output { - self.inner_pskt.global = (self.inner_pskt.global + rhs.inner_pskt.global)?; - macro_rules! combine { - ($left:expr, $right:expr, $err: ty) => { - if $left.len() > $right.len() { - $left.iter_mut().zip($right.iter_mut()).try_for_each(|(left, right)| -> Result<(), $err> { - *left = (std::mem::take(left) + std::mem::take(right))?; - Ok(()) - })?; - $left - } else { - $right.iter_mut().zip($left.iter_mut()).try_for_each(|(left, right)| -> Result<(), $err> { - *left = (std::mem::take(left) + std::mem::take(right))?; - Ok(()) - })?; - $right - } - }; - } - // todo add sort to build deterministic combination - self.inner_pskt.inputs = combine!(self.inner_pskt.inputs, rhs.inner_pskt.inputs, input::CombineError); - self.inner_pskt.outputs = combine!(self.inner_pskt.outputs, rhs.inner_pskt.outputs, output::CombineError); - Ok(self) - } -} - -impl PSKT { - pub fn signer(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } - pub fn finalizer(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -impl PSKT { - pub fn finalize_sync( - self, - final_sig_fn: impl FnOnce(&Inner) -> Result>, E>, - ) -> Result> { - let sigs = final_sig_fn(&self); - self.finalize_internal(sigs) - } - - pub async fn finalize(self, final_sig_fn: F) -> Result> - where - E: Display, - F: FnOnce(&Inner) -> Fut, - Fut: Future>, E>>, - { - let sigs = final_sig_fn(&self).await; - self.finalize_internal(sigs) - } - - pub fn id(&self) -> Option { - self.global.id - } - - pub fn extractor(self) -> Result, TxNotFinalized> { - if self.global.id.is_none() { - Err(TxNotFinalized {}) - } else { - Ok(PSKT { inner_pskt: self.inner_pskt, role: Default::default() }) - } - } - - fn finalize_internal(mut self, sigs: Result>, E>) -> Result> { - let sigs = sigs?; - if sigs.len() != self.inputs.len() { - return Err(FinalizeError::WrongFinalizedSigsCount { expected: self.inputs.len(), actual: sigs.len() }); - } - self.inner_pskt.inputs.iter_mut().enumerate().zip(sigs).try_for_each(|((idx, input), sig)| { - if sig.is_empty() { - return Err(FinalizeError::EmptySignature(idx)); - } - input.sequence = Some(input.sequence.unwrap_or(u64::MAX)); // todo discussable - input.final_script_sig = Some(sig); - Ok(()) - })?; - self.inner_pskt.global.id = Some(self.calculate_id_internal()); - Ok(self) - } -} - -impl PSKT { - pub fn extract_tx_unchecked(self) -> Result (Transaction, Vec>), TxNotFinalized> { - let tx = self.unsigned_tx(); - let entries = tx.entries; - let mut tx = tx.tx; - tx.inputs.iter_mut().zip(self.inner_pskt.inputs).try_for_each(|(dest, src)| { - dest.signature_script = src.final_script_sig.ok_or(TxNotFinalized {})?; - Ok(()) - })?; - Ok(move |mass| { - tx.set_mass(mass); - (tx, entries) - }) - } - - pub fn extract_tx(self) -> Result (Transaction, Vec>), ExtractError> { - let (tx, entries) = self.extract_tx_unchecked()?(0); - - let tx = MutableTransaction::with_entries(tx, entries.into_iter().flatten().collect()); - use kaspa_consensus_core::tx::VerifiableTransaction; - { - let tx = tx.as_verifiable(); - let cache = Cache::new(10_000); - let mut reused_values = SigHashReusedValues::new(); - - tx.populated_inputs().enumerate().try_for_each(|(idx, (input, entry))| { - TxScriptEngine::from_transaction_input(&tx, input, idx, entry, &mut reused_values, &cache)?.execute()?; - >::Ok(()) - })?; - } - let entries = tx.entries; - let tx = tx.tx; - let closure = move |mass| { - tx.set_mass(mass); - (tx, entries) - }; - Ok(closure) - } -} - -/// Error combining pskt. -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -pub enum CombineError { - #[error(transparent)] - Global(#[from] global::CombineError), - #[error(transparent)] - Inputs(#[from] input::CombineError), - #[error(transparent)] - Outputs(#[from] output::CombineError), -} - -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -pub enum FinalizeError { - #[error("Signatures count mismatch")] - WrongFinalizedSigsCount { expected: usize, actual: usize }, - #[error("Signatures at index: {0} is empty")] - EmptySignature(usize), - #[error(transparent)] - FinalaziCb(#[from] E), -} - -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -pub enum ExtractError { - #[error(transparent)] - TxScriptError(#[from] kaspa_txscript_errors::TxScriptError), - #[error(transparent)] - TxNotFinalized(#[from] TxNotFinalized), -} - -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -#[error("Transaction is not finalized")] -pub struct TxNotFinalized {} - -#[cfg(test)] -mod tests { - - // #[test] - // fn it_works() { - // let result = add(2, 2); - // assert_eq!(result, 4); - // } +pub mod prelude { + pub use crate::bundle::*; + pub use crate::global::Global; + pub use crate::input::Input; + pub use crate::output::Output; + pub use crate::pskt::*; + + // not quite sure why it warns of unused imports, + // perhaps due to the fact that enums have no variants? + #[allow(unused_imports)] + pub use crate::role::*; } diff --git a/wallet/pskt/src/output.rs b/wallet/pskt/src/output.rs index 952b63d3fb..ce1d2eec10 100644 --- a/wallet/pskt/src/output.rs +++ b/wallet/pskt/src/output.rs @@ -1,5 +1,5 @@ +use crate::pskt::KeySource; use crate::utils::combine_if_no_conflicts; -use crate::KeySource; use derive_builder::Builder; use kaspa_consensus_core::tx::ScriptPublicKey; use serde::{Deserialize, Serialize}; diff --git a/wallet/pskt/src/pskt.rs b/wallet/pskt/src/pskt.rs new file mode 100644 index 0000000000..024f033769 --- /dev/null +++ b/wallet/pskt/src/pskt.rs @@ -0,0 +1,455 @@ +use kaspa_bip32::{secp256k1, DerivationPath, KeyFingerprint}; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; +use std::{collections::BTreeMap, fmt::Display, fmt::Formatter, future::Future, marker::PhantomData, ops::Deref}; + +pub use crate::error::Error; +pub use crate::global::{Global, GlobalBuilder}; +pub use crate::input::{Input, InputBuilder}; +pub use crate::output::{Output, OutputBuilder}; +pub use crate::role::{Combiner, Constructor, Creator, Extractor, Finalizer, Signer, Updater}; +use kaspa_consensus_core::tx::UtxoEntry; +use kaspa_consensus_core::{ + hashing::{sighash::SigHashReusedValues, sighash_type::SigHashType}, + subnets::SUBNETWORK_ID_NATIVE, + tx::{MutableTransaction, SignableTransaction, Transaction, TransactionId, TransactionInput, TransactionOutput}, +}; +use kaspa_txscript::{caches::Cache, TxScriptEngine}; + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct Inner { + /// The global map. + pub global: Global, + /// The corresponding key-value map for each input in the unsigned transaction. + pub inputs: Vec, + /// The corresponding key-value map for each output in the unsigned transaction. + pub outputs: Vec, +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] +#[repr(u8)] +pub enum Version { + #[default] + Zero = 0, +} + +impl Display for Version { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Version::Zero => write!(f, "{}", Version::Zero as u8), + } + } +} + +/// Full information on the used extended public key: fingerprint of the +/// master extended public key and a derivation path from it. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct KeySource { + #[serde(with = "kaspa_utils::serde_bytes_fixed")] + pub key_fingerprint: KeyFingerprint, + pub derivation_path: DerivationPath, +} + +impl KeySource { + pub fn new(key_fingerprint: KeyFingerprint, derivation_path: DerivationPath) -> Self { + Self { key_fingerprint, derivation_path } + } +} + +pub type PartialSigs = BTreeMap; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Copy, Clone)] +pub enum Signature { + ECDSA(secp256k1::ecdsa::Signature), + Schnorr(secp256k1::schnorr::Signature), +} + +impl Signature { + pub fn into_bytes(self) -> [u8; 64] { + match self { + Signature::ECDSA(s) => s.serialize_compact(), + Signature::Schnorr(s) => s.serialize(), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PSKT { + #[serde(flatten)] + inner_pskt: Inner, + #[serde(skip_serializing, default)] + role: PhantomData, +} + +impl From for PSKT { + fn from(inner_pskt: Inner) -> Self { + PSKT { inner_pskt, role: Default::default() } + } +} + +impl Clone for PSKT { + fn clone(&self) -> Self { + PSKT { inner_pskt: self.inner_pskt.clone(), role: Default::default() } + } +} + +impl Deref for PSKT { + type Target = Inner; + + fn deref(&self) -> &Self::Target { + &self.inner_pskt + } +} + +impl PSKT { + fn unsigned_tx(&self) -> SignableTransaction { + let tx = Transaction::new( + self.global.tx_version, + self.inputs + .iter() + .map(|Input { previous_outpoint, sequence, sig_op_count, .. }| TransactionInput { + previous_outpoint: *previous_outpoint, + signature_script: vec![], + sequence: sequence.unwrap_or(u64::MAX), + sig_op_count: sig_op_count.unwrap_or(0), + }) + .collect(), + self.outputs + .iter() + .map(|Output { amount, script_public_key, .. }: &Output| TransactionOutput { + value: *amount, + script_public_key: script_public_key.clone(), + }) + .collect(), + self.determine_lock_time(), + SUBNETWORK_ID_NATIVE, + 0, + vec![], + ); + let entries = self.inputs.iter().filter_map(|Input { utxo_entry, .. }| utxo_entry.clone()).collect(); + SignableTransaction::with_entries(tx, entries) + } + + fn calculate_id_internal(&self) -> TransactionId { + self.unsigned_tx().tx.id() + } + + fn determine_lock_time(&self) -> u64 { + self.inputs.iter().map(|input: &Input| input.min_time).max().unwrap_or(self.global.fallback_lock_time).unwrap_or(0) + } +} + +impl Default for PSKT { + fn default() -> Self { + PSKT { inner_pskt: Default::default(), role: Default::default() } + } +} + +impl PSKT { + /// Sets the fallback lock time. + pub fn fallback_lock_time(mut self, fallback: u64) -> Self { + self.inner_pskt.global.fallback_lock_time = Some(fallback); + self + } + + // todo generic const + /// Sets the inputs modifiable bit in the transaction modifiable flags. + pub fn inputs_modifiable(mut self) -> Self { + self.inner_pskt.global.inputs_modifiable = true; + self + } + // todo generic const + /// Sets the outputs modifiable bit in the transaction modifiable flags. + pub fn outputs_modifiable(mut self) -> Self { + self.inner_pskt.global.outputs_modifiable = true; + self + } + + pub fn constructor(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +impl PSKT { + // todo generic const + /// Marks that the `PSKT` can not have any more inputs added to it. + pub fn no_more_inputs(mut self) -> Self { + self.inner_pskt.global.inputs_modifiable = false; + self + } + // todo generic const + /// Marks that the `PSKT` can not have any more outputs added to it. + pub fn no_more_outputs(mut self) -> Self { + self.inner_pskt.global.outputs_modifiable = false; + self + } + + /// Adds an input to the PSKT. + pub fn input(mut self, input: Input) -> Self { + self.inner_pskt.inputs.push(input); + self.inner_pskt.global.input_count += 1; + self + } + + /// Adds an output to the PSKT. + pub fn output(mut self, output: Output) -> Self { + self.inner_pskt.outputs.push(output); + self.inner_pskt.global.output_count += 1; + self + } + + /// Returns a PSKT [`Updater`] once construction is completed. + pub fn updater(self) -> PSKT { + let pskt = self.no_more_inputs().no_more_outputs(); + PSKT { inner_pskt: pskt.inner_pskt, role: Default::default() } + } + + pub fn signer(self) -> PSKT { + self.updater().signer() + } + + pub fn combiner(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +impl PSKT { + pub fn set_sequence(mut self, n: u64, input_index: usize) -> Result { + self.inner_pskt.inputs.get_mut(input_index).ok_or(Error::OutOfBounds)?.sequence = Some(n); + Ok(self) + } + + pub fn signer(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } + + pub fn combiner(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +impl PSKT { + // todo use iterator instead of vector + pub fn pass_signature_sync(mut self, sign_fn: SignFn) -> Result + where + E: Display, + SignFn: FnOnce(SignableTransaction, Vec) -> Result, E>, + { + let unsigned_tx = self.unsigned_tx(); + let sighashes = self.inputs.iter().map(|input| input.sighash_type).collect(); + self.inner_pskt.inputs.iter_mut().zip(sign_fn(unsigned_tx, sighashes)?).for_each( + |(input, SignInputOk { signature, pub_key, key_source })| { + input.bip32_derivations.insert(pub_key, key_source); + input.partial_sigs.insert(pub_key, signature); + }, + ); + + Ok(self) + } + // todo use iterator instead of vector + pub async fn pass_signature(mut self, sign_fn: SignFn) -> Result + where + E: Display, + Fut: Future, E>>, + SignFn: FnOnce(SignableTransaction, Vec) -> Fut, + { + let unsigned_tx = self.unsigned_tx(); + let sighashes = self.inputs.iter().map(|input| input.sighash_type).collect(); + self.inner_pskt.inputs.iter_mut().zip(sign_fn(unsigned_tx, sighashes).await?).for_each( + |(input, SignInputOk { signature, pub_key, key_source })| { + input.bip32_derivations.insert(pub_key, key_source); + input.partial_sigs.insert(pub_key, signature); + }, + ); + Ok(self) + } + + pub fn calculate_id(&self) -> TransactionId { + self.calculate_id_internal() + } + + pub fn finalizer(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } + + pub fn combiner(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SignInputOk { + pub signature: Signature, + pub pub_key: secp256k1::PublicKey, + pub key_source: Option, +} + +impl std::ops::Add> for PSKT { + type Output = Result; + + fn add(mut self, mut rhs: PSKT) -> Self::Output { + self.inner_pskt.global = (self.inner_pskt.global + rhs.inner_pskt.global)?; + macro_rules! combine { + ($left:expr, $right:expr, $err: ty) => { + if $left.len() > $right.len() { + $left.iter_mut().zip($right.iter_mut()).try_for_each(|(left, right)| -> Result<(), $err> { + *left = (std::mem::take(left) + std::mem::take(right))?; + Ok(()) + })?; + $left + } else { + $right.iter_mut().zip($left.iter_mut()).try_for_each(|(left, right)| -> Result<(), $err> { + *left = (std::mem::take(left) + std::mem::take(right))?; + Ok(()) + })?; + $right + } + }; + } + // todo add sort to build deterministic combination + self.inner_pskt.inputs = combine!(self.inner_pskt.inputs, rhs.inner_pskt.inputs, crate::input::CombineError); + self.inner_pskt.outputs = combine!(self.inner_pskt.outputs, rhs.inner_pskt.outputs, crate::output::CombineError); + Ok(self) + } +} + +impl PSKT { + pub fn signer(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } + pub fn finalizer(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +impl PSKT { + pub fn finalize_sync( + self, + final_sig_fn: impl FnOnce(&Inner) -> Result>, E>, + ) -> Result> { + let sigs = final_sig_fn(&self); + self.finalize_internal(sigs) + } + + pub async fn finalize(self, final_sig_fn: F) -> Result> + where + E: Display, + F: FnOnce(&Inner) -> Fut, + Fut: Future>, E>>, + { + let sigs = final_sig_fn(&self).await; + self.finalize_internal(sigs) + } + + pub fn id(&self) -> Option { + self.global.id + } + + pub fn extractor(self) -> Result, TxNotFinalized> { + if self.global.id.is_none() { + Err(TxNotFinalized {}) + } else { + Ok(PSKT { inner_pskt: self.inner_pskt, role: Default::default() }) + } + } + + fn finalize_internal(mut self, sigs: Result>, E>) -> Result> { + let sigs = sigs?; + if sigs.len() != self.inputs.len() { + return Err(FinalizeError::WrongFinalizedSigsCount { expected: self.inputs.len(), actual: sigs.len() }); + } + self.inner_pskt.inputs.iter_mut().enumerate().zip(sigs).try_for_each(|((idx, input), sig)| { + if sig.is_empty() { + return Err(FinalizeError::EmptySignature(idx)); + } + input.sequence = Some(input.sequence.unwrap_or(u64::MAX)); // todo discussable + input.final_script_sig = Some(sig); + Ok(()) + })?; + self.inner_pskt.global.id = Some(self.calculate_id_internal()); + Ok(self) + } +} + +impl PSKT { + pub fn extract_tx_unchecked(self) -> Result (Transaction, Vec>), TxNotFinalized> { + let tx = self.unsigned_tx(); + let entries = tx.entries; + let mut tx = tx.tx; + tx.inputs.iter_mut().zip(self.inner_pskt.inputs).try_for_each(|(dest, src)| { + dest.signature_script = src.final_script_sig.ok_or(TxNotFinalized {})?; + Ok(()) + })?; + Ok(move |mass| { + tx.set_mass(mass); + (tx, entries) + }) + } + + pub fn extract_tx(self) -> Result (Transaction, Vec>), ExtractError> { + let (tx, entries) = self.extract_tx_unchecked()?(0); + + let tx = MutableTransaction::with_entries(tx, entries.into_iter().flatten().collect()); + use kaspa_consensus_core::tx::VerifiableTransaction; + { + let tx = tx.as_verifiable(); + let cache = Cache::new(10_000); + let mut reused_values = SigHashReusedValues::new(); + + tx.populated_inputs().enumerate().try_for_each(|(idx, (input, entry))| { + TxScriptEngine::from_transaction_input(&tx, input, idx, entry, &mut reused_values, &cache)?.execute()?; + >::Ok(()) + })?; + } + let entries = tx.entries; + let tx = tx.tx; + let closure = move |mass| { + tx.set_mass(mass); + (tx, entries) + }; + Ok(closure) + } +} + +/// Error combining pskt. +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +pub enum CombineError { + #[error(transparent)] + Global(#[from] crate::global::CombineError), + #[error(transparent)] + Inputs(#[from] crate::input::CombineError), + #[error(transparent)] + Outputs(#[from] crate::output::CombineError), +} + +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +pub enum FinalizeError { + #[error("Signatures count mismatch")] + WrongFinalizedSigsCount { expected: usize, actual: usize }, + #[error("Signatures at index: {0} is empty")] + EmptySignature(usize), + #[error(transparent)] + FinalaziCb(#[from] E), +} + +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +pub enum ExtractError { + #[error(transparent)] + TxScriptError(#[from] kaspa_txscript_errors::TxScriptError), + #[error(transparent)] + TxNotFinalized(#[from] TxNotFinalized), +} + +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +#[error("Transaction is not finalized")] +pub struct TxNotFinalized {} + +#[cfg(test)] +mod tests { + + // #[test] + // fn it_works() { + // let result = add(2, 2); + // assert_eq!(result, 4); + // } +} diff --git a/wallet/pskt/src/wasm/bundle.rs b/wallet/pskt/src/wasm/bundle.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/wallet/pskt/src/wasm/bundle.rs @@ -0,0 +1 @@ + diff --git a/wallet/pskt/src/wasm/error.rs b/wallet/pskt/src/wasm/error.rs new file mode 100644 index 0000000000..77fb0d8b16 --- /dev/null +++ b/wallet/pskt/src/wasm/error.rs @@ -0,0 +1,64 @@ +use super::pskt::State; +use thiserror::Error; +use wasm_bindgen::prelude::*; + +#[derive(Error, Debug)] +pub enum Error { + #[error("{0}")] + Custom(String), + + #[error("Unexpected state: {0}")] + State(String), + + #[error("Constructor argument must be a valid payload, another PSKT instance, Transaction or undefined")] + Ctor(String), + + #[error("Invalid payload")] + InvalidPayload, + + #[error("Transaction not finalized")] + TxNotFinalized(#[from] crate::pskt::TxNotFinalized), + + #[error(transparent)] + Wasm(#[from] workflow_wasm::error::Error), + + #[error("Create state is not allowed for PSKT initialized from transaction or a payload")] + CreateNotAllowed, + + #[error("PSKT must be initialized with a payload or CREATE role")] + NotInitialized, + + #[error(transparent)] + ConsensusClient(#[from] kaspa_consensus_client::error::Error), + + #[error(transparent)] + Pskt(#[from] crate::error::Error), +} + +impl Error { + pub fn custom(msg: T) -> Self { + Error::Custom(msg.to_string()) + } + + pub fn state(state: impl AsRef) -> Self { + Error::State(state.as_ref().display().to_string()) + } +} + +impl From<&str> for Error { + fn from(msg: &str) -> Self { + Error::Custom(msg.to_string()) + } +} + +impl From for Error { + fn from(msg: String) -> Self { + Error::Custom(msg) + } +} + +impl From for JsValue { + fn from(err: Error) -> Self { + JsValue::from_str(&err.to_string()) + } +} diff --git a/wallet/pskt/src/wasm/input.rs b/wallet/pskt/src/wasm/input.rs new file mode 100644 index 0000000000..b6a827daf1 --- /dev/null +++ b/wallet/pskt/src/wasm/input.rs @@ -0,0 +1 @@ +// TODO - InputBuilder & Input diff --git a/wallet/pskt/src/wasm/mod.rs b/wallet/pskt/src/wasm/mod.rs new file mode 100644 index 0000000000..f5e9bea3fb --- /dev/null +++ b/wallet/pskt/src/wasm/mod.rs @@ -0,0 +1,6 @@ +pub mod bundle; +pub mod error; +pub mod input; +pub mod output; +pub mod pskt; +pub mod result; diff --git a/wallet/pskt/src/wasm/output.rs b/wallet/pskt/src/wasm/output.rs new file mode 100644 index 0000000000..eb91824d1a --- /dev/null +++ b/wallet/pskt/src/wasm/output.rs @@ -0,0 +1 @@ +// TODO - OutputBuilder & Output diff --git a/wallet/pskt/src/wasm/pskt.rs b/wallet/pskt/src/wasm/pskt.rs new file mode 100644 index 0000000000..b940ff5181 --- /dev/null +++ b/wallet/pskt/src/wasm/pskt.rs @@ -0,0 +1,310 @@ +use crate::pskt::PSKT as Native; +use crate::role::*; +use kaspa_consensus_core::tx::TransactionId; +use wasm_bindgen::prelude::*; +// use js_sys::Object; +use crate::pskt::Inner; +use kaspa_consensus_client::{ITransactionInput, ITransactionOutput, Transaction, TransactionInput, TransactionOutput}; +use serde::{Deserialize, Serialize}; +use std::sync::MutexGuard; +use std::sync::{Arc, Mutex}; +use workflow_wasm::{ + convert::{Cast, CastFromJs, TryCastFromJs}, + // extensions::object::*, + // error::Error as CastError, +}; + +use super::error::*; +use super::result::*; + +#[derive(Clone, Serialize, Deserialize)] +#[serde(tag = "state", content = "payload")] +pub enum State { + NoOp(Option), + Creator(Native), + Constructor(Native), + Updater(Native), + Signer(Native), + Combiner(Native), + Finalizer(Native), + Extractor(Native), +} + +impl AsRef for State { + fn as_ref(&self) -> &State { + self + } +} + +impl State { + // this is not a Display trait intentionally + pub fn display(&self) -> &'static str { + match self { + State::NoOp(_) => "Init", + State::Creator(_) => "Creator", + State::Constructor(_) => "Constructor", + State::Updater(_) => "Updater", + State::Signer(_) => "Signer", + State::Combiner(_) => "Combiner", + State::Finalizer(_) => "Finalizer", + State::Extractor(_) => "Extractor", + } + } +} + +impl From for PSKT { + fn from(state: State) -> Self { + PSKT { state: Arc::new(Mutex::new(Some(state))) } + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "PSKT | Transaction | string | undefined")] + pub type CtorT; +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct Payload { + data: String, +} + +impl TryFrom for Native { + type Error = Error; + + fn try_from(value: Payload) -> Result { + let Payload { data } = value; + if data.starts_with("PSKT") { + unimplemented!("PSKT binary serialization") + } else { + Ok(serde_json::from_str(&data).map_err(|err| format!("Invalid JSON: {err}"))?) + } + } +} + +#[wasm_bindgen(inspectable)] +#[derive(Clone, CastFromJs)] +pub struct PSKT { + state: Arc>>, +} + +impl TryCastFromJs for PSKT { + type Error = Error; + fn try_cast_from(value: impl AsRef) -> std::result::Result, Self::Error> { + Self::resolve(&value, || { + if let Some(data) = value.as_ref().as_string() { + let pskt_inner: Inner = serde_json::from_str(&data).map_err(|_| Error::InvalidPayload)?; + Ok(PSKT::from(State::NoOp(Some(pskt_inner)))) + } else if let Ok(transaction) = Transaction::try_owned_from(&value) { + let pskt_inner: Inner = transaction.into(); + Ok(PSKT::from(State::NoOp(Some(pskt_inner)))) + } else { + Err(Error::InvalidPayload) + } + }) + } +} + +#[wasm_bindgen] +impl PSKT { + #[wasm_bindgen(constructor)] + pub fn new(payload: CtorT) -> Result { + PSKT::try_owned_from(payload.unchecked_into::().as_ref()).map_err(|err| Error::Ctor(err.to_string())) + } + + #[wasm_bindgen(getter, js_name = "role")] + pub fn role_getter(&self) -> String { + self.state().as_ref().unwrap().display().to_string() + } + + #[wasm_bindgen(getter, js_name = "payload")] + pub fn payload_getter(&self) -> JsValue { + let state = self.state(); + serde_wasm_bindgen::to_value(state.as_ref().unwrap()).unwrap() + } + + fn state(&self) -> MutexGuard> { + self.state.lock().unwrap() + } + + fn take(&self) -> State { + self.state.lock().unwrap().take().unwrap() + } + + fn replace(&self, state: State) -> Result { + self.state.lock().unwrap().replace(state); + Ok(self.clone()) + } + + /// Change role to `CREATOR` + pub fn creator(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => match inner { + None => State::Creator(Native::default()), + Some(_) => Err(Error::CreateNotAllowed)?, + }, + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `CONSTRUCTOR` + pub fn constructor(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Constructor(inner.ok_or(Error::NotInitialized)?.into()), + State::Creator(pskt) => State::Constructor(pskt.constructor()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `UPDATER` + pub fn updater(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Updater(inner.ok_or(Error::NotInitialized)?.into()), + State::Constructor(constructor) => State::Updater(constructor.updater()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `SIGNER` + pub fn signer(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Signer(inner.ok_or(Error::NotInitialized)?.into()), + State::Constructor(pskt) => State::Signer(pskt.signer()), + State::Updater(pskt) => State::Signer(pskt.signer()), + State::Combiner(pskt) => State::Signer(pskt.signer()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `COMBINER` + pub fn combiner(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Combiner(inner.ok_or(Error::NotInitialized)?.into()), + State::Constructor(pskt) => State::Combiner(pskt.combiner()), + State::Updater(pskt) => State::Combiner(pskt.combiner()), + State::Signer(pskt) => State::Combiner(pskt.combiner()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `FINALIZER` + pub fn finalizer(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Finalizer(inner.ok_or(Error::NotInitialized)?.into()), + State::Combiner(pskt) => State::Finalizer(pskt.finalizer()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `EXTRACTOR` + pub fn extractor(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Extractor(inner.ok_or(Error::NotInitialized)?.into()), + State::Finalizer(pskt) => State::Extractor(pskt.extractor()?), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = fallbackLockTime)] + pub fn fallback_lock_time(&self, lock_time: u64) -> Result { + let state = match self.take() { + State::Creator(pskt) => State::Creator(pskt.fallback_lock_time(lock_time)), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = inputsModifiable)] + pub fn inputs_modifiable(&self) -> Result { + let state = match self.take() { + State::Creator(pskt) => State::Creator(pskt.inputs_modifiable()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = outputsModifiable)] + pub fn outputs_modifiable(&self) -> Result { + let state = match self.take() { + State::Creator(pskt) => State::Creator(pskt.outputs_modifiable()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = noMoreInputs)] + pub fn no_more_inputs(&self) -> Result { + let state = match self.take() { + State::Constructor(pskt) => State::Constructor(pskt.no_more_inputs()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = noMoreOutputs)] + pub fn no_more_outputs(&self) -> Result { + let state = match self.take() { + State::Constructor(pskt) => State::Constructor(pskt.no_more_outputs()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + pub fn input(&self, input: &ITransactionInput) -> Result { + let input = TransactionInput::try_owned_from(input)?; + let state = match self.take() { + State::Constructor(pskt) => State::Constructor(pskt.input(input.try_into()?)), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + pub fn output(&self, output: &ITransactionOutput) -> Result { + let output = TransactionOutput::try_owned_from(output)?; + let state = match self.take() { + State::Constructor(pskt) => State::Constructor(pskt.output(output.try_into()?)), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = setSequence)] + pub fn set_sequence(&self, n: u64, input_index: usize) -> Result { + let state = match self.take() { + State::Updater(pskt) => State::Updater(pskt.set_sequence(n, input_index)?), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = calculateId)] + pub fn calculate_id(&self) -> Result { + let state = self.state(); + match state.as_ref().unwrap() { + State::Signer(pskt) => Ok(pskt.calculate_id()), + state => Err(Error::state(state))?, + } + } +} diff --git a/wallet/pskt/src/wasm/result.rs b/wallet/pskt/src/wasm/result.rs new file mode 100644 index 0000000000..32f663388a --- /dev/null +++ b/wallet/pskt/src/wasm/result.rs @@ -0,0 +1 @@ +pub type Result = std::result::Result; From 1dddd9a1cea784271eac2ede0ac06f44b7a56c35 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 28 Jun 2024 18:42:45 +0300 Subject: [PATCH 052/158] wallet guard implementation to block concurrent async access to account operations --- cli/src/cli.rs | 35 ++++++++--- cli/src/extensions/transaction.rs | 28 ++++++--- cli/src/modules/history.rs | 14 ++++- cli/src/modules/reload.rs | 6 +- cli/src/modules/wallet.rs | 7 ++- cli/src/wizards/wallet.rs | 7 ++- wallet/core/src/account/mod.rs | 3 +- wallet/core/src/prelude.rs | 1 + wallet/core/src/wallet/api.rs | 100 +++++++++++++++++++----------- wallet/core/src/wallet/mod.rs | 56 ++++++++++++++--- 10 files changed, 193 insertions(+), 64 deletions(-) diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 4f562e7494..f8d41ee762 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -311,7 +311,9 @@ impl KaspaCli { Events::SyncState { sync_state } => { if sync_state.is_synced() && this.wallet().is_open() { - if let Err(error) = this.wallet().reload(false).await { + let guard = this.wallet().guard(); + let guard = guard.lock().await; + if let Err(error) = this.wallet().reload(false, &guard).await { terrorln!(this, "Unable to reload wallet: {error}"); } } @@ -383,8 +385,11 @@ impl KaspaCli { record } => { if !this.is_mutted() || (this.is_mutted() && this.flags.get(Track::Pending)) { + let guard = this.wallet.guard(); + let guard = guard.lock().await; + let include_utxos = this.flags.get(Track::Utxo); - let tx = record.format_transaction_with_state(&this.wallet,Some("reorg"),include_utxos).await; + let tx = record.format_transaction_with_state(&this.wallet,Some("reorg"),include_utxos, &guard).await; tx.iter().for_each(|line|tprintln!(this,"{NOTIFY} {line}")); } }, @@ -393,8 +398,11 @@ impl KaspaCli { } => { // Pending and coinbase stasis fall under the same `Track` category if !this.is_mutted() || (this.is_mutted() && this.flags.get(Track::Pending)) { + let guard = this.wallet.guard(); + let guard = guard.lock().await; + let include_utxos = this.flags.get(Track::Utxo); - let tx = record.format_transaction_with_state(&this.wallet,Some("stasis"),include_utxos).await; + let tx = record.format_transaction_with_state(&this.wallet,Some("stasis"),include_utxos, &guard).await; tx.iter().for_each(|line|tprintln!(this,"{NOTIFY} {line}")); } }, @@ -411,8 +419,11 @@ impl KaspaCli { record } => { if !this.is_mutted() || (this.is_mutted() && this.flags.get(Track::Pending)) { + let guard = this.wallet.guard(); + let guard = guard.lock().await; + let include_utxos = this.flags.get(Track::Utxo); - let tx = record.format_transaction_with_state(&this.wallet,Some("pending"),include_utxos).await; + let tx = record.format_transaction_with_state(&this.wallet,Some("pending"),include_utxos, &guard).await; tx.iter().for_each(|line|tprintln!(this,"{NOTIFY} {line}")); } }, @@ -420,8 +431,11 @@ impl KaspaCli { record } => { if !this.is_mutted() || (this.is_mutted() && this.flags.get(Track::Tx)) { + let guard = this.wallet.guard(); + let guard = guard.lock().await; + let include_utxos = this.flags.get(Track::Utxo); - let tx = record.format_transaction_with_state(&this.wallet,Some("confirmed"),include_utxos).await; + let tx = record.format_transaction_with_state(&this.wallet,Some("confirmed"),include_utxos, &guard).await; tx.iter().for_each(|line|tprintln!(this,"{NOTIFY} {line}")); } }, @@ -532,6 +546,9 @@ impl KaspaCli { } async fn select_account_with_args(&self, autoselect: bool) -> Result> { + let guard = self.wallet.guard(); + let guard = guard.lock().await; + let mut selection = None; let mut list_by_key = Vec::<(Arc, Vec<(usize, Arc)>)>::new(); @@ -540,7 +557,7 @@ impl KaspaCli { let mut keys = self.wallet.keys().await?; while let Some(key) = keys.try_next().await? { let mut prv_key_accounts = Vec::new(); - let mut accounts = self.wallet.accounts(Some(key.id)).await?; + let mut accounts = self.wallet.accounts(Some(key.id), &guard).await?; while let Some(account) = accounts.next().await { let account = account?; prv_key_accounts.push((flat_list.len(), account.clone())); @@ -643,12 +660,16 @@ impl KaspaCli { } pub async fn list(&self) -> Result<()> { + let guard = self.wallet.guard(); + let guard = guard.lock().await; + let mut keys = self.wallet.keys().await?; tprintln!(self); while let Some(key) = keys.try_next().await? { tprintln!(self, "• {}", style(&key).dim()); - let mut accounts = self.wallet.accounts(Some(key.id)).await?; + + let mut accounts = self.wallet.accounts(Some(key.id), &guard).await?; while let Some(account) = accounts.try_next().await? { let receive_address = account.receive_address()?; tprintln!(self, " • {}", account.get_list_string()?); diff --git a/cli/src/extensions/transaction.rs b/cli/src/extensions/transaction.rs index 70d73615ac..0f88169bff 100644 --- a/cli/src/extensions/transaction.rs +++ b/cli/src/extensions/transaction.rs @@ -48,8 +48,14 @@ impl TransactionTypeExtension for TransactionKind { #[async_trait] pub trait TransactionExtension { - async fn format_transaction(&self, wallet: &Arc, include_utxos: bool) -> Vec; - async fn format_transaction_with_state(&self, wallet: &Arc, state: Option<&str>, include_utxos: bool) -> Vec; + async fn format_transaction(&self, wallet: &Arc, include_utxos: bool, guard: &AsyncMutexGuard<'_, ()>) -> Vec; + async fn format_transaction_with_state( + &self, + wallet: &Arc, + state: Option<&str>, + include_utxos: bool, + guard: &AsyncMutexGuard<'_, ()>, + ) -> Vec; async fn format_transaction_with_args( &self, wallet: &Arc, @@ -58,17 +64,24 @@ pub trait TransactionExtension { include_utxos: bool, history: bool, account: Option>, + guard: &AsyncMutexGuard<'_, ()>, ) -> Vec; } #[async_trait] impl TransactionExtension for TransactionRecord { - async fn format_transaction(&self, wallet: &Arc, include_utxos: bool) -> Vec { - self.format_transaction_with_args(wallet, None, None, include_utxos, false, None).await + async fn format_transaction(&self, wallet: &Arc, include_utxos: bool, guard: &AsyncMutexGuard<'_, ()>) -> Vec { + self.format_transaction_with_args(wallet, None, None, include_utxos, false, None, guard).await } - async fn format_transaction_with_state(&self, wallet: &Arc, state: Option<&str>, include_utxos: bool) -> Vec { - self.format_transaction_with_args(wallet, state, None, include_utxos, false, None).await + async fn format_transaction_with_state( + &self, + wallet: &Arc, + state: Option<&str>, + include_utxos: bool, + guard: &AsyncMutexGuard<'_, ()>, + ) -> Vec { + self.format_transaction_with_args(wallet, state, None, include_utxos, false, None, guard).await } async fn format_transaction_with_args( @@ -79,6 +92,7 @@ impl TransactionExtension for TransactionRecord { include_utxos: bool, history: bool, account: Option>, + guard: &AsyncMutexGuard<'_, ()>, ) -> Vec { let TransactionRecord { id, binding, block_daa_score, transaction_data, .. } = self; @@ -88,7 +102,7 @@ impl TransactionExtension for TransactionRecord { let account = if let Some(account) = account { Some(account) } else { - wallet.get_account_by_id(account_id).await.ok().flatten() + wallet.get_account_by_id(account_id, guard).await.ok().flatten() }; if let Some(account) = account { diff --git a/cli/src/modules/history.rs b/cli/src/modules/history.rs index 299701c51f..8fdf31f4db 100644 --- a/cli/src/modules/history.rs +++ b/cli/src/modules/history.rs @@ -10,6 +10,9 @@ impl History { async fn main(self: Arc, ctx: &Arc, mut argv: Vec, _cmd: &str) -> Result<()> { let ctx = ctx.clone().downcast_arc::()?; + let guard = ctx.wallet().guard(); + let guard = guard.lock().await; + if argv.is_empty() { self.display_help(ctx, argv).await?; return Ok(()); @@ -34,7 +37,15 @@ impl History { match store.load_single(&binding, &network_id, &txid).await { Ok(tx) => { let lines = tx - .format_transaction_with_args(&ctx.wallet(), None, current_daa_score, true, true, Some(account.clone())) + .format_transaction_with_args( + &ctx.wallet(), + None, + current_daa_score, + true, + true, + Some(account.clone()), + &guard, + ) .await; lines.iter().for_each(|line| tprintln!(ctx, "{line}")); } @@ -116,6 +127,7 @@ impl History { include_utxo, true, Some(account.clone()), + &guard, ) .await; lines.iter().for_each(|line| tprintln!(ctx, "{line}")); diff --git a/cli/src/modules/reload.rs b/cli/src/modules/reload.rs index bc1eb717eb..b4c1ed7a52 100644 --- a/cli/src/modules/reload.rs +++ b/cli/src/modules/reload.rs @@ -10,8 +10,12 @@ impl Reload { // workflow_dom::utils::window().location().reload().ok(); let ctx = ctx.clone().downcast_arc::()?; + + let guard = ctx.wallet().guard(); + let guard = guard.lock().await; + tprintln!(ctx, "{}", style("reloading wallet ...").magenta()); - ctx.wallet().reload(true).await?; + ctx.wallet().reload(true, &guard).await?; Ok(()) } diff --git a/cli/src/modules/wallet.rs b/cli/src/modules/wallet.rs index 6019b19086..0c5f44211d 100644 --- a/cli/src/modules/wallet.rs +++ b/cli/src/modules/wallet.rs @@ -9,6 +9,9 @@ impl Wallet { async fn main(self: Arc, ctx: &Arc, mut argv: Vec, cmd: &str) -> Result<()> { let ctx = ctx.clone().downcast_arc::()?; + let guard = ctx.wallet().guard(); + let guard = guard.lock().await; + if argv.is_empty() { return self.display_help(ctx, argv).await; } @@ -67,8 +70,8 @@ impl Wallet { let (wallet_secret, _) = ctx.ask_wallet_secret(None).await?; let _ = ctx.notifier().show(Notification::Processing).await; let args = WalletOpenArgs::default_with_legacy_accounts(); - ctx.wallet().open(&wallet_secret, name, args).await?; - ctx.wallet().activate_accounts(None).await?; + ctx.wallet().open(&wallet_secret, name, args, &guard).await?; + ctx.wallet().activate_accounts(None, &guard).await?; } "close" => { ctx.wallet().close().await?; diff --git a/cli/src/wizards/wallet.rs b/cli/src/wizards/wallet.rs index 8563a8619b..c89e57d2ab 100644 --- a/cli/src/wizards/wallet.rs +++ b/cli/src/wizards/wallet.rs @@ -8,6 +8,9 @@ pub(crate) async fn create(ctx: &Arc, name: Option<&str>, import_with_ let term = ctx.term(); let wallet = ctx.wallet(); + let guard = ctx.wallet().guard(); + let guard = guard.lock().await; + // TODO @aspect let word_count = WordCount::Words12; @@ -173,8 +176,8 @@ pub(crate) async fn create(ctx: &Arc, name: Option<&str>, import_with_ term.writeln(style(receive_address).blue().to_string()); term.writeln(""); - wallet.open(&wallet_secret, name.map(String::from), WalletOpenArgs::default_with_legacy_accounts()).await?; - wallet.activate_accounts(None).await?; + wallet.open(&wallet_secret, name.map(String::from), WalletOpenArgs::default_with_legacy_accounts(), &guard).await?; + wallet.activate_accounts(None, &guard).await?; Ok(()) } diff --git a/wallet/core/src/account/mod.rs b/wallet/core/src/account/mod.rs index b921bf4914..9a4bd940af 100644 --- a/wallet/core/src/account/mod.rs +++ b/wallet/core/src/account/mod.rs @@ -358,13 +358,14 @@ pub trait Account: AnySync + Send + Sync + 'static { payment_secret: Option, abortable: &Abortable, notifier: Option, + guard: &AsyncMutexGuard<'_, ()>, ) -> Result<(GeneratorSummary, Vec)> { let keydata = self.prv_key_data(wallet_secret).await?; let signer = Arc::new(Signer::new(self.clone().as_dyn_arc(), keydata, payment_secret)); let destination_account = self .wallet() - .get_account_by_id(&destination_account_id) + .get_account_by_id(&destination_account_id, guard) .await? .ok_or_else(|| Error::AccountNotFound(destination_account_id))?; diff --git a/wallet/core/src/prelude.rs b/wallet/core/src/prelude.rs index eb9bb2b1e9..0b6963bc78 100644 --- a/wallet/core/src/prelude.rs +++ b/wallet/core/src/prelude.rs @@ -17,6 +17,7 @@ pub use crate::tx::{Fees, PaymentDestination, PaymentOutput, PaymentOutputs}; pub use crate::utxo::balance::{Balance, BalanceStrings}; pub use crate::wallet::args::*; pub use crate::wallet::Wallet; +pub use async_std::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard}; pub use kaspa_addresses::{Address, Prefix as AddressPrefix}; pub use kaspa_bip32::{Language, Mnemonic, WordCount}; pub use kaspa_wallet_keys::secret::Secret; diff --git a/wallet/core/src/wallet/api.rs b/wallet/core/src/wallet/api.rs index 313759ba72..08e945921c 100644 --- a/wallet/core/src/wallet/api.rs +++ b/wallet/core/src/wallet/api.rs @@ -20,6 +20,9 @@ impl WalletApi for super::Wallet { } async fn get_status_call(self: Arc, request: GetStatusRequest) -> Result { + let guard = self.guard(); + let guard = guard.lock().await; + let GetStatusRequest { name } = request; let context = name.and_then(|name| self.inner.retained_contexts.lock().unwrap().get(&name).cloned()); @@ -34,7 +37,7 @@ impl WalletApi for super::Wallet { let (wallet_descriptor, account_descriptors) = if self.is_open() { let wallet_descriptor = self.descriptor(); - let account_descriptors = self.account_descriptors().await.ok(); + let account_descriptors = self.account_descriptors(&guard).await.ok(); (wallet_descriptor, account_descriptors) } else { (None, None) @@ -143,9 +146,12 @@ impl WalletApi for super::Wallet { } async fn wallet_open_call(self: Arc, request: WalletOpenRequest) -> Result { + let guard = self.guard(); + let guard = guard.lock().await; + let WalletOpenRequest { wallet_secret, filename, account_descriptors, legacy_accounts } = request; let args = WalletOpenArgs { account_descriptors, legacy_accounts: legacy_accounts.unwrap_or_default() }; - let account_descriptors = self.open(&wallet_secret, filename, args).await?; + let account_descriptors = self.open(&wallet_secret, filename, args, &guard).await?; Ok(WalletOpenResponse { account_descriptors }) } @@ -159,7 +165,11 @@ impl WalletApi for super::Wallet { if !self.is_open() { return Err(Error::WalletNotOpen); } - self.reload(reactivate).await?; + + let guard = self.guard(); + let guard = guard.lock().await; + + self.reload(reactivate, &guard).await?; Ok(WalletReloadResponse {}) } @@ -222,7 +232,10 @@ impl WalletApi for super::Wallet { async fn accounts_rename_call(self: Arc, request: AccountsRenameRequest) -> Result { let AccountsRenameRequest { account_id, name, wallet_secret } = request; - let account = self.get_account_by_id(&account_id).await?.ok_or(Error::AccountNotFound(account_id))?; + let guard = self.guard(); + let guard = guard.lock().await; + + let account = self.get_account_by_id(&account_id, &guard).await?.ok_or(Error::AccountNotFound(account_id))?; account.rename(&wallet_secret, name.as_deref()).await?; Ok(AccountsRenameResponse {}) @@ -231,8 +244,11 @@ impl WalletApi for super::Wallet { async fn accounts_select_call(self: Arc, request: AccountsSelectRequest) -> Result { let AccountsSelectRequest { account_id } = request; + let guard = self.guard(); + let guard = guard.lock().await; + if let Some(account_id) = account_id { - let account = self.get_account_by_id(&account_id).await?.ok_or(Error::AccountNotFound(account_id))?; + let account = self.get_account_by_id(&account_id, &guard).await?.ok_or(Error::AccountNotFound(account_id))?; self.select(Some(&account)).await?; } else { self.select(None).await?; @@ -243,34 +259,20 @@ impl WalletApi for super::Wallet { } async fn accounts_enumerate_call(self: Arc, _request: AccountsEnumerateRequest) -> Result { - // let iter = self.inner.store.as_account_store().unwrap().iter(None).await.unwrap(); - // let wallet = self.clone(); - - // let stream = iter.then(move |stored| { - // let wallet = wallet.clone(); - - // async move { - // let (stored_account, stored_metadata) = stored.unwrap(); - // if let Some(account) = wallet.legacy_accounts().get(&stored_account.id) { - // account.descriptor() - // } else if let Some(account) = wallet.active_accounts().get(&stored_account.id) { - // account.descriptor() - // } else { - // try_load_account(&wallet, stored_account, stored_metadata).await?.descriptor() - // } - // } - // }); - - // let account_descriptors = stream.try_collect::>().await?; - - let account_descriptors = self.account_descriptors().await?; + let guard = self.guard(); + let guard = guard.lock().await; + + let account_descriptors = self.account_descriptors(&guard).await?; Ok(AccountsEnumerateResponse { account_descriptors }) } async fn accounts_activate_call(self: Arc, request: AccountsActivateRequest) -> Result { let AccountsActivateRequest { account_ids } = request; - self.activate_accounts(account_ids.as_deref()).await?; + let guard = self.guard(); + let guard = guard.lock().await; + + self.activate_accounts(account_ids.as_deref(), &guard).await?; Ok(AccountsActivateResponse {}) } @@ -278,7 +280,10 @@ impl WalletApi for super::Wallet { async fn accounts_deactivate_call(self: Arc, request: AccountsDeactivateRequest) -> Result { let AccountsDeactivateRequest { account_ids } = request; - self.deactivate_accounts(account_ids.as_deref()).await?; + let guard = self.guard(); + let guard = guard.lock().await; + + self.deactivate_accounts(account_ids.as_deref(), &guard).await?; Ok(AccountsDeactivateResponse {}) } @@ -296,7 +301,10 @@ impl WalletApi for super::Wallet { async fn accounts_create_call(self: Arc, request: AccountsCreateRequest) -> Result { let AccountsCreateRequest { wallet_secret, account_create_args } = request; - let account = self.create_account(&wallet_secret, account_create_args, true).await?; + let guard = self.guard(); + let guard = guard.lock().await; + + let account = self.create_account(&wallet_secret, account_create_args, true, &guard).await?; let account_descriptor = account.descriptor()?; Ok(AccountsCreateResponse { account_descriptor }) @@ -308,8 +316,12 @@ impl WalletApi for super::Wallet { ) -> Result { let AccountsEnsureDefaultRequest { wallet_secret, payment_secret, account_kind, mnemonic_phrase } = request; - let account_descriptor = - self.ensure_default_account_impl(&wallet_secret, payment_secret.as_ref(), account_kind, mnemonic_phrase.as_ref()).await?; + let guard = self.guard(); + let guard = guard.lock().await; + + let account_descriptor = self + .ensure_default_account_impl(&wallet_secret, payment_secret.as_ref(), account_kind, mnemonic_phrase.as_ref(), &guard) + .await?; Ok(AccountsEnsureDefaultResponse { account_descriptor }) } @@ -321,7 +333,11 @@ impl WalletApi for super::Wallet { async fn accounts_get_call(self: Arc, request: AccountsGetRequest) -> Result { let AccountsGetRequest { account_id } = request; - let account = self.get_account_by_id(&account_id).await?.ok_or(Error::AccountNotFound(account_id))?; + + let guard = self.guard(); + let guard = guard.lock().await; + + let account = self.get_account_by_id(&account_id, &guard).await?.ok_or(Error::AccountNotFound(account_id))?; let account_descriptor = account.descriptor().unwrap(); Ok(AccountsGetResponse { account_descriptor }) } @@ -332,7 +348,10 @@ impl WalletApi for super::Wallet { ) -> Result { let AccountsCreateNewAddressRequest { account_id, kind } = request; - let account = self.get_account_by_id(&account_id).await?.ok_or(Error::AccountNotFound(account_id))?; + let guard = self.guard(); + let guard = guard.lock().await; + + let account = self.get_account_by_id(&account_id, &guard).await?.ok_or(Error::AccountNotFound(account_id))?; let address = match kind { NewAddressKind::Receive => account.as_derivation_capable()?.new_receive_address().await?, @@ -345,7 +364,9 @@ impl WalletApi for super::Wallet { async fn accounts_send_call(self: Arc, request: AccountsSendRequest) -> Result { let AccountsSendRequest { account_id, wallet_secret, payment_secret, destination, priority_fee_sompi, payload } = request; - let account = self.get_account_by_id(&account_id).await?.ok_or(Error::AccountNotFound(account_id))?; + let guard = self.guard(); + let guard = guard.lock().await; + let account = self.get_account_by_id(&account_id, &guard).await?.ok_or(Error::AccountNotFound(account_id))?; let abortable = Abortable::new(); let (generator_summary, transaction_ids) = @@ -364,7 +385,11 @@ impl WalletApi for super::Wallet { transfer_amount_sompi, } = request; - let source_account = self.get_account_by_id(&source_account_id).await?.ok_or(Error::AccountNotFound(source_account_id))?; + let guard = self.guard(); + let guard = guard.lock().await; + + let source_account = + self.get_account_by_id(&source_account_id, &guard).await?.ok_or(Error::AccountNotFound(source_account_id))?; let abortable = Abortable::new(); let (generator_summary, transaction_ids) = source_account @@ -376,6 +401,7 @@ impl WalletApi for super::Wallet { payment_secret, &abortable, None, + &guard, ) .await?; @@ -385,7 +411,9 @@ impl WalletApi for super::Wallet { async fn accounts_estimate_call(self: Arc, request: AccountsEstimateRequest) -> Result { let AccountsEstimateRequest { account_id, destination, priority_fee_sompi, payload } = request; - let account = self.get_account_by_id(&account_id).await?.ok_or(Error::AccountNotFound(account_id))?; + let guard = self.guard(); + let guard = guard.lock().await; + let account = self.get_account_by_id(&account_id, &guard).await?.ok_or(Error::AccountNotFound(account_id))?; // Abort currently running async estimate for the same account if present. The estimate // call can be invoked continuously by the client/UI. If the estimate call is diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index d20f041adb..d160bd8161 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -24,6 +24,8 @@ use kaspa_notify::{ use kaspa_wrpc_client::{KaspaRpcClient, Resolver, WrpcEncoding}; use workflow_core::task::spawn; +pub type WalletGuard<'l> = AsyncMutexGuard<'l, ()>; + #[derive(Debug)] pub struct EncryptedMnemonic> { pub cipher: T, // raw @@ -90,6 +92,8 @@ pub struct Inner { wallet_bus: Channel, estimation_abortables: Mutex>, retained_contexts: Mutex>>>, + // Mutex used to protect concurrent access to accounts at the wallet api level + guard: Arc>, } /// @@ -155,6 +159,7 @@ impl Wallet { wallet_bus, estimation_abortables: Mutex::new(HashMap::new()), retained_contexts: Mutex::new(HashMap::new()), + guard: Arc::new(AsyncMutex::new(())), }), }; @@ -165,6 +170,20 @@ impl Wallet { &self.inner } + // + // Mutex used to protect concurrent access to accounts + // at the wallet api level. This is a global lock that + // is required by various wallet operations. + // + // Due to the fact that Rust Wallet API is async, it is + // possible for clients to concurrently execute API calls + // that can "trip over each-other", causing incorrect + // account states. + // + pub fn guard(&self) -> Arc> { + self.inner.guard.clone() + } + pub fn is_resident(&self) -> Result { Ok(self.store().location()? == StorageDescriptor::Resident) } @@ -204,10 +223,12 @@ impl Wallet { Ok(()) } - pub async fn reload(self: &Arc, reactivate: bool) -> Result<()> { + pub async fn reload(self: &Arc, reactivate: bool, _guard: &WalletGuard<'_>) -> Result<()> { if self.is_open() { // similar to reset(), but effectively reboots the wallet + // let _guard = self.inner.guard.lock().await; + let accounts = self.active_accounts().collect(); let account_descriptors = Some(accounts.iter().map(|account| account.descriptor()).collect::>>()?); let wallet_descriptor = self.store().descriptor(); @@ -293,6 +314,8 @@ impl Wallet { filename: Option, args: WalletOpenArgs, ) -> Result>> { + // let _guard = self.inner.guard.lock().await; + let filename = filename.or_else(|| self.settings().get(WalletSettings::Wallet)); // let name = Some(make_filename(&name, &None)); @@ -357,6 +380,7 @@ impl Wallet { wallet_secret: &Secret, filename: Option, args: WalletOpenArgs, + _guard: &WalletGuard<'_>, ) -> Result>> { // This is a wrapper of open_impl() that catches errors and notifies the UI match self.open_impl(wallet_secret, filename, args).await { @@ -369,6 +393,8 @@ impl Wallet { } async fn activate_accounts_impl(self: &Arc, account_ids: Option<&[AccountId]>) -> Result> { + // let _guard = self.inner.guard.lock().await; + let stored_accounts = if let Some(ids) = account_ids { self.inner.store.as_account_store().unwrap().load_multiple(ids).await? } else { @@ -399,7 +425,7 @@ impl Wallet { } /// Activates accounts (performs account address space counts, initializes balance tracking, etc.) - pub async fn activate_accounts(self: &Arc, account_ids: Option<&[AccountId]>) -> Result<()> { + pub async fn activate_accounts(self: &Arc, account_ids: Option<&[AccountId]>, _guard: &WalletGuard<'_>) -> Result<()> { // This is a wrapper of activate_accounts_impl() that catches errors and notifies the UI if let Err(err) = self.activate_accounts_impl(account_ids).await { self.notify(Events::WalletError { message: err.to_string() }).await?; @@ -409,7 +435,9 @@ impl Wallet { } } - pub async fn deactivate_accounts(self: &Arc, ids: Option<&[AccountId]>) -> Result<()> { + pub async fn deactivate_accounts(self: &Arc, ids: Option<&[AccountId]>, _guard: &WalletGuard<'_>) -> Result<()> { + let _guard = self.inner.guard.lock().await; + let (ids, futures) = if let Some(ids) = ids { let accounts = ids.iter().map(|id| self.active_accounts().get(id).ok_or(Error::AccountNotFound(*id))).collect::>>()?; @@ -424,7 +452,9 @@ impl Wallet { Ok(()) } - pub async fn account_descriptors(self: Arc) -> Result> { + pub async fn account_descriptors(self: Arc, _guard: &WalletGuard<'_>) -> Result> { + // let _guard = self.inner.guard.lock().await; + let iter = self.inner.store.as_account_store().unwrap().iter(None).await.unwrap(); let wallet = self.clone(); @@ -604,6 +634,7 @@ impl Wallet { wallet_secret: &Secret, account_create_args: AccountCreateArgs, notify: bool, + _guard: &WalletGuard<'_>, ) -> Result> { let account = match account_create_args { AccountCreateArgs::Bip32 { prv_key_data_args, account_args } => { @@ -847,7 +878,13 @@ impl Wallet { Ok((wallet_descriptor, storage_descriptor, mnemonic, account)) } - pub async fn get_account_by_id(self: &Arc, account_id: &AccountId) -> Result>> { + pub async fn get_account_by_id( + self: &Arc, + account_id: &AccountId, + _guard: &WalletGuard<'_>, + ) -> Result>> { + let _guard = self.inner.guard.lock().await; + if let Some(account) = self.active_accounts().get(account_id) { Ok(Some(account.clone())) } else { @@ -1036,7 +1073,11 @@ impl Wallet { Ok(matches) } - pub async fn accounts(self: &Arc, filter: Option) -> Result>>> { + pub async fn accounts( + self: &Arc, + filter: Option, + _guard: &WalletGuard<'_>, + ) -> Result>>> { let iter = self.inner.store.as_account_store().unwrap().iter(filter).await.unwrap(); let wallet = self.clone(); @@ -1557,6 +1598,7 @@ impl Wallet { payment_secret: Option<&Secret>, kind: AccountKind, mnemonic_phrase: Option<&Secret>, + guard: &WalletGuard<'_>, ) -> Result { if kind != BIP32_ACCOUNT_KIND { return Err(Error::custom("Account kind is not supported")); @@ -1582,7 +1624,7 @@ impl Wallet { let account_create_args = AccountCreateArgs::new_bip32(prv_key_data_id, payment_secret.cloned(), None, None); - let account = self.clone().create_account(wallet_secret, account_create_args, false).await?; + let account = self.clone().create_account(wallet_secret, account_create_args, false, guard).await?; self.store().flush(wallet_secret).await?; From db213fe953675dd62d667e20d0212da753dfb6ae Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 29 Jun 2024 13:14:56 +0300 Subject: [PATCH 053/158] tx serialization example --- .../javascript/transactions/serialize.js | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 wasm/examples/nodejs/javascript/transactions/serialize.js diff --git a/wasm/examples/nodejs/javascript/transactions/serialize.js b/wasm/examples/nodejs/javascript/transactions/serialize.js new file mode 100644 index 0000000000..977ed1e8ab --- /dev/null +++ b/wasm/examples/nodejs/javascript/transactions/serialize.js @@ -0,0 +1,48 @@ +const { + Address, + createTransactions, + initConsolePanicHook, + Mnemonic, + XPrv, + PrivateKeyGenerator, + payToAddressScript, +} = require('../../../../nodejs/kaspa'); + + +(async () => { + + const networkId = 'mainnet'; + + const mnemonic = Mnemonic.random(); + const xprv = new XPrv(mnemonic.toSeed()); + const privateKey = new PrivateKeyGenerator(xprv, false, 0n).receiveKey(1); + const address = privateKey.toAddress(networkId); + const scriptPublicKey = payToAddressScript(address); + const entries = [{ + address, + outpoint: { + transactionId: '1b84324c701b16c1cfbbd713a5ff87edf78bc5c92a92866f86d7e32ab5cd387d', + index: 0 + }, + scriptPublicKey, + amount: 50000000000n, + isCoinbase: true, + blockDaaScore: 342n + }]; + + const { transactions, summary } = await createTransactions({ + entries, + outputs: [{ + address: 'kaspa:qpamkvhgh0kzx50gwvvp5xs8ktmqutcy3dfs9dc3w7lm9rq0zs76vf959mmrp', + amount: 400000000n + }], + changeAddress: address, + priorityFee: 0n, + networkId + }); + + for (const pending of transactions) { + const tx = pending.serializeToObject(); + console.log(tx); + } +})(); From fd8d23d60f78fe65f232dcdb6e7d97a3cf34d12a Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 29 Jun 2024 13:27:57 +0300 Subject: [PATCH 054/158] change struct serialization version fields from u32 to u16 --- consensus/core/src/api/stats.rs | 8 +- notify/src/scope.rs | 44 ++-- rpc/core/src/api/notifications.rs | 24 +- rpc/core/src/model/message.rs | 392 +++++++++++++++--------------- 4 files changed, 234 insertions(+), 234 deletions(-) diff --git a/consensus/core/src/api/stats.rs b/consensus/core/src/api/stats.rs index a004a41370..86ede997ae 100644 --- a/consensus/core/src/api/stats.rs +++ b/consensus/core/src/api/stats.rs @@ -16,7 +16,7 @@ impl BlockCount { impl Serializer for BlockCount { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.header_count, writer)?; store!(u64, &self.block_count, writer)?; @@ -24,9 +24,9 @@ impl Serializer for BlockCount { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; - let header_count: u64 = load!(u64, reader)?; - let block_count: u64 = load!(u64, reader)?; + let _version = load!(u16, reader)?; + let header_count = load!(u64, reader)?; + let block_count = load!(u64, reader)?; Ok(Self { header_count, block_count }) } diff --git a/notify/src/scope.rs b/notify/src/scope.rs index ea00016332..715bbe26a9 100644 --- a/notify/src/scope.rs +++ b/notify/src/scope.rs @@ -56,12 +56,12 @@ impl Scope { impl Serializer for Scope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Scope, self, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; + let _version = load!(u16, reader)?; load!(Scope, reader) } } @@ -71,11 +71,11 @@ pub struct BlockAddedScope {} impl Serializer for BlockAddedScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -99,13 +99,13 @@ impl std::fmt::Display for VirtualChainChangedScope { impl Serializer for VirtualChainChangedScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(bool, &self.include_accepted_transaction_ids, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; - let include_accepted_transaction_ids: bool = load!(bool, reader)?; + let _version = load!(u16, reader)?; + let include_accepted_transaction_ids = load!(bool, reader)?; Ok(Self { include_accepted_transaction_ids }) } } @@ -115,11 +115,11 @@ pub struct FinalityConflictScope {} impl Serializer for FinalityConflictScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -129,11 +129,11 @@ pub struct FinalityConflictResolvedScope {} impl Serializer for FinalityConflictResolvedScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -170,13 +170,13 @@ impl UtxosChangedScope { impl Serializer for UtxosChangedScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec
, &self.addresses, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; - let addresses: Vec
= load!(Vec
, reader)?; + let _version = load!(u16, reader)?; + let addresses = load!(Vec
, reader)?; Ok(Self { addresses }) } } @@ -186,11 +186,11 @@ pub struct SinkBlueScoreChangedScope {} impl Serializer for SinkBlueScoreChangedScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -200,11 +200,11 @@ pub struct VirtualDaaScoreChangedScope {} impl Serializer for VirtualDaaScoreChangedScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -214,11 +214,11 @@ pub struct PruningPointUtxoSetOverrideScope {} impl Serializer for PruningPointUtxoSetOverrideScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -228,11 +228,11 @@ pub struct NewBlockTemplateScope {} impl Serializer for NewBlockTemplateScope { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } diff --git a/rpc/core/src/api/notifications.rs b/rpc/core/src/api/notifications.rs index 3fbb2652e1..18a4808e26 100644 --- a/rpc/core/src/api/notifications.rs +++ b/rpc/core/src/api/notifications.rs @@ -115,42 +115,42 @@ impl NotificationTrait for Notification { impl Serializer for Notification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; match self { Notification::BlockAdded(notification) => { - store!(u32, &0, writer)?; + store!(u16, &0, writer)?; serialize!(BlockAddedNotification, notification, writer)?; } Notification::VirtualChainChanged(notification) => { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; serialize!(VirtualChainChangedNotification, notification, writer)?; } Notification::FinalityConflict(notification) => { - store!(u32, &2, writer)?; + store!(u16, &2, writer)?; serialize!(FinalityConflictNotification, notification, writer)?; } Notification::FinalityConflictResolved(notification) => { - store!(u32, &3, writer)?; + store!(u16, &3, writer)?; serialize!(FinalityConflictResolvedNotification, notification, writer)?; } Notification::UtxosChanged(notification) => { - store!(u32, &4, writer)?; + store!(u16, &4, writer)?; serialize!(UtxosChangedNotification, notification, writer)?; } Notification::SinkBlueScoreChanged(notification) => { - store!(u32, &5, writer)?; + store!(u16, &5, writer)?; serialize!(SinkBlueScoreChangedNotification, notification, writer)?; } Notification::VirtualDaaScoreChanged(notification) => { - store!(u32, &6, writer)?; + store!(u16, &6, writer)?; serialize!(VirtualDaaScoreChangedNotification, notification, writer)?; } Notification::PruningPointUtxoSetOverride(notification) => { - store!(u32, &7, writer)?; + store!(u16, &7, writer)?; serialize!(PruningPointUtxoSetOverrideNotification, notification, writer)?; } Notification::NewBlockTemplate(notification) => { - store!(u32, &8, writer)?; + store!(u16, &8, writer)?; serialize!(NewBlockTemplateNotification, notification, writer)?; } } @@ -158,8 +158,8 @@ impl Serializer for Notification { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version: u32 = load!(u32, reader)?; - match load!(u32, reader)? { + let _version = load!(u16, reader)?; + match load!(u16, reader)? { 0 => { let notification = deserialize!(BlockAddedNotification, reader)?; Ok(Notification::BlockAdded(notification)) diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index e4bbc7defb..c335651958 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -31,7 +31,7 @@ impl SubmitBlockRequest { impl Serializer for SubmitBlockRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcBlock, &self.block, writer)?; store!(bool, &self.allow_non_daa_blocks, writer)?; @@ -39,7 +39,7 @@ impl Serializer for SubmitBlockRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let block = load!(RpcBlock, reader)?; let allow_non_daa_blocks = load!(bool, reader)?; @@ -93,13 +93,13 @@ pub struct SubmitBlockResponse { impl Serializer for SubmitBlockResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(SubmitBlockReport, &self.report, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let report = load!(SubmitBlockReport, reader)?; Ok(Self { report }) @@ -126,7 +126,7 @@ impl GetBlockTemplateRequest { impl Serializer for GetBlockTemplateRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcAddress, &self.pay_address, writer)?; store!(RpcExtraData, &self.extra_data, writer)?; @@ -134,7 +134,7 @@ impl Serializer for GetBlockTemplateRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let pay_address = load!(RpcAddress, reader)?; let extra_data = load!(RpcExtraData, reader)?; @@ -156,7 +156,7 @@ pub struct GetBlockTemplateResponse { impl Serializer for GetBlockTemplateResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcBlock, &self.block, writer)?; store!(bool, &self.is_synced, writer)?; @@ -164,7 +164,7 @@ impl Serializer for GetBlockTemplateResponse { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let block = load!(RpcBlock, reader)?; let is_synced = load!(bool, reader)?; @@ -190,7 +190,7 @@ impl GetBlockRequest { impl Serializer for GetBlockRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcHash, &self.hash, writer)?; store!(bool, &self.include_transactions, writer)?; @@ -198,7 +198,7 @@ impl Serializer for GetBlockRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let hash = load!(RpcHash, reader)?; let include_transactions = load!(bool, reader)?; @@ -214,14 +214,14 @@ pub struct GetBlockResponse { impl Serializer for GetBlockResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcBlock, &self.block, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let block = load!(RpcBlock, reader)?; Ok(Self { block }) @@ -235,12 +235,12 @@ pub struct GetInfoRequest {} impl Serializer for GetInfoRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -259,7 +259,7 @@ pub struct GetInfoResponse { impl Serializer for GetInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(String, &self.p2p_id, writer)?; store!(u64, &self.mempool_size, writer)?; store!(String, &self.server_version, writer)?; @@ -272,7 +272,7 @@ impl Serializer for GetInfoResponse { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let p2p_id = load!(String, reader)?; let mempool_size = load!(u64, reader)?; let server_version = load!(String, reader)?; @@ -291,12 +291,12 @@ pub struct GetCurrentNetworkRequest {} impl Serializer for GetCurrentNetworkRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -315,13 +315,13 @@ impl GetCurrentNetworkResponse { impl Serializer for GetCurrentNetworkResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcNetworkType, &self.network, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let network = load!(RpcNetworkType, reader)?; Ok(Self { network }) } @@ -333,12 +333,12 @@ pub struct GetPeerAddressesRequest {} impl Serializer for GetPeerAddressesRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -358,14 +358,14 @@ impl GetPeerAddressesResponse { impl Serializer for GetPeerAddressesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.known_addresses, writer)?; store!(Vec, &self.banned_addresses, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let known_addresses = load!(Vec, reader)?; let banned_addresses = load!(Vec, reader)?; Ok(Self { known_addresses, banned_addresses }) @@ -378,12 +378,12 @@ pub struct GetSinkRequest {} impl Serializer for GetSinkRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -402,13 +402,13 @@ impl GetSinkResponse { impl Serializer for GetSinkResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcHash, &self.sink, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let sink = load!(RpcHash, reader)?; Ok(Self { sink }) } @@ -431,7 +431,7 @@ impl GetMempoolEntryRequest { impl Serializer for GetMempoolEntryRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcTransactionId, &self.transaction_id, writer)?; store!(bool, &self.include_orphan_pool, writer)?; store!(bool, &self.filter_transaction_pool, writer)?; @@ -440,7 +440,7 @@ impl Serializer for GetMempoolEntryRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let transaction_id = load!(RpcTransactionId, reader)?; let include_orphan_pool = load!(bool, reader)?; let filter_transaction_pool = load!(bool, reader)?; @@ -463,13 +463,13 @@ impl GetMempoolEntryResponse { impl Serializer for GetMempoolEntryResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcMempoolEntry, &self.mempool_entry, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let mempool_entry = load!(RpcMempoolEntry, reader)?; Ok(Self { mempool_entry }) } @@ -491,7 +491,7 @@ impl GetMempoolEntriesRequest { impl Serializer for GetMempoolEntriesRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(bool, &self.include_orphan_pool, writer)?; store!(bool, &self.filter_transaction_pool, writer)?; @@ -499,7 +499,7 @@ impl Serializer for GetMempoolEntriesRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let include_orphan_pool = load!(bool, reader)?; let filter_transaction_pool = load!(bool, reader)?; @@ -521,13 +521,13 @@ impl GetMempoolEntriesResponse { impl Serializer for GetMempoolEntriesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.mempool_entries, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let mempool_entries = load!(Vec, reader)?; Ok(Self { mempool_entries }) } @@ -539,12 +539,12 @@ pub struct GetConnectedPeerInfoRequest {} impl Serializer for GetConnectedPeerInfoRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -563,13 +563,13 @@ impl GetConnectedPeerInfoResponse { impl Serializer for GetConnectedPeerInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.peer_info, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let peer_info = load!(Vec, reader)?; Ok(Self { peer_info }) } @@ -590,7 +590,7 @@ impl AddPeerRequest { impl Serializer for AddPeerRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcContextualPeerAddress, &self.peer_address, writer)?; store!(bool, &self.is_permanent, writer)?; @@ -598,7 +598,7 @@ impl Serializer for AddPeerRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let peer_address = load!(RpcContextualPeerAddress, reader)?; let is_permanent = load!(bool, reader)?; @@ -612,12 +612,12 @@ pub struct AddPeerResponse {} impl Serializer for AddPeerResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -637,7 +637,7 @@ impl SubmitTransactionRequest { impl Serializer for SubmitTransactionRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; // TODO store!(RpcTransaction, &self.transaction, writer)?; store!(bool, &self.allow_orphan, writer)?; @@ -646,7 +646,7 @@ impl Serializer for SubmitTransactionRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let transaction = load!(RpcTransaction, reader)?; let allow_orphan = load!(bool, reader)?; @@ -668,14 +668,14 @@ impl SubmitTransactionResponse { impl Serializer for SubmitTransactionResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcTransactionId, &self.transaction_id, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let transaction_id = load!(RpcTransactionId, reader)?; Ok(Self { transaction_id }) @@ -696,14 +696,14 @@ impl GetSubnetworkRequest { impl Serializer for GetSubnetworkRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcSubnetworkId, &self.subnetwork_id, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let subnetwork_id = load!(RpcSubnetworkId, reader)?; Ok(Self { subnetwork_id }) @@ -724,14 +724,14 @@ impl GetSubnetworkResponse { impl Serializer for GetSubnetworkResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.gas_limit, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let gas_limit = load!(u64, reader)?; Ok(Self { gas_limit }) @@ -753,7 +753,7 @@ impl GetVirtualChainFromBlockRequest { impl Serializer for GetVirtualChainFromBlockRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcHash, &self.start_hash, writer)?; store!(bool, &self.include_accepted_transaction_ids, writer)?; @@ -761,7 +761,7 @@ impl Serializer for GetVirtualChainFromBlockRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let start_hash = load!(RpcHash, reader)?; let include_accepted_transaction_ids = load!(bool, reader)?; @@ -789,7 +789,7 @@ impl GetVirtualChainFromBlockResponse { impl Serializer for GetVirtualChainFromBlockResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.removed_chain_block_hashes, writer)?; store!(Vec, &self.added_chain_block_hashes, writer)?; store!(Vec, &self.accepted_transaction_ids, writer)?; @@ -798,7 +798,7 @@ impl Serializer for GetVirtualChainFromBlockResponse { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let removed_chain_block_hashes = load!(Vec, reader)?; let added_chain_block_hashes = load!(Vec, reader)?; let accepted_transaction_ids = load!(Vec, reader)?; @@ -823,7 +823,7 @@ impl GetBlocksRequest { impl Serializer for GetBlocksRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Option, &self.low_hash, writer)?; store!(bool, &self.include_blocks, writer)?; store!(bool, &self.include_transactions, writer)?; @@ -832,7 +832,7 @@ impl Serializer for GetBlocksRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let low_hash = load!(Option, reader)?; let include_blocks = load!(bool, reader)?; let include_transactions = load!(bool, reader)?; @@ -856,7 +856,7 @@ impl GetBlocksResponse { impl Serializer for GetBlocksResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.block_hashes, writer)?; store!(Vec, &self.blocks, writer)?; @@ -864,7 +864,7 @@ impl Serializer for GetBlocksResponse { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let block_hashes = load!(Vec, reader)?; let blocks = load!(Vec, reader)?; @@ -878,12 +878,12 @@ pub struct GetBlockCountRequest {} impl Serializer for GetBlockCountRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -896,12 +896,12 @@ pub struct GetBlockDagInfoRequest {} impl Serializer for GetBlockDagInfoRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -951,7 +951,7 @@ impl GetBlockDagInfoResponse { impl Serializer for GetBlockDagInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcNetworkId, &self.network, writer)?; store!(u64, &self.block_count, writer)?; store!(u64, &self.header_count, writer)?; @@ -967,7 +967,7 @@ impl Serializer for GetBlockDagInfoResponse { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let network = load!(RpcNetworkId, reader)?; let block_count = load!(u64, reader)?; let header_count = load!(u64, reader)?; @@ -1008,14 +1008,14 @@ impl ResolveFinalityConflictRequest { impl Serializer for ResolveFinalityConflictRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcHash, &self.finality_block_hash, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let finality_block_hash = load!(RpcHash, reader)?; Ok(Self { finality_block_hash }) @@ -1028,12 +1028,12 @@ pub struct ResolveFinalityConflictResponse {} impl Serializer for ResolveFinalityConflictResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -1044,12 +1044,12 @@ pub struct ShutdownRequest {} impl Serializer for ShutdownRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -1060,12 +1060,12 @@ pub struct ShutdownResponse {} impl Serializer for ShutdownResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -1086,7 +1086,7 @@ impl GetHeadersRequest { impl Serializer for GetHeadersRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcHash, &self.start_hash, writer)?; store!(u64, &self.limit, writer)?; store!(bool, &self.is_ascending, writer)?; @@ -1095,7 +1095,7 @@ impl Serializer for GetHeadersRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let start_hash = load!(RpcHash, reader)?; let limit = load!(u64, reader)?; let is_ascending = load!(bool, reader)?; @@ -1118,14 +1118,14 @@ impl GetHeadersResponse { impl Serializer for GetHeadersResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.headers, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let headers = load!(Vec, reader)?; Ok(Self { headers }) @@ -1146,14 +1146,14 @@ impl GetBalanceByAddressRequest { impl Serializer for GetBalanceByAddressRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcAddress, &self.address, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let address = load!(RpcAddress, reader)?; Ok(Self { address }) @@ -1174,14 +1174,14 @@ impl GetBalanceByAddressResponse { impl Serializer for GetBalanceByAddressResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.balance, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let balance = load!(u64, reader)?; Ok(Self { balance }) @@ -1202,14 +1202,14 @@ impl GetBalancesByAddressesRequest { impl Serializer for GetBalancesByAddressesRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.addresses, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let addresses = load!(Vec, reader)?; Ok(Self { addresses }) @@ -1230,14 +1230,14 @@ impl GetBalancesByAddressesResponse { impl Serializer for GetBalancesByAddressesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.entries, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let entries = load!(Vec, reader)?; Ok(Self { entries }) @@ -1250,12 +1250,12 @@ pub struct GetSinkBlueScoreRequest {} impl Serializer for GetSinkBlueScoreRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -1274,14 +1274,14 @@ impl GetSinkBlueScoreResponse { impl Serializer for GetSinkBlueScoreResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.blue_score, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let blue_score = load!(u64, reader)?; Ok(Self { blue_score }) @@ -1302,14 +1302,14 @@ impl GetUtxosByAddressesRequest { impl Serializer for GetUtxosByAddressesRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.addresses, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let addresses = load!(Vec, reader)?; Ok(Self { addresses }) @@ -1330,14 +1330,14 @@ impl GetUtxosByAddressesResponse { impl Serializer for GetUtxosByAddressesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.entries, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let entries = load!(Vec, reader)?; Ok(Self { entries }) @@ -1358,14 +1358,14 @@ impl BanRequest { impl Serializer for BanRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcIpAddress, &self.ip, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let ip = load!(RpcIpAddress, reader)?; Ok(Self { ip }) @@ -1378,12 +1378,12 @@ pub struct BanResponse {} impl Serializer for BanResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -1402,14 +1402,14 @@ impl UnbanRequest { impl Serializer for UnbanRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcIpAddress, &self.ip, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let ip = load!(RpcIpAddress, reader)?; Ok(Self { ip }) @@ -1422,12 +1422,12 @@ pub struct UnbanResponse {} impl Serializer for UnbanResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -1447,7 +1447,7 @@ impl EstimateNetworkHashesPerSecondRequest { impl Serializer for EstimateNetworkHashesPerSecondRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u32, &self.window_size, writer)?; store!(Option, &self.start_hash, writer)?; @@ -1455,7 +1455,7 @@ impl Serializer for EstimateNetworkHashesPerSecondRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let window_size = load!(u32, reader)?; let start_hash = load!(Option, reader)?; @@ -1477,14 +1477,14 @@ impl EstimateNetworkHashesPerSecondResponse { impl Serializer for EstimateNetworkHashesPerSecondResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.network_hashes_per_second, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let network_hashes_per_second = load!(u64, reader)?; Ok(Self { network_hashes_per_second }) @@ -1508,7 +1508,7 @@ impl GetMempoolEntriesByAddressesRequest { impl Serializer for GetMempoolEntriesByAddressesRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.addresses, writer)?; store!(bool, &self.include_orphan_pool, writer)?; store!(bool, &self.filter_transaction_pool, writer)?; @@ -1517,7 +1517,7 @@ impl Serializer for GetMempoolEntriesByAddressesRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let addresses = load!(Vec, reader)?; let include_orphan_pool = load!(bool, reader)?; let filter_transaction_pool = load!(bool, reader)?; @@ -1540,14 +1540,14 @@ impl GetMempoolEntriesByAddressesResponse { impl Serializer for GetMempoolEntriesByAddressesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.entries, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let entries = load!(Vec, reader)?; Ok(Self { entries }) @@ -1560,12 +1560,12 @@ pub struct GetCoinSupplyRequest {} impl Serializer for GetCoinSupplyRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -1585,7 +1585,7 @@ impl GetCoinSupplyResponse { impl Serializer for GetCoinSupplyResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.max_sompi, writer)?; store!(u64, &self.circulating_sompi, writer)?; @@ -1593,7 +1593,7 @@ impl Serializer for GetCoinSupplyResponse { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let max_sompi = load!(u64, reader)?; let circulating_sompi = load!(u64, reader)?; @@ -1643,7 +1643,7 @@ pub struct GetMetricsRequest { impl Serializer for GetMetricsRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(bool, &self.process_metrics, writer)?; store!(bool, &self.connection_metrics, writer)?; store!(bool, &self.bandwidth_metrics, writer)?; @@ -1654,7 +1654,7 @@ impl Serializer for GetMetricsRequest { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let process_metrics = load!(bool, reader)?; let connection_metrics = load!(bool, reader)?; let bandwidth_metrics = load!(bool, reader)?; @@ -1681,7 +1681,7 @@ pub struct ProcessMetrics { impl Serializer for ProcessMetrics { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.resident_set_size, writer)?; store!(u64, &self.virtual_memory_size, writer)?; store!(u32, &self.core_num, writer)?; @@ -1696,7 +1696,7 @@ impl Serializer for ProcessMetrics { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let resident_set_size = load!(u64, reader)?; let virtual_memory_size = load!(u64, reader)?; let core_num = load!(u32, reader)?; @@ -1736,7 +1736,7 @@ pub struct ConnectionMetrics { impl Serializer for ConnectionMetrics { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u32, &self.borsh_live_connections, writer)?; store!(u64, &self.borsh_connection_attempts, writer)?; store!(u64, &self.borsh_handshake_failures, writer)?; @@ -1749,7 +1749,7 @@ impl Serializer for ConnectionMetrics { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let borsh_live_connections = load!(u32, reader)?; let borsh_connection_attempts = load!(u64, reader)?; let borsh_handshake_failures = load!(u64, reader)?; @@ -1785,7 +1785,7 @@ pub struct BandwidthMetrics { impl Serializer for BandwidthMetrics { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.borsh_bytes_tx, writer)?; store!(u64, &self.borsh_bytes_rx, writer)?; store!(u64, &self.json_bytes_tx, writer)?; @@ -1799,7 +1799,7 @@ impl Serializer for BandwidthMetrics { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let borsh_bytes_tx = load!(u64, reader)?; let borsh_bytes_rx = load!(u64, reader)?; let json_bytes_tx = load!(u64, reader)?; @@ -1846,7 +1846,7 @@ pub struct ConsensusMetrics { impl Serializer for ConsensusMetrics { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.node_blocks_submitted_count, writer)?; store!(u64, &self.node_headers_processed_count, writer)?; store!(u64, &self.node_dependencies_processed_count, writer)?; @@ -1867,7 +1867,7 @@ impl Serializer for ConsensusMetrics { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let node_blocks_submitted_count = load!(u64, reader)?; let node_headers_processed_count = load!(u64, reader)?; let node_dependencies_processed_count = load!(u64, reader)?; @@ -1912,14 +1912,14 @@ pub struct StorageMetrics { impl Serializer for StorageMetrics { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.storage_size_bytes, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let storage_size_bytes = load!(u64, reader)?; Ok(Self { storage_size_bytes }) @@ -1952,7 +1952,7 @@ impl GetMetricsResponse { impl Serializer for GetMetricsResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.server_time, writer)?; serialize!(Option, &self.process_metrics, writer)?; serialize!(Option, &self.connection_metrics, writer)?; @@ -1964,7 +1964,7 @@ impl Serializer for GetMetricsResponse { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let server_time = load!(u64, reader)?; let process_metrics = deserialize!(Option, reader)?; let connection_metrics = deserialize!(Option, reader)?; @@ -1982,12 +1982,12 @@ pub struct GetServerInfoRequest {} impl Serializer for GetServerInfoRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2005,7 +2005,7 @@ pub struct GetServerInfoResponse { impl Serializer for GetServerInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; // TODO store!([u16; 4], &self.rpc_api_version, writer)?; store!(String, &self.server_version, writer)?; @@ -2018,7 +2018,7 @@ impl Serializer for GetServerInfoResponse { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let rpc_api_version = load!([u16; 4], reader)?; let server_version = load!(String, reader)?; let network_id = load!(RpcNetworkId, reader)?; @@ -2036,12 +2036,12 @@ pub struct GetSyncStatusRequest {} impl Serializer for GetSyncStatusRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2054,13 +2054,13 @@ pub struct GetSyncStatusResponse { impl Serializer for GetSyncStatusResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(bool, &self.is_synced, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let is_synced = load!(bool, reader)?; Ok(Self { is_synced }) } @@ -2080,13 +2080,13 @@ impl GetDaaScoreTimestampEstimateRequest { impl Serializer for GetDaaScoreTimestampEstimateRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.daa_scores, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let daa_scores = load!(Vec, reader)?; Ok(Self { daa_scores }) } @@ -2106,13 +2106,13 @@ impl GetDaaScoreTimestampEstimateResponse { impl Serializer for GetDaaScoreTimestampEstimateResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.timestamps, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let timestamps = load!(Vec, reader)?; Ok(Self { timestamps }) } @@ -2141,13 +2141,13 @@ impl NotifyBlockAddedRequest { impl Serializer for NotifyBlockAddedRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let command = load!(Command, reader)?; Ok(Self { command }) } @@ -2159,12 +2159,12 @@ pub struct NotifyBlockAddedResponse {} impl Serializer for NotifyBlockAddedResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2181,13 +2181,13 @@ pub struct BlockAddedNotification { impl Serializer for BlockAddedNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcBlock, &self.block, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let block = load!(RpcBlock, reader)?; Ok(Self { block: block.into() }) } @@ -2215,14 +2215,14 @@ impl NotifyVirtualChainChangedRequest { impl Serializer for NotifyVirtualChainChangedRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(bool, &self.include_accepted_transaction_ids, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let include_accepted_transaction_ids = load!(bool, reader)?; let command = load!(Command, reader)?; Ok(Self { include_accepted_transaction_ids, command }) @@ -2235,12 +2235,12 @@ pub struct NotifyVirtualChainChangedResponse {} impl Serializer for NotifyVirtualChainChangedResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2259,7 +2259,7 @@ pub struct VirtualChainChangedNotification { impl Serializer for VirtualChainChangedNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.removed_chain_block_hashes, writer)?; store!(Vec, &self.added_chain_block_hashes, writer)?; store!(Vec, &self.accepted_transaction_ids, writer)?; @@ -2267,7 +2267,7 @@ impl Serializer for VirtualChainChangedNotification { } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let removed_chain_block_hashes = load!(Vec, reader)?; let added_chain_block_hashes = load!(Vec, reader)?; let accepted_transaction_ids = load!(Vec, reader)?; @@ -2296,13 +2296,13 @@ impl NotifyFinalityConflictRequest { impl Serializer for NotifyFinalityConflictRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let command = load!(Command, reader)?; Ok(Self { command }) } @@ -2314,12 +2314,12 @@ pub struct NotifyFinalityConflictResponse {} impl Serializer for NotifyFinalityConflictResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2332,13 +2332,13 @@ pub struct FinalityConflictNotification { impl Serializer for FinalityConflictNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcHash, &self.violating_block_hash, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let violating_block_hash = load!(RpcHash, reader)?; Ok(Self { violating_block_hash }) } @@ -2361,13 +2361,13 @@ impl NotifyFinalityConflictResolvedRequest { impl Serializer for NotifyFinalityConflictResolvedRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let command = load!(Command, reader)?; Ok(Self { command }) } @@ -2379,12 +2379,12 @@ pub struct NotifyFinalityConflictResolvedResponse {} impl Serializer for NotifyFinalityConflictResolvedResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2397,13 +2397,13 @@ pub struct FinalityConflictResolvedNotification { impl Serializer for FinalityConflictResolvedNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(RpcHash, &self.finality_block_hash, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let finality_block_hash = load!(RpcHash, reader)?; Ok(Self { finality_block_hash }) } @@ -2436,14 +2436,14 @@ impl NotifyUtxosChangedRequest { impl Serializer for NotifyUtxosChangedRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.addresses, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let addresses = load!(Vec, reader)?; let command = load!(Command, reader)?; Ok(Self { addresses, command }) @@ -2456,12 +2456,12 @@ pub struct NotifyUtxosChangedResponse {} impl Serializer for NotifyUtxosChangedResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2508,14 +2508,14 @@ impl UtxosChangedNotification { impl Serializer for UtxosChangedNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Vec, &self.added, writer)?; store!(Vec, &self.removed, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let added = load!(Vec, reader)?; let removed = load!(Vec, reader)?; Ok(Self { added: added.into(), removed: removed.into() }) @@ -2543,13 +2543,13 @@ impl NotifySinkBlueScoreChangedRequest { impl Serializer for NotifySinkBlueScoreChangedRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let command = load!(Command, reader)?; Ok(Self { command }) } @@ -2561,12 +2561,12 @@ pub struct NotifySinkBlueScoreChangedResponse {} impl Serializer for NotifySinkBlueScoreChangedResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2583,13 +2583,13 @@ pub struct SinkBlueScoreChangedNotification { impl Serializer for SinkBlueScoreChangedNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.sink_blue_score, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let sink_blue_score = load!(u64, reader)?; Ok(Self { sink_blue_score }) } @@ -2616,13 +2616,13 @@ impl NotifyVirtualDaaScoreChangedRequest { impl Serializer for NotifyVirtualDaaScoreChangedRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let command = load!(Command, reader)?; Ok(Self { command }) } @@ -2634,12 +2634,12 @@ pub struct NotifyVirtualDaaScoreChangedResponse {} impl Serializer for NotifyVirtualDaaScoreChangedResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2656,13 +2656,13 @@ pub struct VirtualDaaScoreChangedNotification { impl Serializer for VirtualDaaScoreChangedNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.virtual_daa_score, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let virtual_daa_score = load!(u64, reader)?; Ok(Self { virtual_daa_score }) } @@ -2685,13 +2685,13 @@ impl NotifyPruningPointUtxoSetOverrideRequest { impl Serializer for NotifyPruningPointUtxoSetOverrideRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let command = load!(Command, reader)?; Ok(Self { command }) } @@ -2703,12 +2703,12 @@ pub struct NotifyPruningPointUtxoSetOverrideResponse {} impl Serializer for NotifyPruningPointUtxoSetOverrideResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2719,12 +2719,12 @@ pub struct PruningPointUtxoSetOverrideNotification {} impl Serializer for PruningPointUtxoSetOverrideNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2748,13 +2748,13 @@ impl NotifyNewBlockTemplateRequest { impl Serializer for NotifyNewBlockTemplateRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Command, &self.command, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let command = load!(Command, reader)?; Ok(Self { command }) } @@ -2766,12 +2766,12 @@ pub struct NotifyNewBlockTemplateResponse {} impl Serializer for NotifyNewBlockTemplateResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2786,12 +2786,12 @@ pub struct NewBlockTemplateNotification {} impl Serializer for NewBlockTemplateNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; Ok(Self {}) } } @@ -2813,13 +2813,13 @@ impl SubscribeResponse { impl Serializer for SubscribeResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(u64, &self.id, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let id = load!(u64, reader)?; Ok(Self { id }) } From 274c86671ce46f011f85bd8925510122b162859f Mon Sep 17 00:00:00 2001 From: aspect Date: Sat, 29 Jun 2024 13:47:19 +0300 Subject: [PATCH 055/158] WIP - decoupling RPC type aliases (#45) --- consensus/core/src/header.rs | 6 + rothschild/src/main.rs | 22 +- rpc/core/Cargo.toml | 2 +- rpc/core/src/convert/block.rs | 6 +- rpc/core/src/convert/tx.rs | 4 +- rpc/core/src/convert/utxo.rs | 6 +- rpc/core/src/model/address.rs | 38 +++- rpc/core/src/model/block.rs | 71 ++++++- rpc/core/src/model/header.rs | 180 +++++++++++++++- rpc/core/src/model/mempool.rs | 36 +++- rpc/core/src/model/message.rs | 53 +++-- rpc/core/src/model/tests.rs | 15 +- rpc/core/src/model/tx.rs | 260 +++++++++++++++++++++++- rpc/core/src/wasm/convert.rs | 3 +- rpc/grpc/core/src/convert/header.rs | 14 +- rpc/service/src/converter/consensus.rs | 2 +- testing/integration/src/common/utils.rs | 6 +- 17 files changed, 640 insertions(+), 84 deletions(-) diff --git a/consensus/core/src/header.rs b/consensus/core/src/header.rs index b6c2b9bc7e..de5f79c682 100644 --- a/consensus/core/src/header.rs +++ b/consensus/core/src/header.rs @@ -92,6 +92,12 @@ impl Header { } } +impl AsRef
for Header { + fn as_ref(&self) -> &Header { + self + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rothschild/src/main.rs b/rothschild/src/main.rs index bbabca71b7..2154cec5c7 100644 --- a/rothschild/src/main.rs +++ b/rothschild/src/main.rs @@ -13,7 +13,7 @@ use kaspa_consensus_core::{ use kaspa_core::{info, kaspad_env::version, time::unix_now, warn}; use kaspa_grpc_client::{ClientPool, GrpcClient}; use kaspa_notify::subscription::context::SubscriptionContext; -use kaspa_rpc_core::{api::rpc::RpcApi, notify::mode::NotificationMode}; +use kaspa_rpc_core::{api::rpc::RpcApi, notify::mode::NotificationMode, RpcUtxoEntry}; use kaspa_txscript::pay_to_address_script; use parking_lot::Mutex; use rayon::prelude::*; @@ -323,7 +323,7 @@ async fn populate_pending_outpoints_from_mempool( for entry in entries { for entry in entry.sending { for input in entry.transaction.inputs { - pending_outpoints.insert(input.previous_outpoint, now); + pending_outpoints.insert(input.previous_outpoint.into(), now); } } } @@ -337,20 +337,20 @@ async fn fetch_spendable_utxos( ) -> Vec<(TransactionOutpoint, UtxoEntry)> { let resp = rpc_client.get_utxos_by_addresses(vec![kaspa_addr]).await.unwrap(); let dag_info = rpc_client.get_block_dag_info().await.unwrap(); - let mut utxos = Vec::with_capacity(resp.len()); - for resp_entry in resp - .into_iter() - .filter(|resp_entry| is_utxo_spendable(&resp_entry.utxo_entry, dag_info.virtual_daa_score, coinbase_maturity)) + + let mut utxos = resp.into_iter() + .filter(|entry| { + is_utxo_spendable(&entry.utxo_entry, dag_info.virtual_daa_score, coinbase_maturity) + }) + .map(|entry| (TransactionOutpoint::from(entry.outpoint), UtxoEntry::from(entry.utxo_entry))) // Eliminates UTXOs we already tried to spend so we don't try to spend them again in this period - .filter(|utxo| !pending.contains_key(&utxo.outpoint)) - { - utxos.push((resp_entry.outpoint, resp_entry.utxo_entry)); - } + .filter(|(outpoint,_)| !pending.contains_key(outpoint)) + .collect::>(); utxos.sort_by(|a, b| b.1.amount.cmp(&a.1.amount)); utxos } -fn is_utxo_spendable(entry: &UtxoEntry, virtual_daa_score: u64, coinbase_maturity: u64) -> bool { +fn is_utxo_spendable(entry: &RpcUtxoEntry, virtual_daa_score: u64, coinbase_maturity: u64) -> bool { let needed_confs = if !entry.is_coinbase { 10 } else { diff --git a/rpc/core/Cargo.toml b/rpc/core/Cargo.toml index ef010a0205..ac02f7513e 100644 --- a/rpc/core/Cargo.toml +++ b/rpc/core/Cargo.toml @@ -42,6 +42,7 @@ hex.workspace = true js-sys.workspace = true log.workspace = true paste.workspace = true +rand.workspace = true serde-wasm-bindgen.workspace = true serde.workspace = true smallvec.workspace = true @@ -51,7 +52,6 @@ wasm-bindgen.workspace = true workflow-core.workspace = true workflow-serializer.workspace = true workflow-wasm.workspace = true -rand.workspace = true [dev-dependencies] serde_json.workspace = true diff --git a/rpc/core/src/convert/block.rs b/rpc/core/src/convert/block.rs index 3f66b617f2..6d1d165c18 100644 --- a/rpc/core/src/convert/block.rs +++ b/rpc/core/src/convert/block.rs @@ -10,7 +10,7 @@ use kaspa_consensus_core::block::{Block, MutableBlock}; impl From<&Block> for RpcBlock { fn from(item: &Block) -> Self { Self { - header: (*item.header).clone(), + header: item.header.as_ref().into(), transactions: item.transactions.iter().map(RpcTransaction::from).collect(), // TODO: Implement a populating process inspired from kaspad\app\rpc\rpccontext\verbosedata.go verbose_data: None, @@ -21,7 +21,7 @@ impl From<&Block> for RpcBlock { impl From<&MutableBlock> for RpcBlock { fn from(item: &MutableBlock) -> Self { Self { - header: item.header.clone(), + header: item.header.as_ref().into(), transactions: item.transactions.iter().map(RpcTransaction::from).collect(), verbose_data: None, } @@ -36,7 +36,7 @@ impl TryFrom<&RpcBlock> for Block { type Error = RpcError; fn try_from(item: &RpcBlock) -> RpcResult { Ok(Self { - header: Arc::new(item.header.clone()), + header: Arc::new(item.header.as_ref().into()), transactions: Arc::new( item.transactions .iter() diff --git a/rpc/core/src/convert/tx.rs b/rpc/core/src/convert/tx.rs index 44a9389a4b..78fe6c77ca 100644 --- a/rpc/core/src/convert/tx.rs +++ b/rpc/core/src/convert/tx.rs @@ -36,7 +36,7 @@ impl From<&TransactionOutput> for RpcTransactionOutput { impl From<&TransactionInput> for RpcTransactionInput { fn from(item: &TransactionInput) -> Self { Self { - previous_outpoint: item.previous_outpoint, + previous_outpoint: item.previous_outpoint.into(), signature_script: item.signature_script.clone(), sequence: item.sequence, sig_op_count: item.sig_op_count, @@ -83,6 +83,6 @@ impl TryFrom<&RpcTransactionOutput> for TransactionOutput { impl TryFrom<&RpcTransactionInput> for TransactionInput { type Error = RpcError; fn try_from(item: &RpcTransactionInput) -> RpcResult { - Ok(Self::new(item.previous_outpoint, item.signature_script.clone(), item.sequence, item.sig_op_count)) + Ok(Self::new(item.previous_outpoint.into(), item.signature_script.clone(), item.sequence, item.sig_op_count)) } } diff --git a/rpc/core/src/convert/utxo.rs b/rpc/core/src/convert/utxo.rs index 305fb0931f..a0376580d7 100644 --- a/rpc/core/src/convert/utxo.rs +++ b/rpc/core/src/convert/utxo.rs @@ -1,6 +1,6 @@ +use crate::RpcUtxoEntry; use crate::RpcUtxosByAddressesEntry; use kaspa_addresses::Prefix; -use kaspa_consensus_core::tx::UtxoEntry; use kaspa_index_core::indexed_utxos::UtxoSetByScriptPublicKey; use kaspa_txscript::extract_script_pub_key_address; @@ -16,8 +16,8 @@ pub fn utxo_set_into_rpc(item: &UtxoSetByScriptPublicKey, prefix: Option .iter() .map(|(outpoint, entry)| RpcUtxosByAddressesEntry { address: address.clone(), - outpoint: *outpoint, - utxo_entry: UtxoEntry::new(entry.amount, script_public_key.clone(), entry.block_daa_score, entry.is_coinbase), + outpoint: (*outpoint).into(), + utxo_entry: RpcUtxoEntry::new(entry.amount, script_public_key.clone(), entry.block_daa_score, entry.is_coinbase), }) .collect::>() }) diff --git a/rpc/core/src/model/address.rs b/rpc/core/src/model/address.rs index 720cb4f86d..69bece8ffd 100644 --- a/rpc/core/src/model/address.rs +++ b/rpc/core/src/model/address.rs @@ -1,11 +1,11 @@ use crate::{RpcTransactionOutpoint, RpcUtxoEntry}; -use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; +use workflow_serializer::prelude::*; pub type RpcAddress = kaspa_addresses::Address; /// Represents a UTXO entry of an address returned by the `GetUtxosByAddresses` RPC. -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcUtxosByAddressesEntry { pub address: Option, @@ -13,8 +13,25 @@ pub struct RpcUtxosByAddressesEntry { pub utxo_entry: RpcUtxoEntry, } +impl Serializer for RpcUtxosByAddressesEntry { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; // version + store!(Option, &self.address, writer)?; + serialize!(RpcTransactionOutpoint, &self.outpoint, writer)?; + serialize!(RpcUtxoEntry, &self.utxo_entry, writer) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u8 = load!(u8, reader)?; + let address = load!(Option, reader)?; + let outpoint = deserialize!(RpcTransactionOutpoint, reader)?; + let utxo_entry = deserialize!(RpcUtxoEntry, reader)?; + Ok(Self { address, outpoint, utxo_entry }) + } +} + /// Represents a balance of an address returned by the `GetBalancesByAddresses` RPC. -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcBalancesByAddressesEntry { pub address: RpcAddress, @@ -22,3 +39,18 @@ pub struct RpcBalancesByAddressesEntry { /// Balance of `address` if available pub balance: Option, } + +impl Serializer for RpcBalancesByAddressesEntry { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; // version + store!(RpcAddress, &self.address, writer)?; + store!(Option, &self.balance, writer) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version: u8 = load!(u8, reader)?; + let address = load!(RpcAddress, reader)?; + let balance = load!(Option, reader)?; + Ok(Self { address, balance }) + } +} diff --git a/rpc/core/src/model/block.rs b/rpc/core/src/model/block.rs index c4c501afb9..9efe6a8e88 100644 --- a/rpc/core/src/model/block.rs +++ b/rpc/core/src/model/block.rs @@ -1,8 +1,8 @@ use crate::prelude::{RpcHash, RpcHeader, RpcTransaction}; -use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; +use workflow_serializer::prelude::*; -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcBlock { pub header: RpcHeader, @@ -10,7 +10,27 @@ pub struct RpcBlock { pub verbose_data: Option, } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for RpcBlock { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + serialize!(RpcHeader, &self.header, writer)?; + serialize!(Vec, &self.transactions, writer)?; + serialize!(Option, &self.verbose_data, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + let header = deserialize!(RpcHeader, reader)?; + let transactions = deserialize!(Vec, reader)?; + let verbose_data = deserialize!(Option, reader)?; + + Ok(Self { header, transactions, verbose_data }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcBlockVerboseData { pub hash: RpcHash, @@ -25,6 +45,51 @@ pub struct RpcBlockVerboseData { pub is_chain_block: bool, } +impl Serializer for RpcBlockVerboseData { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + store!(RpcHash, &self.hash, writer)?; + store!(f64, &self.difficulty, writer)?; + store!(RpcHash, &self.selected_parent_hash, writer)?; + store!(Vec, &self.transaction_ids, writer)?; + store!(bool, &self.is_header_only, writer)?; + store!(u64, &self.blue_score, writer)?; + store!(Vec, &self.children_hashes, writer)?; + store!(Vec, &self.merge_set_blues_hashes, writer)?; + store!(Vec, &self.merge_set_reds_hashes, writer)?; + store!(bool, &self.is_chain_block, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + let hash = load!(RpcHash, reader)?; + let difficulty = load!(f64, reader)?; + let selected_parent_hash = load!(RpcHash, reader)?; + let transaction_ids = load!(Vec, reader)?; + let is_header_only = load!(bool, reader)?; + let blue_score = load!(u64, reader)?; + let children_hashes = load!(Vec, reader)?; + let merge_set_blues_hashes = load!(Vec, reader)?; + let merge_set_reds_hashes = load!(Vec, reader)?; + let is_chain_block = load!(bool, reader)?; + + Ok(Self { + hash, + difficulty, + selected_parent_hash, + transaction_ids, + is_header_only, + blue_score, + children_hashes, + merge_set_blues_hashes, + merge_set_reds_hashes, + is_chain_block, + }) + } +} + cfg_if::cfg_if! { if #[cfg(feature = "wasm32-sdk")] { use wasm_bindgen::prelude::*; diff --git a/rpc/core/src/model/header.rs b/rpc/core/src/model/header.rs index e116f87eae..28d582479b 100644 --- a/rpc/core/src/model/header.rs +++ b/rpc/core/src/model/header.rs @@ -1 +1,179 @@ -pub type RpcHeader = kaspa_consensus_core::header::Header; +// pub type RpcHeader = kaspa_consensus_core::header::Header; +use borsh::{BorshDeserialize, BorshSerialize}; +use kaspa_consensus_core::header::Header; +use kaspa_consensus_core::BlueWorkType; +use kaspa_hashes::Hash; +use serde::{Deserialize, Serialize}; +use workflow_serializer::prelude::*; + +#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcHeader { + /// Cached hash + pub hash: Hash, + pub version: u16, + pub parents_by_level: Vec>, + pub hash_merkle_root: Hash, + pub accepted_id_merkle_root: Hash, + pub utxo_commitment: Hash, + /// Timestamp is in milliseconds + pub timestamp: u64, + pub bits: u32, + pub nonce: u64, + pub daa_score: u64, + pub blue_work: BlueWorkType, + pub blue_score: u64, + pub pruning_point: Hash, +} + +impl RpcHeader { + pub fn direct_parents(&self) -> &[Hash] { + if self.parents_by_level.is_empty() { + &[] + } else { + &self.parents_by_level[0] + } + } +} + +impl AsRef for RpcHeader { + fn as_ref(&self) -> &RpcHeader { + self + } +} + +impl From
for RpcHeader { + fn from(header: Header) -> Self { + Self { + hash: header.hash, + version: header.version, + parents_by_level: header.parents_by_level, + hash_merkle_root: header.hash_merkle_root, + accepted_id_merkle_root: header.accepted_id_merkle_root, + utxo_commitment: header.utxo_commitment, + timestamp: header.timestamp, + bits: header.bits, + nonce: header.nonce, + daa_score: header.daa_score, + blue_work: header.blue_work, + blue_score: header.blue_score, + pruning_point: header.pruning_point, + } + } +} + +impl From<&Header> for RpcHeader { + fn from(header: &Header) -> Self { + Self { + hash: header.hash, + version: header.version, + parents_by_level: header.parents_by_level.clone(), + hash_merkle_root: header.hash_merkle_root, + accepted_id_merkle_root: header.accepted_id_merkle_root, + utxo_commitment: header.utxo_commitment, + timestamp: header.timestamp, + bits: header.bits, + nonce: header.nonce, + daa_score: header.daa_score, + blue_work: header.blue_work, + blue_score: header.blue_score, + pruning_point: header.pruning_point, + } + } +} + +impl From for Header { + fn from(header: RpcHeader) -> Self { + Self { + hash: header.hash, + version: header.version, + parents_by_level: header.parents_by_level, + hash_merkle_root: header.hash_merkle_root, + accepted_id_merkle_root: header.accepted_id_merkle_root, + utxo_commitment: header.utxo_commitment, + timestamp: header.timestamp, + bits: header.bits, + nonce: header.nonce, + daa_score: header.daa_score, + blue_work: header.blue_work, + blue_score: header.blue_score, + pruning_point: header.pruning_point, + } + } +} + +impl From<&RpcHeader> for Header { + fn from(header: &RpcHeader) -> Self { + Self { + hash: header.hash, + version: header.version, + parents_by_level: header.parents_by_level.clone(), + hash_merkle_root: header.hash_merkle_root, + accepted_id_merkle_root: header.accepted_id_merkle_root, + utxo_commitment: header.utxo_commitment, + timestamp: header.timestamp, + bits: header.bits, + nonce: header.nonce, + daa_score: header.daa_score, + blue_work: header.blue_work, + blue_score: header.blue_score, + pruning_point: header.pruning_point, + } + } +} + +impl Serializer for RpcHeader { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + + store!(Hash, &self.hash, writer)?; + store!(u16, &self.version, writer)?; + store!(Vec>, &self.parents_by_level, writer)?; + store!(Hash, &self.hash_merkle_root, writer)?; + store!(Hash, &self.accepted_id_merkle_root, writer)?; + store!(Hash, &self.utxo_commitment, writer)?; + store!(u64, &self.timestamp, writer)?; + store!(u32, &self.bits, writer)?; + store!(u64, &self.nonce, writer)?; + store!(u64, &self.daa_score, writer)?; + store!(BlueWorkType, &self.blue_work, writer)?; + store!(u64, &self.blue_score, writer)?; + store!(Hash, &self.pruning_point, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u32, reader)?; + + let hash = load!(Hash, reader)?; + let version = load!(u16, reader)?; + let parents_by_level = load!(Vec>, reader)?; + let hash_merkle_root = load!(Hash, reader)?; + let accepted_id_merkle_root = load!(Hash, reader)?; + let utxo_commitment = load!(Hash, reader)?; + let timestamp = load!(u64, reader)?; + let bits = load!(u32, reader)?; + let nonce = load!(u64, reader)?; + let daa_score = load!(u64, reader)?; + let blue_work = load!(BlueWorkType, reader)?; + let blue_score = load!(u64, reader)?; + let pruning_point = load!(Hash, reader)?; + + Ok(Self { + hash, + version, + parents_by_level, + hash_merkle_root, + accepted_id_merkle_root, + utxo_commitment, + timestamp, + bits, + nonce, + daa_score, + blue_work, + blue_score, + pruning_point, + }) + } +} diff --git a/rpc/core/src/model/mempool.rs b/rpc/core/src/model/mempool.rs index bd08b745a0..81ddc8f928 100644 --- a/rpc/core/src/model/mempool.rs +++ b/rpc/core/src/model/mempool.rs @@ -1,9 +1,9 @@ use super::RpcAddress; use super::RpcTransaction; -use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; +use workflow_serializer::prelude::*; -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct RpcMempoolEntry { pub fee: u64, pub transaction: RpcTransaction, @@ -16,7 +16,22 @@ impl RpcMempoolEntry { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for RpcMempoolEntry { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u64, &self.fee, writer)?; + serialize!(RpcTransaction, &self.transaction, writer)?; + store!(bool, &self.is_orphan, writer) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let fee = load!(u64, reader)?; + let transaction = deserialize!(RpcTransaction, reader)?; + let is_orphan = load!(bool, reader)?; + Ok(Self { fee, transaction, is_orphan }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct RpcMempoolEntryByAddress { pub address: RpcAddress, pub sending: Vec, @@ -29,6 +44,21 @@ impl RpcMempoolEntryByAddress { } } +impl Serializer for RpcMempoolEntryByAddress { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(RpcAddress, &self.address, writer)?; + serialize!(Vec, &self.sending, writer)?; + serialize!(Vec, &self.receiving, writer) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let address = load!(RpcAddress, reader)?; + let sending = deserialize!(Vec, reader)?; + let receiving = deserialize!(Vec, reader)?; + Ok(Self { address, sending, receiving }) + } +} + cfg_if::cfg_if! { if #[cfg(feature = "wasm32-sdk")] { use wasm_bindgen::prelude::*; diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index c335651958..f86656b98f 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -32,7 +32,7 @@ impl SubmitBlockRequest { impl Serializer for SubmitBlockRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(RpcBlock, &self.block, writer)?; + serialize!(RpcBlock, &self.block, writer)?; store!(bool, &self.allow_non_daa_blocks, writer)?; Ok(()) @@ -40,7 +40,7 @@ impl Serializer for SubmitBlockRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let block = load!(RpcBlock, reader)?; + let block = deserialize!(RpcBlock, reader)?; let allow_non_daa_blocks = load!(bool, reader)?; Ok(Self { block, allow_non_daa_blocks }) @@ -157,7 +157,7 @@ pub struct GetBlockTemplateResponse { impl Serializer for GetBlockTemplateResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(RpcBlock, &self.block, writer)?; + serialize!(RpcBlock, &self.block, writer)?; store!(bool, &self.is_synced, writer)?; Ok(()) @@ -165,7 +165,7 @@ impl Serializer for GetBlockTemplateResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let block = load!(RpcBlock, reader)?; + let block = deserialize!(RpcBlock, reader)?; let is_synced = load!(bool, reader)?; Ok(Self { block, is_synced }) @@ -215,14 +215,14 @@ pub struct GetBlockResponse { impl Serializer for GetBlockResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(RpcBlock, &self.block, writer)?; + serialize!(RpcBlock, &self.block, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let block = load!(RpcBlock, reader)?; + let block = deserialize!(RpcBlock, reader)?; Ok(Self { block }) } @@ -464,13 +464,13 @@ impl GetMempoolEntryResponse { impl Serializer for GetMempoolEntryResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(RpcMempoolEntry, &self.mempool_entry, writer)?; + serialize!(RpcMempoolEntry, &self.mempool_entry, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let mempool_entry = load!(RpcMempoolEntry, reader)?; + let mempool_entry = deserialize!(RpcMempoolEntry, reader)?; Ok(Self { mempool_entry }) } } @@ -522,13 +522,13 @@ impl GetMempoolEntriesResponse { impl Serializer for GetMempoolEntriesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(Vec, &self.mempool_entries, writer)?; + serialize!(Vec, &self.mempool_entries, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let mempool_entries = load!(Vec, reader)?; + let mempool_entries = deserialize!(Vec, reader)?; Ok(Self { mempool_entries }) } } @@ -638,8 +638,7 @@ impl SubmitTransactionRequest { impl Serializer for SubmitTransactionRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - // TODO - store!(RpcTransaction, &self.transaction, writer)?; + serialize!(RpcTransaction, &self.transaction, writer)?; store!(bool, &self.allow_orphan, writer)?; Ok(()) @@ -647,7 +646,7 @@ impl Serializer for SubmitTransactionRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let transaction = load!(RpcTransaction, reader)?; + let transaction = deserialize!(RpcTransaction, reader)?; let allow_orphan = load!(bool, reader)?; Ok(Self { transaction, allow_orphan }) @@ -858,7 +857,7 @@ impl Serializer for GetBlocksResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; store!(Vec, &self.block_hashes, writer)?; - store!(Vec, &self.blocks, writer)?; + serialize!(Vec, &self.blocks, writer)?; Ok(()) } @@ -866,7 +865,7 @@ impl Serializer for GetBlocksResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let block_hashes = load!(Vec, reader)?; - let blocks = load!(Vec, reader)?; + let blocks = deserialize!(Vec, reader)?; Ok(Self { block_hashes, blocks }) } @@ -1231,14 +1230,14 @@ impl GetBalancesByAddressesResponse { impl Serializer for GetBalancesByAddressesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(Vec, &self.entries, writer)?; + serialize!(Vec, &self.entries, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let entries = load!(Vec, reader)?; + let entries = deserialize!(Vec, reader)?; Ok(Self { entries }) } @@ -1331,14 +1330,14 @@ impl GetUtxosByAddressesResponse { impl Serializer for GetUtxosByAddressesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(Vec, &self.entries, writer)?; + serialize!(Vec, &self.entries, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let entries = load!(Vec, reader)?; + let entries = deserialize!(Vec, reader)?; Ok(Self { entries }) } @@ -1541,14 +1540,14 @@ impl GetMempoolEntriesByAddressesResponse { impl Serializer for GetMempoolEntriesByAddressesResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(Vec, &self.entries, writer)?; + serialize!(Vec, &self.entries, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let entries = load!(Vec, reader)?; + let entries = deserialize!(Vec, reader)?; Ok(Self { entries }) } @@ -2182,13 +2181,13 @@ pub struct BlockAddedNotification { impl Serializer for BlockAddedNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(RpcBlock, &self.block, writer)?; + serialize!(RpcBlock, &self.block, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let block = load!(RpcBlock, reader)?; + let block = deserialize!(RpcBlock, reader)?; Ok(Self { block: block.into() }) } } @@ -2509,15 +2508,15 @@ impl UtxosChangedNotification { impl Serializer for UtxosChangedNotification { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(Vec, &self.added, writer)?; - store!(Vec, &self.removed, writer)?; + serialize!(Vec, &self.added, writer)?; + serialize!(Vec, &self.removed, writer)?; Ok(()) } fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let added = load!(Vec, reader)?; - let removed = load!(Vec, reader)?; + let added = deserialize!(Vec, reader)?; + let removed = deserialize!(Vec, reader)?; Ok(Self { added: added.into(), removed: removed.into() }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index f9a06032d5..ec2715f709 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -4,10 +4,9 @@ mod mockery { use crate::{model::*, RpcScriptClass}; use kaspa_addresses::{Prefix, Version}; use kaspa_consensus_core::api::BlockCount; - use kaspa_consensus_core::header::Header; use kaspa_consensus_core::network::NetworkType; use kaspa_consensus_core::subnets::SubnetworkId; - use kaspa_consensus_core::tx::{ScriptPublicKey, TransactionOutpoint, UtxoEntry}; + use kaspa_consensus_core::tx::ScriptPublicKey; use kaspa_hashes::Hash; use kaspa_math::Uint192; use kaspa_notify::subscription::Command; @@ -136,9 +135,9 @@ mod mockery { } } - impl Mock for Header { + impl Mock for RpcHeader { fn mock() -> Self { - Header { + RpcHeader { version: mock(), timestamp: mock(), bits: mock(), @@ -297,15 +296,15 @@ mod mockery { } } - impl Mock for UtxoEntry { + impl Mock for RpcUtxoEntry { fn mock() -> Self { - UtxoEntry { amount: mock(), script_public_key: mock(), block_daa_score: mock(), is_coinbase: true } + RpcUtxoEntry { amount: mock(), script_public_key: mock(), block_daa_score: mock(), is_coinbase: true } } } - impl Mock for TransactionOutpoint { + impl Mock for RpcTransactionOutpoint { fn mock() -> Self { - TransactionOutpoint { transaction_id: mock(), index: mock() } + RpcTransactionOutpoint { transaction_id: mock(), index: mock() } } } diff --git a/rpc/core/src/model/tx.rs b/rpc/core/src/model/tx.rs index bb13f797de..4b3e25f508 100644 --- a/rpc/core/src/model/tx.rs +++ b/rpc/core/src/model/tx.rs @@ -1,9 +1,12 @@ use borsh::{BorshDeserialize, BorshSerialize}; use kaspa_addresses::Address; use kaspa_consensus_core::tx::{ - ScriptPublicKey, ScriptVec, TransactionId, TransactionInput, TransactionOutpoint, TransactionOutput, UtxoEntry, + ScriptPublicKey, ScriptVec, TransactionId, TransactionIndexType, TransactionInput, TransactionOutpoint, TransactionOutput, + UtxoEntry, }; +use kaspa_utils::serde_bytes_fixed_ref; use serde::{Deserialize, Serialize}; +use workflow_serializer::prelude::*; use crate::prelude::{RpcHash, RpcScriptClass, RpcSubnetworkId}; @@ -12,13 +15,120 @@ pub type RpcTransactionId = TransactionId; pub type RpcScriptVec = ScriptVec; pub type RpcScriptPublicKey = ScriptPublicKey; -pub type RpcUtxoEntry = UtxoEntry; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcUtxoEntry { + pub amount: u64, + pub script_public_key: ScriptPublicKey, + pub block_daa_score: u64, + pub is_coinbase: bool, +} + +impl RpcUtxoEntry { + pub fn new(amount: u64, script_public_key: ScriptPublicKey, block_daa_score: u64, is_coinbase: bool) -> Self { + Self { amount, script_public_key, block_daa_score, is_coinbase } + } +} + +impl From for RpcUtxoEntry { + fn from(entry: UtxoEntry) -> Self { + Self { + amount: entry.amount, + script_public_key: entry.script_public_key, + block_daa_score: entry.block_daa_score, + is_coinbase: entry.is_coinbase, + } + } +} + +impl From for UtxoEntry { + fn from(entry: RpcUtxoEntry) -> Self { + Self { + amount: entry.amount, + script_public_key: entry.script_public_key, + block_daa_score: entry.block_daa_score, + is_coinbase: entry.is_coinbase, + } + } +} + +impl Serializer for RpcUtxoEntry { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + store!(u64, &self.amount, writer)?; + store!(ScriptPublicKey, &self.script_public_key, writer)?; + store!(u64, &self.block_daa_score, writer)?; + store!(bool, &self.is_coinbase, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + let amount = load!(u64, reader)?; + let script_public_key = load!(ScriptPublicKey, reader)?; + let block_daa_score = load!(u64, reader)?; + let is_coinbase = load!(bool, reader)?; + + Ok(Self { amount, script_public_key, block_daa_score, is_coinbase }) + } +} /// Represents a Kaspa transaction outpoint -pub type RpcTransactionOutpoint = TransactionOutpoint; +#[derive(Eq, Hash, PartialEq, Debug, Copy, Clone, Serialize, Deserialize)] +pub struct RpcTransactionOutpoint { + #[serde(with = "serde_bytes_fixed_ref")] + pub transaction_id: TransactionId, + pub index: TransactionIndexType, +} + +impl From for RpcTransactionOutpoint { + fn from(outpoint: TransactionOutpoint) -> Self { + Self { transaction_id: outpoint.transaction_id, index: outpoint.index } + } +} + +impl From for TransactionOutpoint { + fn from(outpoint: RpcTransactionOutpoint) -> Self { + Self { transaction_id: outpoint.transaction_id, index: outpoint.index } + } +} + +impl From for RpcTransactionOutpoint { + fn from(outpoint: kaspa_consensus_client::TransactionOutpoint) -> Self { + TransactionOutpoint::from(outpoint).into() + // Self { transaction_id: outpoint.transaction_id, index: outpoint.index } + } +} + +impl From for kaspa_consensus_client::TransactionOutpoint { + fn from(outpoint: RpcTransactionOutpoint) -> Self { + // Self { transaction_id: outpoint.transaction_id, index: outpoint.index } + TransactionOutpoint::from(outpoint).into() + } +} + +impl Serializer for RpcTransactionOutpoint { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + store!(TransactionId, &self.transaction_id, writer)?; + store!(TransactionIndexType, &self.index, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + let transaction_id = load!(TransactionId, reader)?; + let index = load!(TransactionIndexType, reader)?; + + Ok(Self { transaction_id, index }) + } +} /// Represents a Kaspa transaction input -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcTransactionInput { pub previous_outpoint: RpcTransactionOutpoint, @@ -32,7 +142,7 @@ pub struct RpcTransactionInput { impl From for RpcTransactionInput { fn from(input: TransactionInput) -> Self { Self { - previous_outpoint: input.previous_outpoint, + previous_outpoint: input.previous_outpoint.into(), signature_script: input.signature_script, sequence: input.sequence, sig_op_count: input.sig_op_count, @@ -47,13 +157,49 @@ impl RpcTransactionInput { } } +impl Serializer for RpcTransactionInput { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + serialize!(RpcTransactionOutpoint, &self.previous_outpoint, writer)?; + store!(Vec, &self.signature_script, writer)?; + store!(u64, &self.sequence, writer)?; + store!(u8, &self.sig_op_count, writer)?; + serialize!(Option, &self.verbose_data, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + let previous_outpoint = deserialize!(RpcTransactionOutpoint, reader)?; + let signature_script = load!(Vec, reader)?; + let sequence = load!(u64, reader)?; + let sig_op_count = load!(u8, reader)?; + let verbose_data = deserialize!(Option, reader)?; + + Ok(Self { previous_outpoint, signature_script, sequence, sig_op_count, verbose_data }) + } +} + /// Represent Kaspa transaction input verbose data -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcTransactionInputVerboseData {} +impl Serializer for RpcTransactionInputVerboseData { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + Ok(Self {}) + } +} + /// Represents a Kaspad transaction output -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcTransactionOutput { pub value: u64, @@ -73,16 +219,54 @@ impl From for RpcTransactionOutput { } } +impl Serializer for RpcTransactionOutput { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + store!(u64, &self.value, writer)?; + store!(RpcScriptPublicKey, &self.script_public_key, writer)?; + serialize!(Option, &self.verbose_data, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + let value = load!(u64, reader)?; + let script_public_key = load!(RpcScriptPublicKey, reader)?; + let verbose_data = deserialize!(Option, reader)?; + + Ok(Self { value, script_public_key, verbose_data }) + } +} + /// Represent Kaspa transaction output verbose data -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcTransactionOutputVerboseData { pub script_public_key_type: RpcScriptClass, pub script_public_key_address: Address, } +impl Serializer for RpcTransactionOutputVerboseData { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + store!(RpcScriptClass, &self.script_public_key_type, writer)?; + store!(Address, &self.script_public_key_address, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + let script_public_key_type = load!(RpcScriptClass, reader)?; + let script_public_key_address = load!(Address, reader)?; + + Ok(Self { script_public_key_type, script_public_key_address }) + } +} + /// Represents a Kaspa transaction -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcTransaction { pub version: u16, @@ -97,8 +281,40 @@ pub struct RpcTransaction { pub verbose_data: Option, } +impl Serializer for RpcTransaction { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + store!(u16, &self.version, writer)?; + serialize!(Vec, &self.inputs, writer)?; + serialize!(Vec, &self.outputs, writer)?; + store!(u64, &self.lock_time, writer)?; + store!(RpcSubnetworkId, &self.subnetwork_id, writer)?; + store!(u64, &self.gas, writer)?; + store!(Vec, &self.payload, writer)?; + store!(u64, &self.mass, writer)?; + serialize!(Option, &self.verbose_data, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _struct_version = load!(u16, reader)?; + let version = load!(u16, reader)?; + let inputs = deserialize!(Vec, reader)?; + let outputs = deserialize!(Vec, reader)?; + let lock_time = load!(u64, reader)?; + let subnetwork_id = load!(RpcSubnetworkId, reader)?; + let gas = load!(u64, reader)?; + let payload = load!(Vec, reader)?; + let mass = load!(u64, reader)?; + let verbose_data = deserialize!(Option, reader)?; + + Ok(Self { version, inputs, outputs, lock_time, subnetwork_id, gas, payload, mass, verbose_data }) + } +} + /// Represent Kaspa transaction verbose data -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcTransactionVerboseData { pub transaction_id: RpcTransactionId, @@ -108,6 +324,30 @@ pub struct RpcTransactionVerboseData { pub block_time: u64, } +impl Serializer for RpcTransactionVerboseData { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + store!(RpcTransactionId, &self.transaction_id, writer)?; + store!(RpcHash, &self.hash, writer)?; + store!(u64, &self.mass, writer)?; + store!(RpcHash, &self.block_hash, writer)?; + store!(u64, &self.block_time, writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + let transaction_id = load!(RpcTransactionId, reader)?; + let hash = load!(RpcHash, reader)?; + let mass = load!(u64, reader)?; + let block_hash = load!(RpcHash, reader)?; + let block_time = load!(u64, reader)?; + + Ok(Self { transaction_id, hash, mass, block_hash, block_time }) + } +} + /// Represents accepted transaction ids #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "camelCase")] diff --git a/rpc/core/src/wasm/convert.rs b/rpc/core/src/wasm/convert.rs index 0c33cf0ec3..ce38e32c4a 100644 --- a/rpc/core/src/wasm/convert.rs +++ b/rpc/core/src/wasm/convert.rs @@ -1,12 +1,11 @@ use crate::model::*; use kaspa_consensus_client::*; -use kaspa_consensus_core::tx as cctx; use std::sync::Arc; impl From for UtxoEntry { fn from(entry: RpcUtxosByAddressesEntry) -> UtxoEntry { let RpcUtxosByAddressesEntry { address, outpoint, utxo_entry } = entry; - let cctx::UtxoEntry { amount, script_public_key, block_daa_score, is_coinbase } = utxo_entry; + let RpcUtxoEntry { amount, script_public_key, block_daa_score, is_coinbase } = utxo_entry; UtxoEntry { address, outpoint: outpoint.into(), amount, script_public_key, block_daa_score, is_coinbase } } } diff --git a/rpc/grpc/core/src/convert/header.rs b/rpc/grpc/core/src/convert/header.rs index f4d78b7c11..66296a169e 100644 --- a/rpc/grpc/core/src/convert/header.rs +++ b/rpc/grpc/core/src/convert/header.rs @@ -1,5 +1,6 @@ use crate::protowire; use crate::{from, try_from}; +use kaspa_consensus_core::header::Header; use kaspa_rpc_core::{FromRpcHex, RpcError, RpcHash, RpcResult, ToRpcHex}; use std::str::FromStr; @@ -31,8 +32,11 @@ from!(item: &Vec, protowire::RpcBlockLevelParents, { Self { parent_hash // ---------------------------------------------------------------------------- try_from!(item: &protowire::RpcBlockHeader, kaspa_rpc_core::RpcHeader, { + + // TODO - restructure using dual structs (with and without hash) + // We re-hash the block to remain as most trustless as possible - Self::new_finalized( + let header = Header::new_finalized( item.version.try_into()?, item.parents.iter().map(Vec::::try_from).collect::>>>()?, RpcHash::from_str(&item.hash_merkle_root)?, @@ -45,7 +49,9 @@ try_from!(item: &protowire::RpcBlockHeader, kaspa_rpc_core::RpcHeader, { kaspa_rpc_core::RpcBlueWorkType::from_rpc_hex(&item.blue_work)?, item.blue_score, RpcHash::from_str(&item.pruning_point)?, - ) + ); + + header.into() }); try_from!(item: &protowire::RpcBlockLevelParents, Vec, { @@ -55,6 +61,7 @@ try_from!(item: &protowire::RpcBlockLevelParents, Vec, { #[cfg(test)] mod tests { use crate::protowire; + use kaspa_consensus_core::header::Header; use kaspa_rpc_core::{RpcHash, RpcHeader}; fn new_unique() -> RpcHash { @@ -106,7 +113,7 @@ mod tests { #[test] fn test_rpc_header() { - let r = RpcHeader::new_finalized( + let r = Header::new_finalized( 0, vec![vec![new_unique(), new_unique(), new_unique()], vec![new_unique()], vec![new_unique(), new_unique()]], new_unique(), @@ -120,6 +127,7 @@ mod tests { 1928374, new_unique(), ); + let r = RpcHeader::from(r); let p: protowire::RpcBlockHeader = (&r).into(); let r2: RpcHeader = (&p).try_into().unwrap(); let p2: protowire::RpcBlockHeader = (&r2).into(); diff --git a/rpc/service/src/converter/consensus.rs b/rpc/service/src/converter/consensus.rs index 9f3f5b661d..dda04899f5 100644 --- a/rpc/service/src/converter/consensus.rs +++ b/rpc/service/src/converter/consensus.rs @@ -81,7 +81,7 @@ impl ConsensusConverter { vec![] }; - Ok(RpcBlock { header: (*block.header).clone(), transactions, verbose_data }) + Ok(RpcBlock { header: block.header.as_ref().into(), transactions, verbose_data }) } pub fn get_mempool_entry(&self, consensus: &ConsensusProxy, transaction: &MutableTransaction) -> RpcMempoolEntry { diff --git a/testing/integration/src/common/utils.rs b/testing/integration/src/common/utils.rs index 824bda3886..f8c547766f 100644 --- a/testing/integration/src/common/utils.rs +++ b/testing/integration/src/common/utils.rs @@ -16,7 +16,7 @@ use kaspa_consensus_core::{ }; use kaspa_core::info; use kaspa_grpc_client::GrpcClient; -use kaspa_rpc_core::{api::rpc::RpcApi, BlockAddedNotification, Notification, VirtualDaaScoreChangedNotification}; +use kaspa_rpc_core::{api::rpc::RpcApi, BlockAddedNotification, Notification, RpcUtxoEntry, VirtualDaaScoreChangedNotification}; use kaspa_txscript::pay_to_address_script; use rayon::prelude::{IntoParallelIterator, ParallelIterator}; use secp256k1::Keypair; @@ -170,13 +170,13 @@ pub async fn fetch_spendable_utxos( { assert!(resp_entry.address.is_some()); assert_eq!(*resp_entry.address.as_ref().unwrap(), address); - utxos.push((resp_entry.outpoint, resp_entry.utxo_entry)); + utxos.push((TransactionOutpoint::from(resp_entry.outpoint), UtxoEntry::from(resp_entry.utxo_entry))); } utxos.sort_by(|a, b| b.1.amount.cmp(&a.1.amount)); utxos } -pub fn is_utxo_spendable(entry: &UtxoEntry, virtual_daa_score: u64, coinbase_maturity: u64) -> bool { +pub fn is_utxo_spendable(entry: &RpcUtxoEntry, virtual_daa_score: u64, coinbase_maturity: u64) -> bool { let needed_confirmations = if !entry.is_coinbase { 10 } else { coinbase_maturity }; entry.block_daa_score + needed_confirmations <= virtual_daa_score } From 5b25d9a4c3303716e4214e0a86d6f1efa2f8acf0 Mon Sep 17 00:00:00 2001 From: aspect Date: Sat, 29 Jun 2024 14:07:45 +0300 Subject: [PATCH 056/158] Provisional RpcConnection propagation via RpcApi methods (#46) * provisional RpcConnection propagation via RpcApi methods * lints --- cli/src/modules/rpc.rs | 57 +++--- rpc/core/src/api/connection.rs | 7 + rpc/core/src/api/mod.rs | 1 + rpc/core/src/api/rpc.rs | 228 ++++++++++++++------- rpc/grpc/client/src/route.rs | 4 +- rpc/grpc/server/src/tests/rpc_core_mock.rs | 144 ++++++++++--- rpc/macros/src/grpc/server.rs | 3 +- rpc/macros/src/wrpc/client.rs | 4 +- rpc/macros/src/wrpc/server.rs | 3 +- rpc/macros/src/wrpc/wasm.rs | 4 +- rpc/service/src/service.rs | 139 ++++++++++--- testing/integration/src/rpc_tests.rs | 224 +++++++++++--------- wallet/core/src/tests/rpc_core_mock.rs | 144 ++++++++++--- 13 files changed, 672 insertions(+), 290 deletions(-) create mode 100644 rpc/core/src/api/connection.rs diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index 8b497c52be..945fd85024 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -42,15 +42,15 @@ impl Rpc { self.println(&ctx, result); } RpcApiOps::GetServerInfo => { - let result = rpc.get_server_info_call(GetServerInfoRequest {}).await?; + let result = rpc.get_server_info_call(None, GetServerInfoRequest {}).await?; self.println(&ctx, result); } RpcApiOps::GetSyncStatus => { - let result = rpc.get_sync_status_call(GetSyncStatusRequest {}).await?; + let result = rpc.get_sync_status_call(None, GetSyncStatusRequest {}).await?; self.println(&ctx, result); } RpcApiOps::GetCurrentNetwork => { - let result = rpc.get_current_network_call(GetCurrentNetworkRequest {}).await?; + let result = rpc.get_current_network_call(None, GetCurrentNetworkRequest {}).await?; self.println(&ctx, result); } // RpcApiOps::SubmitBlock => { @@ -62,11 +62,11 @@ impl Rpc { // self.println(&ctx, result); // } RpcApiOps::GetPeerAddresses => { - let result = rpc.get_peer_addresses_call(GetPeerAddressesRequest {}).await?; + let result = rpc.get_peer_addresses_call(None, GetPeerAddressesRequest {}).await?; self.println(&ctx, result); } RpcApiOps::GetSink => { - let result = rpc.get_sink_call(GetSinkRequest {}).await?; + let result = rpc.get_sink_call(None, GetSinkRequest {}).await?; self.println(&ctx, result); } // RpcApiOps::GetMempoolEntry => { @@ -76,12 +76,15 @@ impl Rpc { RpcApiOps::GetMempoolEntries => { // TODO let result = rpc - .get_mempool_entries_call(GetMempoolEntriesRequest { include_orphan_pool: true, filter_transaction_pool: true }) + .get_mempool_entries_call( + None, + GetMempoolEntriesRequest { include_orphan_pool: true, filter_transaction_pool: true }, + ) .await?; self.println(&ctx, result); } RpcApiOps::GetConnectedPeerInfo => { - let result = rpc.get_connected_peer_info_call(GetConnectedPeerInfoRequest {}).await?; + let result = rpc.get_connected_peer_info_call(None, GetConnectedPeerInfoRequest {}).await?; self.println(&ctx, result); } RpcApiOps::AddPeer => { @@ -90,7 +93,7 @@ impl Rpc { } let peer_address = argv.remove(0).parse::()?; let is_permanent = argv.remove(0).parse::().unwrap_or(false); - let result = rpc.add_peer_call(AddPeerRequest { peer_address, is_permanent }).await?; + let result = rpc.add_peer_call(None, AddPeerRequest { peer_address, is_permanent }).await?; self.println(&ctx, result); } // RpcApiOps::SubmitTransaction => { @@ -103,7 +106,7 @@ impl Rpc { } let hash = argv.remove(0); let hash = RpcHash::from_hex(hash.as_str())?; - let result = rpc.get_block_call(GetBlockRequest { hash, include_transactions: true }).await?; + let result = rpc.get_block_call(None, GetBlockRequest { hash, include_transactions: true }).await?; self.println(&ctx, result); } // RpcApiOps::GetSubnetwork => { @@ -119,11 +122,11 @@ impl Rpc { // self.println(&ctx, result); // } RpcApiOps::GetBlockCount => { - let result = rpc.get_block_count_call(GetBlockCountRequest {}).await?; + let result = rpc.get_block_count_call(None, GetBlockCountRequest {}).await?; self.println(&ctx, result); } RpcApiOps::GetBlockDagInfo => { - let result = rpc.get_block_dag_info_call(GetBlockDagInfoRequest {}).await?; + let result = rpc.get_block_dag_info_call(None, GetBlockDagInfoRequest {}).await?; self.println(&ctx, result); } // RpcApiOps::ResolveFinalityConflict => { @@ -131,7 +134,7 @@ impl Rpc { // self.println(&ctx, result); // } RpcApiOps::Shutdown => { - let result = rpc.shutdown_call(ShutdownRequest {}).await?; + let result = rpc.shutdown_call(None, ShutdownRequest {}).await?; self.println(&ctx, result); } // RpcApiOps::GetHeaders => { @@ -143,7 +146,7 @@ impl Rpc { return Err(Error::custom("Please specify at least one address")); } let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::, _>>()?; - let result = rpc.get_utxos_by_addresses_call(GetUtxosByAddressesRequest { addresses }).await?; + let result = rpc.get_utxos_by_addresses_call(None, GetUtxosByAddressesRequest { addresses }).await?; self.println(&ctx, result); } RpcApiOps::GetBalanceByAddress => { @@ -152,7 +155,7 @@ impl Rpc { } let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::, _>>()?; for address in addresses { - let result = rpc.get_balance_by_address_call(GetBalanceByAddressRequest { address }).await?; + let result = rpc.get_balance_by_address_call(None, GetBalanceByAddressRequest { address }).await?; self.println(&ctx, sompi_to_kaspa(result.balance)); } } @@ -161,11 +164,11 @@ impl Rpc { return Err(Error::custom("Please specify at least one address")); } let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::, _>>()?; - let result = rpc.get_balances_by_addresses_call(GetBalancesByAddressesRequest { addresses }).await?; + let result = rpc.get_balances_by_addresses_call(None, GetBalancesByAddressesRequest { addresses }).await?; self.println(&ctx, result); } RpcApiOps::GetSinkBlueScore => { - let result = rpc.get_sink_blue_score_call(GetSinkBlueScoreRequest {}).await?; + let result = rpc.get_sink_blue_score_call(None, GetSinkBlueScoreRequest {}).await?; self.println(&ctx, result); } RpcApiOps::Ban => { @@ -173,7 +176,7 @@ impl Rpc { return Err(Error::custom("Please specify peer IP address")); } let ip: RpcIpAddress = argv.remove(0).parse()?; - let result = rpc.ban_call(BanRequest { ip }).await?; + let result = rpc.ban_call(None, BanRequest { ip }).await?; self.println(&ctx, result); } RpcApiOps::Unban => { @@ -181,11 +184,11 @@ impl Rpc { return Err(Error::custom("Please specify peer IP address")); } let ip: RpcIpAddress = argv.remove(0).parse()?; - let result = rpc.unban_call(UnbanRequest { ip }).await?; + let result = rpc.unban_call(None, UnbanRequest { ip }).await?; self.println(&ctx, result); } RpcApiOps::GetInfo => { - let result = rpc.get_info_call(GetInfoRequest {}).await?; + let result = rpc.get_info_call(None, GetInfoRequest {}).await?; self.println(&ctx, result); } // RpcApiOps::EstimateNetworkHashesPerSecond => { @@ -200,16 +203,15 @@ impl Rpc { let include_orphan_pool = true; let filter_transaction_pool = true; let result = rpc - .get_mempool_entries_by_addresses_call(GetMempoolEntriesByAddressesRequest { - addresses, - include_orphan_pool, - filter_transaction_pool, - }) + .get_mempool_entries_by_addresses_call( + None, + GetMempoolEntriesByAddressesRequest { addresses, include_orphan_pool, filter_transaction_pool }, + ) .await?; self.println(&ctx, result); } RpcApiOps::GetCoinSupply => { - let result = rpc.get_coin_supply_call(GetCoinSupplyRequest {}).await?; + let result = rpc.get_coin_supply_call(None, GetCoinSupplyRequest {}).await?; self.println(&ctx, result); } RpcApiOps::GetDaaScoreTimestampEstimate => { @@ -220,8 +222,9 @@ impl Rpc { match daa_score_result { Ok(daa_scores) => { - let result = - rpc.get_daa_score_timestamp_estimate_call(GetDaaScoreTimestampEstimateRequest { daa_scores }).await?; + let result = rpc + .get_daa_score_timestamp_estimate_call(None, GetDaaScoreTimestampEstimateRequest { daa_scores }) + .await?; self.println(&ctx, result); } Err(_err) => { diff --git a/rpc/core/src/api/connection.rs b/rpc/core/src/api/connection.rs new file mode 100644 index 0000000000..5b4254288d --- /dev/null +++ b/rpc/core/src/api/connection.rs @@ -0,0 +1,7 @@ +use std::sync::Arc; + +pub trait RpcConnection: Send + Sync { + fn id(&self) -> u64; +} + +pub type DynRpcConnection = Arc; diff --git a/rpc/core/src/api/mod.rs b/rpc/core/src/api/mod.rs index 6bc968b46f..1373bd6e0b 100644 --- a/rpc/core/src/api/mod.rs +++ b/rpc/core/src/api/mod.rs @@ -1,3 +1,4 @@ +pub mod connection; pub mod ctl; pub mod notifications; pub mod ops; diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 293dc2d425..9ee7db84a9 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -4,6 +4,7 @@ //! All data provided by the RCP server can be trusted by the client //! No data submitted by the client to the server can be trusted +use crate::api::connection::DynRpcConnection; use crate::{model::*, notify::connection::ChannelConnection, RpcResult}; use async_trait::async_trait; use downcast::{downcast_sync, AnySync}; @@ -21,10 +22,10 @@ pub const MAX_SAFE_WINDOW_SIZE: u32 = 10_000; pub trait RpcApi: Sync + Send + AnySync { /// async fn ping(&self) -> RpcResult<()> { - self.ping_call(PingRequest {}).await?; + self.ping_call(None, PingRequest {}).await?; Ok(()) } - async fn ping_call(&self, request: PingRequest) -> RpcResult; + async fn ping_call(&self, connection: Option<&DynRpcConnection>, request: PingRequest) -> RpcResult; // --- @@ -36,65 +37,90 @@ pub trait RpcApi: Sync + Send + AnySync { consensus_metrics: bool, storage_metrics: bool, ) -> RpcResult { - self.get_metrics_call(GetMetricsRequest { - process_metrics, - connection_metrics, - bandwidth_metrics, - consensus_metrics, - storage_metrics, - }) + self.get_metrics_call( + None, + GetMetricsRequest { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics }, + ) .await } - async fn get_metrics_call(&self, request: GetMetricsRequest) -> RpcResult; + async fn get_metrics_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetMetricsRequest, + ) -> RpcResult; // get_info alternative that carries only version, network_id (full), is_synced, virtual_daa_score // these are the only variables needed to negotiate a wRPC connection (besides the wRPC handshake) async fn get_server_info(&self) -> RpcResult { - self.get_server_info_call(GetServerInfoRequest {}).await + self.get_server_info_call(None, GetServerInfoRequest {}).await } - async fn get_server_info_call(&self, request: GetServerInfoRequest) -> RpcResult; + async fn get_server_info_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetServerInfoRequest, + ) -> RpcResult; // Get current sync status of the node (should be converted to a notification subscription) async fn get_sync_status(&self) -> RpcResult { - Ok(self.get_sync_status_call(GetSyncStatusRequest {}).await?.is_synced) + Ok(self.get_sync_status_call(None, GetSyncStatusRequest {}).await?.is_synced) } - async fn get_sync_status_call(&self, request: GetSyncStatusRequest) -> RpcResult; + async fn get_sync_status_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetSyncStatusRequest, + ) -> RpcResult; // --- /// Requests the network the node is currently running against. async fn get_current_network(&self) -> RpcResult { - Ok(self.get_current_network_call(GetCurrentNetworkRequest {}).await?.network) + Ok(self.get_current_network_call(None, GetCurrentNetworkRequest {}).await?.network) } - async fn get_current_network_call(&self, request: GetCurrentNetworkRequest) -> RpcResult; + async fn get_current_network_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetCurrentNetworkRequest, + ) -> RpcResult; /// Submit a block into the DAG. /// /// Blocks are generally expected to have been generated using the get_block_template call. async fn submit_block(&self, block: RpcBlock, allow_non_daa_blocks: bool) -> RpcResult { - self.submit_block_call(SubmitBlockRequest::new(block, allow_non_daa_blocks)).await + self.submit_block_call(None, SubmitBlockRequest::new(block, allow_non_daa_blocks)).await } - async fn submit_block_call(&self, request: SubmitBlockRequest) -> RpcResult; + async fn submit_block_call( + &self, + connection: Option<&DynRpcConnection>, + request: SubmitBlockRequest, + ) -> RpcResult; /// Request a current block template. /// /// Callers are expected to solve the block template and submit it using the submit_block call. async fn get_block_template(&self, pay_address: RpcAddress, extra_data: RpcExtraData) -> RpcResult { - self.get_block_template_call(GetBlockTemplateRequest::new(pay_address, extra_data)).await + self.get_block_template_call(None, GetBlockTemplateRequest::new(pay_address, extra_data)).await } - async fn get_block_template_call(&self, request: GetBlockTemplateRequest) -> RpcResult; + async fn get_block_template_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetBlockTemplateRequest, + ) -> RpcResult; /// Requests the list of known kaspad addresses in the current network (mainnet, testnet, etc.) async fn get_peer_addresses(&self) -> RpcResult { - self.get_peer_addresses_call(GetPeerAddressesRequest {}).await + self.get_peer_addresses_call(None, GetPeerAddressesRequest {}).await } - async fn get_peer_addresses_call(&self, request: GetPeerAddressesRequest) -> RpcResult; + async fn get_peer_addresses_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetPeerAddressesRequest, + ) -> RpcResult; /// requests the hash of the current virtual's selected parent. async fn get_sink(&self) -> RpcResult { - self.get_sink_call(GetSinkRequest {}).await + self.get_sink_call(None, GetSinkRequest {}).await } - async fn get_sink_call(&self, request: GetSinkRequest) -> RpcResult; + async fn get_sink_call(&self, connection: Option<&DynRpcConnection>, request: GetSinkRequest) -> RpcResult; /// Requests information about a specific transaction in the mempool. async fn get_mempool_entry( @@ -104,53 +130,73 @@ pub trait RpcApi: Sync + Send + AnySync { filter_transaction_pool: bool, ) -> RpcResult { Ok(self - .get_mempool_entry_call(GetMempoolEntryRequest::new(transaction_id, include_orphan_pool, filter_transaction_pool)) + .get_mempool_entry_call(None, GetMempoolEntryRequest::new(transaction_id, include_orphan_pool, filter_transaction_pool)) .await? .mempool_entry) } - async fn get_mempool_entry_call(&self, request: GetMempoolEntryRequest) -> RpcResult; + async fn get_mempool_entry_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetMempoolEntryRequest, + ) -> RpcResult; /// Requests information about all the transactions currently in the mempool. async fn get_mempool_entries(&self, include_orphan_pool: bool, filter_transaction_pool: bool) -> RpcResult> { Ok(self - .get_mempool_entries_call(GetMempoolEntriesRequest::new(include_orphan_pool, filter_transaction_pool)) + .get_mempool_entries_call(None, GetMempoolEntriesRequest::new(include_orphan_pool, filter_transaction_pool)) .await? .mempool_entries) } - async fn get_mempool_entries_call(&self, request: GetMempoolEntriesRequest) -> RpcResult; + async fn get_mempool_entries_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetMempoolEntriesRequest, + ) -> RpcResult; /// requests information about all the p2p peers currently connected to this node. async fn get_connected_peer_info(&self) -> RpcResult { - self.get_connected_peer_info_call(GetConnectedPeerInfoRequest {}).await + self.get_connected_peer_info_call(None, GetConnectedPeerInfoRequest {}).await } - async fn get_connected_peer_info_call(&self, request: GetConnectedPeerInfoRequest) -> RpcResult; + async fn get_connected_peer_info_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetConnectedPeerInfoRequest, + ) -> RpcResult; /// Adds a peer to the node's outgoing connection list. /// /// This will, in most cases, result in the node connecting to said peer. async fn add_peer(&self, peer_address: RpcContextualPeerAddress, is_permanent: bool) -> RpcResult<()> { - self.add_peer_call(AddPeerRequest::new(peer_address, is_permanent)).await?; + self.add_peer_call(None, AddPeerRequest::new(peer_address, is_permanent)).await?; Ok(()) } - async fn add_peer_call(&self, request: AddPeerRequest) -> RpcResult; + async fn add_peer_call(&self, connection: Option<&DynRpcConnection>, request: AddPeerRequest) -> RpcResult; /// Submits a transaction to the mempool. async fn submit_transaction(&self, transaction: RpcTransaction, allow_orphan: bool) -> RpcResult { - Ok(self.submit_transaction_call(SubmitTransactionRequest { transaction, allow_orphan }).await?.transaction_id) + Ok(self.submit_transaction_call(None, SubmitTransactionRequest { transaction, allow_orphan }).await?.transaction_id) } - async fn submit_transaction_call(&self, request: SubmitTransactionRequest) -> RpcResult; + async fn submit_transaction_call( + &self, + connection: Option<&DynRpcConnection>, + request: SubmitTransactionRequest, + ) -> RpcResult; /// Requests information about a specific block. async fn get_block(&self, hash: RpcHash, include_transactions: bool) -> RpcResult { - Ok(self.get_block_call(GetBlockRequest::new(hash, include_transactions)).await?.block) + Ok(self.get_block_call(None, GetBlockRequest::new(hash, include_transactions)).await?.block) } - async fn get_block_call(&self, request: GetBlockRequest) -> RpcResult; + async fn get_block_call(&self, connection: Option<&DynRpcConnection>, request: GetBlockRequest) -> RpcResult; /// Requests information about a specific subnetwork. async fn get_subnetwork(&self, subnetwork_id: RpcSubnetworkId) -> RpcResult { - self.get_subnetwork_call(GetSubnetworkRequest::new(subnetwork_id)).await + self.get_subnetwork_call(None, GetSubnetworkRequest::new(subnetwork_id)).await } - async fn get_subnetwork_call(&self, request: GetSubnetworkRequest) -> RpcResult; + async fn get_subnetwork_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetSubnetworkRequest, + ) -> RpcResult; /// Requests the virtual selected parent chain from some `start_hash` to this node's current virtual. async fn get_virtual_chain_from_block( @@ -158,11 +204,15 @@ pub trait RpcApi: Sync + Send + AnySync { start_hash: RpcHash, include_accepted_transaction_ids: bool, ) -> RpcResult { - self.get_virtual_chain_from_block_call(GetVirtualChainFromBlockRequest::new(start_hash, include_accepted_transaction_ids)) - .await + self.get_virtual_chain_from_block_call( + None, + GetVirtualChainFromBlockRequest::new(start_hash, include_accepted_transaction_ids), + ) + .await } async fn get_virtual_chain_from_block_call( &self, + connection: Option<&DynRpcConnection>, request: GetVirtualChainFromBlockRequest, ) -> RpcResult; @@ -173,61 +223,79 @@ pub trait RpcApi: Sync + Send + AnySync { include_blocks: bool, include_transactions: bool, ) -> RpcResult { - self.get_blocks_call(GetBlocksRequest::new(low_hash, include_blocks, include_transactions)).await + self.get_blocks_call(None, GetBlocksRequest::new(low_hash, include_blocks, include_transactions)).await } - async fn get_blocks_call(&self, request: GetBlocksRequest) -> RpcResult; + async fn get_blocks_call(&self, connection: Option<&DynRpcConnection>, request: GetBlocksRequest) -> RpcResult; /// Requests the current number of blocks in this node. /// /// Note that this number may decrease as pruning occurs. async fn get_block_count(&self) -> RpcResult { - self.get_block_count_call(GetBlockCountRequest {}).await + self.get_block_count_call(None, GetBlockCountRequest {}).await } - async fn get_block_count_call(&self, request: GetBlockCountRequest) -> RpcResult; + async fn get_block_count_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetBlockCountRequest, + ) -> RpcResult; /// Requests general information about the current state of this node's DAG. async fn get_block_dag_info(&self) -> RpcResult { - self.get_block_dag_info_call(GetBlockDagInfoRequest {}).await + self.get_block_dag_info_call(None, GetBlockDagInfoRequest {}).await } - async fn get_block_dag_info_call(&self, request: GetBlockDagInfoRequest) -> RpcResult; + async fn get_block_dag_info_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetBlockDagInfoRequest, + ) -> RpcResult; /// async fn resolve_finality_conflict(&self, finality_block_hash: RpcHash) -> RpcResult<()> { - self.resolve_finality_conflict_call(ResolveFinalityConflictRequest::new(finality_block_hash)).await?; + self.resolve_finality_conflict_call(None, ResolveFinalityConflictRequest::new(finality_block_hash)).await?; Ok(()) } async fn resolve_finality_conflict_call( &self, + connection: Option<&DynRpcConnection>, request: ResolveFinalityConflictRequest, ) -> RpcResult; /// Shuts down this node. async fn shutdown(&self) -> RpcResult<()> { - self.shutdown_call(ShutdownRequest {}).await?; + self.shutdown_call(None, ShutdownRequest {}).await?; Ok(()) } - async fn shutdown_call(&self, request: ShutdownRequest) -> RpcResult; + async fn shutdown_call(&self, connection: Option<&DynRpcConnection>, request: ShutdownRequest) -> RpcResult; /// Requests headers between the given `start_hash` and the current virtual, up to the given limit. async fn get_headers(&self, start_hash: RpcHash, limit: u64, is_ascending: bool) -> RpcResult> { - Ok(self.get_headers_call(GetHeadersRequest::new(start_hash, limit, is_ascending)).await?.headers) + Ok(self.get_headers_call(None, GetHeadersRequest::new(start_hash, limit, is_ascending)).await?.headers) } - async fn get_headers_call(&self, request: GetHeadersRequest) -> RpcResult; + async fn get_headers_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetHeadersRequest, + ) -> RpcResult; /// Returns the total balance in unspent transactions towards a given address. /// /// This call is only available when this node was started with `--utxoindex`. async fn get_balance_by_address(&self, address: RpcAddress) -> RpcResult { - Ok(self.get_balance_by_address_call(GetBalanceByAddressRequest::new(address)).await?.balance) + Ok(self.get_balance_by_address_call(None, GetBalanceByAddressRequest::new(address)).await?.balance) } - async fn get_balance_by_address_call(&self, request: GetBalanceByAddressRequest) -> RpcResult; + async fn get_balance_by_address_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetBalanceByAddressRequest, + ) -> RpcResult; /// async fn get_balances_by_addresses(&self, addresses: Vec) -> RpcResult> { - Ok(self.get_balances_by_addresses_call(GetBalancesByAddressesRequest::new(addresses)).await?.entries) + Ok(self.get_balances_by_addresses_call(None, GetBalancesByAddressesRequest::new(addresses)).await?.entries) } async fn get_balances_by_addresses_call( &self, + connection: Option<&DynRpcConnection>, request: GetBalancesByAddressesRequest, ) -> RpcResult; @@ -235,45 +303,54 @@ pub trait RpcApi: Sync + Send + AnySync { /// /// This call is only available when this node was started with `--utxoindex`. async fn get_utxos_by_addresses(&self, addresses: Vec) -> RpcResult> { - Ok(self.get_utxos_by_addresses_call(GetUtxosByAddressesRequest::new(addresses)).await?.entries) + Ok(self.get_utxos_by_addresses_call(None, GetUtxosByAddressesRequest::new(addresses)).await?.entries) } - async fn get_utxos_by_addresses_call(&self, request: GetUtxosByAddressesRequest) -> RpcResult; + async fn get_utxos_by_addresses_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetUtxosByAddressesRequest, + ) -> RpcResult; /// Requests the blue score of the current selected parent of the virtual block. async fn get_sink_blue_score(&self) -> RpcResult { - Ok(self.get_sink_blue_score_call(GetSinkBlueScoreRequest {}).await?.blue_score) + Ok(self.get_sink_blue_score_call(None, GetSinkBlueScoreRequest {}).await?.blue_score) } - async fn get_sink_blue_score_call(&self, request: GetSinkBlueScoreRequest) -> RpcResult; + async fn get_sink_blue_score_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetSinkBlueScoreRequest, + ) -> RpcResult; /// Bans the given ip. async fn ban(&self, ip: RpcIpAddress) -> RpcResult<()> { - self.ban_call(BanRequest::new(ip)).await?; + self.ban_call(None, BanRequest::new(ip)).await?; Ok(()) } - async fn ban_call(&self, request: BanRequest) -> RpcResult; + async fn ban_call(&self, connection: Option<&DynRpcConnection>, request: BanRequest) -> RpcResult; /// Unbans the given ip. async fn unban(&self, ip: RpcIpAddress) -> RpcResult<()> { - self.unban_call(UnbanRequest::new(ip)).await?; + self.unban_call(None, UnbanRequest::new(ip)).await?; Ok(()) } - async fn unban_call(&self, request: UnbanRequest) -> RpcResult; + async fn unban_call(&self, connection: Option<&DynRpcConnection>, request: UnbanRequest) -> RpcResult; /// Returns info about the node. - async fn get_info_call(&self, request: GetInfoRequest) -> RpcResult; async fn get_info(&self) -> RpcResult { - self.get_info_call(GetInfoRequest {}).await + self.get_info_call(None, GetInfoRequest {}).await } + async fn get_info_call(&self, connection: Option<&DynRpcConnection>, request: GetInfoRequest) -> RpcResult; /// async fn estimate_network_hashes_per_second(&self, window_size: u32, start_hash: Option) -> RpcResult { Ok(self - .estimate_network_hashes_per_second_call(EstimateNetworkHashesPerSecondRequest::new(window_size, start_hash)) + .estimate_network_hashes_per_second_call(None, EstimateNetworkHashesPerSecondRequest::new(window_size, start_hash)) .await? .network_hashes_per_second) } async fn estimate_network_hashes_per_second_call( &self, + connection: Option<&DynRpcConnection>, request: EstimateNetworkHashesPerSecondRequest, ) -> RpcResult; @@ -285,30 +362,35 @@ pub trait RpcApi: Sync + Send + AnySync { filter_transaction_pool: bool, ) -> RpcResult> { Ok(self - .get_mempool_entries_by_addresses_call(GetMempoolEntriesByAddressesRequest::new( - addresses, - include_orphan_pool, - filter_transaction_pool, - )) + .get_mempool_entries_by_addresses_call( + None, + GetMempoolEntriesByAddressesRequest::new(addresses, include_orphan_pool, filter_transaction_pool), + ) .await? .entries) } async fn get_mempool_entries_by_addresses_call( &self, + connection: Option<&DynRpcConnection>, request: GetMempoolEntriesByAddressesRequest, ) -> RpcResult; /// async fn get_coin_supply(&self) -> RpcResult { - self.get_coin_supply_call(GetCoinSupplyRequest {}).await + self.get_coin_supply_call(None, GetCoinSupplyRequest {}).await } - async fn get_coin_supply_call(&self, request: GetCoinSupplyRequest) -> RpcResult; + async fn get_coin_supply_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetCoinSupplyRequest, + ) -> RpcResult; async fn get_daa_score_timestamp_estimate(&self, daa_scores: Vec) -> RpcResult> { - Ok(self.get_daa_score_timestamp_estimate_call(GetDaaScoreTimestampEstimateRequest { daa_scores }).await?.timestamps) + Ok(self.get_daa_score_timestamp_estimate_call(None, GetDaaScoreTimestampEstimateRequest { daa_scores }).await?.timestamps) } async fn get_daa_score_timestamp_estimate_call( &self, + connection: Option<&DynRpcConnection>, request: GetDaaScoreTimestampEstimateRequest, ) -> RpcResult; diff --git a/rpc/grpc/client/src/route.rs b/rpc/grpc/client/src/route.rs index 5bb1bf3950..bb5b5ce56d 100644 --- a/rpc/grpc/client/src/route.rs +++ b/rpc/grpc/client/src/route.rs @@ -9,12 +9,14 @@ macro_rules! route { clippy::type_repetition_in_bounds, clippy::used_underscore_binding )] - fn $fn<'life0, 'async_trait>( + fn $fn<'life0, 'life1, 'async_trait>( &'life0 self, + _connection : ::core::option::Option<&'life1 Arc>, request: [<$name Request>], ) -> ::core::pin::Pin]>> + ::core::marker::Send + 'async_trait>> where 'life0: 'async_trait, + 'life1: 'async_trait, Self: 'async_trait, { Box::pin(async move { diff --git a/rpc/grpc/server/src/tests/rpc_core_mock.rs b/rpc/grpc/server/src/tests/rpc_core_mock.rs index ddf78ccbd7..e6abf8aa3b 100644 --- a/rpc/grpc/server/src/tests/rpc_core_mock.rs +++ b/rpc/grpc/server/src/tests/rpc_core_mock.rs @@ -6,7 +6,7 @@ use kaspa_notify::notifier::{Notifier, Notify}; use kaspa_notify::scope::Scope; use kaspa_notify::subscription::context::SubscriptionContext; use kaspa_notify::subscription::{MutationPolicies, UtxosChangedMutationPolicy}; -use kaspa_rpc_core::{api::rpc::RpcApi, *}; +use kaspa_rpc_core::{api::connection::DynRpcConnection, api::rpc::RpcApi, *}; use kaspa_rpc_core::{notify::connection::ChannelConnection, RpcResult}; use std::sync::Arc; @@ -66,7 +66,7 @@ impl RpcCoreMock { #[async_trait] impl RpcApi for RpcCoreMock { // This fn needs to succeed while the client connects - async fn get_info_call(&self, _request: GetInfoRequest) -> RpcResult { + async fn get_info_call(&self, _connection: Option<&DynRpcConnection>, _request: GetInfoRequest) -> RpcResult { Ok(GetInfoResponse { p2p_id: "p2p-mock".to_string(), mempool_size: 1234, @@ -78,133 +78,213 @@ impl RpcApi for RpcCoreMock { }) } - async fn ping_call(&self, _request: PingRequest) -> RpcResult { + async fn ping_call(&self, _connection: Option<&DynRpcConnection>, _request: PingRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_metrics_call(&self, _request: GetMetricsRequest) -> RpcResult { + async fn get_metrics_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetMetricsRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_server_info_call(&self, _request: GetServerInfoRequest) -> RpcResult { + async fn get_server_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetServerInfoRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_sync_status_call(&self, _request: GetSyncStatusRequest) -> RpcResult { + async fn get_sync_status_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSyncStatusRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_current_network_call(&self, _request: GetCurrentNetworkRequest) -> RpcResult { + async fn get_current_network_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetCurrentNetworkRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn submit_block_call(&self, _request: SubmitBlockRequest) -> RpcResult { + async fn submit_block_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: SubmitBlockRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_template_call(&self, _request: GetBlockTemplateRequest) -> RpcResult { + async fn get_block_template_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBlockTemplateRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_peer_addresses_call(&self, _request: GetPeerAddressesRequest) -> RpcResult { + async fn get_peer_addresses_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetPeerAddressesRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_sink_call(&self, _request: GetSinkRequest) -> RpcResult { + async fn get_sink_call(&self, _connection: Option<&DynRpcConnection>, _request: GetSinkRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_mempool_entry_call(&self, _request: GetMempoolEntryRequest) -> RpcResult { + async fn get_mempool_entry_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetMempoolEntryRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_mempool_entries_call(&self, _request: GetMempoolEntriesRequest) -> RpcResult { + async fn get_mempool_entries_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetMempoolEntriesRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_connected_peer_info_call(&self, _request: GetConnectedPeerInfoRequest) -> RpcResult { + async fn get_connected_peer_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetConnectedPeerInfoRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn add_peer_call(&self, _request: AddPeerRequest) -> RpcResult { + async fn add_peer_call(&self, _connection: Option<&DynRpcConnection>, _request: AddPeerRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn submit_transaction_call(&self, _request: SubmitTransactionRequest) -> RpcResult { + async fn submit_transaction_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: SubmitTransactionRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_call(&self, _request: GetBlockRequest) -> RpcResult { + async fn get_block_call(&self, _connection: Option<&DynRpcConnection>, _request: GetBlockRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_subnetwork_call(&self, _request: GetSubnetworkRequest) -> RpcResult { + async fn get_subnetwork_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSubnetworkRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } async fn get_virtual_chain_from_block_call( &self, + _connection: Option<&DynRpcConnection>, _request: GetVirtualChainFromBlockRequest, ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_blocks_call(&self, _request: GetBlocksRequest) -> RpcResult { + async fn get_blocks_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBlocksRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_count_call(&self, _request: GetBlockCountRequest) -> RpcResult { + async fn get_block_count_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBlockCountRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_dag_info_call(&self, _request: GetBlockDagInfoRequest) -> RpcResult { + async fn get_block_dag_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBlockDagInfoRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } async fn resolve_finality_conflict_call( &self, + _connection: Option<&DynRpcConnection>, _request: ResolveFinalityConflictRequest, ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn shutdown_call(&self, _request: ShutdownRequest) -> RpcResult { + async fn shutdown_call(&self, _connection: Option<&DynRpcConnection>, _request: ShutdownRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_headers_call(&self, _request: GetHeadersRequest) -> RpcResult { + async fn get_headers_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetHeadersRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_balance_by_address_call(&self, _request: GetBalanceByAddressRequest) -> RpcResult { + async fn get_balance_by_address_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBalanceByAddressRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } async fn get_balances_by_addresses_call( &self, + _connection: Option<&DynRpcConnection>, _request: GetBalancesByAddressesRequest, ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_utxos_by_addresses_call(&self, _request: GetUtxosByAddressesRequest) -> RpcResult { + async fn get_utxos_by_addresses_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetUtxosByAddressesRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_sink_blue_score_call(&self, _request: GetSinkBlueScoreRequest) -> RpcResult { + async fn get_sink_blue_score_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSinkBlueScoreRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn ban_call(&self, _request: BanRequest) -> RpcResult { + async fn ban_call(&self, _connection: Option<&DynRpcConnection>, _request: BanRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn unban_call(&self, _request: UnbanRequest) -> RpcResult { + async fn unban_call(&self, _connection: Option<&DynRpcConnection>, _request: UnbanRequest) -> RpcResult { Err(RpcError::NotImplemented) } async fn estimate_network_hashes_per_second_call( &self, + _connection: Option<&DynRpcConnection>, _request: EstimateNetworkHashesPerSecondRequest, ) -> RpcResult { Err(RpcError::NotImplemented) @@ -212,17 +292,23 @@ impl RpcApi for RpcCoreMock { async fn get_mempool_entries_by_addresses_call( &self, + _connection: Option<&DynRpcConnection>, _request: GetMempoolEntriesByAddressesRequest, ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_coin_supply_call(&self, _request: GetCoinSupplyRequest) -> RpcResult { + async fn get_coin_supply_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetCoinSupplyRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } async fn get_daa_score_timestamp_estimate_call( &self, + _connection: Option<&DynRpcConnection>, _request: GetDaaScoreTimestampEstimateRequest, ) -> RpcResult { Err(RpcError::NotImplemented) diff --git a/rpc/macros/src/grpc/server.rs b/rpc/macros/src/grpc/server.rs index 91dea1dd96..f6f3f33b99 100644 --- a/rpc/macros/src/grpc/server.rs +++ b/rpc/macros/src/grpc/server.rs @@ -72,7 +72,8 @@ impl ToTokens for RpcTable { Box::pin(async move { let mut response: #kaspad_response_type = match request.payload { Some(Payload::#request_type(ref request)) => match request.try_into() { - Ok(request) => server_ctx.core_service.#fn_call(request).await.into(), + // TODO: RPC-CONNECTION + Ok(request) => server_ctx.core_service.#fn_call(None,request).await.into(), Err(err) => #response_message_type::from(err).into(), }, _ => { diff --git a/rpc/macros/src/wrpc/client.rs b/rpc/macros/src/wrpc/client.rs index 49c09c491f..12f41687a7 100644 --- a/rpc/macros/src/wrpc/client.rs +++ b/rpc/macros/src/wrpc/client.rs @@ -52,12 +52,14 @@ impl ToTokens for RpcTable { // the async implementation of the RPC caller is inlined targets.push(quote! { - fn #fn_call<'life0, 'async_trait>( + fn #fn_call<'life0, 'life1, 'async_trait>( &'life0 self, + _connection : ::core::option::Option<&'life1 Arc>, request: #request_type, ) -> ::core::pin::Pin> + ::core::marker::Send + 'async_trait>> where 'life0: 'async_trait, + 'life1: 'async_trait, Self: 'async_trait, { use workflow_serializer::prelude::*; diff --git a/rpc/macros/src/wrpc/server.rs b/rpc/macros/src/wrpc/server.rs index dfb1491d85..ee2afc5513 100644 --- a/rpc/macros/src/wrpc/server.rs +++ b/rpc/macros/src/wrpc/server.rs @@ -53,7 +53,8 @@ impl ToTokens for RpcTable { interface.method(#rpc_api_ops::#handler, method!(|server_ctx: #server_ctx_type, connection_ctx: #connection_ctx_type, request: Serializable<#request_type>| async move { let verbose = server_ctx.verbose(); if verbose { workflow_log::log_info!("request: {:?}",request); } - let response: #response_type = server_ctx.rpc_service(&connection_ctx).#fn_call(request.into_inner()).await + // TODO: RPC-CONNECT + let response: #response_type = server_ctx.rpc_service(&connection_ctx).#fn_call(None, request.into_inner()).await .map_err(|e|ServerError::Text(e.to_string()))?; if verbose { workflow_log::log_info!("response: {:?}",response); } Ok(Serializable(response)) diff --git a/rpc/macros/src/wrpc/wasm.rs b/rpc/macros/src/wrpc/wasm.rs index 0118220199..30af3e74a3 100644 --- a/rpc/macros/src/wrpc/wasm.rs +++ b/rpc/macros/src/wrpc/wasm.rs @@ -59,7 +59,7 @@ impl ToTokens for RpcHandlers { pub async fn #fn_no_suffix(&self, request : Option<#ts_request_type>) -> Result<#ts_response_type> { let request: #request_type = request.unwrap_or_default().try_into()?; // log_info!("request: {:#?}",request); - let result: RpcResult<#response_type> = self.inner.client.#fn_call(request).await; + let result: RpcResult<#response_type> = self.inner.client.#fn_call(None, request).await; // log_info!("result: {:#?}",result); let response: #response_type = result.map_err(|err|wasm_bindgen::JsError::new(&err.to_string()))?; //log_info!("response: {:#?}",response); @@ -83,7 +83,7 @@ impl ToTokens for RpcHandlers { #[wasm_bindgen(js_name = #fn_camel)] pub async fn #fn_no_suffix(&self, request: #ts_request_type) -> Result<#ts_response_type> { let request: #request_type = request.try_into()?; - let result: RpcResult<#response_type> = self.inner.client.#fn_call(request).await; + let result: RpcResult<#response_type> = self.inner.client.#fn_call(None, request).await; let response: #response_type = result.map_err(|err|wasm_bindgen::JsError::new(&err.to_string()))?; Ok(response.try_into()?) } diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index b276ff2406..9eadc85b17 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -53,6 +53,7 @@ use kaspa_p2p_lib::common::ProtocolError; use kaspa_perf_monitor::{counters::CountersSnapshot, Monitor as PerfMonitor}; use kaspa_rpc_core::{ api::{ + connection::DynRpcConnection, ops::RPC_API_VERSION, rpc::{RpcApi, MAX_SAFE_WINDOW_SIZE}, }, @@ -275,7 +276,11 @@ impl RpcCoreService { #[async_trait] impl RpcApi for RpcCoreService { - async fn submit_block_call(&self, request: SubmitBlockRequest) -> RpcResult { + async fn submit_block_call( + &self, + _connection: Option<&DynRpcConnection>, + request: SubmitBlockRequest, + ) -> RpcResult { let session = self.consensus_manager.consensus().unguarded_session(); // TODO: consider adding an error field to SubmitBlockReport to document both the report and error fields @@ -333,7 +338,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and } } - async fn get_block_template_call(&self, request: GetBlockTemplateRequest) -> RpcResult { + async fn get_block_template_call( + &self, + _connection: Option<&DynRpcConnection>, + request: GetBlockTemplateRequest, + ) -> RpcResult { trace!("incoming GetBlockTemplate request"); if *self.config.net == NetworkType::Mainnet && !self.config.enable_mainnet_mining { @@ -365,7 +374,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and }) } - async fn get_block_call(&self, request: GetBlockRequest) -> RpcResult { + async fn get_block_call(&self, _connection: Option<&DynRpcConnection>, request: GetBlockRequest) -> RpcResult { // TODO: test let session = self.consensus_manager.consensus().session().await; let block = session.async_get_block_even_if_header_only(request.hash).await?; @@ -377,7 +386,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and }) } - async fn get_blocks_call(&self, request: GetBlocksRequest) -> RpcResult { + async fn get_blocks_call( + &self, + _connection: Option<&DynRpcConnection>, + request: GetBlocksRequest, + ) -> RpcResult { // Validate that user didn't set include_transactions without setting include_blocks if !request.include_blocks && request.include_transactions { return Err(RpcError::InvalidGetBlocksRequest); @@ -426,7 +439,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(GetBlocksResponse { block_hashes, blocks }) } - async fn get_info_call(&self, _request: GetInfoRequest) -> RpcResult { + async fn get_info_call(&self, _connection: Option<&DynRpcConnection>, _request: GetInfoRequest) -> RpcResult { let is_nearly_synced = self.consensus_manager.consensus().unguarded_session().async_is_nearly_synced().await; Ok(GetInfoResponse { p2p_id: self.flow_context.node_id.to_string(), @@ -439,7 +452,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and }) } - async fn get_mempool_entry_call(&self, request: GetMempoolEntryRequest) -> RpcResult { + async fn get_mempool_entry_call( + &self, + _connection: Option<&DynRpcConnection>, + request: GetMempoolEntryRequest, + ) -> RpcResult { let query = self.extract_tx_query(request.filter_transaction_pool, request.include_orphan_pool)?; let Some(transaction) = self.mining_manager.clone().get_transaction(request.transaction_id, query).await else { return Err(RpcError::TransactionNotFound(request.transaction_id)); @@ -448,7 +465,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(GetMempoolEntryResponse::new(self.consensus_converter.get_mempool_entry(&session, &transaction))) } - async fn get_mempool_entries_call(&self, request: GetMempoolEntriesRequest) -> RpcResult { + async fn get_mempool_entries_call( + &self, + _connection: Option<&DynRpcConnection>, + request: GetMempoolEntriesRequest, + ) -> RpcResult { let query = self.extract_tx_query(request.filter_transaction_pool, request.include_orphan_pool)?; let session = self.consensus_manager.consensus().unguarded_session(); let (transactions, orphans) = self.mining_manager.clone().get_all_transactions(query).await; @@ -462,6 +483,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and async fn get_mempool_entries_by_addresses_call( &self, + _connection: Option<&DynRpcConnection>, request: GetMempoolEntriesByAddressesRequest, ) -> RpcResult { let query = self.extract_tx_query(request.filter_transaction_pool, request.include_orphan_pool)?; @@ -485,7 +507,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(GetMempoolEntriesByAddressesResponse::new(mempool_entries)) } - async fn submit_transaction_call(&self, request: SubmitTransactionRequest) -> RpcResult { + async fn submit_transaction_call( + &self, + _connection: Option<&DynRpcConnection>, + request: SubmitTransactionRequest, + ) -> RpcResult { let allow_orphan = self.config.unsafe_rpc && request.allow_orphan; if !self.config.unsafe_rpc && request.allow_orphan { warn!("SubmitTransaction RPC command called with AllowOrphan enabled while node in safe RPC mode -- switching to ForbidOrphan."); @@ -506,25 +532,38 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(SubmitTransactionResponse::new(transaction_id)) } - async fn get_current_network_call(&self, _: GetCurrentNetworkRequest) -> RpcResult { + async fn get_current_network_call( + &self, + _connection: Option<&DynRpcConnection>, + _: GetCurrentNetworkRequest, + ) -> RpcResult { Ok(GetCurrentNetworkResponse::new(*self.config.net)) } - async fn get_subnetwork_call(&self, _: GetSubnetworkRequest) -> RpcResult { + async fn get_subnetwork_call( + &self, + _connection: Option<&DynRpcConnection>, + _: GetSubnetworkRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_sink_call(&self, _: GetSinkRequest) -> RpcResult { + async fn get_sink_call(&self, _connection: Option<&DynRpcConnection>, _: GetSinkRequest) -> RpcResult { Ok(GetSinkResponse::new(self.consensus_manager.consensus().unguarded_session().async_get_sink().await)) } - async fn get_sink_blue_score_call(&self, _: GetSinkBlueScoreRequest) -> RpcResult { + async fn get_sink_blue_score_call( + &self, + _connection: Option<&DynRpcConnection>, + _: GetSinkBlueScoreRequest, + ) -> RpcResult { let session = self.consensus_manager.consensus().unguarded_session(); Ok(GetSinkBlueScoreResponse::new(session.async_get_ghostdag_data(session.async_get_sink().await).await?.blue_score)) } async fn get_virtual_chain_from_block_call( &self, + _connection: Option<&DynRpcConnection>, request: GetVirtualChainFromBlockRequest, ) -> RpcResult { let session = self.consensus_manager.consensus().session().await; @@ -537,11 +576,19 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(GetVirtualChainFromBlockResponse::new(virtual_chain.removed, virtual_chain.added, accepted_transaction_ids)) } - async fn get_block_count_call(&self, _: GetBlockCountRequest) -> RpcResult { + async fn get_block_count_call( + &self, + _connection: Option<&DynRpcConnection>, + _: GetBlockCountRequest, + ) -> RpcResult { Ok(self.consensus_manager.consensus().unguarded_session().async_estimate_block_count().await) } - async fn get_utxos_by_addresses_call(&self, request: GetUtxosByAddressesRequest) -> RpcResult { + async fn get_utxos_by_addresses_call( + &self, + _connection: Option<&DynRpcConnection>, + request: GetUtxosByAddressesRequest, + ) -> RpcResult { if !self.config.utxoindex { return Err(RpcError::NoUtxoIndex); } @@ -551,7 +598,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(GetUtxosByAddressesResponse::new(self.index_converter.get_utxos_by_addresses_entries(&entry_map))) } - async fn get_balance_by_address_call(&self, request: GetBalanceByAddressRequest) -> RpcResult { + async fn get_balance_by_address_call( + &self, + _connection: Option<&DynRpcConnection>, + request: GetBalanceByAddressRequest, + ) -> RpcResult { if !self.config.utxoindex { return Err(RpcError::NoUtxoIndex); } @@ -562,6 +613,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and async fn get_balances_by_addresses_call( &self, + _connection: Option<&DynRpcConnection>, request: GetBalancesByAddressesRequest, ) -> RpcResult { if !self.config.utxoindex { @@ -580,7 +632,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(GetBalancesByAddressesResponse::new(entries)) } - async fn get_coin_supply_call(&self, _: GetCoinSupplyRequest) -> RpcResult { + async fn get_coin_supply_call( + &self, + _connection: Option<&DynRpcConnection>, + _: GetCoinSupplyRequest, + ) -> RpcResult { if !self.config.utxoindex { return Err(RpcError::NoUtxoIndex); } @@ -591,6 +647,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and async fn get_daa_score_timestamp_estimate_call( &self, + _connection: Option<&DynRpcConnection>, request: GetDaaScoreTimestampEstimateRequest, ) -> RpcResult { let session = self.consensus_manager.consensus().session().await; @@ -647,15 +704,23 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(GetDaaScoreTimestampEstimateResponse::new(timestamps)) } - async fn ping_call(&self, _: PingRequest) -> RpcResult { + async fn ping_call(&self, _connection: Option<&DynRpcConnection>, _: PingRequest) -> RpcResult { Ok(PingResponse {}) } - async fn get_headers_call(&self, _request: GetHeadersRequest) -> RpcResult { + async fn get_headers_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetHeadersRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_dag_info_call(&self, _: GetBlockDagInfoRequest) -> RpcResult { + async fn get_block_dag_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _: GetBlockDagInfoRequest, + ) -> RpcResult { let session = self.consensus_manager.consensus().unguarded_session(); let (consensus_stats, tips, pruning_point, sink) = join!(session.async_get_stats(), session.async_get_tips(), session.async_pruning_point(), session.async_get_sink()); @@ -675,6 +740,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and async fn estimate_network_hashes_per_second_call( &self, + _connection: Option<&DynRpcConnection>, request: EstimateNetworkHashesPerSecondRequest, ) -> RpcResult { if !self.config.unsafe_rpc && request.window_size > MAX_SAFE_WINDOW_SIZE { @@ -704,7 +770,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and )) } - async fn add_peer_call(&self, request: AddPeerRequest) -> RpcResult { + async fn add_peer_call(&self, _connection: Option<&DynRpcConnection>, request: AddPeerRequest) -> RpcResult { if !self.config.unsafe_rpc { warn!("AddPeer RPC command called while node in safe RPC mode -- ignoring."); return Err(RpcError::UnavailableInSafeMode); @@ -718,12 +784,16 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(AddPeerResponse {}) } - async fn get_peer_addresses_call(&self, _: GetPeerAddressesRequest) -> RpcResult { + async fn get_peer_addresses_call( + &self, + _connection: Option<&DynRpcConnection>, + _: GetPeerAddressesRequest, + ) -> RpcResult { let address_manager = self.flow_context.address_manager.lock(); Ok(GetPeerAddressesResponse::new(address_manager.get_all_addresses(), address_manager.get_all_banned_addresses())) } - async fn ban_call(&self, request: BanRequest) -> RpcResult { + async fn ban_call(&self, _connection: Option<&DynRpcConnection>, request: BanRequest) -> RpcResult { if !self.config.unsafe_rpc { warn!("Ban RPC command called while node in safe RPC mode -- ignoring."); return Err(RpcError::UnavailableInSafeMode); @@ -740,7 +810,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(BanResponse {}) } - async fn unban_call(&self, request: UnbanRequest) -> RpcResult { + async fn unban_call(&self, _connection: Option<&DynRpcConnection>, request: UnbanRequest) -> RpcResult { if !self.config.unsafe_rpc { warn!("Unban RPC command called while node in safe RPC mode -- ignoring."); return Err(RpcError::UnavailableInSafeMode); @@ -754,13 +824,17 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(UnbanResponse {}) } - async fn get_connected_peer_info_call(&self, _: GetConnectedPeerInfoRequest) -> RpcResult { + async fn get_connected_peer_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _: GetConnectedPeerInfoRequest, + ) -> RpcResult { let peers = self.flow_context.hub().active_peers(); let peer_info = self.protocol_converter.get_peers_info(&peers); Ok(GetConnectedPeerInfoResponse::new(peer_info)) } - async fn shutdown_call(&self, _: ShutdownRequest) -> RpcResult { + async fn shutdown_call(&self, _connection: Option<&DynRpcConnection>, _: ShutdownRequest) -> RpcResult { if !self.config.unsafe_rpc { warn!("Shutdown RPC command called while node in safe RPC mode -- ignoring."); return Err(RpcError::UnavailableInSafeMode); @@ -783,6 +857,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and async fn resolve_finality_conflict_call( &self, + _connection: Option<&DynRpcConnection>, _request: ResolveFinalityConflictRequest, ) -> RpcResult { if !self.config.unsafe_rpc { @@ -792,7 +867,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and Err(RpcError::NotImplemented) } - async fn get_metrics_call(&self, req: GetMetricsRequest) -> RpcResult { + async fn get_metrics_call(&self, _connection: Option<&DynRpcConnection>, req: GetMetricsRequest) -> RpcResult { let CountersSnapshot { resident_set_size, virtual_memory_size, @@ -882,7 +957,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(response) } - async fn get_server_info_call(&self, _request: GetServerInfoRequest) -> RpcResult { + async fn get_server_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetServerInfoRequest, + ) -> RpcResult { let session = self.consensus_manager.consensus().unguarded_session(); let is_synced: bool = self.has_sufficient_peer_connectivity() && session.async_is_nearly_synced().await; let virtual_daa_score = session.get_virtual_daa_score(); @@ -897,7 +976,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and }) } - async fn get_sync_status_call(&self, _request: GetSyncStatusRequest) -> RpcResult { + async fn get_sync_status_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSyncStatusRequest, + ) -> RpcResult { let session = self.consensus_manager.consensus().unguarded_session(); let is_synced: bool = self.has_sufficient_peer_connectivity() && session.async_is_nearly_synced().await; Ok(GetSyncStatusResponse { is_synced }) diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs index 7115547232..18fb1619ef 100644 --- a/testing/integration/src/rpc_tests.rs +++ b/testing/integration/src/rpc_tests.rs @@ -79,21 +79,24 @@ async fn sanity_test() { .unwrap(); // Before submitting a first block, the sink is the genesis, - let response = rpc_client.get_sink_call(GetSinkRequest {}).await.unwrap(); + let response = rpc_client.get_sink_call(None, GetSinkRequest {}).await.unwrap(); assert_eq!(response.sink, SIMNET_GENESIS.hash); - let response = rpc_client.get_sink_blue_score_call(GetSinkBlueScoreRequest {}).await.unwrap(); + let response = rpc_client.get_sink_blue_score_call(None, GetSinkBlueScoreRequest {}).await.unwrap(); assert_eq!(response.blue_score, 0); // the block count is 0 - let response = rpc_client.get_block_count_call(GetBlockCountRequest {}).await.unwrap(); + let response = rpc_client.get_block_count_call(None, GetBlockCountRequest {}).await.unwrap(); assert_eq!(response.block_count, 0); // and the virtual chain is the genesis only let response = rpc_client - .get_virtual_chain_from_block_call(GetVirtualChainFromBlockRequest { - start_hash: SIMNET_GENESIS.hash, - include_accepted_transaction_ids: false, - }) + .get_virtual_chain_from_block_call( + None, + GetVirtualChainFromBlockRequest { + start_hash: SIMNET_GENESIS.hash, + include_accepted_transaction_ids: false, + }, + ) .await .unwrap(); assert!(response.added_chain_block_hashes.is_empty()); @@ -101,10 +104,13 @@ async fn sanity_test() { // Get a block template let GetBlockTemplateResponse { block, is_synced } = rpc_client - .get_block_template_call(GetBlockTemplateRequest { - pay_address: Address::new(Prefix::Simnet, Version::PubKey, &[0u8; 32]), - extra_data: Vec::new(), - }) + .get_block_template_call( + None, + GetBlockTemplateRequest { + pay_address: Address::new(Prefix::Simnet, Version::PubKey, &[0u8; 32]), + extra_data: Vec::new(), + }, + ) .await .unwrap(); assert!(!is_synced); @@ -131,19 +137,22 @@ async fn sanity_test() { } // After submitting a first block, the sink is the submitted block, - let response = rpc_client.get_sink_call(GetSinkRequest {}).await.unwrap(); + let response = rpc_client.get_sink_call(None, GetSinkRequest {}).await.unwrap(); assert_eq!(response.sink, block.header.hash); // the block count is 1 - let response = rpc_client.get_block_count_call(GetBlockCountRequest {}).await.unwrap(); + let response = rpc_client.get_block_count_call(None, GetBlockCountRequest {}).await.unwrap(); assert_eq!(response.block_count, 1); // and the virtual chain from genesis contains the added block let response = rpc_client - .get_virtual_chain_from_block_call(GetVirtualChainFromBlockRequest { - start_hash: SIMNET_GENESIS.hash, - include_accepted_transaction_ids: false, - }) + .get_virtual_chain_from_block_call( + None, + GetVirtualChainFromBlockRequest { + start_hash: SIMNET_GENESIS.hash, + include_accepted_transaction_ids: false, + }, + ) .await .unwrap(); assert!(response.added_chain_block_hashes.contains(&block.header.hash)); @@ -158,7 +167,7 @@ async fn sanity_test() { KaspadPayloadOps::GetCurrentNetwork => { let rpc_client = client.clone(); tst!(op, { - let response = rpc_client.get_current_network_call(GetCurrentNetworkRequest {}).await.unwrap(); + let response = rpc_client.get_current_network_call(None, GetCurrentNetworkRequest {}).await.unwrap(); assert_eq!(response.network, network_id.network_type); }) } @@ -166,11 +175,12 @@ async fn sanity_test() { KaspadPayloadOps::GetBlock => { let rpc_client = client.clone(); tst!(op, { - let result = rpc_client.get_block_call(GetBlockRequest { hash: 0.into(), include_transactions: false }).await; + let result = + rpc_client.get_block_call(None, GetBlockRequest { hash: 0.into(), include_transactions: false }).await; assert!(result.is_err()); let response = rpc_client - .get_block_call(GetBlockRequest { hash: SIMNET_GENESIS.hash, include_transactions: false }) + .get_block_call(None, GetBlockRequest { hash: SIMNET_GENESIS.hash, include_transactions: false }) .await .unwrap(); assert_eq!(response.block.header.hash, SIMNET_GENESIS.hash); @@ -181,7 +191,7 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let response = rpc_client - .get_blocks_call(GetBlocksRequest { include_blocks: true, include_transactions: false, low_hash: None }) + .get_blocks_call(None, GetBlocksRequest { include_blocks: true, include_transactions: false, low_hash: None }) .await .unwrap(); assert_eq!(response.blocks.len(), 1, "genesis block should be returned"); @@ -193,7 +203,7 @@ async fn sanity_test() { KaspadPayloadOps::GetInfo => { let rpc_client = client.clone(); tst!(op, { - let response = rpc_client.get_info_call(GetInfoRequest {}).await.unwrap(); + let response = rpc_client.get_info_call(None, GetInfoRequest {}).await.unwrap(); assert_eq!(response.server_version, kaspa_core::kaspad_env::version().to_string()); assert_eq!(response.mempool_size, 0); assert!(response.is_utxo_indexed); @@ -220,11 +230,14 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let response_result = rpc_client - .get_mempool_entry_call(GetMempoolEntryRequest { - transaction_id: 0.into(), - include_orphan_pool: true, - filter_transaction_pool: false, - }) + .get_mempool_entry_call( + None, + GetMempoolEntryRequest { + transaction_id: 0.into(), + include_orphan_pool: true, + filter_transaction_pool: false, + }, + ) .await; // Test Get Mempool Entry: // TODO: Fix by adding actual mempool entries this can get because otherwise it errors out @@ -236,10 +249,10 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let response = rpc_client - .get_mempool_entries_call(GetMempoolEntriesRequest { - include_orphan_pool: true, - filter_transaction_pool: false, - }) + .get_mempool_entries_call( + None, + GetMempoolEntriesRequest { include_orphan_pool: true, filter_transaction_pool: false }, + ) .await .unwrap(); assert!(response.mempool_entries.is_empty()); @@ -249,7 +262,7 @@ async fn sanity_test() { KaspadPayloadOps::GetConnectedPeerInfo => { let rpc_client = client.clone(); tst!(op, { - let response = rpc_client.get_connected_peer_info_call(GetConnectedPeerInfoRequest {}).await.unwrap(); + let response = rpc_client.get_connected_peer_info_call(None, GetConnectedPeerInfoRequest {}).await.unwrap(); assert!(response.peer_info.is_empty()); }) } @@ -258,12 +271,12 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let peer_address = ContextualNetAddress::from_str("1.2.3.4").unwrap(); - let _ = rpc_client.add_peer_call(AddPeerRequest { peer_address, is_permanent: true }).await.unwrap(); + let _ = rpc_client.add_peer_call(None, AddPeerRequest { peer_address, is_permanent: true }).await.unwrap(); // Add peer only adds the IP to a connection request. It will only be added to known_addresses if it // actually can be connected to. So in this test we can't expect it to be added unless we set up an // actual peer. - let response = rpc_client.get_peer_addresses_call(GetPeerAddressesRequest {}).await.unwrap(); + let response = rpc_client.get_peer_addresses_call(None, GetPeerAddressesRequest {}).await.unwrap(); assert!(response.known_addresses.is_empty()); }) } @@ -274,14 +287,14 @@ async fn sanity_test() { let peer_address = ContextualNetAddress::from_str("5.6.7.8").unwrap(); let ip = peer_address.normalize(1).ip; - let _ = rpc_client.add_peer_call(AddPeerRequest { peer_address, is_permanent: false }).await.unwrap(); - let _ = rpc_client.ban_call(BanRequest { ip }).await.unwrap(); + let _ = rpc_client.add_peer_call(None, AddPeerRequest { peer_address, is_permanent: false }).await.unwrap(); + let _ = rpc_client.ban_call(None, BanRequest { ip }).await.unwrap(); - let response = rpc_client.get_peer_addresses_call(GetPeerAddressesRequest {}).await.unwrap(); + let response = rpc_client.get_peer_addresses_call(None, GetPeerAddressesRequest {}).await.unwrap(); assert!(response.banned_addresses.contains(&ip)); - let _ = rpc_client.unban_call(UnbanRequest { ip }).await.unwrap(); - let response = rpc_client.get_peer_addresses_call(GetPeerAddressesRequest {}).await.unwrap(); + let _ = rpc_client.unban_call(None, UnbanRequest { ip }).await.unwrap(); + let response = rpc_client.get_peer_addresses_call(None, GetPeerAddressesRequest {}).await.unwrap(); assert!(!response.banned_addresses.contains(&ip)); }) } @@ -305,7 +318,7 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let result = - rpc_client.get_subnetwork_call(GetSubnetworkRequest { subnetwork_id: SubnetworkId::from_byte(0) }).await; + rpc_client.get_subnetwork_call(None, GetSubnetworkRequest { subnetwork_id: SubnetworkId::from_byte(0) }).await; // Err because it's currently unimplemented assert!(result.is_err()); @@ -323,7 +336,7 @@ async fn sanity_test() { KaspadPayloadOps::GetBlockDagInfo => { let rpc_client = client.clone(); tst!(op, { - let response = rpc_client.get_block_dag_info_call(GetBlockDagInfoRequest {}).await.unwrap(); + let response = rpc_client.get_block_dag_info_call(None, GetBlockDagInfoRequest {}).await.unwrap(); assert_eq!(response.network, network_id); }) } @@ -332,9 +345,10 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let response_result = rpc_client - .resolve_finality_conflict_call(ResolveFinalityConflictRequest { - finality_block_hash: Hash::from_bytes([0; 32]), - }) + .resolve_finality_conflict_call( + None, + ResolveFinalityConflictRequest { finality_block_hash: Hash::from_bytes([0; 32]) }, + ) .await; // Err because it's currently unimplemented @@ -346,7 +360,7 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let response_result = rpc_client - .get_headers_call(GetHeadersRequest { start_hash: SIMNET_GENESIS.hash, limit: 1, is_ascending: true }) + .get_headers_call(None, GetHeadersRequest { start_hash: SIMNET_GENESIS.hash, limit: 1, is_ascending: true }) .await; // Err because it's currently unimplemented @@ -358,7 +372,8 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let addresses = vec![Address::new(Prefix::Simnet, Version::PubKey, &[0u8; 32])]; - let response = rpc_client.get_utxos_by_addresses_call(GetUtxosByAddressesRequest { addresses }).await.unwrap(); + let response = + rpc_client.get_utxos_by_addresses_call(None, GetUtxosByAddressesRequest { addresses }).await.unwrap(); assert!(response.entries.is_empty()); }) } @@ -367,9 +382,10 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let response = rpc_client - .get_balance_by_address_call(GetBalanceByAddressRequest { - address: Address::new(Prefix::Simnet, Version::PubKey, &[0u8; 32]), - }) + .get_balance_by_address_call( + None, + GetBalanceByAddressRequest { address: Address::new(Prefix::Simnet, Version::PubKey, &[0u8; 32]) }, + ) .await .unwrap(); assert_eq!(response.balance, 0); @@ -381,7 +397,7 @@ async fn sanity_test() { tst!(op, { let addresses = vec![Address::new(Prefix::Simnet, Version::PubKey, &[1u8; 32])]; let response = rpc_client - .get_balances_by_addresses_call(GetBalancesByAddressesRequest::new(addresses.clone())) + .get_balances_by_addresses_call(None, GetBalancesByAddressesRequest::new(addresses.clone())) .await .unwrap(); assert_eq!(response.entries.len(), 1); @@ -389,7 +405,7 @@ async fn sanity_test() { assert_eq!(response.entries[0].balance, Some(0)); let response = - rpc_client.get_balances_by_addresses_call(GetBalancesByAddressesRequest::new(vec![])).await.unwrap(); + rpc_client.get_balances_by_addresses_call(None, GetBalancesByAddressesRequest::new(vec![])).await.unwrap(); assert!(response.entries.is_empty()); }) } @@ -397,7 +413,7 @@ async fn sanity_test() { KaspadPayloadOps::GetSinkBlueScore => { let rpc_client = client.clone(); tst!(op, { - let response = rpc_client.get_sink_blue_score_call(GetSinkBlueScoreRequest {}).await.unwrap(); + let response = rpc_client.get_sink_blue_score_call(None, GetSinkBlueScoreRequest {}).await.unwrap(); // A concurrent test may have added a single block so the blue score can be either 0 or 1 assert!(response.blue_score < 2); }) @@ -407,10 +423,10 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let response_result = rpc_client - .estimate_network_hashes_per_second_call(EstimateNetworkHashesPerSecondRequest { - window_size: 1000, - start_hash: None, - }) + .estimate_network_hashes_per_second_call( + None, + EstimateNetworkHashesPerSecondRequest { window_size: 1000, start_hash: None }, + ) .await; // The current DAA window is almost empty so an error is expected assert!(response_result.is_err()); @@ -422,11 +438,10 @@ async fn sanity_test() { tst!(op, { let addresses = vec![Address::new(Prefix::Simnet, Version::PubKey, &[0u8; 32])]; let response = rpc_client - .get_mempool_entries_by_addresses_call(GetMempoolEntriesByAddressesRequest::new( - addresses.clone(), - true, - false, - )) + .get_mempool_entries_by_addresses_call( + None, + GetMempoolEntriesByAddressesRequest::new(addresses.clone(), true, false), + ) .await .unwrap(); assert_eq!(response.entries.len(), 1); @@ -439,7 +454,7 @@ async fn sanity_test() { KaspadPayloadOps::GetCoinSupply => { let rpc_client = client.clone(); tst!(op, { - let response = rpc_client.get_coin_supply_call(GetCoinSupplyRequest {}).await.unwrap(); + let response = rpc_client.get_coin_supply_call(None, GetCoinSupplyRequest {}).await.unwrap(); assert_eq!(response.circulating_sompi, 0); assert_eq!(response.max_sompi, MAX_SOMPI); }) @@ -448,7 +463,7 @@ async fn sanity_test() { KaspadPayloadOps::Ping => { let rpc_client = client.clone(); tst!(op, { - let _ = rpc_client.ping_call(PingRequest {}).await.unwrap(); + let _ = rpc_client.ping_call(None, PingRequest {}).await.unwrap(); }) } @@ -456,52 +471,64 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let get_metrics_call_response = rpc_client - .get_metrics_call(GetMetricsRequest { - consensus_metrics: true, - connection_metrics: true, - bandwidth_metrics: true, - process_metrics: true, - storage_metrics: true, - }) + .get_metrics_call( + None, + GetMetricsRequest { + consensus_metrics: true, + connection_metrics: true, + bandwidth_metrics: true, + process_metrics: true, + storage_metrics: true, + }, + ) .await .unwrap(); assert!(get_metrics_call_response.process_metrics.is_some()); assert!(get_metrics_call_response.consensus_metrics.is_some()); let get_metrics_call_response = rpc_client - .get_metrics_call(GetMetricsRequest { - consensus_metrics: false, - connection_metrics: true, - bandwidth_metrics: true, - process_metrics: true, - storage_metrics: true, - }) + .get_metrics_call( + None, + GetMetricsRequest { + consensus_metrics: false, + connection_metrics: true, + bandwidth_metrics: true, + process_metrics: true, + storage_metrics: true, + }, + ) .await .unwrap(); assert!(get_metrics_call_response.process_metrics.is_some()); assert!(get_metrics_call_response.consensus_metrics.is_none()); let get_metrics_call_response = rpc_client - .get_metrics_call(GetMetricsRequest { - consensus_metrics: true, - connection_metrics: true, - bandwidth_metrics: false, - process_metrics: false, - storage_metrics: false, - }) + .get_metrics_call( + None, + GetMetricsRequest { + consensus_metrics: true, + connection_metrics: true, + bandwidth_metrics: false, + process_metrics: false, + storage_metrics: false, + }, + ) .await .unwrap(); assert!(get_metrics_call_response.process_metrics.is_none()); assert!(get_metrics_call_response.consensus_metrics.is_some()); let get_metrics_call_response = rpc_client - .get_metrics_call(GetMetricsRequest { - consensus_metrics: false, - connection_metrics: true, - bandwidth_metrics: false, - process_metrics: false, - storage_metrics: false, - }) + .get_metrics_call( + None, + GetMetricsRequest { + consensus_metrics: false, + connection_metrics: true, + bandwidth_metrics: false, + process_metrics: false, + storage_metrics: false, + }, + ) .await .unwrap(); assert!(get_metrics_call_response.process_metrics.is_none()); @@ -512,7 +539,7 @@ async fn sanity_test() { KaspadPayloadOps::GetServerInfo => { let rpc_client = client.clone(); tst!(op, { - let response = rpc_client.get_server_info_call(GetServerInfoRequest {}).await.unwrap(); + let response = rpc_client.get_server_info_call(None, GetServerInfoRequest {}).await.unwrap(); assert!(response.has_utxo_index); // we set utxoindex above assert_eq!(response.network_id, network_id); }) @@ -521,7 +548,7 @@ async fn sanity_test() { KaspadPayloadOps::GetSyncStatus => { let rpc_client = client.clone(); tst!(op, { - let _ = rpc_client.get_sync_status_call(GetSyncStatusRequest {}).await.unwrap(); + let _ = rpc_client.get_sync_status_call(None, GetSyncStatusRequest {}).await.unwrap(); }) } @@ -529,9 +556,10 @@ async fn sanity_test() { let rpc_client = client.clone(); tst!(op, { let results = rpc_client - .get_daa_score_timestamp_estimate_call(GetDaaScoreTimestampEstimateRequest { - daa_scores: vec![0, 500, 2000, u64::MAX], - }) + .get_daa_score_timestamp_estimate_call( + None, + GetDaaScoreTimestampEstimateRequest { daa_scores: vec![0, 500, 2000, u64::MAX] }, + ) .await .unwrap(); @@ -540,7 +568,7 @@ async fn sanity_test() { } let results = rpc_client - .get_daa_score_timestamp_estimate_call(GetDaaScoreTimestampEstimateRequest { daa_scores: vec![] }) + .get_daa_score_timestamp_estimate_call(None, GetDaaScoreTimestampEstimateRequest { daa_scores: vec![] }) .await .unwrap(); @@ -636,7 +664,7 @@ async fn sanity_test() { // Shutdown should only be tested after everything let rpc_client = client.clone(); - let _ = rpc_client.shutdown_call(ShutdownRequest {}).await.unwrap(); + let _ = rpc_client.shutdown_call(None, ShutdownRequest {}).await.unwrap(); // // Fold-up diff --git a/wallet/core/src/tests/rpc_core_mock.rs b/wallet/core/src/tests/rpc_core_mock.rs index 6c335d59a6..3143fc5461 100644 --- a/wallet/core/src/tests/rpc_core_mock.rs +++ b/wallet/core/src/tests/rpc_core_mock.rs @@ -9,7 +9,7 @@ use kaspa_notify::scope::Scope; use kaspa_notify::subscription::context::SubscriptionContext; use kaspa_notify::subscription::{MutationPolicies, UtxosChangedMutationPolicy}; use kaspa_rpc_core::api::ctl::RpcCtl; -use kaspa_rpc_core::{api::rpc::RpcApi, *}; +use kaspa_rpc_core::{api::connection::DynRpcConnection, api::rpc::RpcApi, *}; use kaspa_rpc_core::{notify::connection::ChannelConnection, RpcResult}; use std::sync::Arc; @@ -83,7 +83,7 @@ impl Default for RpcCoreMock { #[async_trait] impl RpcApi for RpcCoreMock { // This fn needs to succeed while the client connects - async fn get_info_call(&self, _request: GetInfoRequest) -> RpcResult { + async fn get_info_call(&self, _connection: Option<&DynRpcConnection>, _request: GetInfoRequest) -> RpcResult { Ok(GetInfoResponse { p2p_id: "wallet-mock".to_string(), mempool_size: 1234, @@ -95,133 +95,213 @@ impl RpcApi for RpcCoreMock { }) } - async fn ping_call(&self, _request: PingRequest) -> RpcResult { + async fn ping_call(&self, _connection: Option<&DynRpcConnection>, _request: PingRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_metrics_call(&self, _request: GetMetricsRequest) -> RpcResult { + async fn get_metrics_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetMetricsRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_server_info_call(&self, _request: GetServerInfoRequest) -> RpcResult { + async fn get_server_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetServerInfoRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_sync_status_call(&self, _request: GetSyncStatusRequest) -> RpcResult { + async fn get_sync_status_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSyncStatusRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_current_network_call(&self, _request: GetCurrentNetworkRequest) -> RpcResult { + async fn get_current_network_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetCurrentNetworkRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn submit_block_call(&self, _request: SubmitBlockRequest) -> RpcResult { + async fn submit_block_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: SubmitBlockRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_template_call(&self, _request: GetBlockTemplateRequest) -> RpcResult { + async fn get_block_template_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBlockTemplateRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_peer_addresses_call(&self, _request: GetPeerAddressesRequest) -> RpcResult { + async fn get_peer_addresses_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetPeerAddressesRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_sink_call(&self, _request: GetSinkRequest) -> RpcResult { + async fn get_sink_call(&self, _connection: Option<&DynRpcConnection>, _request: GetSinkRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_mempool_entry_call(&self, _request: GetMempoolEntryRequest) -> RpcResult { + async fn get_mempool_entry_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetMempoolEntryRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_mempool_entries_call(&self, _request: GetMempoolEntriesRequest) -> RpcResult { + async fn get_mempool_entries_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetMempoolEntriesRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_connected_peer_info_call(&self, _request: GetConnectedPeerInfoRequest) -> RpcResult { + async fn get_connected_peer_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetConnectedPeerInfoRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn add_peer_call(&self, _request: AddPeerRequest) -> RpcResult { + async fn add_peer_call(&self, _connection: Option<&DynRpcConnection>, _request: AddPeerRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn submit_transaction_call(&self, _request: SubmitTransactionRequest) -> RpcResult { + async fn submit_transaction_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: SubmitTransactionRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_call(&self, _request: GetBlockRequest) -> RpcResult { + async fn get_block_call(&self, _connection: Option<&DynRpcConnection>, _request: GetBlockRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_subnetwork_call(&self, _request: GetSubnetworkRequest) -> RpcResult { + async fn get_subnetwork_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSubnetworkRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } async fn get_virtual_chain_from_block_call( &self, + _connection: Option<&DynRpcConnection>, _request: GetVirtualChainFromBlockRequest, ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_blocks_call(&self, _request: GetBlocksRequest) -> RpcResult { + async fn get_blocks_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBlocksRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_count_call(&self, _request: GetBlockCountRequest) -> RpcResult { + async fn get_block_count_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBlockCountRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_block_dag_info_call(&self, _request: GetBlockDagInfoRequest) -> RpcResult { + async fn get_block_dag_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBlockDagInfoRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } async fn resolve_finality_conflict_call( &self, + _connection: Option<&DynRpcConnection>, _request: ResolveFinalityConflictRequest, ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn shutdown_call(&self, _request: ShutdownRequest) -> RpcResult { + async fn shutdown_call(&self, _connection: Option<&DynRpcConnection>, _request: ShutdownRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_headers_call(&self, _request: GetHeadersRequest) -> RpcResult { + async fn get_headers_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetHeadersRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_balance_by_address_call(&self, _request: GetBalanceByAddressRequest) -> RpcResult { + async fn get_balance_by_address_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetBalanceByAddressRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } async fn get_balances_by_addresses_call( &self, + _connection: Option<&DynRpcConnection>, _request: GetBalancesByAddressesRequest, ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_utxos_by_addresses_call(&self, _request: GetUtxosByAddressesRequest) -> RpcResult { + async fn get_utxos_by_addresses_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetUtxosByAddressesRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_sink_blue_score_call(&self, _request: GetSinkBlueScoreRequest) -> RpcResult { + async fn get_sink_blue_score_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSinkBlueScoreRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn ban_call(&self, _request: BanRequest) -> RpcResult { + async fn ban_call(&self, _connection: Option<&DynRpcConnection>, _request: BanRequest) -> RpcResult { Err(RpcError::NotImplemented) } - async fn unban_call(&self, _request: UnbanRequest) -> RpcResult { + async fn unban_call(&self, _connection: Option<&DynRpcConnection>, _request: UnbanRequest) -> RpcResult { Err(RpcError::NotImplemented) } async fn estimate_network_hashes_per_second_call( &self, + _connection: Option<&DynRpcConnection>, _request: EstimateNetworkHashesPerSecondRequest, ) -> RpcResult { Err(RpcError::NotImplemented) @@ -229,17 +309,23 @@ impl RpcApi for RpcCoreMock { async fn get_mempool_entries_by_addresses_call( &self, + _connection: Option<&DynRpcConnection>, _request: GetMempoolEntriesByAddressesRequest, ) -> RpcResult { Err(RpcError::NotImplemented) } - async fn get_coin_supply_call(&self, _request: GetCoinSupplyRequest) -> RpcResult { + async fn get_coin_supply_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetCoinSupplyRequest, + ) -> RpcResult { Err(RpcError::NotImplemented) } async fn get_daa_score_timestamp_estimate_call( &self, + _connection: Option<&DynRpcConnection>, _request: GetDaaScoreTimestampEstimateRequest, ) -> RpcResult { Err(RpcError::NotImplemented) From 2d87f7b54e4ddfe552516711c7a4d243ba4764d0 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 29 Jun 2024 14:35:18 +0300 Subject: [PATCH 057/158] change api version handling affecting get_server_info_call --- rpc/core/src/api/ops.rs | 17 ++++++----------- rpc/core/src/model/message.rs | 16 +++++++++++----- rpc/core/src/model/tests.rs | 3 ++- rpc/grpc/core/proto/rpc.proto | 13 +++++++------ rpc/grpc/core/src/convert/message.rs | 6 ++++-- rpc/service/src/service.rs | 3 ++- wallet/core/src/utxo/processor.rs | 17 +++++++++-------- 7 files changed, 41 insertions(+), 34 deletions(-) diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index 37e64a795e..d8e9481ff3 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -3,17 +3,12 @@ use kaspa_notify::events::EventType; use serde::{Deserialize, Serialize}; use workflow_core::enums::Describe; -/// Rpc Api version (4 x short values); First short is reserved. -/// The version format is as follows: `[reserved, major, minor, patch]`. -/// The difference in the major version value indicates breaking binary API changes -/// (i.e. changes in non-versioned model data structures) -/// If such change occurs, BorshRPC-client should refuse to connect to the -/// server and should request a client-side upgrade. JsonRPC-client may opt-in to -/// continue interop, but data structures should handle mutations by pre-filtering -/// or using Serde attributes. This applies only to RPC infrastructure that uses internal -/// data structures and does not affect gRPC. gRPC should issue and handle its -/// own versioning. -pub const RPC_API_VERSION: [u16; 4] = [0, 1, 0, 0]; +/// API version. Change in this value should result +/// in the client refusing to connect. +pub const RPC_API_VERSION: u16 = 1; +/// API revision. Change in this value denotes +/// backwards-compatible changes. +pub const RPC_API_REVISION: u16 = 0; #[derive(Describe, Clone, Copy, Debug, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index f86656b98f..f61c62209c 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1994,7 +1994,8 @@ impl Serializer for GetServerInfoRequest { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetServerInfoResponse { - pub rpc_api_version: [u16; 4], + pub rpc_api_version: u16, + pub rpc_api_revision: u16, pub server_version: String, pub network_id: RpcNetworkId, pub has_utxo_index: bool, @@ -2005,8 +2006,10 @@ pub struct GetServerInfoResponse { impl Serializer for GetServerInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - // TODO - store!([u16; 4], &self.rpc_api_version, writer)?; + + store!(u16, &self.rpc_api_version, writer)?; + store!(u16, &self.rpc_api_revision, writer)?; + store!(String, &self.server_version, writer)?; store!(RpcNetworkId, &self.network_id, writer)?; store!(bool, &self.has_utxo_index, writer)?; @@ -2018,14 +2021,17 @@ impl Serializer for GetServerInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let rpc_api_version = load!([u16; 4], reader)?; + + let rpc_api_version = load!(u16, reader)?; + let rpc_api_revision = load!(u16, reader)?; + let server_version = load!(String, reader)?; let network_id = load!(RpcNetworkId, reader)?; let has_utxo_index = load!(bool, reader)?; let is_synced = load!(bool, reader)?; let virtual_daa_score = load!(u64, reader)?; - Ok(Self { rpc_api_version, server_version, network_id, has_utxo_index, is_synced, virtual_daa_score }) + Ok(Self { rpc_api_version, rpc_api_revision, server_version, network_id, has_utxo_index, is_synced, virtual_daa_score }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index ec2715f709..a918077490 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -946,7 +946,8 @@ mod mockery { impl Mock for GetServerInfoResponse { fn mock() -> Self { GetServerInfoResponse { - rpc_api_version: [mock(), mock(), mock(), mock()], + rpc_api_version: mock(), + rpc_api_revision: mock(), server_version: "0.4.2".to_string(), network_id: NetworkType::Mainnet.try_into().unwrap(), has_utxo_index: true, diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index 21902835ed..057feb6ee7 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -832,12 +832,13 @@ message GetServerInfoRequestMessage{ } message GetServerInfoResponseMessage{ - repeated uint32 rpcApiVersion = 1; // Expecting exactly 4 elements - string serverVersion = 2; - string networkId = 3; - bool hasUtxoIndex = 4; - bool isSynced = 5; - uint64 virtualDaaScore = 6; + uint32 rpcApiVersion = 1; + uint32 rpcApiRevision = 2; + string serverVersion = 3; + string networkId = 4; + bool hasUtxoIndex = 5; + bool isSynced = 6; + uint64 virtualDaaScore = 7; RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index 27eb78d50c..05af57774a 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -420,7 +420,8 @@ from!(item: RpcResult<&kaspa_rpc_core::GetMetricsResponse>, protowire::GetMetric from!(&kaspa_rpc_core::GetServerInfoRequest, protowire::GetServerInfoRequestMessage); from!(item: RpcResult<&kaspa_rpc_core::GetServerInfoResponse>, protowire::GetServerInfoResponseMessage, { Self { - rpc_api_version: item.rpc_api_version.iter().map(|x| *x as u32).collect(), + rpc_api_version: item.rpc_api_version as u32, + rpc_api_revision: item.rpc_api_revision as u32, server_version: item.server_version.clone(), network_id: item.network_id.to_string(), has_utxo_index: item.has_utxo_index, @@ -813,7 +814,8 @@ try_from!(item: &protowire::GetMetricsResponseMessage, RpcResult, { Self { - rpc_api_version: item.rpc_api_version.iter().map(|x| *x as u16).collect::>().as_slice().try_into().map_err(|_| RpcError::RpcApiVersionFormatError)?, + rpc_api_version: item.rpc_api_version as u16, + rpc_api_revision: item.rpc_api_revision as u16, server_version: item.server_version.clone(), network_id: NetworkId::from_str(&item.network_id)?, has_utxo_index: item.has_utxo_index, diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index 9eadc85b17..65c4dcc47b 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -54,7 +54,7 @@ use kaspa_perf_monitor::{counters::CountersSnapshot, Monitor as PerfMonitor}; use kaspa_rpc_core::{ api::{ connection::DynRpcConnection, - ops::RPC_API_VERSION, + ops::{RPC_API_REVISION, RPC_API_VERSION}, rpc::{RpcApi, MAX_SAFE_WINDOW_SIZE}, }, model::*, @@ -968,6 +968,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(GetServerInfoResponse { rpc_api_version: RPC_API_VERSION, + rpc_api_revision: RPC_API_REVISION, server_version: version().to_string(), network_id: self.config.net, has_utxo_index: self.config.utxoindex, diff --git a/wallet/core/src/utxo/processor.rs b/wallet/core/src/utxo/processor.rs index e788272f23..39f5af4c75 100644 --- a/wallet/core/src/utxo/processor.rs +++ b/wallet/core/src/utxo/processor.rs @@ -14,7 +14,7 @@ use kaspa_notify::{ use kaspa_rpc_core::{ api::{ ctl::{RpcCtl, RpcState}, - ops::RPC_API_VERSION, + ops::{RPC_API_REVISION, RPC_API_VERSION}, }, message::UtxosChangedNotification, GetServerInfoResponse, @@ -437,14 +437,21 @@ impl UtxoProcessor { pub async fn init_state_from_server(&self) -> Result { let GetServerInfoResponse { + rpc_api_version, + rpc_api_revision, server_version, network_id: server_network_id, has_utxo_index, is_synced, virtual_daa_score, - rpc_api_version, } = self.rpc_api().get_server_info().await?; + if rpc_api_version > RPC_API_VERSION { + let current = format!("{RPC_API_VERSION}.{RPC_API_REVISION}"); + let connected = format!("{rpc_api_version}.{rpc_api_revision}"); + return Err(Error::RpcApiVersion(current, connected)); + } + if !has_utxo_index { self.notify(Events::UtxoIndexNotEnabled { url: self.rpc_url() }).await?; return Err(Error::MissingUtxoIndex); @@ -455,12 +462,6 @@ impl UtxoProcessor { return Err(Error::InvalidNetworkType(network_id.to_string(), server_network_id.to_string())); } - if rpc_api_version[0] > RPC_API_VERSION[0] || rpc_api_version[1] > RPC_API_VERSION[1] { - let current = RPC_API_VERSION.iter().map(|v| v.to_string()).collect::>().join("."); - let connected = rpc_api_version.iter().map(|v| v.to_string()).collect::>().join("."); - return Err(Error::RpcApiVersion(current, connected)); - } - self.inner.current_daa_score.store(virtual_daa_score, Ordering::SeqCst); log_trace!("Connected to kaspad: '{server_version}' on '{server_network_id}'; SYNC: {is_synced} DAA: {virtual_daa_score}"); From 819bfcac1c636348a2990c944033ce4d8520022e Mon Sep 17 00:00:00 2001 From: 1bananagirl <168954040+1bananagirl@users.noreply.github.com> Date: Sun, 30 Jun 2024 00:37:24 +0200 Subject: [PATCH 058/158] Wallet watch-only accounts (#59) * Prints extended public keys on export mnemonic command (feature of go kaspawallet). * Watch-only account implementation for bip32 and multisig kind of accounts with new command account import watchonly. * Refactor code for less variables. * Patch import type for command. * CLI Support for watch only accounts in select account with args function. * Function sig_op_count equals implemented. * Helper function in wallet to format XPUB according to network. Converter NetworkId for Prefix. BIP32-Watch Account renamed from WatchOnly, multisig feature removed. Multisig account import as watch-only command added. * cli watch-only header in list and select. * Resolve merge. --- Cargo.lock | 1 + cli/src/cli.rs | 34 ++ cli/src/error.rs | 6 + cli/src/modules/account.rs | 31 +- cli/src/modules/details.rs | 12 + cli/src/modules/export.rs | 43 ++- cli/src/wizards/account.rs | 59 ++++ wallet/bip32/Cargo.toml | 3 +- wallet/bip32/src/prefix.rs | 13 + wallet/core/src/account/kind.rs | 1 + wallet/core/src/account/mod.rs | 8 + wallet/core/src/account/variants/bip32.rs | 4 + .../core/src/account/variants/bip32watch.rs | 251 +++++++++++++++ wallet/core/src/account/variants/mod.rs | 2 + wallet/core/src/account/variants/multisig.rs | 18 +- wallet/core/src/account/variants/watchonly.rs | 300 ++++++++++++++++++ wallet/core/src/derivation.rs | 2 +- wallet/core/src/deterministic.rs | 31 +- wallet/core/src/error.rs | 8 +- wallet/core/src/factory.rs | 1 + wallet/core/src/wallet/args.rs | 15 + wallet/core/src/wallet/mod.rs | 36 +++ wallet/keys/src/imports.rs | 2 +- wallet/keys/src/xpub.rs | 26 ++ 24 files changed, 885 insertions(+), 22 deletions(-) create mode 100644 wallet/core/src/account/variants/bip32watch.rs create mode 100644 wallet/core/src/account/variants/watchonly.rs diff --git a/Cargo.lock b/Cargo.lock index 0d4703b8ad..cda7c2255c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2509,6 +2509,7 @@ dependencies = [ "getrandom 0.2.14", "hmac", "js-sys", + "kaspa-consensus-core", "kaspa-utils", "once_cell", "pbkdf2", diff --git a/cli/src/cli.rs b/cli/src/cli.rs index f8d41ee762..2a2f2c1ad9 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -6,6 +6,7 @@ use crate::modules::node::Node; use crate::notifier::{Notification, Notifier}; use crate::result::Result; use kaspa_daemon::{DaemonEvent, DaemonKind, Daemons}; +use kaspa_wallet_core::account::Account; use kaspa_wallet_core::rpc::DynRpcApi; use kaspa_wallet_core::storage::{IdT, PrvKeyDataInfo}; use kaspa_wrpc_client::KaspaRpcClient; @@ -567,6 +568,16 @@ impl KaspaCli { list_by_key.push((key.clone(), prv_key_accounts)); } + let mut watch_accounts = Vec::<(usize, Arc)>::new(); + let mut unfiltered_accounts = self.wallet.accounts(None, &guard).await?; + + while let Some(account) = unfiltered_accounts.try_next().await? { + if account.feature().is_some() { + watch_accounts.push((flat_list.len(), account.clone())); + flat_list.push(account.clone()); + } + } + if flat_list.is_empty() { return Err(Error::NoAccounts); } else if autoselect && flat_list.len() == 1 { @@ -586,6 +597,16 @@ impl KaspaCli { }) }); + if !watch_accounts.is_empty() { + tprintln!(self, "• watch-only"); + } + + watch_accounts.iter().for_each(|(seq, account)| { + let seq = style(seq.to_string()).cyan(); + let ls_string = account.get_list_string().unwrap_or_else(|err| panic!("{err}")); + tprintln!(self, " {seq}: {ls_string}"); + }); + tprintln!(self); let range = if flat_list.len() > 1 { format!("[{}..{}] ", 0, flat_list.len() - 1) } else { "".to_string() }; @@ -676,6 +697,19 @@ impl KaspaCli { tprintln!(self, " {}", style(receive_address.to_string()).blue()); } } + + let mut unfiltered_accounts = self.wallet.accounts(None, &guard).await?; + let mut feature_header_printed = false; + while let Some(account) = unfiltered_accounts.try_next().await? { + if let Some(feature) = account.feature() { + if !feature_header_printed { + tprintln!(self, "{}", style("• watch-only").dim()); + feature_header_printed = true; + } + tprintln!(self, " • {}", account.get_list_string().unwrap()); + tprintln!(self, " • {}", style(feature).cyan()); + } + } tprintln!(self); Ok(()) diff --git a/cli/src/error.rs b/cli/src/error.rs index a1701be355..10f43118fa 100644 --- a/cli/src/error.rs +++ b/cli/src/error.rs @@ -72,6 +72,9 @@ pub enum Error { #[error("wallet secret is required")] WalletSecretRequired, + #[error("watch-only wallet kpub is required")] + WalletBip32WatchXpubRequired, + #[error("wallet secrets do not match")] WalletSecretMatch, @@ -84,6 +87,9 @@ pub enum Error { #[error("key data not found")] KeyDataNotFound, + #[error("no key data to export for watch-only account")] + WatchOnlyAccountNoKeyData, + #[error("no accounts found, please create an account to continue")] NoAccounts, diff --git a/cli/src/modules/account.rs b/cli/src/modules/account.rs index be627047e0..45b8cd1b76 100644 --- a/cli/src/modules/account.rs +++ b/cli/src/modules/account.rs @@ -84,6 +84,8 @@ impl Account { "account import mnemonic multisig [additional keys]", "Import mnemonic and additional keys for a multisig account", ), + ("account import bip32-watch", "Import a extended public key for a watch-only bip32 account"), + ("account import multisig-watch", "Import extended public keys for a watch-only multisig account"), ], None, )?; @@ -175,9 +177,36 @@ impl Account { return Ok(()); } + "bip32-watch" => { + let account_name = if argv.is_empty() { + None + } else { + let name = argv.remove(0); + let name = name.trim().to_string(); + Some(name) + }; + + let account_name = account_name.as_deref(); + wizards::account::bip32_watch(&ctx, account_name).await?; + } + "multisig-watch" => { + let account_name = if argv.is_empty() { + None + } else { + let name = argv.remove(0); + let name = name.trim().to_string(); + + Some(name) + }; + + let account_name = account_name.as_deref(); + wizards::account::multisig_watch(&ctx, account_name).await?; + + return Ok(()); + } _ => { tprintln!(ctx, "unknown account import type: '{import_kind}'"); - tprintln!(ctx, "supported import types are: 'mnemonic' or 'legacy-data'\r\n"); + tprintln!(ctx, "supported import types are: 'mnemonic', 'legacy-data' or 'multisig-watch'\r\n"); return Ok(()); } } diff --git a/cli/src/modules/details.rs b/cli/src/modules/details.rs index ed44a9c825..896ecd3e87 100644 --- a/cli/src/modules/details.rs +++ b/cli/src/modules/details.rs @@ -27,6 +27,18 @@ impl Details { tprintln!(ctx.term(), "{:>4}{}", "", style(address.to_string()).blue()); }); + if let Some(xpub_keys) = account.xpub_keys() { + if account.feature().is_some() { + if let Some(feature) = account.feature() { + tprintln!(ctx.term(), "Feature: {}", style(feature).cyan()); + } + tprintln!(ctx.term(), "Extended public keys:"); + xpub_keys.iter().for_each(|xpub| { + tprintln!(ctx.term(), "{:>4}{}", "", style(ctx.wallet().network_format_xpub(xpub)).dim()); + }); + } + } + Ok(()) } } diff --git a/cli/src/modules/export.rs b/cli/src/modules/export.rs index 8a6b26e577..006cd7d36a 100644 --- a/cli/src/modules/export.rs +++ b/cli/src/modules/export.rs @@ -1,5 +1,5 @@ use crate::imports::*; -use kaspa_wallet_core::account::{multisig::MultiSig, Account, MULTISIG_ACCOUNT_KIND}; +use kaspa_wallet_core::account::{multisig::MultiSig, Account, BIP32_ACCOUNT_KIND, MULTISIG_ACCOUNT_KIND}; #[derive(Default, Handler)] #[help("Export transactions, a wallet or a private key")] @@ -32,8 +32,8 @@ impl Export { async fn export_multisig_account(ctx: Arc, account: Arc) -> Result<()> { match &account.prv_key_data_ids() { - None => Err(Error::KeyDataNotFound), - Some(v) if v.is_empty() => Err(Error::KeyDataNotFound), + None => Err(Error::WatchOnlyAccountNoKeyData), + Some(v) if v.is_empty() => Err(Error::WatchOnlyAccountNoKeyData), Some(prv_key_data_ids) => { let wallet_secret = Secret::new(ctx.term().ask(true, "Enter wallet password: ").await?.trim().as_bytes().to_vec()); if wallet_secret.as_ref().is_empty() { @@ -45,26 +45,38 @@ async fn export_multisig_account(ctx: Arc, account: Arc) -> let prv_key_data_store = ctx.store().as_prv_key_data_store()?; let mut generated_xpub_keys = Vec::with_capacity(prv_key_data_ids.len()); + for (id, prv_key_data_id) in prv_key_data_ids.iter().enumerate() { let prv_key_data = prv_key_data_store.load_key_data(&wallet_secret, prv_key_data_id).await?.unwrap(); let mnemonic = prv_key_data.as_mnemonic(None).unwrap().unwrap(); + let xpub_key: kaspa_bip32::ExtendedPublicKey = + prv_key_data.create_xpub(None, MULTISIG_ACCOUNT_KIND.into(), 0).await?; // todo it can be done concurrently + + tprintln!(ctx, ""); + tprintln!(ctx, "extended public key {}:", id + 1); + tprintln!(ctx, ""); + tprintln!(ctx, "{}", ctx.wallet().network_format_xpub(&xpub_key)); + tprintln!(ctx, ""); + tprintln!(ctx, "mnemonic {}:", id + 1); tprintln!(ctx, ""); tprintln!(ctx, "{}", mnemonic.phrase()); tprintln!(ctx, ""); - let xpub_key = prv_key_data.create_xpub(None, MULTISIG_ACCOUNT_KIND.into(), 0).await?; // todo it can be done concurrently generated_xpub_keys.push(xpub_key); } - - let additional = account.xpub_keys().iter().filter(|xpub| !generated_xpub_keys.contains(xpub)); - additional.enumerate().for_each(|(idx, xpub)| { - if idx == 0 { - tprintln!(ctx, "additional xpubs: "); - } - tprintln!(ctx, "{xpub}"); - }); + let test = account.xpub_keys(); + + if let Some(keys) = test { + let additional = keys.iter().filter(|item| !generated_xpub_keys.contains(item)); + additional.enumerate().for_each(|(idx, xpub)| { + if idx == 0 { + tprintln!(ctx, "additional xpubs: "); + } + tprintln!(ctx, "{}", ctx.wallet().network_format_xpub(xpub)); + }); + } Ok(()) } } @@ -94,6 +106,13 @@ async fn export_single_key_account(ctx: Arc, account: Arc let prv_key_data = keydata.payload.decrypt(payment_secret.as_ref())?; let mnemonic = prv_key_data.as_ref().as_mnemonic()?; + let xpub_key = keydata.create_xpub(None, BIP32_ACCOUNT_KIND.into(), 0).await?; // todo it can be done concurrently + + tprintln!(ctx, "extended public key:"); + tprintln!(ctx, ""); + tprintln!(ctx, "{}", ctx.wallet().network_format_xpub(&xpub_key)); + tprintln!(ctx, ""); + match mnemonic { None => { tprintln!(ctx, "mnemonic is not available for this private key"); diff --git a/cli/src/wizards/account.rs b/cli/src/wizards/account.rs index 7a3afb73b1..9d3d4d5916 100644 --- a/cli/src/wizards/account.rs +++ b/cli/src/wizards/account.rs @@ -85,3 +85,62 @@ async fn create_multisig(ctx: &Arc, account_name: Option, mnem wallet.select(Some(&account)).await?; Ok(()) } + +pub(crate) async fn bip32_watch(ctx: &Arc, name: Option<&str>) -> Result<()> { + let term = ctx.term(); + let wallet = ctx.wallet(); + + let name = if let Some(name) = name { + Some(name.to_string()) + } else { + Some(term.ask(false, "Please enter account name (optional, press to skip): ").await?.trim().to_string()) + }; + + let mut xpub_keys = Vec::with_capacity(1); + let xpub_key = term.ask(false, "Enter extended public key: ").await?; + xpub_keys.push(xpub_key.trim().to_owned()); + + let wallet_secret = Secret::new(term.ask(true, "Enter wallet password: ").await?.trim().as_bytes().to_vec()); + if wallet_secret.as_ref().is_empty() { + return Err(Error::WalletSecretRequired); + } + + let account_create_args_bip32_watch = AccountCreateArgsBip32Watch::new(name, xpub_keys); + let account = wallet.create_account_bip32_watch(&wallet_secret, account_create_args_bip32_watch).await?; + + tprintln!(ctx, "\naccount created: {}\n", account.get_list_string()?); + wallet.select(Some(&account)).await?; + Ok(()) +} + +pub(crate) async fn multisig_watch(ctx: &Arc, name: Option<&str>) -> Result<()> { + let term = ctx.term(); + + let account_name = if let Some(name) = name { + Some(name.to_string()) + } else { + Some(term.ask(false, "Please enter account name (optional, press to skip): ").await?.trim().to_string()) + }; + + let term = ctx.term(); + let wallet = ctx.wallet(); + let (wallet_secret, _) = ctx.ask_wallet_secret(None).await?; + let minimum_signatures: u16 = term.ask(false, "Enter the minimum number of signatures required: ").await?.parse()?; + + let prv_key_data_args = Vec::with_capacity(0); + + let answer = term.ask(false, "Enter the number of extended public keys: ").await?.trim().to_string(); //.parse()?; + let xpub_keys_len: usize = if answer.is_empty() { 0 } else { answer.parse()? }; + + let mut xpub_keys = Vec::with_capacity(xpub_keys_len); + for i in 1..=xpub_keys_len { + let xpub_key = term.ask(false, &format!("Enter extended public {i} key: ")).await?; + xpub_keys.push(xpub_key.trim().to_owned()); + } + let account = + wallet.create_account_multisig(&wallet_secret, prv_key_data_args, xpub_keys, account_name, minimum_signatures).await?; + + tprintln!(ctx, "\naccount created: {}\n", account.get_list_string()?); + wallet.select(Some(&account)).await?; + Ok(()) +} diff --git a/wallet/bip32/Cargo.toml b/wallet/bip32/Cargo.toml index b4eb982024..efc31baf48 100644 --- a/wallet/bip32/Cargo.toml +++ b/wallet/bip32/Cargo.toml @@ -31,6 +31,7 @@ thiserror.workspace = true wasm-bindgen.workspace = true workflow-wasm.workspace = true zeroize.workspace = true +kaspa-consensus-core.workspace = true [dev-dependencies] -faster-hex.workspace = true \ No newline at end of file +faster-hex.workspace = true diff --git a/wallet/bip32/src/prefix.rs b/wallet/bip32/src/prefix.rs index 8a4bcc6273..daff8267e7 100644 --- a/wallet/bip32/src/prefix.rs +++ b/wallet/bip32/src/prefix.rs @@ -6,6 +6,7 @@ use core::{ fmt::{self, Debug, Display}, str, }; +use kaspa_consensus_core::network::{NetworkId, NetworkType}; /// BIP32 extended key prefixes a.k.a. "versions" (e.g. `xpub`, `xprv`) /// @@ -234,6 +235,18 @@ impl TryFrom<&str> for Prefix { } } +impl From for Prefix { + fn from(value: NetworkId) -> Self { + let network_type = value.network_type(); + match network_type { + NetworkType::Mainnet => Prefix::KPUB, + NetworkType::Devnet => Prefix::KTUB, + NetworkType::Simnet => Prefix::KTUB, + NetworkType::Testnet => Prefix::KTUB, + } + } +} + #[cfg(test)] mod tests { use super::Prefix; diff --git a/wallet/core/src/account/kind.rs b/wallet/core/src/account/kind.rs index faf968f296..21330d0dda 100644 --- a/wallet/core/src/account/kind.rs +++ b/wallet/core/src/account/kind.rs @@ -66,6 +66,7 @@ impl FromStr for AccountKind { "bip32" => Ok(BIP32_ACCOUNT_KIND.into()), "multisig" => Ok(MULTISIG_ACCOUNT_KIND.into()), "keypair" => Ok(KEYPAIR_ACCOUNT_KIND.into()), + "bip32watch" => Ok(BIP32_WATCH_ACCOUNT_KIND.into()), _ => Err(Error::InvalidAccountKind), } } diff --git a/wallet/core/src/account/mod.rs b/wallet/core/src/account/mod.rs index 9a4bd940af..7794665e12 100644 --- a/wallet/core/src/account/mod.rs +++ b/wallet/core/src/account/mod.rs @@ -116,6 +116,14 @@ pub trait Account: AnySync + Send + Sync + 'static { self.context().settings.name.clone() } + fn feature(&self) -> Option { + None + } + + fn xpub_keys(&self) -> Option<&ExtendedPublicKeys> { + None + } + fn name_or_id(&self) -> String { if let Some(name) = self.name() { if name.is_empty() { diff --git a/wallet/core/src/account/variants/bip32.rs b/wallet/core/src/account/variants/bip32.rs index 0b2909ad09..6dc4f73b2a 100644 --- a/wallet/core/src/account/variants/bip32.rs +++ b/wallet/core/src/account/variants/bip32.rs @@ -169,6 +169,10 @@ impl Account for Bip32 { BIP32_ACCOUNT_KIND.into() } + // fn xpub_keys(&self) -> Option<&ExtendedPublicKeys> { + // None + // } + fn prv_key_data_id(&self) -> Result<&PrvKeyDataId> { Ok(&self.prv_key_data_id) } diff --git a/wallet/core/src/account/variants/bip32watch.rs b/wallet/core/src/account/variants/bip32watch.rs new file mode 100644 index 0000000000..d2cf1debd5 --- /dev/null +++ b/wallet/core/src/account/variants/bip32watch.rs @@ -0,0 +1,251 @@ +//! +//! bip32-watch account implementation +//! + +use crate::account::Inner; +use crate::derivation::{AddressDerivationManager, AddressDerivationManagerTrait}; +use crate::imports::*; + +pub const BIP32_WATCH_ACCOUNT_KIND: &str = "kaspa-bip32-watch-standard"; + +pub struct Ctor {} + +#[async_trait] +impl Factory for Ctor { + fn name(&self) -> String { + "bip32watch".to_string() + } + + fn description(&self) -> String { + "Kaspa Core bip32-watch Account".to_string() + } + + async fn try_load( + &self, + wallet: &Arc, + storage: &AccountStorage, + meta: Option>, + ) -> Result> { + Ok(Arc::new(bip32watch::Bip32Watch::try_load(wallet, storage, meta).await?)) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub struct Payload { + pub xpub_keys: ExtendedPublicKeys, + pub ecdsa: bool, +} + +impl Payload { + pub fn new(xpub_keys: Arc>, ecdsa: bool) -> Self { + Self { xpub_keys, ecdsa } + } + + pub fn try_load(storage: &AccountStorage) -> Result { + Ok(Self::try_from_slice(storage.serialized.as_slice())?) + } +} + +impl Storable for Payload { + // a unique number used for binary + // serialization data alignment check + const STORAGE_MAGIC: u32 = 0x92014137; + // binary serialization version + const STORAGE_VERSION: u32 = 0; +} + +impl AccountStorable for Payload {} + +impl BorshSerialize for Payload { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + StorageHeader::new(Self::STORAGE_MAGIC, Self::STORAGE_VERSION).serialize(writer)?; + BorshSerialize::serialize(&self.xpub_keys, writer)?; + BorshSerialize::serialize(&self.ecdsa, writer)?; + + Ok(()) + } +} + +impl BorshDeserialize for Payload { + fn deserialize(buf: &mut &[u8]) -> IoResult { + let StorageHeader { version: _, .. } = + StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + + let xpub_keys = BorshDeserialize::deserialize(buf)?; + let ecdsa = BorshDeserialize::deserialize(buf)?; + + Ok(Self { xpub_keys, ecdsa }) + } +} + +pub struct Bip32Watch { + inner: Arc, + xpub_keys: ExtendedPublicKeys, + ecdsa: bool, + derivation: Arc, +} + +impl Bip32Watch { + pub async fn try_new(wallet: &Arc, name: Option, xpub_keys: ExtendedPublicKeys, ecdsa: bool) -> Result { + let settings = AccountSettings { name, ..Default::default() }; + + let public_key = xpub_keys.first().ok_or_else(|| Error::Bip32WatchXpubRequired)?.public_key(); + + let (id, storage_key) = make_account_hashes(from_bip32_watch(public_key)); + + let inner = Arc::new(Inner::new(wallet, id, storage_key, settings)); + + let derivation = + AddressDerivationManager::new(wallet, BIP32_WATCH_ACCOUNT_KIND.into(), &xpub_keys, ecdsa, 0, None, 1, Default::default()) + .await?; + + Ok(Self { inner, xpub_keys, ecdsa, derivation }) + } + + pub async fn try_load(wallet: &Arc, storage: &AccountStorage, meta: Option>) -> Result { + let storable = Payload::try_load(storage)?; + let inner = Arc::new(Inner::from_storage(wallet, storage)); + let Payload { xpub_keys, ecdsa, .. } = storable; + let address_derivation_indexes = meta.and_then(|meta| meta.address_derivation_indexes()).unwrap_or_default(); + + let derivation = AddressDerivationManager::new( + wallet, + BIP32_WATCH_ACCOUNT_KIND.into(), + &xpub_keys, + ecdsa, + 0, + None, + 1, + address_derivation_indexes, + ) + .await?; + + Ok(Self { inner, xpub_keys, ecdsa, derivation }) + } + + pub fn get_address_range_for_scan(&self, range: std::ops::Range) -> Result> { + let receive_addresses = self.derivation.receive_address_manager().get_range_with_args(range.clone(), false)?; + let change_addresses = self.derivation.change_address_manager().get_range_with_args(range, false)?; + Ok(receive_addresses.into_iter().chain(change_addresses).collect::>()) + } + + // pub fn xpub_keys(&self) -> &ExtendedPublicKeys { + // &self.xpub_keys + // } +} + +#[async_trait] +impl Account for Bip32Watch { + fn inner(&self) -> &Arc { + &self.inner + } + + fn account_kind(&self) -> AccountKind { + BIP32_WATCH_ACCOUNT_KIND.into() + } + + fn feature(&self) -> Option { + let info = "bip32-watch"; + Some(info.into()) + } + + fn xpub_keys(&self) -> Option<&ExtendedPublicKeys> { + Some(&self.xpub_keys) + } + + fn prv_key_data_id(&self) -> Result<&PrvKeyDataId> { + Err(Error::Bip32WatchAccount) + } + + fn as_dyn_arc(self: Arc) -> Arc { + self + } + + fn sig_op_count(&self) -> u8 { + u8::try_from(self.xpub_keys.len()).unwrap() + } + + fn minimum_signatures(&self) -> u16 { + 1 + } + + fn receive_address(&self) -> Result
{ + self.derivation.receive_address_manager().current_address() + } + fn change_address(&self) -> Result
{ + self.derivation.change_address_manager().current_address() + } + + fn to_storage(&self) -> Result { + let settings = self.context().settings.clone(); + let storable = Payload::new(self.xpub_keys.clone(), self.ecdsa); + + let storage = AccountStorage::try_new( + BIP32_WATCH_ACCOUNT_KIND.into(), + self.id(), + self.storage_key(), + AssocPrvKeyDataIds::None, + settings, + storable, + )?; + + Ok(storage) + } + + fn metadata(&self) -> Result> { + let metadata = AccountMetadata::new(self.inner.id, self.derivation.address_derivation_meta()); + Ok(Some(metadata)) + } + + fn descriptor(&self) -> Result { + let descriptor = AccountDescriptor::new( + BIP32_WATCH_ACCOUNT_KIND.into(), + *self.id(), + self.name(), + AssocPrvKeyDataIds::None, + self.receive_address().ok(), + self.change_address().ok(), + ) + .with_property(AccountDescriptorProperty::XpubKeys, self.xpub_keys.clone().into()) + .with_property(AccountDescriptorProperty::Ecdsa, self.ecdsa.into()) + .with_property(AccountDescriptorProperty::DerivationMeta, self.derivation.address_derivation_meta().into()); + + Ok(descriptor) + } + + fn as_derivation_capable(self: Arc) -> Result> { + Ok(self.clone()) + } +} + +impl DerivationCapableAccount for Bip32Watch { + fn derivation(&self) -> Arc { + self.derivation.clone() + } + + fn account_index(&self) -> u64 { + 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::*; + + #[test] + fn test_storage_bip32watch() -> Result<()> { + let storable_in = Payload::new(vec![make_xpub()].into(), false); + let guard = StorageGuard::new(&storable_in); + let storable_out = guard.validate()?; + + assert_eq!(storable_in.ecdsa, storable_out.ecdsa); + assert_eq!(storable_in.xpub_keys.len(), storable_out.xpub_keys.len()); + for idx in 0..storable_in.xpub_keys.len() { + assert_eq!(storable_in.xpub_keys[idx], storable_out.xpub_keys[idx]); + } + + Ok(()) + } +} diff --git a/wallet/core/src/account/variants/mod.rs b/wallet/core/src/account/variants/mod.rs index e321e95079..fa105b4e03 100644 --- a/wallet/core/src/account/variants/mod.rs +++ b/wallet/core/src/account/variants/mod.rs @@ -3,12 +3,14 @@ //! pub mod bip32; +pub mod bip32watch; pub mod keypair; pub mod legacy; pub mod multisig; pub mod resident; pub use bip32::BIP32_ACCOUNT_KIND; +pub use bip32watch::BIP32_WATCH_ACCOUNT_KIND; pub use keypair::KEYPAIR_ACCOUNT_KIND; pub use legacy::LEGACY_ACCOUNT_KIND; pub use multisig::MULTISIG_ACCOUNT_KIND; diff --git a/wallet/core/src/account/variants/multisig.rs b/wallet/core/src/account/variants/multisig.rs index 9a8044aa20..5abf20b6b0 100644 --- a/wallet/core/src/account/variants/multisig.rs +++ b/wallet/core/src/account/variants/multisig.rs @@ -157,8 +157,8 @@ impl MultiSig { self.minimum_signatures } - pub fn xpub_keys(&self) -> &ExtendedPublicKeys { - &self.xpub_keys + fn watch_only(&self) -> bool { + self.prv_key_data_ids.is_none() } } @@ -172,6 +172,17 @@ impl Account for MultiSig { MULTISIG_ACCOUNT_KIND.into() } + fn feature(&self) -> Option { + match self.watch_only() { + true => Some("multisig-watch".to_string()), + false => None, + } + } + + fn xpub_keys(&self) -> Option<&ExtendedPublicKeys> { + Some(&self.xpub_keys) + } + fn prv_key_data_id(&self) -> Result<&PrvKeyDataId> { Err(Error::AccountKindFeature) } @@ -181,8 +192,7 @@ impl Account for MultiSig { } fn sig_op_count(&self) -> u8 { - // TODO @maxim - 1 + u8::try_from(self.xpub_keys.len()).unwrap() } fn minimum_signatures(&self) -> u16 { diff --git a/wallet/core/src/account/variants/watchonly.rs b/wallet/core/src/account/variants/watchonly.rs new file mode 100644 index 0000000000..7212ffdfce --- /dev/null +++ b/wallet/core/src/account/variants/watchonly.rs @@ -0,0 +1,300 @@ +//! +//! Watch-only account implementation +//! + +use crate::account::Inner; +use crate::derivation::{AddressDerivationManager, AddressDerivationManagerTrait}; +use crate::imports::*; + +pub const WATCH_ONLY_ACCOUNT_KIND: &str = "kaspa-watch-only-standard"; + +pub struct Ctor {} + +#[async_trait] +impl Factory for Ctor { + fn name(&self) -> String { + "watchonly".to_string() + } + + fn description(&self) -> String { + "Kaspa Core watch-only Account".to_string() + } + + async fn try_load( + &self, + wallet: &Arc, + storage: &AccountStorage, + meta: Option>, + ) -> Result> { + Ok(Arc::new(watchonly::WatchOnly::try_load(wallet, storage, meta).await?)) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub struct Payload { + pub xpub_keys: ExtendedPublicKeys, + pub minimum_signatures: u16, + pub ecdsa: bool, +} + +impl Payload { + pub fn new(xpub_keys: Arc>, minimum_signatures: u16, ecdsa: bool) -> Self { + Self { xpub_keys, minimum_signatures, ecdsa } + } + + pub fn try_load(storage: &AccountStorage) -> Result { + Ok(Self::try_from_slice(storage.serialized.as_slice())?) + } +} + +impl Storable for Payload { + // a unique number used for binary + // serialization data alignment check + const STORAGE_MAGIC: u32 = 0x92014137; + // binary serialization version + const STORAGE_VERSION: u32 = 0; +} + +impl AccountStorable for Payload {} + +impl BorshSerialize for Payload { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + StorageHeader::new(Self::STORAGE_MAGIC, Self::STORAGE_VERSION).serialize(writer)?; + BorshSerialize::serialize(&self.xpub_keys, writer)?; + BorshSerialize::serialize(&self.minimum_signatures, writer)?; + BorshSerialize::serialize(&self.ecdsa, writer)?; + + Ok(()) + } +} + +impl BorshDeserialize for Payload { + fn deserialize(buf: &mut &[u8]) -> IoResult { + let StorageHeader { version: _, .. } = + StorageHeader::deserialize(buf)?.try_magic(Self::STORAGE_MAGIC)?.try_version(Self::STORAGE_VERSION)?; + + let xpub_keys = BorshDeserialize::deserialize(buf)?; + let minimum_signatures = BorshDeserialize::deserialize(buf)?; + let ecdsa = BorshDeserialize::deserialize(buf)?; + + Ok(Self { xpub_keys, minimum_signatures, ecdsa }) + } +} + +pub struct WatchOnly { + inner: Arc, + xpub_keys: ExtendedPublicKeys, + minimum_signatures: u16, + ecdsa: bool, + derivation: Arc, +} + +impl WatchOnly { + pub async fn try_new( + wallet: &Arc, + name: Option, + xpub_keys: ExtendedPublicKeys, + minimum_signatures: u16, + ecdsa: bool, + ) -> Result { + let settings = AccountSettings { name, ..Default::default() }; + + let public_key = xpub_keys.first().ok_or_else(|| Error::WatchOnlyXpubRequired)?.public_key(); + + let storable = Payload::new(xpub_keys.clone(), minimum_signatures, ecdsa); + + let (id, storage_key) = match xpub_keys.len() { + 1 => make_account_hashes(from_watch_only(&public_key)), + _ => make_account_hashes(from_watch_only_multisig(&None, &storable)), + }; + let inner = Arc::new(Inner::new(wallet, id, storage_key, settings)); + + let derivation = match xpub_keys.len() { + 1 => { + AddressDerivationManager::new( + wallet, + WATCH_ONLY_ACCOUNT_KIND.into(), + &xpub_keys, + ecdsa, + 0, + None, + 1, + Default::default(), + ) + .await? + } + _ => { + AddressDerivationManager::new( + wallet, + MULTISIG_ACCOUNT_KIND.into(), + &xpub_keys, + ecdsa, + 0, + Some(u32::MIN), + minimum_signatures, + Default::default(), + ) + .await? + } + }; + + Ok(Self { inner, xpub_keys, minimum_signatures, ecdsa, derivation }) + } + + pub async fn try_load(wallet: &Arc, storage: &AccountStorage, meta: Option>) -> Result { + let storable = Payload::try_load(storage)?; + let inner = Arc::new(Inner::from_storage(wallet, storage)); + let Payload { xpub_keys, minimum_signatures, ecdsa, .. } = storable; + let address_derivation_indexes = meta.and_then(|meta| meta.address_derivation_indexes()).unwrap_or_default(); + + let derivation = match xpub_keys.len() { + 1 => { + AddressDerivationManager::new( + wallet, + WATCH_ONLY_ACCOUNT_KIND.into(), + &xpub_keys, + ecdsa, + 0, + None, + 1, + address_derivation_indexes, + ) + .await? + } + _ => { + AddressDerivationManager::new( + wallet, + MULTISIG_ACCOUNT_KIND.into(), + &xpub_keys, + ecdsa, + 0, + Some(u32::MIN), + minimum_signatures, + address_derivation_indexes, + ) + .await? + } + }; + + Ok(Self { inner, xpub_keys, minimum_signatures, ecdsa, derivation }) + } + + pub fn get_address_range_for_scan(&self, range: std::ops::Range) -> Result> { + let receive_addresses = self.derivation.receive_address_manager().get_range_with_args(range.clone(), false)?; + let change_addresses = self.derivation.change_address_manager().get_range_with_args(range, false)?; + Ok(receive_addresses.into_iter().chain(change_addresses).collect::>()) + } + + pub fn xpub_keys(&self) -> &ExtendedPublicKeys { + &self.xpub_keys + } +} + +#[async_trait] +impl Account for WatchOnly { + fn inner(&self) -> &Arc { + &self.inner + } + + fn account_kind(&self) -> AccountKind { + WATCH_ONLY_ACCOUNT_KIND.into() + } + + fn prv_key_data_id(&self) -> Result<&PrvKeyDataId> { + Err(Error::WatchOnlyAccount) + } + + fn as_dyn_arc(self: Arc) -> Arc { + self + } + + fn sig_op_count(&self) -> u8 { + u8::try_from(self.xpub_keys.len()).unwrap() + } + + fn minimum_signatures(&self) -> u16 { + self.minimum_signatures + } + + fn receive_address(&self) -> Result
{ + self.derivation.receive_address_manager().current_address() + } + fn change_address(&self) -> Result
{ + self.derivation.change_address_manager().current_address() + } + + fn to_storage(&self) -> Result { + let settings = self.context().settings.clone(); + let storable = Payload::new(self.xpub_keys.clone(), self.minimum_signatures, self.ecdsa); + + let storage = AccountStorage::try_new( + WATCH_ONLY_ACCOUNT_KIND.into(), + self.id(), + self.storage_key(), + AssocPrvKeyDataIds::None, + settings, + storable, + )?; + + Ok(storage) + } + + fn metadata(&self) -> Result> { + let metadata = AccountMetadata::new(self.inner.id, self.derivation.address_derivation_meta()); + Ok(Some(metadata)) + } + + fn descriptor(&self) -> Result { + let descriptor = AccountDescriptor::new( + WATCH_ONLY_ACCOUNT_KIND.into(), + *self.id(), + self.name(), + AssocPrvKeyDataIds::None, + self.receive_address().ok(), + self.change_address().ok(), + ) + .with_property(AccountDescriptorProperty::XpubKeys, self.xpub_keys.clone().into()) + .with_property(AccountDescriptorProperty::Ecdsa, self.ecdsa.into()) + .with_property(AccountDescriptorProperty::DerivationMeta, self.derivation.address_derivation_meta().into()); + + Ok(descriptor) + } + + fn as_derivation_capable(self: Arc) -> Result> { + Ok(self.clone()) + } + +} + +impl DerivationCapableAccount for WatchOnly { + fn derivation(&self) -> Arc { + self.derivation.clone() + } + + fn account_index(&self) -> u64 { + 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::*; + + #[test] + fn test_storage_watchonly() -> Result<()> { + let storable_in = Payload::new(vec![make_xpub()].into(), 1, false); + let guard = StorageGuard::new(&storable_in); + let storable_out = guard.validate()?; + + assert_eq!(storable_in.minimum_signatures, storable_out.minimum_signatures); + assert_eq!(storable_in.ecdsa, storable_out.ecdsa); + assert_eq!(storable_in.xpub_keys.len(), storable_out.xpub_keys.len()); + for idx in 0..storable_in.xpub_keys.len() { + assert_eq!(storable_in.xpub_keys[idx], storable_out.xpub_keys[idx]); + } + + Ok(()) + } +} diff --git a/wallet/core/src/derivation.rs b/wallet/core/src/derivation.rs index 15ad785035..335541d3ac 100644 --- a/wallet/core/src/derivation.rs +++ b/wallet/core/src/derivation.rs @@ -204,7 +204,7 @@ impl AddressDerivationManager { let derivator: Arc = match account_kind.as_ref() { LEGACY_ACCOUNT_KIND => Arc::new(WalletDerivationManagerV0::from_extended_public_key(xpub.clone(), cosigner_index)?), MULTISIG_ACCOUNT_KIND => { - let cosigner_index = cosigner_index.ok_or(Error::InvalidAccountKind)?; + let cosigner_index = cosigner_index.unwrap_or(0); Arc::new(WalletDerivationManager::from_extended_public_key(xpub.clone(), Some(cosigner_index))?) } _ => Arc::new(WalletDerivationManager::from_extended_public_key(xpub.clone(), cosigner_index)?), diff --git a/wallet/core/src/deterministic.rs b/wallet/core/src/deterministic.rs index c383da6012..2f4d3da990 100644 --- a/wallet/core/src/deterministic.rs +++ b/wallet/core/src/deterministic.rs @@ -2,7 +2,7 @@ //! Deterministic byte sequence generation (used by Account ids). //! -pub use crate::account::{bip32, keypair, legacy, multisig}; +pub use crate::account::{bip32, bip32watch, keypair, legacy, multisig}; use crate::encryption::sha256_hash; use crate::imports::*; use crate::storage::PrvKeyDataId; @@ -148,6 +148,22 @@ pub fn from_multisig(prv_key_data_ids: &Option( + prv_key_data_ids: &Option>>, + data: &bip32watch::Payload, +) -> [Hash; N] { + let hashable = DeterministicHashData { + account_kind: &multisig::MULTISIG_ACCOUNT_KIND.into(), + prv_key_data_ids, + ecdsa: Some(data.ecdsa), + account_index: None, + secp256k1_public_key: None, + data: Some(data.xpub_keys.try_to_vec().unwrap()), + }; + make_hashes(hashable) +} + /// Create deterministic hashes from keypair account data. pub(crate) fn from_keypair(prv_key_data_id: &PrvKeyDataId, data: &keypair::Payload) -> [Hash; N] { let hashable = DeterministicHashData { @@ -174,6 +190,19 @@ pub fn from_public_key(account_kind: &AccountKind, public_key: & make_hashes(hashable) } +/// Create deterministic hashes from bip32-watch. +pub fn from_bip32_watch(public_key: &PublicKey) -> [Hash; N] { + let hashable: DeterministicHashData<[PrvKeyDataId; 0]> = DeterministicHashData { + account_kind: &bip32watch::BIP32_WATCH_ACCOUNT_KIND.into(), + prv_key_data_ids: &None, + ecdsa: None, + account_index: Some(0), + secp256k1_public_key: Some(public_key.serialize().to_vec()), + data: None, + }; + make_hashes(hashable) +} + /// Create deterministic hashes from arbitrary data (supplied data slice must be deterministic). pub fn from_data(account_kind: &AccountKind, data: &[u8]) -> [Hash; N] { let hashable: DeterministicHashData<[PrvKeyDataId; 0]> = DeterministicHashData { diff --git a/wallet/core/src/error.rs b/wallet/core/src/error.rs index a89b1dcf00..6c2d6dd362 100644 --- a/wallet/core/src/error.rs +++ b/wallet/core/src/error.rs @@ -186,7 +186,7 @@ pub enum Error { #[error("{0}")] TryFromEnum(#[from] workflow_core::enums::TryFromError), - #[error("Account factory found for type: {0}")] + #[error("Account factory not found for type: {0}")] AccountFactoryNotFound(AccountKind), #[error("Account not found: {0}")] @@ -231,6 +231,12 @@ pub enum Error { #[error("Not allowed on a resident account")] ResidentAccount, + #[error("Not allowed on an bip32-watch account")] + Bip32WatchAccount, + + #[error("At least one xpub is required for a bip32-watch account")] + Bip32WatchXpubRequired, + #[error("This feature is not supported by this account type")] AccountKindFeature, diff --git a/wallet/core/src/factory.rs b/wallet/core/src/factory.rs index c0472059c2..515093fdc7 100644 --- a/wallet/core/src/factory.rs +++ b/wallet/core/src/factory.rs @@ -32,6 +32,7 @@ pub fn factories() -> &'static FactoryMap { (LEGACY_ACCOUNT_KIND.into(), Arc::new(legacy::Ctor {})), (MULTISIG_ACCOUNT_KIND.into(), Arc::new(multisig::Ctor {})), (KEYPAIR_ACCOUNT_KIND.into(), Arc::new(keypair::Ctor {})), + (BIP32_WATCH_ACCOUNT_KIND.into(), Arc::new(bip32watch::Ctor {})), ]; let external = EXTERNAL.get_or_init(|| Mutex::new(AHashMap::new())).lock().unwrap().clone(); diff --git a/wallet/core/src/wallet/args.rs b/wallet/core/src/wallet/args.rs index 05d57c445e..a5fa378bb5 100644 --- a/wallet/core/src/wallet/args.rs +++ b/wallet/core/src/wallet/args.rs @@ -113,6 +113,18 @@ impl AccountCreateArgsBip32 { } } +#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +pub struct AccountCreateArgsBip32Watch { + pub account_name: Option, + pub xpub_keys: Vec, +} + +impl AccountCreateArgsBip32Watch { + pub fn new(account_name: Option, xpub_keys: Vec) -> Self { + Self { account_name, xpub_keys } + } +} + #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct PrvKeyDataArgs { pub prv_key_data_id: PrvKeyDataId, @@ -142,6 +154,9 @@ pub enum AccountCreateArgs { name: Option, minimum_signatures: u16, }, + Bip32Watch { + account_args: AccountCreateArgsBip32Watch, + }, } impl AccountCreateArgs { diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index d160bd8161..4cbaee1fcd 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -21,6 +21,7 @@ use kaspa_notify::{ listener::ListenerId, scope::{Scope, VirtualDaaScoreChangedScope}, }; +use kaspa_wallet_keys::xpub::NetworkTaggedXpub; use kaspa_wrpc_client::{KaspaRpcClient, Resolver, WrpcEncoding}; use workflow_core::task::spawn; @@ -647,6 +648,7 @@ impl Wallet { AccountCreateArgs::Multisig { prv_key_data_args, additional_xpub_keys, name, minimum_signatures } => { self.create_account_multisig(wallet_secret, prv_key_data_args, additional_xpub_keys, name, minimum_signatures).await? } + AccountCreateArgs::Bip32Watch { account_args } => self.create_account_bip32_watch(wallet_secret, account_args).await?, }; if notify { @@ -774,6 +776,36 @@ impl Wallet { Ok(account) } + pub async fn create_account_bip32_watch( + self: &Arc, + wallet_secret: &Secret, + account_args: AccountCreateArgsBip32Watch, + ) -> Result> { + let account_store = self.inner.store.clone().as_account_store()?; + + let AccountCreateArgsBip32Watch { account_name, xpub_keys } = account_args; + + let xpub_keys = Arc::new( + xpub_keys + .into_iter() + .map(|xpub_key| { + ExtendedPublicKeySecp256k1::from_str(&xpub_key).map_err(|err| Error::InvalidExtendedPublicKey(xpub_key, err)) + }) + .collect::>>()?, + ); + + let account: Arc = Arc::new(bip32watch::Bip32Watch::try_new(self, account_name, xpub_keys, false).await?); + + if account_store.load_single(account.id()).await?.is_some() { + return Err(Error::AccountAlreadyExists(*account.id())); + } + + self.inner.store.clone().as_account_store()?.store_single(&account.to_storage()?, None).await?; + self.inner.store.commit(wallet_secret).await?; + + Ok(account) + } + async fn create_account_legacy( self: &Arc, wallet_secret: &Secret, @@ -1631,6 +1663,10 @@ impl Wallet { Ok(account.descriptor()?) } } + + pub fn network_format_xpub(&self, xpub_key: &ExtendedPublicKeySecp256k1) -> String { + NetworkTaggedXpub::from((xpub_key.clone(), self.network_id().unwrap())).to_string() + } } // fn decrypt_mnemonic>( diff --git a/wallet/keys/src/imports.rs b/wallet/keys/src/imports.rs index 6597aad3c3..a1c0a5e0d2 100644 --- a/wallet/keys/src/imports.rs +++ b/wallet/keys/src/imports.rs @@ -15,7 +15,7 @@ pub use borsh::{BorshDeserialize, BorshSerialize}; pub use js_sys::Array; pub use kaspa_addresses::{Address, Version as AddressVersion}; pub use kaspa_bip32::{ChildNumber, ExtendedPrivateKey, ExtendedPublicKey, SecretKey}; -pub use kaspa_consensus_core::network::NetworkTypeT; +pub use kaspa_consensus_core::network::{NetworkId, NetworkTypeT}; pub use kaspa_utils::hex::*; pub use kaspa_wasm_core::types::*; pub use serde::{Deserialize, Serialize}; diff --git a/wallet/keys/src/xpub.rs b/wallet/keys/src/xpub.rs index 0ef461f63e..e91df41c0e 100644 --- a/wallet/keys/src/xpub.rs +++ b/wallet/keys/src/xpub.rs @@ -1,3 +1,6 @@ +use kaspa_bip32::Prefix; +use std::{fmt, str::FromStr}; + use crate::imports::*; /// @@ -81,3 +84,26 @@ impl TryCastFromJs for XPub { }) } } + +pub struct NetworkTaggedXpub { + pub xpub: ExtendedPublicKey, + pub network_id: NetworkId, +} +// impl NetworkTaggedXpub { + +// } + +impl fmt::Display for NetworkTaggedXpub { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let obj: XPub = self.xpub.clone().into(); + write!(f, "{}", obj.inner.to_string(Some(Prefix::from(self.network_id)))) + } +} + +type TaggedXpub = (ExtendedPublicKey, NetworkId); + +impl From for NetworkTaggedXpub { + fn from(value: TaggedXpub) -> Self { + Self { xpub: value.0, network_id: value.1 } + } +} From 859d84e76848ace3e3138552c48333ee881fd38b Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 30 Jun 2024 15:25:38 +0300 Subject: [PATCH 059/158] update resolver to use v2 API --- rpc/wrpc/client/src/resolver.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index 4fd159b40b..5ffb9a50a9 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -6,7 +6,7 @@ use rand::seq::SliceRandom; use rand::thread_rng; use workflow_http::get_json; -const DEFAULT_VERSION: usize = 1; +const DEFAULT_VERSION: usize = 2; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResolverRecord { @@ -68,7 +68,7 @@ impl Resolver { } async fn fetch_node_info(&self, url: &str, encoding: Encoding, network_id: NetworkId) -> Result { - let url = format!("{}/v{}/wrpc/{}/{}", url, DEFAULT_VERSION, encoding, network_id); + let url = format!("{}/v{}/kaspad/wrpc/{}/{}", url, DEFAULT_VERSION, encoding, network_id); let node = get_json::(&url).await.map_err(|error| Error::custom(format!("Unable to connect to {url}: {error}")))?; Ok(node) From 5b1e4f9ab8e7903fd815c4a11f17c4667f7a0f76 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 30 Jun 2024 15:30:07 +0300 Subject: [PATCH 060/158] change default resolver subdomains --- rpc/wrpc/client/Resolvers.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/wrpc/client/Resolvers.toml b/rpc/wrpc/client/Resolvers.toml index 295a352d2f..423e9c0fde 100644 --- a/rpc/wrpc/client/Resolvers.toml +++ b/rpc/wrpc/client/Resolvers.toml @@ -1,9 +1,9 @@ [[resolver]] -url = "https://beacon.kaspa-ng.org" +url = "https://resolver.kaspa-ng.org" enable = true [[resolver]] -url = "https://beacon.kaspa-ng.io" +url = "https://resolver.kaspa-ng.io" enable = true [[resolver]] From babbf640f31f3e6bec48f05dfc925ccbc1d42bdb Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 3 Jul 2024 02:35:18 +0300 Subject: [PATCH 061/158] resolver v2 updates --- rpc/wrpc/client/src/resolver.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index 4fd159b40b..f7d4db604c 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -1,12 +1,15 @@ +use std::sync::OnceLock; + use crate::error::Error; use crate::imports::*; use crate::node::NodeDescriptor; pub use futures::future::join_all; use rand::seq::SliceRandom; use rand::thread_rng; +use workflow_core::runtime; use workflow_http::get_json; -const DEFAULT_VERSION: usize = 1; +const DEFAULT_VERSION: usize = 2; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResolverRecord { @@ -67,8 +70,31 @@ impl Resolver { self.inner.urls.clone() } + fn make_url(&self, url: &str, encoding: Encoding, network_id: NetworkId) -> String { + static SSL: OnceLock = OnceLock::new(); + + let ssl = *SSL.get_or_init(|| { + if runtime::is_web() { + js_sys::Reflect::get(&js_sys::global(), &"location".into()) + .and_then(|location| js_sys::Reflect::get(&location, &"protocol".into())) + .ok() + .and_then(|protocol| protocol.as_string()) + .map(|protocol| protocol.starts_with("https")) + .unwrap_or(false) + } else { + false + } + }); + + if ssl { + format!("{}/v{}/rk/wrpc/tls/{}/{}", url, DEFAULT_VERSION, encoding, network_id) + } else { + format!("{}/v{}/rk/wrpc/any/{}/{}", url, DEFAULT_VERSION, encoding, network_id) + } + } + async fn fetch_node_info(&self, url: &str, encoding: Encoding, network_id: NetworkId) -> Result { - let url = format!("{}/v{}/wrpc/{}/{}", url, DEFAULT_VERSION, encoding, network_id); + let url = self.make_url(url, encoding, network_id); let node = get_json::(&url).await.map_err(|error| Error::custom(format!("Unable to connect to {url}: {error}")))?; Ok(node) From 2745ab42631de6da903316a6ff1431fae93439f5 Mon Sep 17 00:00:00 2001 From: KaffinPX <73744616+KaffinPX@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:56:31 +0300 Subject: [PATCH 062/158] Refactorize some addresses and Script related parts (#61) * Refactorize some addresses and Script related parts * A ScriptBuilder example on TypeScript * addOps Opcodes are now BinaryT * Move txscript bindings to relevant folders * Sort lines of deps and imports * Lint --- Cargo.lock | 6 + consensus/client/src/lib.rs | 3 - consensus/client/src/utils.rs | 12 +- crypto/txscript/Cargo.toml | 9 + crypto/txscript/src/error.rs | 89 +++++++ crypto/txscript/src/lib.rs | 4 + crypto/txscript/src/result.rs | 1 + crypto/txscript/src/script_builder.rs | 2 +- crypto/txscript/src/wasm/builder.rs | 171 +++++++++++++ crypto/txscript/src/wasm/mod.rs | 15 ++ .../txscript/src/wasm/opcodes.rs | 241 +++--------------- wallet/core/src/derivation.rs | 12 +- wasm/Cargo.toml | 3 + .../nodejs/typescript/src/scriptBuilder.ts | 13 + wasm/src/lib.rs | 2 + 15 files changed, 371 insertions(+), 212 deletions(-) create mode 100644 crypto/txscript/src/error.rs create mode 100644 crypto/txscript/src/result.rs create mode 100644 crypto/txscript/src/wasm/builder.rs create mode 100644 crypto/txscript/src/wasm/mod.rs rename consensus/client/src/script.rs => crypto/txscript/src/wasm/opcodes.rs (51%) create mode 100644 wasm/examples/nodejs/typescript/src/scriptBuilder.ts diff --git a/Cargo.lock b/Cargo.lock index 16d198c36e..dcaef44db0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3374,6 +3374,7 @@ version = "0.14.1" dependencies = [ "blake2b_simd", "borsh", + "cfg-if 1.0.0", "criterion", "hex", "indexmap 2.2.6", @@ -3382,16 +3383,20 @@ dependencies = [ "kaspa-consensus-core", "kaspa-hashes", "kaspa-txscript-errors", + "kaspa-utils", + "kaspa-wasm-core", "log", "parking_lot", "rand 0.8.5", "secp256k1", "serde", + "serde-wasm-bindgen", "serde_json", "sha2", "smallvec", "thiserror", "wasm-bindgen", + "workflow-wasm", ] [[package]] @@ -3651,6 +3656,7 @@ dependencies = [ "kaspa-math", "kaspa-pow", "kaspa-rpc-core", + "kaspa-txscript", "kaspa-utils", "kaspa-wallet-core", "kaspa-wallet-keys", diff --git a/consensus/client/src/lib.rs b/consensus/client/src/lib.rs index c897773f77..1c46d49191 100644 --- a/consensus/client/src/lib.rs +++ b/consensus/client/src/lib.rs @@ -16,10 +16,8 @@ cfg_if::cfg_if! { mod utils; mod hash; mod sign; - mod script; mod serializable; - pub use header::*; pub use input::*; pub use transaction::*; @@ -27,7 +25,6 @@ cfg_if::cfg_if! { pub use utils::*; pub use hash::*; // pub use signing::*; - pub use script::*; pub use sign::sign_with_multiple_v3; } } diff --git a/consensus/client/src/utils.rs b/consensus/client/src/utils.rs index 47d3a90b61..48e4cdfbd1 100644 --- a/consensus/client/src/utils.rs +++ b/consensus/client/src/utils.rs @@ -1,7 +1,10 @@ use crate::imports::*; use crate::result::Result; use kaspa_addresses::*; -use kaspa_consensus_core::tx::ScriptPublicKeyT; +use kaspa_consensus_core::{ + network::{NetworkType, NetworkTypeT}, + tx::ScriptPublicKeyT, +}; use kaspa_txscript::{script_class::ScriptClass, standard}; use kaspa_utils::hex::ToHex; use kaspa_wasm_core::types::{BinaryT, HexString}; @@ -40,10 +43,11 @@ pub fn pay_to_script_hash_signature_script(redeem_script: BinaryT, signature: Bi /// @param prefix - The address prefix. /// @category Wallet SDK #[wasm_bindgen(js_name = addressFromScriptPublicKey)] -pub fn address_from_script_public_key(script_public_key: ScriptPublicKeyT, prefix: String) -> Result { +pub fn address_from_script_public_key(script_public_key: ScriptPublicKeyT, network: &NetworkTypeT) -> Result { let script_public_key = ScriptPublicKey::try_cast_from(script_public_key)?; - let prefix = Prefix::try_from(prefix.as_str())?; - match standard::extract_script_pub_key_address(script_public_key.as_ref(), prefix) { + let network_type = NetworkType::try_from(network)?; + + match standard::extract_script_pub_key_address(script_public_key.as_ref(), network_type.into()) { Ok(address) => Ok(AddressOrUndefinedT::from(JsValue::from(address))), Err(_) => Ok(AddressOrUndefinedT::from(JsValue::UNDEFINED)), } diff --git a/crypto/txscript/Cargo.toml b/crypto/txscript/Cargo.toml index 6084df0b2a..c7f352e1b5 100644 --- a/crypto/txscript/Cargo.toml +++ b/crypto/txscript/Cargo.toml @@ -9,24 +9,33 @@ include.workspace = true license.workspace = true repository.workspace = true +[features] +wasm32-sdk = [] + [dependencies] blake2b_simd.workspace = true borsh.workspace = true +cfg-if.workspace = true indexmap.workspace = true itertools.workspace = true kaspa-addresses.workspace = true kaspa-consensus-core.workspace = true kaspa-hashes.workspace = true kaspa-txscript-errors.workspace = true +kaspa-utils.workspace = true +kaspa-wasm-core.workspace = true log.workspace = true parking_lot.workspace = true rand.workspace = true secp256k1.workspace = true +serde_json.workspace = true +serde-wasm-bindgen.workspace = true serde.workspace = true sha2.workspace = true smallvec.workspace = true thiserror.workspace = true wasm-bindgen.workspace = true +workflow-wasm.workspace = true [dev-dependencies] criterion.workspace = true diff --git a/crypto/txscript/src/error.rs b/crypto/txscript/src/error.rs new file mode 100644 index 0000000000..7d45fb05e0 --- /dev/null +++ b/crypto/txscript/src/error.rs @@ -0,0 +1,89 @@ +use crate::script_builder; +use thiserror::Error; +use wasm_bindgen::{JsError, JsValue}; +use workflow_wasm::jserror::JsErrorData; + +#[derive(Debug, Error, Clone)] +pub enum Error { + #[error("{0}")] + Custom(String), + + #[error(transparent)] + JsValue(JsErrorData), + + #[error(transparent)] + Wasm(#[from] workflow_wasm::error::Error), + + #[error(transparent)] + ScriptBuilder(#[from] script_builder::ScriptBuilderError), + + #[error("{0}")] + ParseInt(#[from] std::num::ParseIntError), + + #[error(transparent)] + SerdeWasmBindgen(JsErrorData), + + #[error(transparent)] + NetworkType(#[from] kaspa_consensus_core::network::NetworkTypeError), + + #[error("Error converting property `{0}`: {1}")] + Convert(&'static str, String), + + #[error("Error processing JSON: {0}")] + SerdeJson(String), +} + +impl Error { + pub fn custom>(msg: T) -> Self { + Error::Custom(msg.into()) + } + + pub fn convert(prop: &'static str, msg: S) -> Self { + Self::Convert(prop, msg.to_string()) + } +} + +impl From for Error { + fn from(err: String) -> Self { + Self::Custom(err) + } +} + +impl From<&str> for Error { + fn from(err: &str) -> Self { + Self::Custom(err.to_string()) + } +} + +impl From for JsValue { + fn from(value: Error) -> Self { + match value { + Error::JsValue(js_error_data) => js_error_data.into(), + _ => JsValue::from(value.to_string()), + } + } +} + +impl From for Error { + fn from(err: JsValue) -> Self { + Self::JsValue(err.into()) + } +} + +impl From for Error { + fn from(err: JsError) -> Self { + Self::JsValue(err.into()) + } +} + +impl From for Error { + fn from(err: serde_json::Error) -> Self { + Self::SerdeJson(err.to_string()) + } +} + +impl From for Error { + fn from(err: serde_wasm_bindgen::Error) -> Self { + Self::SerdeWasmBindgen(JsValue::from(err).into()) + } +} diff --git a/crypto/txscript/src/lib.rs b/crypto/txscript/src/lib.rs index 77cef45bcd..b145fb90e5 100644 --- a/crypto/txscript/src/lib.rs +++ b/crypto/txscript/src/lib.rs @@ -3,10 +3,14 @@ extern crate core; pub mod caches; mod data_stack; +pub mod error; pub mod opcodes; +pub mod result; pub mod script_builder; pub mod script_class; pub mod standard; +#[cfg(feature = "wasm32-sdk")] +pub mod wasm; use crate::caches::Cache; use crate::data_stack::{DataStack, Stack}; diff --git a/crypto/txscript/src/result.rs b/crypto/txscript/src/result.rs new file mode 100644 index 0000000000..4c8cb83f54 --- /dev/null +++ b/crypto/txscript/src/result.rs @@ -0,0 +1 @@ +pub type Result = std::result::Result; diff --git a/crypto/txscript/src/script_builder.rs b/crypto/txscript/src/script_builder.rs index c7aa05fee7..f1d7d716a9 100644 --- a/crypto/txscript/src/script_builder.rs +++ b/crypto/txscript/src/script_builder.rs @@ -69,7 +69,7 @@ impl ScriptBuilder { &self.script } - #[cfg(test)] + #[cfg(any(test, target_arch = "wasm32"))] pub fn extend(&mut self, data: &[u8]) { self.script.extend(data); } diff --git a/crypto/txscript/src/wasm/builder.rs b/crypto/txscript/src/wasm/builder.rs new file mode 100644 index 0000000000..ce5d3ba3c7 --- /dev/null +++ b/crypto/txscript/src/wasm/builder.rs @@ -0,0 +1,171 @@ +use crate::result::Result; +use crate::{script_builder as native, standard}; +use kaspa_consensus_core::tx::ScriptPublicKey; +use kaspa_utils::hex::ToHex; +use kaspa_wasm_core::types::{BinaryT, HexString}; +use std::cell::{Ref, RefCell, RefMut}; +use std::rc::Rc; +use wasm_bindgen::prelude::wasm_bindgen; +use workflow_wasm::prelude::*; + +/// ScriptBuilder provides a facility for building custom scripts. It allows +/// you to push opcodes, ints, and data while respecting canonical encoding. In +/// general it does not ensure the script will execute correctly, however any +/// data pushes which would exceed the maximum allowed script engine limits and +/// are therefore guaranteed not to execute will not be pushed and will result in +/// the Script function returning an error. +/// +/// @see {@link Opcode} +/// @category Consensus +#[derive(Clone)] +#[wasm_bindgen(inspectable)] +pub struct ScriptBuilder { + script_builder: Rc>, +} + +impl ScriptBuilder { + #[inline] + pub fn inner(&self) -> Ref<'_, native::ScriptBuilder> { + self.script_builder.borrow() + } + + #[inline] + pub fn inner_mut(&self) -> RefMut<'_, native::ScriptBuilder> { + self.script_builder.borrow_mut() + } +} + +impl Default for ScriptBuilder { + fn default() -> Self { + Self { script_builder: Rc::new(RefCell::new(native::ScriptBuilder::new())) } + } +} + +#[wasm_bindgen] +impl ScriptBuilder { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + Self::default() + } + + /// Creates a new ScriptBuilder over an existing script. + /// Supplied script can be represented as an `Uint8Array` or a `HexString`. + #[wasm_bindgen(js_name = "fromScript")] + pub fn from_script(script: BinaryT) -> Result { + let builder = ScriptBuilder::default(); + let script = script.try_as_vec_u8()?; + builder.inner_mut().extend(&script); + + Ok(builder) + } + + /// Pushes the passed opcode to the end of the script. The script will not + /// be modified if pushing the opcode would cause the script to exceed the + /// maximum allowed script engine size. + #[wasm_bindgen(js_name = "addOp")] + pub fn add_op(&self, op: u8) -> Result { + let mut inner = self.inner_mut(); + inner.add_op(op)?; + + Ok(self.clone()) + } + + /// Adds the passed opcodes to the end of the script. + /// Supplied opcodes can be represented as an `Uint8Array` or a `HexString`. + #[wasm_bindgen(js_name = "addOps")] + pub fn add_ops(&self, opcodes: BinaryT) -> Result { + let opcodes = opcodes.try_as_vec_u8()?; + self.inner_mut().add_ops(&opcodes)?; + + Ok(self.clone()) + } + + /// AddData pushes the passed data to the end of the script. It automatically + /// chooses canonical opcodes depending on the length of the data. + /// + /// A zero length buffer will lead to a push of empty data onto the stack (Op0 = OpFalse) + /// and any push of data greater than [`MAX_SCRIPT_ELEMENT_SIZE`](kaspa_txscript::MAX_SCRIPT_ELEMENT_SIZE) will not modify + /// the script since that is not allowed by the script engine. + /// + /// Also, the script will not be modified if pushing the data would cause the script to + /// exceed the maximum allowed script engine size [`MAX_SCRIPTS_SIZE`](kaspa_txscript::MAX_SCRIPTS_SIZE). + #[wasm_bindgen(js_name = "addData")] + pub fn add_data(&self, data: BinaryT) -> Result { + let data = data.try_as_vec_u8()?; + + let mut inner = self.inner_mut(); + inner.add_data(&data)?; + + Ok(self.clone()) + } + + #[wasm_bindgen(js_name = "addI64")] + pub fn add_i64(&self, value: i64) -> Result { + let mut inner = self.inner_mut(); + inner.add_i64(value)?; + + Ok(self.clone()) + } + + #[wasm_bindgen(js_name = "addLockTime")] + pub fn add_lock_time(&self, lock_time: u64) -> Result { + let mut inner = self.inner_mut(); + inner.add_lock_time(lock_time)?; + + Ok(self.clone()) + } + + #[wasm_bindgen(js_name = "addSequence")] + pub fn add_sequence(&self, sequence: u64) -> Result { + let mut inner = self.inner_mut(); + inner.add_sequence(sequence)?; + + Ok(self.clone()) + } + + #[wasm_bindgen(js_name = "canonicalDataSize")] + pub fn canonical_data_size(data: BinaryT) -> Result { + let data = data.try_as_vec_u8()?; + let size = native::ScriptBuilder::canonical_data_size(&data) as u32; + + Ok(size) + } + + /// Get script bytes represented by a hex string. + #[wasm_bindgen(js_name = "toString")] + pub fn to_string_js(&self) -> HexString { + let inner = self.inner(); + + HexString::from(inner.script()) + } + + /// Drains (empties) the script builder, returning the + /// script bytes represented by a hex string. + pub fn drain(&self) -> HexString { + let mut inner = self.inner_mut(); + + HexString::from(inner.drain().as_slice()) + } + + /// Creates an equivalent pay-to-script-hash script. + /// Can be used to create an P2SH address. + /// @see {@link addressFromScriptPublicKey} + #[wasm_bindgen(js_name = "createPayToScriptHashScript")] + pub fn pay_to_script_hash_script(&self) -> ScriptPublicKey { + let inner = self.inner(); + let script = inner.script(); + + standard::pay_to_script_hash_script(script) + } + + /// Generates a signature script that fits a pay-to-script-hash script. + #[wasm_bindgen(js_name = "encodePayToScriptHashSignatureScript")] + pub fn pay_to_script_hash_signature_script(&self, signature: BinaryT) -> Result { + let inner = self.inner(); + let script = inner.script(); + let signature = signature.try_as_vec_u8()?; + let generated_script = standard::pay_to_script_hash_signature_script(script.into(), signature)?; + + Ok(generated_script.to_hex().into()) + } +} diff --git a/crypto/txscript/src/wasm/mod.rs b/crypto/txscript/src/wasm/mod.rs new file mode 100644 index 0000000000..e88e580c7d --- /dev/null +++ b/crypto/txscript/src/wasm/mod.rs @@ -0,0 +1,15 @@ +//! +//! WASM32 bindings for the txscript framework components. +//! + +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(any(feature = "wasm32-sdk", feature = "wasm32-core"))] { + pub mod opcodes; + pub mod builder; + + pub use self::opcodes::*; + pub use self::builder::*; + } +} diff --git a/consensus/client/src/script.rs b/crypto/txscript/src/wasm/opcodes.rs similarity index 51% rename from consensus/client/src/script.rs rename to crypto/txscript/src/wasm/opcodes.rs index 7392b1d856..40492cc837 100644 --- a/consensus/client/src/script.rs +++ b/crypto/txscript/src/wasm/opcodes.rs @@ -1,20 +1,12 @@ -use std::cell::{Ref, RefCell, RefMut}; -use std::rc::Rc; +pub use wasm_bindgen::prelude::*; -use kaspa_wasm_core::types::{BinaryT, HexString}; - -use crate::imports::*; -use crate::result::Result; -use kaspa_txscript::script_builder as native; +/// Kaspa Transaction Script Opcodes +/// @see {@link ScriptBuilder} +/// @category Consensus +#[wasm_bindgen] +pub enum Opcodes { + OpFalse = 0x00, -#[wasm_bindgen(typescript_custom_section)] -const TS_SCRIPT_OPCODES: &'static str = r#" -/** - * Kaspa Transaction Script Opcodes - * @see {@link ScriptBuilder} - * @category Consensus - */ -export enum Opcode { OpData1 = 0x01, OpData2 = 0x02, OpData3 = 0x03, @@ -90,15 +82,17 @@ export enum Opcode { OpData73 = 0x49, OpData74 = 0x4a, OpData75 = 0x4b, + OpPushData1 = 0x4c, OpPushData2 = 0x4d, OpPushData4 = 0x4e, + Op1Negate = 0x4f, - /** - * Reserved - */ + OpReserved = 0x50, - Op1 = 0x51, + + OpTrue = 0x51, + Op2 = 0x52, Op3 = 0x53, Op4 = 0x54, @@ -114,27 +108,21 @@ export enum Opcode { Op14 = 0x5e, Op15 = 0x5f, Op16 = 0x60, + OpNop = 0x61, - /** - * Reserved - */ OpVer = 0x62, OpIf = 0x63, OpNotIf = 0x64, - /** - * Reserved - */ OpVerIf = 0x65, - /** - * Reserved - */ OpVerNotIf = 0x66, + OpElse = 0x67, OpEndIf = 0x68, OpVerify = 0x69, OpReturn = 0x6a, OpToAltStack = 0x6b, OpFromAltStack = 0x6c, + Op2Drop = 0x6d, Op2Dup = 0x6e, Op3Dup = 0x6f, @@ -148,88 +136,57 @@ export enum Opcode { OpNip = 0x77, OpOver = 0x78, OpPick = 0x79, + OpRoll = 0x7a, OpRot = 0x7b, OpSwap = 0x7c, OpTuck = 0x7d, - /** - * Disabled - */ + + /// Splice opcodes. OpCat = 0x7e, - /** - * Disabled - */ OpSubStr = 0x7f, - /** - * Disabled - */ OpLeft = 0x80, - /** - * Disabled - */ OpRight = 0x81, + OpSize = 0x82, - /** - * Disabled - */ + + /// Bitwise logic opcodes. OpInvert = 0x83, - /** - * Disabled - */ OpAnd = 0x84, - /** - * Disabled - */ OpOr = 0x85, - /** - * Disabled - */ OpXor = 0x86, + OpEqual = 0x87, OpEqualVerify = 0x88, + OpReserved1 = 0x89, OpReserved2 = 0x8a, + + /// Numeric related opcodes. Op1Add = 0x8b, Op1Sub = 0x8c, - /** - * Disabled - */ Op2Mul = 0x8d, - /** - * Disabled - */ Op2Div = 0x8e, OpNegate = 0x8f, OpAbs = 0x90, OpNot = 0x91, Op0NotEqual = 0x92, + OpAdd = 0x93, OpSub = 0x94, - /** - * Disabled - */ OpMul = 0x95, - /** - * Disabled - */ OpDiv = 0x96, - /** - * Disabled - */ OpMod = 0x97, - /** - * Disabled - */ OpLShift = 0x98, - /** - * Disabled - */ OpRShift = 0x99, + OpBoolAnd = 0x9a, OpBoolOr = 0x9b, + OpNumEqual = 0x9c, OpNumEqualVerify = 0x9d, OpNumNotEqual = 0x9e, + OpLessThan = 0x9f, OpGreaterThan = 0xa0, OpLessThanOrEqual = 0xa1, @@ -237,10 +194,16 @@ export enum Opcode { OpMin = 0xa3, OpMax = 0xa4, OpWithin = 0xa5, + + /// Undefined opcodes. OpUnknown166 = 0xa6, OpUnknown167 = 0xa7, - OpSha256 = 0xa8, + + /// Crypto opcodes. + OpSHA256 = 0xa8, + OpCheckMultiSigECDSA = 0xa9, + OpBlake2b = 0xaa, OpCheckSigECDSA = 0xab, OpCheckSig = 0xac, @@ -249,6 +212,8 @@ export enum Opcode { OpCheckMultiSigVerify = 0xaf, OpCheckLockTimeVerify = 0xb0, OpCheckSequenceVerify = 0xb1, + + /// Undefined opcodes. OpUnknown178 = 0xb2, OpUnknown179 = 0xb3, OpUnknown180 = 0xb4, @@ -321,6 +286,7 @@ export enum Opcode { OpUnknown247 = 0xf7, OpUnknown248 = 0xf8, OpUnknown249 = 0xf9, + OpSmallInteger = 0xfa, OpPubKeys = 0xfb, OpUnknown252 = 0xfc, @@ -328,130 +294,3 @@ export enum Opcode { OpPubKey = 0xfe, OpInvalidOpCode = 0xff, } - -"#; - -/// -/// ScriptBuilder provides a facility for building custom scripts. It allows -/// you to push opcodes, ints, and data while respecting canonical encoding. In -/// general it does not ensure the script will execute correctly, however any -/// data pushes which would exceed the maximum allowed script engine limits and -/// are therefore guaranteed not to execute will not be pushed and will result in -/// the Script function returning an error. -/// -/// @see {@link Opcode} -/// @category Consensus -#[derive(Clone)] -#[wasm_bindgen(inspectable)] -pub struct ScriptBuilder { - script_builder: Rc>, -} - -impl ScriptBuilder { - #[inline] - pub fn inner(&self) -> Ref<'_, native::ScriptBuilder> { - self.script_builder.borrow() - } - - #[inline] - pub fn inner_mut(&self) -> RefMut<'_, native::ScriptBuilder> { - self.script_builder.borrow_mut() - } -} - -impl Default for ScriptBuilder { - fn default() -> Self { - Self { script_builder: Rc::new(RefCell::new(kaspa_txscript::script_builder::ScriptBuilder::new())) } - } -} - -#[wasm_bindgen] -impl ScriptBuilder { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[wasm_bindgen(getter)] - pub fn data(&self) -> HexString { - self.script() - } - - /// Get script bytes represented by a hex string. - pub fn script(&self) -> HexString { - let inner = self.inner(); - HexString::from(inner.script()) - } - - /// Drains (empties) the script builder, returning the - /// script bytes represented by a hex string. - pub fn drain(&self) -> HexString { - let mut inner = self.inner_mut(); - HexString::from(inner.drain().as_slice()) - } - - #[wasm_bindgen(js_name = canonicalDataSize)] - pub fn canonical_data_size(data: BinaryT) -> Result { - let data = data.try_as_vec_u8()?; - let size = native::ScriptBuilder::canonical_data_size(&data) as u32; - Ok(size) - } - - /// Pushes the passed opcode to the end of the script. The script will not - /// be modified if pushing the opcode would cause the script to exceed the - /// maximum allowed script engine size. - #[wasm_bindgen(js_name = addOp)] - pub fn add_op(&self, op: u8) -> Result { - let mut inner = self.inner_mut(); - inner.add_op(op)?; - Ok(self.clone()) - } - - /// Adds the passed opcodes to the end of the script. - /// Supplied opcodes can be represented as a `Uint8Array` or a `HexString`. - #[wasm_bindgen(js_name = "addOps")] - pub fn add_ops(&self, opcodes: JsValue) -> Result { - let opcodes = opcodes.try_as_vec_u8()?; - self.inner_mut().add_ops(&opcodes)?; - Ok(self.clone()) - } - - /// AddData pushes the passed data to the end of the script. It automatically - /// chooses canonical opcodes depending on the length of the data. - /// - /// A zero length buffer will lead to a push of empty data onto the stack (Op0 = OpFalse) - /// and any push of data greater than [`MAX_SCRIPT_ELEMENT_SIZE`](kaspa_txscript::MAX_SCRIPT_ELEMENT_SIZE) will not modify - /// the script since that is not allowed by the script engine. - /// - /// Also, the script will not be modified if pushing the data would cause the script to - /// exceed the maximum allowed script engine size [`MAX_SCRIPTS_SIZE`](kaspa_txscript::MAX_SCRIPTS_SIZE). - #[wasm_bindgen(js_name = addData)] - pub fn add_data(&self, data: BinaryT) -> Result { - let data = data.try_as_vec_u8()?; - - let mut inner = self.inner_mut(); - inner.add_data(&data)?; - Ok(self.clone()) - } - - #[wasm_bindgen(js_name = addI64)] - pub fn add_i64(&self, value: i64) -> Result { - let mut inner = self.inner_mut(); - inner.add_i64(value)?; - Ok(self.clone()) - } - - #[wasm_bindgen(js_name = addLockTime)] - pub fn add_lock_time(&self, lock_time: u64) -> Result { - let mut inner = self.inner_mut(); - inner.add_lock_time(lock_time)?; - Ok(self.clone()) - } - - #[wasm_bindgen(js_name = addSequence)] - pub fn add_sequence(&self, sequence: u64) -> Result { - let mut inner = self.inner_mut(); - inner.add_sequence(sequence)?; - Ok(self.clone()) - } -} diff --git a/wallet/core/src/derivation.rs b/wallet/core/src/derivation.rs index 15ad785035..3eb617e396 100644 --- a/wallet/core/src/derivation.rs +++ b/wallet/core/src/derivation.rs @@ -15,7 +15,7 @@ use crate::error::Error; use crate::imports::*; use crate::result::Result; use kaspa_bip32::{AddressType, DerivationPath, ExtendedPrivateKey, ExtendedPublicKey, Language, Mnemonic, SecretKeyExt}; -use kaspa_consensus_core::network::NetworkType; +use kaspa_consensus_core::network::{NetworkType, NetworkTypeT}; use kaspa_txscript::{ extract_script_pub_key_address, multisig_redeem_script, multisig_redeem_script_ecdsa, pay_to_script_hash_script, }; @@ -459,12 +459,18 @@ pub fn create_multisig_address( #[wasm_bindgen(js_name=createAddress)] pub fn create_address_js( key: PublicKeyT, - network_type: NetworkType, + network: &NetworkTypeT, ecdsa: Option, account_kind: Option, ) -> Result
{ let public_key = PublicKey::try_cast_from(key)?; - create_address(1, vec![public_key.as_ref().try_into()?], network_type.into(), ecdsa.unwrap_or(false), account_kind) + create_address( + 1, + vec![public_key.as_ref().try_into()?], + NetworkType::try_from(network)?.into(), + ecdsa.unwrap_or(false), + account_kind, + ) } /// @category Wallet SDK diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 78a747e19f..136d81f151 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -23,6 +23,7 @@ kaspa-consensus-wasm.workspace = true kaspa-core.workspace = true kaspa-math.workspace = true kaspa-pow.workspace = true +kaspa-txscript.workspace = true kaspa-rpc-core.workspace = true kaspa-utils.workspace = true kaspa-wasm-core.workspace = true @@ -40,10 +41,12 @@ workflow-wasm.workspace = true wasm32-sdk = [ "kaspa-wallet-core/wasm32-sdk", "kaspa-pow/wasm32-sdk", + "kaspa-txscript/wasm32-sdk", ] wasm32-core = [ "kaspa-wallet-core/wasm32-core", "kaspa-pow/wasm32-sdk", + "kaspa-txscript/wasm32-sdk", ] wasm32-rpc = [ "kaspa-consensus-core/wasm32-sdk", diff --git a/wasm/examples/nodejs/typescript/src/scriptBuilder.ts b/wasm/examples/nodejs/typescript/src/scriptBuilder.ts new file mode 100644 index 0000000000..4e09c82990 --- /dev/null +++ b/wasm/examples/nodejs/typescript/src/scriptBuilder.ts @@ -0,0 +1,13 @@ +import { ScriptBuilder, Opcodes, addressFromScriptPublicKey, NetworkType } from "../../../../nodejs/kaspa" + +// An OpTrue is an always spendable script +const myScript = new ScriptBuilder() + .addOp(Opcodes.OpTrue) + +const P2SHScript = myScript.createPayToScriptHashScript() +const address = addressFromScriptPublicKey(P2SHScript, NetworkType.Mainnet) + +// Payable address +console.log(address!.toString()) +// Unlock signature script +console.log(myScript.createPayToScriptHashSignatureScript("")) \ No newline at end of file diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 912ed9428b..e0e1f07cf5 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -145,6 +145,7 @@ cfg_if::cfg_if! { pub use kaspa_addresses::{Address, Version as AddressVersion}; pub use kaspa_consensus_core::tx::{ScriptPublicKey, Transaction, TransactionInput, TransactionOutpoint, TransactionOutput}; pub use kaspa_pow::wasm::*; + pub use kaspa_txscript::wasm::*; pub mod rpc { //! Kaspa RPC interface @@ -171,6 +172,7 @@ cfg_if::cfg_if! { pub use kaspa_addresses::{Address, Version as AddressVersion}; pub use kaspa_consensus_core::tx::{ScriptPublicKey, Transaction, TransactionInput, TransactionOutpoint, TransactionOutput}; pub use kaspa_pow::wasm::*; + pub use kaspa_txscript::wasm::*; pub mod rpc { //! Kaspa RPC interface From fb853c88a568fcad59709452b448de7482a0b659 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 4 Jul 2024 16:56:39 +0300 Subject: [PATCH 063/158] fix wasm subscribe_daa_score --- rpc/wrpc/wasm/src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index 35cabd8d84..9217866b97 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -796,7 +796,7 @@ impl RpcClient { #[wasm_bindgen(js_name = subscribeVirtualDaaScoreChanged)] pub async fn subscribe_daa_score(&self) -> Result<()> { if let Some(listener_id) = self.listener_id() { - self.inner.client.stop_notify(listener_id, Scope::VirtualDaaScoreChanged(VirtualDaaScoreChangedScope {})).await?; + self.inner.client.start_notify(listener_id, Scope::VirtualDaaScoreChanged(VirtualDaaScoreChangedScope {})).await?; } else { log_error!("RPC unsubscribe on a closed connection"); } From efd05ebdf36da58151508b411d4173df0bc1b8b9 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 7 Jul 2024 02:00:39 +0300 Subject: [PATCH 064/158] expose native RpcClient multiplexer in KaspaRpcClient --- rpc/wrpc/client/src/client.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index 0d274069a0..3d228fdcd8 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -372,6 +372,10 @@ impl KaspaRpcClient { &self.inner.rpc_ctl } + pub fn ctl_multiplexer(&self) -> Multiplexer { + self.inner.wrpc_ctl_multiplexer.clone() + } + /// Start background RPC services. pub async fn start(&self) -> Result<()> { if !self.inner.background_services_running.load(Ordering::SeqCst) { From 75ce5b2462e9e89146740f8ac07249b5b890c790 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 7 Jul 2024 02:01:59 +0300 Subject: [PATCH 065/158] WIP (local wRPC) --- Cargo.lock | 95 +++++++++++++++--------------------------------------- Cargo.toml | 48 +++++++++++++-------------- 2 files changed, 50 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 389ef78d82..1b8ffe3ff6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6080,9 +6080,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", @@ -6332,9 +6332,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" dependencies = [ "byteorder", "bytes", @@ -6346,7 +6346,6 @@ dependencies = [ "rand 0.8.5", "sha1", "thiserror", - "url", "utf-8", ] @@ -6885,9 +6884,7 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69fe24347c0f2af138170b09f69b977258a871dc7987a60030a21773a96f582" +version = "0.13.3" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6900,9 +6897,7 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7685fc9a612e1cd8da852b5cea8f7b978121549ae239e7bc1e191302b5c4033f" +version = "0.13.3" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6932,9 +6927,7 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05d272b45174884602afa82dc26e97f4bbb1392ef169c0b096300a481c0d890" +version = "0.13.3" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6949,9 +6942,7 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537841fcd3e3e80e13eccd8275520f2994d5002a36ee954c6d685d690babe334" +version = "0.13.3" dependencies = [ "futures", "js-sys", @@ -6967,9 +6958,7 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f358e9976f6fad2448301a5656d5d4d53f8081f69faa19f61415e80083d94a" +version = "0.13.3" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6983,9 +6972,7 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74c615c6fe85ec1a2fe842763520be781256fa13e9710dabb0e067777f03c7e" +version = "0.13.3" dependencies = [ "cfg-if 1.0.0", "console", @@ -6999,9 +6986,7 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2263943df045dc8007685e3b710a606ac1ed5f066fdab24c1674674ef1835c87" +version = "0.13.3" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7012,9 +6997,7 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60222ed88d27300d28176f2a6c1e454c32d26c9264f0a1bc710439fc570e38ab" +version = "0.13.3" dependencies = [ "borsh", "futures", @@ -7033,9 +7016,7 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d7d555f20dff92855e2c0bb8ae4c48d5a9a85d729e46a3bd13068f823562f6" +version = "0.13.3" dependencies = [ "ahash", "async-trait", @@ -7057,9 +7038,7 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c7e88e74cbe7504e8622f9b46f1880eef722e842bde368fc8c4cb8ba51be84" +version = "0.13.3" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7082,9 +7061,7 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b61f8e569d6a7ede17e9c44c9030c322fe2fd86486ec5e3ef29d6c7a468486" +version = "0.13.3" dependencies = [ "ahash", "async-std", @@ -7112,9 +7089,7 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e12859e5869e643faefb7c1051cd54830d6686df791486c0e24dc243fd772de" +version = "0.13.3" dependencies = [ "parse-variants", "proc-macro-error", @@ -7125,9 +7100,7 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1acc7bb2af256394f4468871bad06192899e05b0b3eac6e0b86f2419b26fffa" +version = "0.13.3" dependencies = [ "ahash", "borsh", @@ -7136,12 +7109,10 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e50629a28c063226ad832610ef068645094785dc21dcfc09d73a7fd73e3bb59" +version = "0.13.3" dependencies = [ "async-std", - "base64 0.21.7", + "base64 0.22.1", "cfg-if 1.0.0", "chrome-sys", "faster-hex 0.9.0", @@ -7164,9 +7135,7 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eb2273998932ac249227779a619b205b55d579ccd54bb492593b39e2341f164" +version = "0.13.3" dependencies = [ "futures", "thiserror", @@ -7176,9 +7145,7 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2682bb5ea1dbfb6a5e1c624e6280741e0eb0ed39a5d3c7b738157ca64a5cd42" +version = "0.13.3" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7192,9 +7159,7 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d53f3d0a4e1afd5d7336d41328931e297951e7811e7c74e950255afe0d73076" +version = "0.13.3" dependencies = [ "async-std", "async-trait", @@ -7221,9 +7186,7 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5fb04baa73f6aa40e38174dda63d26b5717e0fed2a27345c7409ae1faa73387" +version = "0.13.3" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7237,9 +7200,7 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770586ce7cd1084e0033b785a22258b63917b4ba703cf80ba3f97053b83ccb10" +version = "0.13.3" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7258,9 +7219,7 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dca209b50904b71b415281c00be817e6fa779b43cc373d70283711195f47c09" +version = "0.13.3" dependencies = [ "js-sys", "proc-macro-error", @@ -7272,9 +7231,7 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab64d7729b0dbcc1185c48191180548a69a71d3cd199929c93c36f63fdbec276" +version = "0.13.3" dependencies = [ "ahash", "async-channel 2.2.1", diff --git a/Cargo.toml b/Cargo.toml index 82b751afc1..73bbcb6270 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,34 +274,34 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.13.2" } -workflow-d3 = { version = "0.13.2" } -workflow-dom = { version = "0.13.2" } -workflow-http = { version = "0.13.2" } -workflow-log = { version = "0.13.2" } -workflow-node = { version = "0.13.2" } -workflow-nw = { version = "0.13.2" } -workflow-rpc = { version = "0.13.2" } -workflow-serializer = { version = "0.13.2" } -workflow-store = { version = "0.13.2" } -workflow-terminal = { version = "0.13.2" } -workflow-wasm = { version = "0.13.2" } +# workflow-core = { version = "0.13.2" } +# workflow-d3 = { version = "0.13.2" } +# workflow-dom = { version = "0.13.2" } +# workflow-http = { version = "0.13.2" } +# workflow-log = { version = "0.13.2" } +# workflow-node = { version = "0.13.2" } +# workflow-nw = { version = "0.13.2" } +# workflow-rpc = { version = "0.13.2" } +# workflow-serializer = { version = "0.13.2" } +# workflow-store = { version = "0.13.2" } +# workflow-terminal = { version = "0.13.2" } +# workflow-wasm = { version = "0.13.2" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs # into a sibling folder from https://github.com/workflow-rs/workflow-rs -# workflow-core = { path = "../workflow-rs/core" } -# workflow-d3 = { path = "../workflow-rs/d3" } -# workflow-dom = { path = "../workflow-rs/dom" } -# workflow-http = { path = "../workflow-rs/http" } -# workflow-log = { path = "../workflow-rs/log" } -# workflow-node = { path = "../workflow-rs/node" } -# workflow-nw = { path = "../workflow-rs/nw" } -# workflow-rpc = { path = "../workflow-rs/rpc" } -# workflow-serializer = { path = "../workflow-rs/serializer" } -# workflow-store = { path = "../workflow-rs/store" } -# workflow-terminal = { path = "../workflow-rs/terminal" } -# workflow-wasm = { path = "../workflow-rs/wasm" } +workflow-core = { path = "../workflow-rs/core" } +workflow-d3 = { path = "../workflow-rs/d3" } +workflow-dom = { path = "../workflow-rs/dom" } +workflow-http = { path = "../workflow-rs/http" } +workflow-log = { path = "../workflow-rs/log" } +workflow-node = { path = "../workflow-rs/node" } +workflow-nw = { path = "../workflow-rs/nw" } +workflow-rpc = { path = "../workflow-rs/rpc" } +workflow-serializer = { path = "../workflow-rs/serializer" } +workflow-store = { path = "../workflow-rs/store" } +workflow-terminal = { path = "../workflow-rs/terminal" } +workflow-wasm = { path = "../workflow-rs/wasm" } # --- # workflow-core = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } From 0a074a3d4c4bafb661b544b1ce801901f32d36a5 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 7 Jul 2024 08:14:24 +0300 Subject: [PATCH 066/158] breakdown wRPC serialization trait into two ser/de; improve future compatibility patterns; --- cli/src/modules/rpc.rs | 5 +- cli/src/modules/settings.rs | 7 +- consensus/core/src/api/stats.rs | 2 + metrics/core/src/data.rs | 4 - notify/src/scope.rs | 30 ++++ rpc/core/src/api/notifications.rs | 2 + rpc/core/src/model/address.rs | 4 + rpc/core/src/model/block.rs | 8 +- rpc/core/src/model/header.rs | 6 +- rpc/core/src/model/mempool.rs | 4 + rpc/core/src/model/message.rs | 206 ++++++++++++++++++++++++++- rpc/core/src/model/tests.rs | 32 ++++- rpc/core/src/model/tx.rs | 16 +++ rpc/macros/src/grpc/server.rs | 2 +- rpc/macros/src/wrpc/server.rs | 2 +- testing/integration/src/rpc_tests.rs | 2 +- 16 files changed, 311 insertions(+), 21 deletions(-) diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index 945fd85024..84167bd261 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -246,9 +246,8 @@ impl Rpc { async fn display_help(self: Arc, ctx: Arc, _argv: Vec) -> Result<()> { // RpcApiOps that do not contain docs are not displayed - let help = RpcApiOps::list() - .iter() - .filter_map(|op| op.doc().is_not_empty().then_some((op.as_str().to_case(Case::Kebab).to_string(), op.doc()))) + let help = RpcApiOps::into_iter() + .filter_map(|op| op.rustdoc().is_not_empty().then_some((op.as_str().to_case(Case::Kebab).to_string(), op.rustdoc()))) .collect::>(); ctx.term().help(&help, None)?; diff --git a/cli/src/modules/settings.rs b/cli/src/modules/settings.rs index e7214418a3..b144c4cc3b 100644 --- a/cli/src/modules/settings.rs +++ b/cli/src/modules/settings.rs @@ -9,12 +9,11 @@ impl Settings { let ctx = ctx.clone().downcast_arc::()?; tprintln!(ctx, "\nSettings:\n"); - let list = WalletSettings::list(); - let list = list - .iter() + // let list = WalletSettings::list(); + let list = WalletSettings::into_iter() .map(|setting| { let value: String = ctx.wallet().settings().get(setting.clone()).unwrap_or_else(|| "-".to_string()); - let descr = setting.descr(); + let descr = setting.describe(); (setting.as_str().to_lowercase(), value, descr) }) .collect::>(); diff --git a/consensus/core/src/api/stats.rs b/consensus/core/src/api/stats.rs index 86ede997ae..c2fea489cd 100644 --- a/consensus/core/src/api/stats.rs +++ b/consensus/core/src/api/stats.rs @@ -22,7 +22,9 @@ impl Serializer for BlockCount { Ok(()) } +} +impl Deserializer for BlockCount { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let header_count = load!(u64, reader)?; diff --git a/metrics/core/src/data.rs b/metrics/core/src/data.rs index 67c083b101..b46a3b09bb 100644 --- a/metrics/core/src/data.rs +++ b/metrics/core/src/data.rs @@ -37,10 +37,6 @@ impl MetricGroup { } impl MetricGroup { - pub fn iter() -> impl Iterator { - [MetricGroup::System, MetricGroup::Storage, MetricGroup::Connections, MetricGroup::Network].into_iter() - } - pub fn metrics(&self) -> impl Iterator { match self { MetricGroup::System => [ diff --git a/notify/src/scope.rs b/notify/src/scope.rs index 715bbe26a9..1fe7924711 100644 --- a/notify/src/scope.rs +++ b/notify/src/scope.rs @@ -60,6 +60,9 @@ impl Serializer for Scope { store!(Scope, self, writer)?; Ok(()) } +} + +impl Deserializer for Scope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; load!(Scope, reader) @@ -74,6 +77,9 @@ impl Serializer for BlockAddedScope { store!(u16, &1, writer)?; Ok(()) } +} + +impl Deserializer for BlockAddedScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -103,6 +109,9 @@ impl Serializer for VirtualChainChangedScope { store!(bool, &self.include_accepted_transaction_ids, writer)?; Ok(()) } +} + +impl Deserializer for VirtualChainChangedScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let include_accepted_transaction_ids = load!(bool, reader)?; @@ -118,6 +127,9 @@ impl Serializer for FinalityConflictScope { store!(u16, &1, writer)?; Ok(()) } +} + +impl Deserializer for FinalityConflictScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -132,6 +144,9 @@ impl Serializer for FinalityConflictResolvedScope { store!(u16, &1, writer)?; Ok(()) } +} + +impl Deserializer for FinalityConflictResolvedScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -174,6 +189,9 @@ impl Serializer for UtxosChangedScope { store!(Vec
, &self.addresses, writer)?; Ok(()) } +} + +impl Deserializer for UtxosChangedScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let addresses = load!(Vec
, reader)?; @@ -189,6 +207,9 @@ impl Serializer for SinkBlueScoreChangedScope { store!(u16, &1, writer)?; Ok(()) } +} + +impl Deserializer for SinkBlueScoreChangedScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -203,6 +224,9 @@ impl Serializer for VirtualDaaScoreChangedScope { store!(u16, &1, writer)?; Ok(()) } +} + +impl Deserializer for VirtualDaaScoreChangedScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -217,6 +241,9 @@ impl Serializer for PruningPointUtxoSetOverrideScope { store!(u16, &1, writer)?; Ok(()) } +} + +impl Deserializer for PruningPointUtxoSetOverrideScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -231,6 +258,9 @@ impl Serializer for NewBlockTemplateScope { store!(u16, &1, writer)?; Ok(()) } +} + +impl Deserializer for NewBlockTemplateScope { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) diff --git a/rpc/core/src/api/notifications.rs b/rpc/core/src/api/notifications.rs index 18a4808e26..e07a7c4d98 100644 --- a/rpc/core/src/api/notifications.rs +++ b/rpc/core/src/api/notifications.rs @@ -156,7 +156,9 @@ impl Serializer for Notification { } Ok(()) } +} +impl Deserializer for Notification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; match load!(u16, reader)? { diff --git a/rpc/core/src/model/address.rs b/rpc/core/src/model/address.rs index 69bece8ffd..e2c069a50b 100644 --- a/rpc/core/src/model/address.rs +++ b/rpc/core/src/model/address.rs @@ -20,7 +20,9 @@ impl Serializer for RpcUtxosByAddressesEntry { serialize!(RpcTransactionOutpoint, &self.outpoint, writer)?; serialize!(RpcUtxoEntry, &self.utxo_entry, writer) } +} +impl Deserializer for RpcUtxosByAddressesEntry { fn deserialize(reader: &mut R) -> std::io::Result { let _version: u8 = load!(u8, reader)?; let address = load!(Option, reader)?; @@ -46,7 +48,9 @@ impl Serializer for RpcBalancesByAddressesEntry { store!(RpcAddress, &self.address, writer)?; store!(Option, &self.balance, writer) } +} +impl Deserializer for RpcBalancesByAddressesEntry { fn deserialize(reader: &mut R) -> std::io::Result { let _version: u8 = load!(u8, reader)?; let address = load!(RpcAddress, reader)?; diff --git a/rpc/core/src/model/block.rs b/rpc/core/src/model/block.rs index 9efe6a8e88..bfa9a68453 100644 --- a/rpc/core/src/model/block.rs +++ b/rpc/core/src/model/block.rs @@ -12,16 +12,18 @@ pub struct RpcBlock { impl Serializer for RpcBlock { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; serialize!(RpcHeader, &self.header, writer)?; serialize!(Vec, &self.transactions, writer)?; serialize!(Option, &self.verbose_data, writer)?; Ok(()) } +} +impl Deserializer for RpcBlock { fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let header = deserialize!(RpcHeader, reader)?; let transactions = deserialize!(Vec, reader)?; let verbose_data = deserialize!(Option, reader)?; @@ -61,7 +63,9 @@ impl Serializer for RpcBlockVerboseData { Ok(()) } +} +impl Deserializer for RpcBlockVerboseData { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u8, reader)?; let hash = load!(RpcHash, reader)?; diff --git a/rpc/core/src/model/header.rs b/rpc/core/src/model/header.rs index 28d582479b..59648ef458 100644 --- a/rpc/core/src/model/header.rs +++ b/rpc/core/src/model/header.rs @@ -124,7 +124,7 @@ impl From<&RpcHeader> for Header { impl Serializer for RpcHeader { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer)?; + store!(u16, &1, writer)?; store!(Hash, &self.hash, writer)?; store!(u16, &self.version, writer)?; @@ -142,9 +142,11 @@ impl Serializer for RpcHeader { Ok(()) } +} +impl Deserializer for RpcHeader { fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader)?; + let _version = load!(u16, reader)?; let hash = load!(Hash, reader)?; let version = load!(u16, reader)?; diff --git a/rpc/core/src/model/mempool.rs b/rpc/core/src/model/mempool.rs index 81ddc8f928..1a04bed756 100644 --- a/rpc/core/src/model/mempool.rs +++ b/rpc/core/src/model/mempool.rs @@ -22,7 +22,9 @@ impl Serializer for RpcMempoolEntry { serialize!(RpcTransaction, &self.transaction, writer)?; store!(bool, &self.is_orphan, writer) } +} +impl Deserializer for RpcMempoolEntry { fn deserialize(reader: &mut R) -> std::io::Result { let fee = load!(u64, reader)?; let transaction = deserialize!(RpcTransaction, reader)?; @@ -50,7 +52,9 @@ impl Serializer for RpcMempoolEntryByAddress { serialize!(Vec, &self.sending, writer)?; serialize!(Vec, &self.receiving, writer) } +} +impl Deserializer for RpcMempoolEntryByAddress { fn deserialize(reader: &mut R) -> std::io::Result { let address = load!(RpcAddress, reader)?; let sending = deserialize!(Vec, reader)?; diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index f86656b98f..6aac54b481 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -37,7 +37,9 @@ impl Serializer for SubmitBlockRequest { Ok(()) } +} +impl Deserializer for SubmitBlockRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let block = deserialize!(RpcBlock, reader)?; @@ -97,7 +99,9 @@ impl Serializer for SubmitBlockResponse { store!(SubmitBlockReport, &self.report, writer)?; Ok(()) } +} +impl Deserializer for SubmitBlockResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let report = load!(SubmitBlockReport, reader)?; @@ -132,7 +136,9 @@ impl Serializer for GetBlockTemplateRequest { Ok(()) } +} +impl Deserializer for GetBlockTemplateRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let pay_address = load!(RpcAddress, reader)?; @@ -162,7 +168,9 @@ impl Serializer for GetBlockTemplateResponse { Ok(()) } +} +impl Deserializer for GetBlockTemplateResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let block = deserialize!(RpcBlock, reader)?; @@ -196,7 +204,9 @@ impl Serializer for GetBlockRequest { Ok(()) } +} +impl Deserializer for GetBlockRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let hash = load!(RpcHash, reader)?; @@ -219,7 +229,9 @@ impl Serializer for GetBlockResponse { Ok(()) } +} +impl Deserializer for GetBlockResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let block = deserialize!(RpcBlock, reader)?; @@ -238,7 +250,9 @@ impl Serializer for GetInfoRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetInfoRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -270,7 +284,9 @@ impl Serializer for GetInfoResponse { Ok(()) } +} +impl Deserializer for GetInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let p2p_id = load!(String, reader)?; @@ -294,7 +310,9 @@ impl Serializer for GetCurrentNetworkRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetCurrentNetworkRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -319,7 +337,9 @@ impl Serializer for GetCurrentNetworkResponse { store!(RpcNetworkType, &self.network, writer)?; Ok(()) } +} +impl Deserializer for GetCurrentNetworkResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let network = load!(RpcNetworkType, reader)?; @@ -336,7 +356,9 @@ impl Serializer for GetPeerAddressesRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetPeerAddressesRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -363,7 +385,9 @@ impl Serializer for GetPeerAddressesResponse { store!(Vec, &self.banned_addresses, writer)?; Ok(()) } +} +impl Deserializer for GetPeerAddressesResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let known_addresses = load!(Vec, reader)?; @@ -381,7 +405,9 @@ impl Serializer for GetSinkRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetSinkRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -406,7 +432,9 @@ impl Serializer for GetSinkResponse { store!(RpcHash, &self.sink, writer)?; Ok(()) } +} +impl Deserializer for GetSinkResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let sink = load!(RpcHash, reader)?; @@ -438,7 +466,9 @@ impl Serializer for GetMempoolEntryRequest { Ok(()) } +} +impl Deserializer for GetMempoolEntryRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let transaction_id = load!(RpcTransactionId, reader)?; @@ -467,7 +497,9 @@ impl Serializer for GetMempoolEntryResponse { serialize!(RpcMempoolEntry, &self.mempool_entry, writer)?; Ok(()) } +} +impl Deserializer for GetMempoolEntryResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let mempool_entry = deserialize!(RpcMempoolEntry, reader)?; @@ -497,7 +529,9 @@ impl Serializer for GetMempoolEntriesRequest { Ok(()) } +} +impl Deserializer for GetMempoolEntriesRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let include_orphan_pool = load!(bool, reader)?; @@ -525,7 +559,9 @@ impl Serializer for GetMempoolEntriesResponse { serialize!(Vec, &self.mempool_entries, writer)?; Ok(()) } +} +impl Deserializer for GetMempoolEntriesResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let mempool_entries = deserialize!(Vec, reader)?; @@ -542,7 +578,9 @@ impl Serializer for GetConnectedPeerInfoRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetConnectedPeerInfoRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -567,7 +605,9 @@ impl Serializer for GetConnectedPeerInfoResponse { store!(Vec, &self.peer_info, writer)?; Ok(()) } +} +impl Deserializer for GetConnectedPeerInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let peer_info = load!(Vec, reader)?; @@ -596,7 +636,9 @@ impl Serializer for AddPeerRequest { Ok(()) } +} +impl Deserializer for AddPeerRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let peer_address = load!(RpcContextualPeerAddress, reader)?; @@ -615,7 +657,9 @@ impl Serializer for AddPeerResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for AddPeerResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -643,7 +687,9 @@ impl Serializer for SubmitTransactionRequest { Ok(()) } +} +impl Deserializer for SubmitTransactionRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let transaction = deserialize!(RpcTransaction, reader)?; @@ -672,7 +718,9 @@ impl Serializer for SubmitTransactionResponse { Ok(()) } +} +impl Deserializer for SubmitTransactionResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let transaction_id = load!(RpcTransactionId, reader)?; @@ -700,7 +748,9 @@ impl Serializer for GetSubnetworkRequest { Ok(()) } +} +impl Deserializer for GetSubnetworkRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let subnetwork_id = load!(RpcSubnetworkId, reader)?; @@ -728,7 +778,9 @@ impl Serializer for GetSubnetworkResponse { Ok(()) } +} +impl Deserializer for GetSubnetworkResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let gas_limit = load!(u64, reader)?; @@ -758,7 +810,9 @@ impl Serializer for GetVirtualChainFromBlockRequest { Ok(()) } +} +impl Deserializer for GetVirtualChainFromBlockRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let start_hash = load!(RpcHash, reader)?; @@ -795,7 +849,9 @@ impl Serializer for GetVirtualChainFromBlockResponse { Ok(()) } +} +impl Deserializer for GetVirtualChainFromBlockResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let removed_chain_block_hashes = load!(Vec, reader)?; @@ -829,7 +885,9 @@ impl Serializer for GetBlocksRequest { Ok(()) } +} +impl Deserializer for GetBlocksRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let low_hash = load!(Option, reader)?; @@ -861,7 +919,9 @@ impl Serializer for GetBlocksResponse { Ok(()) } +} +impl Deserializer for GetBlocksResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let block_hashes = load!(Vec, reader)?; @@ -880,7 +940,9 @@ impl Serializer for GetBlockCountRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetBlockCountRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -898,7 +960,9 @@ impl Serializer for GetBlockDagInfoRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetBlockDagInfoRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -964,7 +1028,9 @@ impl Serializer for GetBlockDagInfoResponse { Ok(()) } +} +impl Deserializer for GetBlockDagInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let network = load!(RpcNetworkId, reader)?; @@ -1012,7 +1078,9 @@ impl Serializer for ResolveFinalityConflictRequest { Ok(()) } +} +impl Deserializer for ResolveFinalityConflictRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let finality_block_hash = load!(RpcHash, reader)?; @@ -1030,7 +1098,9 @@ impl Serializer for ResolveFinalityConflictResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for ResolveFinalityConflictResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -1046,7 +1116,9 @@ impl Serializer for ShutdownRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for ShutdownRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -1062,7 +1134,9 @@ impl Serializer for ShutdownResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for ShutdownResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -1092,7 +1166,9 @@ impl Serializer for GetHeadersRequest { Ok(()) } +} +impl Deserializer for GetHeadersRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let start_hash = load!(RpcHash, reader)?; @@ -1122,7 +1198,9 @@ impl Serializer for GetHeadersResponse { Ok(()) } +} +impl Deserializer for GetHeadersResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let headers = load!(Vec, reader)?; @@ -1150,7 +1228,9 @@ impl Serializer for GetBalanceByAddressRequest { Ok(()) } +} +impl Deserializer for GetBalanceByAddressRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let address = load!(RpcAddress, reader)?; @@ -1178,7 +1258,9 @@ impl Serializer for GetBalanceByAddressResponse { Ok(()) } +} +impl Deserializer for GetBalanceByAddressResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let balance = load!(u64, reader)?; @@ -1206,7 +1288,9 @@ impl Serializer for GetBalancesByAddressesRequest { Ok(()) } +} +impl Deserializer for GetBalancesByAddressesRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let addresses = load!(Vec, reader)?; @@ -1234,7 +1318,9 @@ impl Serializer for GetBalancesByAddressesResponse { Ok(()) } +} +impl Deserializer for GetBalancesByAddressesResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let entries = deserialize!(Vec, reader)?; @@ -1252,7 +1338,9 @@ impl Serializer for GetSinkBlueScoreRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetSinkBlueScoreRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -1278,7 +1366,9 @@ impl Serializer for GetSinkBlueScoreResponse { Ok(()) } +} +impl Deserializer for GetSinkBlueScoreResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let blue_score = load!(u64, reader)?; @@ -1306,7 +1396,9 @@ impl Serializer for GetUtxosByAddressesRequest { Ok(()) } +} +impl Deserializer for GetUtxosByAddressesRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let addresses = load!(Vec, reader)?; @@ -1334,7 +1426,9 @@ impl Serializer for GetUtxosByAddressesResponse { Ok(()) } +} +impl Deserializer for GetUtxosByAddressesResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let entries = deserialize!(Vec, reader)?; @@ -1362,7 +1456,9 @@ impl Serializer for BanRequest { Ok(()) } +} +impl Deserializer for BanRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let ip = load!(RpcIpAddress, reader)?; @@ -1380,7 +1476,9 @@ impl Serializer for BanResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for BanResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -1406,7 +1504,9 @@ impl Serializer for UnbanRequest { Ok(()) } +} +impl Deserializer for UnbanRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let ip = load!(RpcIpAddress, reader)?; @@ -1424,7 +1524,9 @@ impl Serializer for UnbanResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for UnbanResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -1452,7 +1554,9 @@ impl Serializer for EstimateNetworkHashesPerSecondRequest { Ok(()) } +} +impl Deserializer for EstimateNetworkHashesPerSecondRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let window_size = load!(u32, reader)?; @@ -1481,7 +1585,9 @@ impl Serializer for EstimateNetworkHashesPerSecondResponse { Ok(()) } +} +impl Deserializer for EstimateNetworkHashesPerSecondResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let network_hashes_per_second = load!(u64, reader)?; @@ -1514,7 +1620,9 @@ impl Serializer for GetMempoolEntriesByAddressesRequest { Ok(()) } +} +impl Deserializer for GetMempoolEntriesByAddressesRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let addresses = load!(Vec, reader)?; @@ -1544,7 +1652,9 @@ impl Serializer for GetMempoolEntriesByAddressesResponse { Ok(()) } +} +impl Deserializer for GetMempoolEntriesByAddressesResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let entries = deserialize!(Vec, reader)?; @@ -1562,7 +1672,9 @@ impl Serializer for GetCoinSupplyRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetCoinSupplyRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -1590,7 +1702,9 @@ impl Serializer for GetCoinSupplyResponse { Ok(()) } +} +impl Deserializer for GetCoinSupplyResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let max_sompi = load!(u64, reader)?; @@ -1608,7 +1722,9 @@ impl Serializer for PingRequest { fn serialize(&self, _writer: &mut W) -> std::io::Result<()> { Ok(()) } +} +impl Deserializer for PingRequest { fn deserialize(_reader: &mut R) -> std::io::Result { Ok(Self {}) } @@ -1622,7 +1738,9 @@ impl Serializer for PingResponse { fn serialize(&self, _writer: &mut W) -> std::io::Result<()> { Ok(()) } +} +impl Deserializer for PingResponse { fn deserialize(_reader: &mut R) -> std::io::Result { Ok(Self {}) } @@ -1651,7 +1769,9 @@ impl Serializer for GetMetricsRequest { Ok(()) } +} +impl Deserializer for GetMetricsRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let process_metrics = load!(bool, reader)?; @@ -1693,7 +1813,9 @@ impl Serializer for ProcessMetrics { Ok(()) } +} +impl Deserializer for ProcessMetrics { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let resident_set_size = load!(u64, reader)?; @@ -1746,7 +1868,9 @@ impl Serializer for ConnectionMetrics { Ok(()) } +} +impl Deserializer for ConnectionMetrics { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let borsh_live_connections = load!(u32, reader)?; @@ -1796,7 +1920,9 @@ impl Serializer for BandwidthMetrics { Ok(()) } +} +impl Deserializer for BandwidthMetrics { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let borsh_bytes_tx = load!(u64, reader)?; @@ -1864,7 +1990,9 @@ impl Serializer for ConsensusMetrics { Ok(()) } +} +impl Deserializer for ConsensusMetrics { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let node_blocks_submitted_count = load!(u64, reader)?; @@ -1916,7 +2044,9 @@ impl Serializer for StorageMetrics { Ok(()) } +} +impl Deserializer for StorageMetrics { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let storage_size_bytes = load!(u64, reader)?; @@ -1961,7 +2091,9 @@ impl Serializer for GetMetricsResponse { Ok(()) } +} +impl Deserializer for GetMetricsResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let server_time = load!(u64, reader)?; @@ -1984,7 +2116,9 @@ impl Serializer for GetServerInfoRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetServerInfoRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2015,7 +2149,9 @@ impl Serializer for GetServerInfoResponse { Ok(()) } +} +impl Deserializer for GetServerInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let rpc_api_version = load!([u16; 4], reader)?; @@ -2038,7 +2174,9 @@ impl Serializer for GetSyncStatusRequest { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for GetSyncStatusRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2057,7 +2195,9 @@ impl Serializer for GetSyncStatusResponse { store!(bool, &self.is_synced, writer)?; Ok(()) } +} +impl Deserializer for GetSyncStatusResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let is_synced = load!(bool, reader)?; @@ -2083,7 +2223,9 @@ impl Serializer for GetDaaScoreTimestampEstimateRequest { store!(Vec, &self.daa_scores, writer)?; Ok(()) } +} +impl Deserializer for GetDaaScoreTimestampEstimateRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let daa_scores = load!(Vec, reader)?; @@ -2109,7 +2251,9 @@ impl Serializer for GetDaaScoreTimestampEstimateResponse { store!(Vec, &self.timestamps, writer)?; Ok(()) } +} +impl Deserializer for GetDaaScoreTimestampEstimateResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let timestamps = load!(Vec, reader)?; @@ -2144,7 +2288,9 @@ impl Serializer for NotifyBlockAddedRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifyBlockAddedRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let command = load!(Command, reader)?; @@ -2161,7 +2307,9 @@ impl Serializer for NotifyBlockAddedResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifyBlockAddedResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2184,7 +2332,9 @@ impl Serializer for BlockAddedNotification { serialize!(RpcBlock, &self.block, writer)?; Ok(()) } +} +impl Deserializer for BlockAddedNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let block = deserialize!(RpcBlock, reader)?; @@ -2219,7 +2369,9 @@ impl Serializer for NotifyVirtualChainChangedRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifyVirtualChainChangedRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let include_accepted_transaction_ids = load!(bool, reader)?; @@ -2237,7 +2389,9 @@ impl Serializer for NotifyVirtualChainChangedResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifyVirtualChainChangedResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2264,7 +2418,9 @@ impl Serializer for VirtualChainChangedNotification { store!(Vec, &self.accepted_transaction_ids, writer)?; Ok(()) } +} +impl Deserializer for VirtualChainChangedNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let removed_chain_block_hashes = load!(Vec, reader)?; @@ -2299,7 +2455,9 @@ impl Serializer for NotifyFinalityConflictRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifyFinalityConflictRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let command = load!(Command, reader)?; @@ -2316,7 +2474,9 @@ impl Serializer for NotifyFinalityConflictResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifyFinalityConflictResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2335,7 +2495,9 @@ impl Serializer for FinalityConflictNotification { store!(RpcHash, &self.violating_block_hash, writer)?; Ok(()) } +} +impl Deserializer for FinalityConflictNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let violating_block_hash = load!(RpcHash, reader)?; @@ -2364,7 +2526,9 @@ impl Serializer for NotifyFinalityConflictResolvedRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifyFinalityConflictResolvedRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let command = load!(Command, reader)?; @@ -2381,7 +2545,9 @@ impl Serializer for NotifyFinalityConflictResolvedResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifyFinalityConflictResolvedResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2400,7 +2566,9 @@ impl Serializer for FinalityConflictResolvedNotification { store!(RpcHash, &self.finality_block_hash, writer)?; Ok(()) } +} +impl Deserializer for FinalityConflictResolvedNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let finality_block_hash = load!(RpcHash, reader)?; @@ -2440,7 +2608,9 @@ impl Serializer for NotifyUtxosChangedRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifyUtxosChangedRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let addresses = load!(Vec, reader)?; @@ -2458,7 +2628,9 @@ impl Serializer for NotifyUtxosChangedResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifyUtxosChangedResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2512,7 +2684,9 @@ impl Serializer for UtxosChangedNotification { serialize!(Vec, &self.removed, writer)?; Ok(()) } +} +impl Deserializer for UtxosChangedNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let added = deserialize!(Vec, reader)?; @@ -2546,7 +2720,9 @@ impl Serializer for NotifySinkBlueScoreChangedRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifySinkBlueScoreChangedRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let command = load!(Command, reader)?; @@ -2563,7 +2739,9 @@ impl Serializer for NotifySinkBlueScoreChangedResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifySinkBlueScoreChangedResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2586,7 +2764,9 @@ impl Serializer for SinkBlueScoreChangedNotification { store!(u64, &self.sink_blue_score, writer)?; Ok(()) } +} +impl Deserializer for SinkBlueScoreChangedNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let sink_blue_score = load!(u64, reader)?; @@ -2619,7 +2799,9 @@ impl Serializer for NotifyVirtualDaaScoreChangedRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifyVirtualDaaScoreChangedRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let command = load!(Command, reader)?; @@ -2636,7 +2818,9 @@ impl Serializer for NotifyVirtualDaaScoreChangedResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifyVirtualDaaScoreChangedResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2659,7 +2843,9 @@ impl Serializer for VirtualDaaScoreChangedNotification { store!(u64, &self.virtual_daa_score, writer)?; Ok(()) } +} +impl Deserializer for VirtualDaaScoreChangedNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let virtual_daa_score = load!(u64, reader)?; @@ -2688,7 +2874,9 @@ impl Serializer for NotifyPruningPointUtxoSetOverrideRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifyPruningPointUtxoSetOverrideRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let command = load!(Command, reader)?; @@ -2705,7 +2893,9 @@ impl Serializer for NotifyPruningPointUtxoSetOverrideResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifyPruningPointUtxoSetOverrideResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2721,7 +2911,9 @@ impl Serializer for PruningPointUtxoSetOverrideNotification { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for PruningPointUtxoSetOverrideNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2751,7 +2943,9 @@ impl Serializer for NotifyNewBlockTemplateRequest { store!(Command, &self.command, writer)?; Ok(()) } +} +impl Deserializer for NotifyNewBlockTemplateRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let command = load!(Command, reader)?; @@ -2768,7 +2962,9 @@ impl Serializer for NotifyNewBlockTemplateResponse { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NotifyNewBlockTemplateResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2788,7 +2984,9 @@ impl Serializer for NewBlockTemplateNotification { store!(u16, &1, writer)?; Ok(()) } +} +impl Deserializer for NewBlockTemplateNotification { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; Ok(Self {}) @@ -2816,7 +3014,9 @@ impl Serializer for SubscribeResponse { store!(u64, &self.id, writer)?; Ok(()) } +} +impl Deserializer for SubscribeResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; let id = load!(u64, reader)?; @@ -2833,11 +3033,13 @@ pub struct UnsubscribeResponse {} impl Serializer for UnsubscribeResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &1, writer) + store!(u16, &1, writer) } +} +impl Deserializer for UnsubscribeResponse { fn deserialize(reader: &mut R) -> std::io::Result { - let _version = load!(u32, reader); + let _version = load!(u16, reader); Ok(Self {}) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index ec2715f709..76cb41c935 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -63,7 +63,7 @@ mod mockery { // and comparing A and B buffers. fn test(kind: &str) where - T: Serializer + Mock, + T: Serializer + Deserializer + Mock, { let data = T::mock(); @@ -1225,4 +1225,34 @@ mod mockery { } test!(UnsubscribeResponse); + + struct Misalign; + + impl Mock for Misalign { + fn mock() -> Self { + Misalign + } + } + + impl Serializer for Misalign { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &1, writer)?; + store!(u32, &2, writer)?; + store!(u32, &3, writer)?; + Ok(()) + } + } + + impl Deserializer for Misalign { + fn deserialize(reader: &mut R) -> std::io::Result { + let version: u32 = load!(u32, reader)?; + assert_eq!(version, 1); + Ok(Self) + } + } + + #[test] + fn test_misalignment() { + test::("Misalign"); + } } diff --git a/rpc/core/src/model/tx.rs b/rpc/core/src/model/tx.rs index 4b3e25f508..cdd0bcbf8c 100644 --- a/rpc/core/src/model/tx.rs +++ b/rpc/core/src/model/tx.rs @@ -63,7 +63,9 @@ impl Serializer for RpcUtxoEntry { Ok(()) } +} +impl Deserializer for RpcUtxoEntry { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u8, reader)?; let amount = load!(u64, reader)?; @@ -117,7 +119,9 @@ impl Serializer for RpcTransactionOutpoint { Ok(()) } +} +impl Deserializer for RpcTransactionOutpoint { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u8, reader)?; let transaction_id = load!(TransactionId, reader)?; @@ -168,7 +172,9 @@ impl Serializer for RpcTransactionInput { Ok(()) } +} +impl Deserializer for RpcTransactionInput { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u8, reader)?; let previous_outpoint = deserialize!(RpcTransactionOutpoint, reader)?; @@ -191,7 +197,9 @@ impl Serializer for RpcTransactionInputVerboseData { store!(u8, &1, writer)?; Ok(()) } +} +impl Deserializer for RpcTransactionInputVerboseData { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u8, reader)?; Ok(Self {}) @@ -228,7 +236,9 @@ impl Serializer for RpcTransactionOutput { Ok(()) } +} +impl Deserializer for RpcTransactionOutput { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u8, reader)?; let value = load!(u64, reader)?; @@ -255,7 +265,9 @@ impl Serializer for RpcTransactionOutputVerboseData { Ok(()) } +} +impl Deserializer for RpcTransactionOutputVerboseData { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u8, reader)?; let script_public_key_type = load!(RpcScriptClass, reader)?; @@ -296,7 +308,9 @@ impl Serializer for RpcTransaction { Ok(()) } +} +impl Deserializer for RpcTransaction { fn deserialize(reader: &mut R) -> std::io::Result { let _struct_version = load!(u16, reader)?; let version = load!(u16, reader)?; @@ -335,7 +349,9 @@ impl Serializer for RpcTransactionVerboseData { Ok(()) } +} +impl Deserializer for RpcTransactionVerboseData { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u8, reader)?; let transaction_id = load!(RpcTransactionId, reader)?; diff --git a/rpc/macros/src/grpc/server.rs b/rpc/macros/src/grpc/server.rs index f6f3f33b99..9378117195 100644 --- a/rpc/macros/src/grpc/server.rs +++ b/rpc/macros/src/grpc/server.rs @@ -129,7 +129,7 @@ impl ToTokens for RpcTable { { let mut interface = Interface::new(#server_ctx); - for op in #payload_ops::list() { + for op in #payload_ops::iter() { match op { #(#targets)* } diff --git a/rpc/macros/src/wrpc/server.rs b/rpc/macros/src/wrpc/server.rs index ee2afc5513..092b1edb32 100644 --- a/rpc/macros/src/wrpc/server.rs +++ b/rpc/macros/src/wrpc/server.rs @@ -72,7 +72,7 @@ impl ToTokens for RpcTable { #rpc_api_ops >::new(#server_ctx); - for op in #rpc_api_ops::list() { + for op in #rpc_api_ops::iter() { use workflow_serializer::prelude::*; match op { #(#targets)* diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs index 18fb1619ef..a36acfba99 100644 --- a/testing/integration/src/rpc_tests.rs +++ b/testing/integration/src/rpc_tests.rs @@ -64,7 +64,7 @@ async fn sanity_test() { // The intent of this for/match design (emphasizing the absence of an arm with fallback pattern in the match) // is to force any implementor of a new RpcApi method to add a matching arm here and to strongly incentivize // the adding of an actual sanity test of said new method. - for op in KaspadPayloadOps::list() { + for op in KaspadPayloadOps::iter() { let network_id = daemon.network; let task: JoinHandle<()> = match op { KaspadPayloadOps::SubmitBlock => { From ac5ac4f9c2686821de93e66b35455d2a0f6d3ac9 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 7 Jul 2024 09:11:47 +0300 Subject: [PATCH 067/158] get_connections_call() RPC method + provisional metrics dictionary --- cli/src/modules/rpc.rs | 6 +- metrics/core/src/lib.rs | 3 +- rpc/core/src/api/ops.rs | 2 + rpc/core/src/api/rpc.rs | 21 ++++- rpc/core/src/model/message.rs | 89 ++++++++++++++++++- rpc/core/src/model/tests.rs | 18 ++++ rpc/core/src/wasm/message.rs | 32 +++++++ rpc/grpc/client/src/lib.rs | 1 + rpc/grpc/core/proto/messages.proto | 2 + rpc/grpc/core/proto/rpc.proto | 9 ++ rpc/grpc/core/src/convert/kaspad.rs | 2 + rpc/grpc/core/src/convert/message.rs | 35 +++++++- rpc/grpc/core/src/ops.rs | 1 + .../server/src/request_handler/factory.rs | 1 + rpc/grpc/server/src/tests/rpc_core_mock.rs | 8 ++ rpc/service/src/service.rs | 15 ++++ rpc/wrpc/client/src/client.rs | 1 + rpc/wrpc/resolver/src/connection.rs | 2 +- rpc/wrpc/server/src/router.rs | 1 + rpc/wrpc/wasm/src/client.rs | 2 + testing/integration/src/rpc_tests.rs | 11 +++ wallet/core/src/tests/rpc_core_mock.rs | 8 ++ 22 files changed, 262 insertions(+), 8 deletions(-) diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index 84167bd261..6064e6124d 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -38,7 +38,11 @@ impl Rpc { tprintln!(ctx, "ok"); } RpcApiOps::GetMetrics => { - let result = rpc.get_metrics(true, true, true, true, true).await?; + let result = rpc.get_metrics(true, true, true, true, true, true).await?; + self.println(&ctx, result); + } + RpcApiOps::GetConnections => { + let result = rpc.get_connections().await?; self.println(&ctx, result); } RpcApiOps::GetServerInfo => { diff --git a/metrics/core/src/lib.rs b/metrics/core/src/lib.rs index b30bdfb4b4..ca7eb98f32 100644 --- a/metrics/core/src/lib.rs +++ b/metrics/core/src/lib.rs @@ -122,7 +122,8 @@ impl Metrics { bandwidth_metrics, process_metrics, storage_metrics, - } = rpc.get_metrics(true, true, true, true, true).await?; + custom_metrics: _, + } = rpc.get_metrics(true, true, true, true, true, false).await?; if let Some(consensus_metrics) = consensus_metrics { data.node_blocks_submitted_count = consensus_metrics.node_blocks_submitted_count; diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index 37e64a795e..3282a3f6e8 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -61,6 +61,8 @@ pub enum RpcApiOps { Ping = 110, /// Get metrics for consensus information and node performance GetMetrics, + /// Get current number of active TCP connections + GetConnections, /// Get state information on the node GetServerInfo, /// Get the current sync status of the node diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 9ee7db84a9..ab74605de5 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -29,6 +29,17 @@ pub trait RpcApi: Sync + Send + AnySync { // --- + async fn get_connections(&self) -> RpcResult { + self.get_connections_call(None, GetConnectionsRequest {}).await + } + async fn get_connections_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetConnectionsRequest, + ) -> RpcResult; + + // --- + async fn get_metrics( &self, process_metrics: bool, @@ -36,10 +47,18 @@ pub trait RpcApi: Sync + Send + AnySync { bandwidth_metrics: bool, consensus_metrics: bool, storage_metrics: bool, + custom_metrics: bool, ) -> RpcResult { self.get_metrics_call( None, - GetMetricsRequest { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics }, + GetMetricsRequest { + process_metrics, + connection_metrics, + bandwidth_metrics, + consensus_metrics, + storage_metrics, + custom_metrics, + }, ) .await } diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 6aac54b481..ba41acbd7d 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -4,6 +4,7 @@ use kaspa_consensus_core::api::stats::BlockCount; use kaspa_core::debug; use kaspa_notify::subscription::{context::SubscriptionContext, single::UtxosChangedSubscription, Command}; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::{ fmt::{Display, Formatter}, sync::Arc, @@ -1746,6 +1747,42 @@ impl Deserializer for PingResponse { } } +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetConnectionsRequest {} + +impl Serializer for GetConnectionsRequest { + fn serialize(&self, _writer: &mut W) -> std::io::Result<()> { + Ok(()) + } +} + +impl Deserializer for GetConnectionsRequest { + fn deserialize(_reader: &mut R) -> std::io::Result { + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetConnectionsResponse { + pub active_connections: u32, +} + +impl Serializer for GetConnectionsResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u32, &self.active_connections, writer)?; + Ok(()) + } +} + +impl Deserializer for GetConnectionsResponse { + fn deserialize(reader: &mut R) -> std::io::Result { + let active_connections = load!(u32, reader)?; + Ok(Self { active_connections }) + } +} + // TODO - custom wRPC commands (need review and implementation in gRPC) #[derive(Clone, Debug, Serialize, Deserialize)] @@ -1756,6 +1793,7 @@ pub struct GetMetricsRequest { pub bandwidth_metrics: bool, pub consensus_metrics: bool, pub storage_metrics: bool, + pub custom_metrics: bool, } impl Serializer for GetMetricsRequest { @@ -1766,6 +1804,7 @@ impl Serializer for GetMetricsRequest { store!(bool, &self.bandwidth_metrics, writer)?; store!(bool, &self.consensus_metrics, writer)?; store!(bool, &self.storage_metrics, writer)?; + store!(bool, &self.custom_metrics, writer)?; Ok(()) } @@ -1779,8 +1818,9 @@ impl Deserializer for GetMetricsRequest { let bandwidth_metrics = load!(bool, reader)?; let consensus_metrics = load!(bool, reader)?; let storage_metrics = load!(bool, reader)?; + let custom_metrics = load!(bool, reader)?; - Ok(Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics }) + Ok(Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics, custom_metrics }) } } @@ -2055,6 +2095,28 @@ impl Deserializer for StorageMetrics { } } +// TODO: Custom metrics dictionary +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum CustomMetricValue { + Placeholder, +} + +impl Serializer for CustomMetricValue { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + + Ok(()) + } +} + +impl Deserializer for CustomMetricValue { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + + Ok(CustomMetricValue::Placeholder) + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetMetricsResponse { @@ -2064,6 +2126,8 @@ pub struct GetMetricsResponse { pub bandwidth_metrics: Option, pub consensus_metrics: Option, pub storage_metrics: Option, + // TODO: this is currently a placeholder + pub custom_metrics: Option>, } impl GetMetricsResponse { @@ -2074,8 +2138,17 @@ impl GetMetricsResponse { bandwidth_metrics: Option, consensus_metrics: Option, storage_metrics: Option, + custom_metrics: Option>, ) -> Self { - Self { process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics, server_time } + Self { + process_metrics, + connection_metrics, + bandwidth_metrics, + consensus_metrics, + storage_metrics, + server_time, + custom_metrics, + } } } @@ -2088,6 +2161,7 @@ impl Serializer for GetMetricsResponse { serialize!(Option, &self.bandwidth_metrics, writer)?; serialize!(Option, &self.consensus_metrics, writer)?; serialize!(Option, &self.storage_metrics, writer)?; + serialize!(Option>, &self.custom_metrics, writer)?; Ok(()) } @@ -2102,8 +2176,17 @@ impl Deserializer for GetMetricsResponse { let bandwidth_metrics = deserialize!(Option, reader)?; let consensus_metrics = deserialize!(Option, reader)?; let storage_metrics = deserialize!(Option, reader)?; + let custom_metrics = deserialize!(Option>, reader)?; - Ok(Self { server_time, process_metrics, connection_metrics, bandwidth_metrics, consensus_metrics, storage_metrics }) + Ok(Self { + server_time, + process_metrics, + connection_metrics, + bandwidth_metrics, + consensus_metrics, + storage_metrics, + custom_metrics, + }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index 76cb41c935..ef202a553e 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -906,6 +906,22 @@ mod mockery { test!(PingResponse); + impl Mock for GetConnectionsRequest { + fn mock() -> Self { + GetConnectionsRequest {} + } + } + + test!(GetConnectionsRequest); + + impl Mock for GetConnectionsResponse { + fn mock() -> Self { + GetConnectionsResponse { active_connections: mock() } + } + } + + test!(GetConnectionsResponse); + impl Mock for GetMetricsRequest { fn mock() -> Self { GetMetricsRequest { @@ -914,6 +930,7 @@ mod mockery { bandwidth_metrics: true, consensus_metrics: true, storage_metrics: true, + custom_metrics: false, } } } @@ -929,6 +946,7 @@ mod mockery { bandwidth_metrics: mock(), consensus_metrics: mock(), storage_metrics: mock(), + custom_metrics: None, } } } diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index 56af183dbf..d8e2707453 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -317,6 +317,38 @@ try_from! ( args: GetMetricsResponse, IGetMetricsResponse, { // --- +declare! { + IGetConnectionsRequest, + r#" + /** + * @category Node RPC + */ + export interface IGetConnectionsRequest { } + "#, +} + +try_from! ( args: IGetConnectionsRequest, GetConnectionsRequest, { + Ok(from_value(args.into())?) +}); + +declare! { + IGetConnectionsResponse, + r#" + /** + * @category Node RPC + */ + export interface IGetConnectionsResponse { + [key: string]: any + } + "#, +} + +try_from! ( args: GetConnectionsResponse, IGetConnectionsResponse, { + Ok(to_value(&args)?.into()) +}); + +// --- + declare! { IGetSinkRequest, r#" diff --git a/rpc/grpc/client/src/lib.rs b/rpc/grpc/client/src/lib.rs index c7eebd8d1e..cd86a7ae0a 100644 --- a/rpc/grpc/client/src/lib.rs +++ b/rpc/grpc/client/src/lib.rs @@ -241,6 +241,7 @@ impl RpcApi for GrpcClient { route!(get_sync_status_call, GetSyncStatus); route!(get_server_info_call, GetServerInfo); route!(get_metrics_call, GetMetrics); + route!(get_connections_call, GetConnections); route!(submit_block_call, SubmitBlock); route!(get_block_template_call, GetBlockTemplate); route!(get_block_call, GetBlock); diff --git a/rpc/grpc/core/proto/messages.proto b/rpc/grpc/core/proto/messages.proto index ec72426350..063e6904f8 100644 --- a/rpc/grpc/core/proto/messages.proto +++ b/rpc/grpc/core/proto/messages.proto @@ -59,6 +59,7 @@ message KaspadRequest { GetServerInfoRequestMessage getServerInfoRequest = 1092; GetSyncStatusRequestMessage getSyncStatusRequest = 1094; GetDaaScoreTimestampEstimateRequestMessage GetDaaScoreTimestampEstimateRequest = 1096; + GetConnectionsRequestMessage getConnectionsRequest = 1098; } } @@ -118,6 +119,7 @@ message KaspadResponse { GetServerInfoResponseMessage getServerInfoResponse = 1093; GetSyncStatusResponseMessage getSyncStatusResponse = 1095; GetDaaScoreTimestampEstimateResponseMessage GetDaaScoreTimestampEstimateResponse = 1097; + GetConnectionsResponseMessage getConnectionsResponse= 1099; } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index 21902835ed..072919586d 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -810,12 +810,21 @@ message StorageMetrics{ uint64 storageSizeBytes = 1; } +message GetConnectionsRequestMessage{ +} + +message GetConnectionsResponseMessage{ + uint32 activeConnections = 1; + RPCError error = 1000; +} + message GetMetricsRequestMessage{ bool processMetrics = 1; bool connectionMetrics = 2; bool bandwidthMetrics = 3; bool consensusMetrics = 4; bool storageMetrics = 5; + bool customMetrics = 6; } message GetMetricsResponseMessage{ diff --git a/rpc/grpc/core/src/convert/kaspad.rs b/rpc/grpc/core/src/convert/kaspad.rs index 0fef61523a..1a4028f0b2 100644 --- a/rpc/grpc/core/src/convert/kaspad.rs +++ b/rpc/grpc/core/src/convert/kaspad.rs @@ -54,6 +54,7 @@ pub mod kaspad_request_convert { impl_into_kaspad_request!(GetCoinSupply); impl_into_kaspad_request!(Ping); impl_into_kaspad_request!(GetMetrics); + impl_into_kaspad_request!(GetConnections); impl_into_kaspad_request!(GetServerInfo); impl_into_kaspad_request!(GetSyncStatus); impl_into_kaspad_request!(GetDaaScoreTimestampEstimate); @@ -185,6 +186,7 @@ pub mod kaspad_response_convert { impl_into_kaspad_response!(GetCoinSupply); impl_into_kaspad_response!(Ping); impl_into_kaspad_response!(GetMetrics); + impl_into_kaspad_response!(GetConnections); impl_into_kaspad_response!(GetServerInfo); impl_into_kaspad_response!(GetSyncStatus); impl_into_kaspad_response!(GetDaaScoreTimestampEstimate); diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index 27eb78d50c..4d3e3dec6c 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -404,6 +404,7 @@ from!(item: &kaspa_rpc_core::GetMetricsRequest, protowire::GetMetricsRequestMess bandwidth_metrics: item.bandwidth_metrics, consensus_metrics: item.consensus_metrics, storage_metrics: item.storage_metrics, + custom_metrics: item.custom_metrics, } }); from!(item: RpcResult<&kaspa_rpc_core::GetMetricsResponse>, protowire::GetMetricsResponseMessage, { @@ -414,9 +415,23 @@ from!(item: RpcResult<&kaspa_rpc_core::GetMetricsResponse>, protowire::GetMetric bandwidth_metrics: item.bandwidth_metrics.as_ref().map(|x| x.into()), consensus_metrics: item.consensus_metrics.as_ref().map(|x| x.into()), storage_metrics: item.storage_metrics.as_ref().map(|x| x.into()), + // TODO + // custom_metrics : None, error: None, } }); + +from!(_item: &kaspa_rpc_core::GetConnectionsRequest, protowire::GetConnectionsRequestMessage, { + Self { + } +}); +from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetConnectionsResponseMessage, { + Self { + active_connections: item.active_connections, + error: None, + } +}); + from!(&kaspa_rpc_core::GetServerInfoRequest, protowire::GetServerInfoRequestMessage); from!(item: RpcResult<&kaspa_rpc_core::GetServerInfoResponse>, protowire::GetServerInfoResponseMessage, { Self { @@ -797,7 +812,14 @@ try_from!(&protowire::PingRequestMessage, kaspa_rpc_core::PingRequest); try_from!(&protowire::PingResponseMessage, RpcResult); try_from!(item: &protowire::GetMetricsRequestMessage, kaspa_rpc_core::GetMetricsRequest, { - Self { process_metrics: item.process_metrics, connection_metrics: item.connection_metrics, bandwidth_metrics:item.bandwidth_metrics, consensus_metrics: item.consensus_metrics, storage_metrics: item.storage_metrics } + Self { + process_metrics: item.process_metrics, + connection_metrics: item.connection_metrics, + bandwidth_metrics:item.bandwidth_metrics, + consensus_metrics: item.consensus_metrics, + storage_metrics: item.storage_metrics, + custom_metrics : item.custom_metrics, + } }); try_from!(item: &protowire::GetMetricsResponseMessage, RpcResult, { Self { @@ -807,6 +829,17 @@ try_from!(item: &protowire::GetMetricsResponseMessage, RpcResult, { + Self { + active_connections: item.active_connections, } }); diff --git a/rpc/grpc/core/src/ops.rs b/rpc/grpc/core/src/ops.rs index 7cc23f1609..5a16a1f29b 100644 --- a/rpc/grpc/core/src/ops.rs +++ b/rpc/grpc/core/src/ops.rs @@ -78,6 +78,7 @@ pub enum KaspadPayloadOps { GetCoinSupply, Ping, GetMetrics, + GetConnections, GetServerInfo, GetSyncStatus, GetDaaScoreTimestampEstimate, diff --git a/rpc/grpc/server/src/request_handler/factory.rs b/rpc/grpc/server/src/request_handler/factory.rs index 802cb6cd6b..49a4b86b47 100644 --- a/rpc/grpc/server/src/request_handler/factory.rs +++ b/rpc/grpc/server/src/request_handler/factory.rs @@ -72,6 +72,7 @@ impl Factory { GetCoinSupply, Ping, GetMetrics, + GetConnections, GetServerInfo, GetSyncStatus, GetDaaScoreTimestampEstimate, diff --git a/rpc/grpc/server/src/tests/rpc_core_mock.rs b/rpc/grpc/server/src/tests/rpc_core_mock.rs index e6abf8aa3b..e37c4ea189 100644 --- a/rpc/grpc/server/src/tests/rpc_core_mock.rs +++ b/rpc/grpc/server/src/tests/rpc_core_mock.rs @@ -90,6 +90,14 @@ impl RpcApi for RpcCoreMock { Err(RpcError::NotImplemented) } + async fn get_connections_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetConnectionsRequest, + ) -> RpcResult { + Err(RpcError::NotImplemented) + } + async fn get_server_info_call( &self, _connection: Option<&DynRpcConnection>, diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index 9eadc85b17..bb0a96256b 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -867,6 +867,18 @@ NOTE: This error usually indicates an RPC conversion error between the node and Err(RpcError::NotImplemented) } + async fn get_connections_call( + &self, + _connection: Option<&DynRpcConnection>, + _req: GetConnectionsRequest, + ) -> RpcResult { + let active_connections = (self.wrpc_borsh_counters.active_connections.load(Ordering::Relaxed) + + self.wrpc_json_counters.active_connections.load(Ordering::Relaxed) + + self.flow_context.hub().active_peers_len()) as u32; + + Ok(GetConnectionsResponse { active_connections }) + } + async fn get_metrics_call(&self, _connection: Option<&DynRpcConnection>, req: GetMetricsRequest) -> RpcResult { let CountersSnapshot { resident_set_size, @@ -943,6 +955,8 @@ NOTE: This error usually indicates an RPC conversion error between the node and let storage_metrics = req.storage_metrics.then_some(StorageMetrics { storage_size_bytes: 0 }); + let custom_metrics: Option> = None; + let server_time = unix_now(); let response = GetMetricsResponse { @@ -952,6 +966,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and bandwidth_metrics, consensus_metrics, storage_metrics, + custom_metrics, }; Ok(response) diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index 3d228fdcd8..ba45f0f80b 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -612,6 +612,7 @@ impl RpcApi for KaspaRpcClient { GetMempoolEntry, GetPeerAddresses, GetMetrics, + GetConnections, GetSink, GetSyncStatus, GetSubnetwork, diff --git a/rpc/wrpc/resolver/src/connection.rs b/rpc/wrpc/resolver/src/connection.rs index a0f27ab9e9..88528bc810 100644 --- a/rpc/wrpc/resolver/src/connection.rs +++ b/rpc/wrpc/resolver/src/connection.rs @@ -187,7 +187,7 @@ impl Connection { self.is_synced.store(is_synced, Ordering::Relaxed); if is_synced { - match self.client.get_metrics(false, true, false, false, false).await { + match self.client.get_metrics(false, true, false, false, false, false).await { Ok(metrics) => { if let Some(connection_metrics) = metrics.connection_metrics { // update diff --git a/rpc/wrpc/server/src/router.rs b/rpc/wrpc/server/src/router.rs index f734caf03c..c170e16ddf 100644 --- a/rpc/wrpc/server/src/router.rs +++ b/rpc/wrpc/server/src/router.rs @@ -57,6 +57,7 @@ impl Router { GetMempoolEntry, GetPeerAddresses, GetMetrics, + GetConnections, GetSink, GetSubnetwork, GetSyncStatus, diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index 35cabd8d84..a2940d4c68 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -957,6 +957,8 @@ build_wrpc_wasm_bindgen_interface!( /// performance and status of the Kaspa node. /// Returned information: Memory usage, CPU usage, network activity. GetMetrics, + /// Retrieves current number of network connections + GetConnections, /// Retrieves the current sink block, which is the block with /// the highest cumulative difficulty in the Kaspa BlockDAG. /// Returned information: Sink block hash, sink block height. diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs index a36acfba99..992511bc4b 100644 --- a/testing/integration/src/rpc_tests.rs +++ b/testing/integration/src/rpc_tests.rs @@ -467,6 +467,13 @@ async fn sanity_test() { }) } + KaspadPayloadOps::GetConnections => { + let rpc_client = client.clone(); + tst!(op, { + let _ = rpc_client.get_connections_call(None, GetConnectionsRequest {}).await.unwrap(); + }) + } + KaspadPayloadOps::GetMetrics => { let rpc_client = client.clone(); tst!(op, { @@ -479,6 +486,7 @@ async fn sanity_test() { bandwidth_metrics: true, process_metrics: true, storage_metrics: true, + custom_metrics: true, }, ) .await @@ -495,6 +503,7 @@ async fn sanity_test() { bandwidth_metrics: true, process_metrics: true, storage_metrics: true, + custom_metrics: true, }, ) .await @@ -511,6 +520,7 @@ async fn sanity_test() { bandwidth_metrics: false, process_metrics: false, storage_metrics: false, + custom_metrics: true, }, ) .await @@ -527,6 +537,7 @@ async fn sanity_test() { bandwidth_metrics: false, process_metrics: false, storage_metrics: false, + custom_metrics: true, }, ) .await diff --git a/wallet/core/src/tests/rpc_core_mock.rs b/wallet/core/src/tests/rpc_core_mock.rs index 3143fc5461..ecc01300e9 100644 --- a/wallet/core/src/tests/rpc_core_mock.rs +++ b/wallet/core/src/tests/rpc_core_mock.rs @@ -107,6 +107,14 @@ impl RpcApi for RpcCoreMock { Err(RpcError::NotImplemented) } + async fn get_connections_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetConnectionsRequest, + ) -> RpcResult { + Err(RpcError::NotImplemented) + } + async fn get_server_info_call( &self, _connection: Option<&DynRpcConnection>, From b4b7259dc7de402b1f9fc607524d80bbac6c8141 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 7 Jul 2024 11:06:52 +0300 Subject: [PATCH 068/158] change get_connections() to return the actual value instead of Response struct. --- rpc/core/src/api/rpc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index ab74605de5..a109a143a4 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -29,8 +29,8 @@ pub trait RpcApi: Sync + Send + AnySync { // --- - async fn get_connections(&self) -> RpcResult { - self.get_connections_call(None, GetConnectionsRequest {}).await + async fn get_connections(&self) -> RpcResult { + Ok(self.get_connections_call(None, GetConnectionsRequest {}).await?.active_connections) } async fn get_connections_call( &self, From 6e2e0e83351af094c89773a254a7cef467863030 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 7 Jul 2024 22:58:30 +0300 Subject: [PATCH 069/158] priorityEntries implementation for tx generator --- wallet/core/src/account/mod.rs | 1 + wallet/core/src/tx/generator/generator.rs | 32 +++++++++++++++++-- wallet/core/src/tx/generator/settings.rs | 7 ++++ wallet/core/src/tx/generator/test.rs | 2 ++ .../core/src/wasm/tx/generator/generator.rs | 18 ++++++++++- 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/wallet/core/src/account/mod.rs b/wallet/core/src/account/mod.rs index b921bf4914..56abdc787d 100644 --- a/wallet/core/src/account/mod.rs +++ b/wallet/core/src/account/mod.rs @@ -524,6 +524,7 @@ pub trait DerivationCapableAccount: Account { let settings = GeneratorSettings::try_new_with_iterator( self.wallet().network_id()?, Box::new(utxos.into_iter()), + None, change_address.clone(), 1, 1, diff --git a/wallet/core/src/tx/generator/generator.rs b/wallet/core/src/tx/generator/generator.rs index f56af9ffdf..eae6346511 100644 --- a/wallet/core/src/tx/generator/generator.rs +++ b/wallet/core/src/tx/generator/generator.rs @@ -87,6 +87,11 @@ const TRANSACTION_MASS_BOUNDARY_FOR_STAGE_INPUT_ACCUMULATION: u64 = MAXIMUM_STAN struct Context { /// iterator containing UTXO entries available for transaction generation utxo_source_iterator: Box + Send + Sync + 'static>, + /// List of priority UTXO entries, that are consumed before polling the iterator + priority_utxo_entries: Option>, + /// HashSet containing priority UTXO entries, used for filtering + /// for potential duplicates from the iterator + priority_utxo_entry_filter: Option>, /// total number of UTXOs consumed by the single generator instance aggregated_utxos: usize, /// total fees of all transactions issued by @@ -339,6 +344,7 @@ impl Generator { multiplexer, utxo_iterator, source_utxo_context: utxo_context, + priority_utxo_entries, sig_op_count, minimum_signatures, change_address, @@ -414,8 +420,14 @@ impl Generator { return Err(Error::GeneratorTransactionOutputsAreTooHeavy { mass: mass_sanity_check, kind: "compute mass" }); } + let priority_utxo_entry_filter = priority_utxo_entries.as_ref().map(|entries| entries.iter().cloned().collect()); + // remap to VecDeque as this list gets drained + let priority_utxo_entries = priority_utxo_entries.map(|entries| entries.into_iter().collect::>()); + let context = Mutex::new(Context { utxo_source_iterator: utxo_iterator, + priority_utxo_entries, + priority_utxo_entry_filter, number_of_transactions: 0, aggregated_utxos: 0, aggregate_fees: 0, @@ -527,15 +539,29 @@ impl Generator { } /// Get next UTXO entry. This function obtains UTXO in the following order: - /// 1. From the UTXO stash (used to store UTxOs that were not used in the previous transaction) + /// 1. From the UTXO stash (used to store UTxOs that were consumed during previous transaction generation but were rejected due to various conditions, such as mass overflow) /// 2. From the current stage - /// 3. From the UTXO source iterator + /// 3. From priority UTXO entries + /// 4. From the UTXO source iterator (while filtering against priority UTXO entries) fn get_utxo_entry(&self, context: &mut Context, stage: &mut Stage) -> Option { context .utxo_stash .pop_front() .or_else(|| stage.utxo_iterator.as_mut().and_then(|utxo_stage_iterator| utxo_stage_iterator.next())) - .or_else(|| context.utxo_source_iterator.next()) + .or_else(|| context.priority_utxo_entries.as_mut().and_then(|entries| entries.pop_front())) + .or_else(|| loop { + let utxo_entry = context.utxo_source_iterator.next()?; + + if let Some(filter) = context.priority_utxo_entry_filter.as_ref() { + if filter.contains(&utxo_entry) { + // skip the entry from the iterator intake + // if it has been supplied as a priority entry + continue; + } + } + + break Some(utxo_entry); + }) } /// Calculate relay transaction mass for the current transaction `data` diff --git a/wallet/core/src/tx/generator/settings.rs b/wallet/core/src/tx/generator/settings.rs index 0055d8fb4f..34fd1bb6ef 100644 --- a/wallet/core/src/tx/generator/settings.rs +++ b/wallet/core/src/tx/generator/settings.rs @@ -20,6 +20,8 @@ pub struct GeneratorSettings { pub utxo_iterator: Box + Send + Sync + 'static>, // Utxo Context pub source_utxo_context: Option, + // Priority utxo entries that are consumed before others + pub priority_utxo_entries: Option>, // typically a number of keys required to sign the transaction pub sig_op_count: u8, // number of minimum signatures required to sign the transaction @@ -77,6 +79,7 @@ impl GeneratorSettings { change_address, utxo_iterator: Box::new(utxo_iterator), source_utxo_context: Some(account.utxo_context().clone()), + priority_utxo_entries: None, final_transaction_priority_fee: final_priority_fee, final_transaction_destination, @@ -89,6 +92,7 @@ impl GeneratorSettings { pub fn try_new_with_context( utxo_context: UtxoContext, + priority_utxo_entries: Option>, change_address: Address, sig_op_count: u8, minimum_signatures: u16, @@ -108,6 +112,7 @@ impl GeneratorSettings { change_address, utxo_iterator: Box::new(utxo_iterator), source_utxo_context: Some(utxo_context), + priority_utxo_entries, final_transaction_priority_fee: final_priority_fee, final_transaction_destination, @@ -121,6 +126,7 @@ impl GeneratorSettings { pub fn try_new_with_iterator( network_id: NetworkId, utxo_iterator: Box + Send + Sync + 'static>, + priority_utxo_entries: Option>, change_address: Address, sig_op_count: u8, minimum_signatures: u16, @@ -137,6 +143,7 @@ impl GeneratorSettings { change_address, utxo_iterator: Box::new(utxo_iterator), source_utxo_context: None, + priority_utxo_entries, final_transaction_priority_fee: final_priority_fee, final_transaction_destination, diff --git a/wallet/core/src/tx/generator/test.rs b/wallet/core/src/tx/generator/test.rs index 4cce76ea11..d7f5e7f839 100644 --- a/wallet/core/src/tx/generator/test.rs +++ b/wallet/core/src/tx/generator/test.rs @@ -392,6 +392,7 @@ where let sig_op_count = 1; let minimum_signatures = 1; let utxo_iterator: Box + Send + Sync + 'static> = Box::new(utxo_entries.into_iter()); + let priority_utxo_entries = None; let source_utxo_context = None; let destination_utxo_context = None; let final_priority_fee = fees; @@ -406,6 +407,7 @@ where change_address, utxo_iterator, source_utxo_context, + priority_utxo_entries, destination_utxo_context, final_transaction_priority_fee: final_priority_fee, final_transaction_destination, diff --git a/wallet/core/src/wasm/tx/generator/generator.rs b/wallet/core/src/wasm/tx/generator/generator.rs index 5303b4d3e6..3c7830f9bf 100644 --- a/wallet/core/src/wasm/tx/generator/generator.rs +++ b/wallet/core/src/wasm/tx/generator/generator.rs @@ -64,6 +64,15 @@ interface IGeneratorSettingsObject { * interface, or a {@link UtxoContext} instance. */ entries: IUtxoEntry[] | UtxoEntryReference[] | UtxoContext; + /** + * Optional UTXO entries that will be consumed before those available in `entries`. + * You can use this property to apply custom input selection logic. + * Please note that these inputs are consumed first, then `entries` are consumed + * to generate a desirable transaction output amount. If transaction mass + * overflows, these inputs will be consumed into a batch/sweep transaction + * where the destination if the `changeAddress`. + */ + priorityEntries: IUtxoEntry[], /** * Optional number of signature operations in the transaction. */ @@ -147,6 +156,7 @@ impl Generator { let GeneratorSettings { network_id, source, + priority_utxo_entries, multiplexer, final_transaction_destination, change_address, @@ -167,6 +177,7 @@ impl Generator { native::GeneratorSettings::try_new_with_iterator( network_id, Box::new(utxo_entries.into_iter()), + priority_utxo_entries, change_address, sig_op_count, minimum_signatures, @@ -182,6 +193,7 @@ impl Generator { native::GeneratorSettings::try_new_with_context( utxo_context.into(), + priority_utxo_entries, change_address, sig_op_count, minimum_signatures, @@ -244,6 +256,7 @@ enum GeneratorSource { struct GeneratorSettings { pub network_id: Option, pub source: GeneratorSource, + pub priority_utxo_entries: Option>, pub multiplexer: Option>>, pub final_transaction_destination: PaymentDestination, pub change_address: Option
, @@ -272,9 +285,11 @@ impl TryFrom for GeneratorSettings { } else if let Some(utxo_entries) = args.try_get_value("entries")? { GeneratorSource::UtxoEntries(utxo_entries.try_into_utxo_entry_references()?) } else { - return Err(Error::custom("'entries', 'context' or 'account' property is required for Generator")); + return Err(Error::custom("'entries' property is required for Generator")); }; + let priority_utxo_entries = args.try_get_value("priorityEntries")?.map(|v| v.try_into_utxo_entry_references()).transpose()?; + let sig_op_count = args.get_value("sigOpCount")?; let sig_op_count = if !sig_op_count.is_undefined() { sig_op_count.as_f64().expect("sigOpCount should be a number") as u8 } else { 1 }; @@ -291,6 +306,7 @@ impl TryFrom for GeneratorSettings { let settings = GeneratorSettings { network_id, source: generator_source, + priority_utxo_entries, multiplexer: None, final_transaction_destination, change_address, From 02ac716b3f73d7bb0c8a6930306629b0579ea532 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 8 Jul 2024 01:26:01 +0300 Subject: [PATCH 070/158] fix transaction WASM interface types affecting some function returns --- consensus/client/src/input.rs | 6 +++--- consensus/client/src/transaction.rs | 14 +++++++++----- wallet/core/src/wasm/tx/generator/pending.rs | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/consensus/client/src/input.rs b/consensus/client/src/input.rs index 8b48c2d942..e5a01ce502 100644 --- a/consensus/client/src/input.rs +++ b/consensus/client/src/input.rs @@ -33,8 +33,8 @@ export interface ITransactionInputVerboseData { } #[wasm_bindgen] extern "C" { - #[wasm_bindgen(typescript_type = "ITransactionInput")] - pub type ITransactionInput; + #[wasm_bindgen(typescript_type = "ITransactionInput | TransactionInput")] + pub type TransactionInputT; } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -99,7 +99,7 @@ impl TransactionInput { #[wasm_bindgen] impl TransactionInput { #[wasm_bindgen(constructor)] - pub fn constructor(value: &ITransactionInput) -> Result { + pub fn constructor(value: &TransactionInputT) -> Result { Self::try_owned_from(value) } diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index ef4cdc7919..909e68cc31 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -51,8 +51,12 @@ export interface ITransactionVerboseData { #[wasm_bindgen] extern "C" { - #[wasm_bindgen(typescript_type = "ITransaction")] - pub type ITransaction; + #[wasm_bindgen(typescript_type = "ITransaction | Transaction")] + pub type TransactionT; + #[wasm_bindgen(typescript_type = "(ITransaction | Transaction)[]")] + pub type TransactionArrayAsArgT; + #[wasm_bindgen(typescript_type = "Transaction[]")] + pub type TransactionArrayAsResultT; } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -149,14 +153,14 @@ impl Transaction { } #[wasm_bindgen(constructor)] - pub fn constructor(js_value: &ITransaction) -> std::result::Result { + pub fn constructor(js_value: &TransactionT) -> std::result::Result { Ok(js_value.try_into_owned()?) } #[wasm_bindgen(getter = inputs)] - pub fn get_inputs_as_js_array(&self) -> Array { + pub fn get_inputs_as_js_array(&self) -> TransactionArrayAsResultT { let inputs = self.inner.lock().unwrap().inputs.clone().into_iter().map(JsValue::from); - Array::from_iter(inputs) + Array::from_iter(inputs).unchecked_into() } /// Returns a list of unique addresses used by transaction inputs. diff --git a/wallet/core/src/wasm/tx/generator/pending.rs b/wallet/core/src/wasm/tx/generator/pending.rs index dfa84c9bd7..8de697e557 100644 --- a/wallet/core/src/wasm/tx/generator/pending.rs +++ b/wallet/core/src/wasm/tx/generator/pending.rs @@ -3,7 +3,7 @@ use crate::result::Result; use crate::tx::generator as native; use crate::wasm::PrivateKeyArrayT; use kaspa_consensus_client::{numeric, string}; -use kaspa_consensus_client::{ITransaction, Transaction}; +use kaspa_consensus_client::{Transaction, TransactionT}; use kaspa_wallet_keys::privatekey::PrivateKey; use kaspa_wrpc_wasm::RpcClient; @@ -110,7 +110,7 @@ impl PendingTransaction { /// @see {@link ISerializableTransaction} /// @see {@link Transaction}, {@link ISerializableTransaction} #[wasm_bindgen(js_name = "serializeToObject")] - pub fn serialize_to_object(&self) -> Result { + pub fn serialize_to_object(&self) -> Result { Ok(numeric::SerializableTransaction::from_cctx_transaction(&self.inner.transaction(), self.inner.utxo_entries())? .serialize_to_object()? .into()) From a392c135829dc73577d4f135e711acadb18c3c93 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 8 Jul 2024 01:49:09 +0300 Subject: [PATCH 071/158] input and output types on transactions (WASM) --- consensus/client/src/input.rs | 4 ++++ consensus/client/src/output.rs | 10 ++++++++++ consensus/client/src/transaction.rs | 18 +++++++----------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/consensus/client/src/input.rs b/consensus/client/src/input.rs index e5a01ce502..d25e5f7d95 100644 --- a/consensus/client/src/input.rs +++ b/consensus/client/src/input.rs @@ -35,6 +35,10 @@ export interface ITransactionInputVerboseData { } extern "C" { #[wasm_bindgen(typescript_type = "ITransactionInput | TransactionInput")] pub type TransactionInputT; + #[wasm_bindgen(typescript_type = "(ITransactionInput | TransactionInput)[]")] + pub type TransactionInputArrayAsArgT; + #[wasm_bindgen(typescript_type = "TransactionInput[]")] + pub type TransactionInputArrayAsResultT; } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/consensus/client/src/output.rs b/consensus/client/src/output.rs index 99fe38ec3a..4dddb5067d 100644 --- a/consensus/client/src/output.rs +++ b/consensus/client/src/output.rs @@ -26,6 +26,16 @@ export interface ITransactionOutputVerboseData { } "#; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "ITransactionOutput | TransactionOutput")] + pub type TransactionOutputT; + #[wasm_bindgen(typescript_type = "(ITransactionOutput | TransactionOutput)[]")] + pub type TransactionOutputArrayAsArgT; + #[wasm_bindgen(typescript_type = "TransactionOutput[]")] + pub type TransactionOutputArrayAsResultT; +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionOutputInner { diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index 909e68cc31..d03e6a5128 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -1,9 +1,9 @@ #![allow(non_snake_case)] use crate::imports::*; -use crate::input::TransactionInput; +use crate::input::{TransactionInput, TransactionInputArrayAsArgT, TransactionInputArrayAsResultT}; use crate::outpoint::TransactionOutpoint; -use crate::output::TransactionOutput; +use crate::output::{TransactionOutput, TransactionOutputArrayAsArgT, TransactionOutputArrayAsResultT}; use crate::result::Result; use crate::serializable::{numeric, string, SerializableTransactionT}; use crate::utxo::{UtxoEntryId, UtxoEntryReference}; @@ -53,10 +53,6 @@ export interface ITransactionVerboseData { extern "C" { #[wasm_bindgen(typescript_type = "ITransaction | Transaction")] pub type TransactionT; - #[wasm_bindgen(typescript_type = "(ITransaction | Transaction)[]")] - pub type TransactionArrayAsArgT; - #[wasm_bindgen(typescript_type = "Transaction[]")] - pub type TransactionArrayAsResultT; } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -158,7 +154,7 @@ impl Transaction { } #[wasm_bindgen(getter = inputs)] - pub fn get_inputs_as_js_array(&self) -> TransactionArrayAsResultT { + pub fn get_inputs_as_js_array(&self) -> TransactionInputArrayAsResultT { let inputs = self.inner.lock().unwrap().inputs.clone().into_iter().map(JsValue::from); Array::from_iter(inputs).unchecked_into() } @@ -183,7 +179,7 @@ impl Transaction { } #[wasm_bindgen(setter = inputs)] - pub fn set_inputs_from_js_array(&mut self, js_value: &JsValue) { + pub fn set_inputs_from_js_array(&mut self, js_value: &TransactionInputArrayAsArgT) { let inputs = Array::from(js_value) .iter() .map(|js_value| { @@ -194,13 +190,13 @@ impl Transaction { } #[wasm_bindgen(getter = outputs)] - pub fn get_outputs_as_js_array(&self) -> Array { + pub fn get_outputs_as_js_array(&self) -> TransactionOutputArrayAsResultT { let outputs = self.inner.lock().unwrap().outputs.clone().into_iter().map(JsValue::from); - Array::from_iter(outputs) + Array::from_iter(outputs).unchecked_into() } #[wasm_bindgen(setter = outputs)] - pub fn set_outputs_from_js_array(&mut self, js_value: &JsValue) { + pub fn set_outputs_from_js_array(&mut self, js_value: &TransactionOutputArrayAsArgT) { let outputs = Array::from(js_value) .iter() .map(|js_value| TransactionOutput::try_from(&js_value).unwrap_or_else(|err| panic!("invalid transaction output: {err}"))) From 43a6f07c80a1ce5797d8e9afe1f3efb13ac23eab Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 8 Jul 2024 02:13:04 +0300 Subject: [PATCH 072/158] rpc caps --- rpc/core/src/model/message.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index ba41acbd7d..a2ea78a430 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -2190,6 +2190,19 @@ impl Deserializer for GetMetricsResponse { } } +#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[serde(rename_all = "camelCase")] +#[borsh(use_discriminant = true)] +pub enum RpcCaps { + Full = 0, + Blocks, + UtxoIndex, + Mempool, + Metrics, + Visualizer, + Mining, +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetServerInfoRequest {} From 33d021eb9f8263f47162a4aa5e42959d77ac34a7 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 9 Jul 2024 04:49:38 +0300 Subject: [PATCH 073/158] update client resolver resolution --- rpc/wrpc/client/Resolvers.toml | 8 ++++++-- rpc/wrpc/client/src/resolver.rs | 22 +++++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/rpc/wrpc/client/Resolvers.toml b/rpc/wrpc/client/Resolvers.toml index 295a352d2f..8879512efe 100644 --- a/rpc/wrpc/client/Resolvers.toml +++ b/rpc/wrpc/client/Resolvers.toml @@ -1,9 +1,13 @@ [[resolver]] -url = "https://beacon.kaspa-ng.org" +url = "https://*.kaspa-ng.org" enable = true [[resolver]] -url = "https://beacon.kaspa-ng.io" +url = "https://*.kaspa-ng.io" +enable = true + +[[resolver]] +url = "https://*.kaspa-ng.net" enable = true [[resolver]] diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index f7d4db604c..7210c5b34e 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -10,6 +10,7 @@ use workflow_core::runtime; use workflow_http::get_json; const DEFAULT_VERSION: usize = 2; +const RESOLVERS_PER_FQDN: usize = 16; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResolverRecord { @@ -22,14 +23,29 @@ pub struct ResolverConfig { resolver: Vec, } -fn try_parse_resolvers(toml: &str) -> Result>> { +fn try_parse_resolvers(toml: &str) -> Result> { Ok(toml::from_str::(toml)? .resolver .into_iter() - .filter_map(|resolver| resolver.enable.unwrap_or(true).then_some(Arc::new(resolver.url))) + .filter_map(|resolver| resolver.enable.unwrap_or(true).then_some(resolver.url)) .collect::>()) } +fn transform(source_list: Vec) -> Vec> { + let mut resolvers = vec![]; + for url in source_list.into_iter() { + if url.contains('*') { + for n in 0..RESOLVERS_PER_FQDN { + resolvers.push(Arc::new(url.replace('*', &format!("r{n}")))); + } + } else { + resolvers.push(Arc::new(url)); + } + } + + resolvers +} + #[derive(Debug)] struct Inner { pub urls: Vec>, @@ -53,7 +69,7 @@ impl Default for Resolver { fn default() -> Self { let toml = include_str!("../Resolvers.toml"); let urls = try_parse_resolvers(toml).expect("TOML: Unable to parse RPC Resolver list"); - Self { inner: Arc::new(Inner::new(urls)) } + Self { inner: Arc::new(Inner::new(transform(urls))) } } } From 698a38e70259a20a5d86cd691c9ff732ec0c8c4b Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 9 Jul 2024 23:24:01 +0300 Subject: [PATCH 074/158] GetSystemInfo RPC call --- Cargo.lock | 36 +++++++++++++ Cargo.toml | 1 + cli/src/modules/rpc.rs | 4 ++ rpc/core/src/api/ops.rs | 2 + rpc/core/src/api/rpc.rs | 11 ++++ rpc/core/src/model/message.rs | 50 ++++++++++++++++++- rpc/core/src/model/tests.rs | 16 ++++++ rpc/grpc/client/src/lib.rs | 1 + rpc/grpc/core/proto/messages.proto | 2 + rpc/grpc/core/proto/rpc.proto | 10 ++++ rpc/grpc/core/src/convert/kaspad.rs | 2 + rpc/grpc/core/src/convert/message.rs | 19 +++++++ rpc/grpc/core/src/ops.rs | 1 + .../server/src/request_handler/factory.rs | 1 + rpc/grpc/server/src/tests/rpc_core_mock.rs | 8 +++ rpc/service/src/service.rs | 20 +++++++- rpc/wrpc/client/src/client.rs | 1 + rpc/wrpc/client/src/resolver.rs | 2 +- testing/integration/src/rpc_tests.rs | 7 +++ utils/Cargo.toml | 6 ++- utils/src/lib.rs | 1 + utils/src/sysinfo.rs | 20 ++++++++ wallet/core/src/tests/rpc_core_mock.rs | 8 +++ 23 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 utils/src/sysinfo.rs diff --git a/Cargo.lock b/Cargo.lock index 1b8ffe3ff6..7dc2c2a25d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3415,12 +3415,14 @@ dependencies = [ "ipnet", "itertools 0.11.0", "log", + "num_cpus", "parking_lot", "rand 0.8.5", "rlimit", "serde", "serde_json", "smallvec", + "sysinfo", "thiserror", "tokio", "triggered", @@ -4339,6 +4341,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -5842,6 +5853,21 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if 1.0.0", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -6706,6 +6732,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.5", +] + [[package]] name = "windows-core" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 73bbcb6270..a2f37dafb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -242,6 +242,7 @@ sorted-insert = "0.2.3" statest = "0.2.2" statrs = "0.13.0" # TODO "0.16.0" subtle = { version = "2.5.0", default-features = false } +sysinfo = "0.30.12" tempfile = "3.8.1" textwrap = "0.16.0" thiserror = "1.0.50" diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index 6064e6124d..da11746c88 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -41,6 +41,10 @@ impl Rpc { let result = rpc.get_metrics(true, true, true, true, true, true).await?; self.println(&ctx, result); } + RpcApiOps::GetSystemInfo => { + let result = rpc.get_system_info().await?; + self.println(&ctx, result); + } RpcApiOps::GetConnections => { let result = rpc.get_connections().await?; self.println(&ctx, result); diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index 03422c8cbf..9b36c3b2dd 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -56,6 +56,8 @@ pub enum RpcApiOps { Ping = 110, /// Get metrics for consensus information and node performance GetMetrics, + /// Get system information (RAM available, number of cores, available file descriptors) + GetSystemInfo, /// Get current number of active TCP connections GetConnections, /// Get state information on the node diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index a109a143a4..8857e1a62b 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -29,6 +29,17 @@ pub trait RpcApi: Sync + Send + AnySync { // --- + async fn get_system_info(&self) -> RpcResult { + Ok(self.get_system_info_call(None, GetSystemInfoRequest {}).await?) + } + async fn get_system_info_call( + &self, + connection: Option<&DynRpcConnection>, + request: GetSystemInfoRequest, + ) -> RpcResult; + + // --- + async fn get_connections(&self) -> RpcResult { Ok(self.get_connections_call(None, GetConnectionsRequest {}).await?.active_connections) } diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 9f98016d8b..9745a37392 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1783,7 +1783,55 @@ impl Deserializer for GetConnectionsResponse { } } -// TODO - custom wRPC commands (need review and implementation in gRPC) +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetSystemInfoRequest {} + +impl Serializer for GetSystemInfoRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + + Ok(()) + } +} + +impl Deserializer for GetSystemInfoRequest { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + + Ok(Self {}) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetSystemInfoResponse { + pub cpu_physical_cores: u16, + pub total_memory: u64, + pub fd_limit: u32, +} + +impl Serializer for GetSystemInfoResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + store!(u16, &self.cpu_physical_cores, writer)?; + store!(u64, &self.total_memory, writer)?; + store!(u32, &self.fd_limit, writer)?; + + Ok(()) + } +} + +impl Deserializer for GetSystemInfoResponse { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + let cpu_physical_cores = load!(u16, reader)?; + let total_memory = load!(u64, reader)?; + let fd_limit = load!(u32, reader)?; + + Ok(Self { cpu_physical_cores, total_memory, fd_limit }) + } +} #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index c4790dcbea..fba3e7a71d 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -922,6 +922,22 @@ mod mockery { test!(GetConnectionsResponse); + impl Mock for GetSystemInfoRequest { + fn mock() -> Self { + GetSystemInfoRequest {} + } + } + + test!(GetSystemInfoRequest); + + impl Mock for GetSystemInfoResponse { + fn mock() -> Self { + GetSystemInfoResponse { cpu_physical_cores: mock(), total_memory: mock(), fd_limit: mock() } + } + } + + test!(GetSystemInfoResponse); + impl Mock for GetMetricsRequest { fn mock() -> Self { GetMetricsRequest { diff --git a/rpc/grpc/client/src/lib.rs b/rpc/grpc/client/src/lib.rs index cd86a7ae0a..882f2a039b 100644 --- a/rpc/grpc/client/src/lib.rs +++ b/rpc/grpc/client/src/lib.rs @@ -242,6 +242,7 @@ impl RpcApi for GrpcClient { route!(get_server_info_call, GetServerInfo); route!(get_metrics_call, GetMetrics); route!(get_connections_call, GetConnections); + route!(get_system_info_call, GetSystemInfo); route!(submit_block_call, SubmitBlock); route!(get_block_template_call, GetBlockTemplate); route!(get_block_call, GetBlock); diff --git a/rpc/grpc/core/proto/messages.proto b/rpc/grpc/core/proto/messages.proto index 063e6904f8..1919c71ece 100644 --- a/rpc/grpc/core/proto/messages.proto +++ b/rpc/grpc/core/proto/messages.proto @@ -60,6 +60,7 @@ message KaspadRequest { GetSyncStatusRequestMessage getSyncStatusRequest = 1094; GetDaaScoreTimestampEstimateRequestMessage GetDaaScoreTimestampEstimateRequest = 1096; GetConnectionsRequestMessage getConnectionsRequest = 1098; + GetSystemInfoRequestMessage getSystemInfoRequest = 1100; } } @@ -120,6 +121,7 @@ message KaspadResponse { GetSyncStatusResponseMessage getSyncStatusResponse = 1095; GetDaaScoreTimestampEstimateResponseMessage GetDaaScoreTimestampEstimateResponse = 1097; GetConnectionsResponseMessage getConnectionsResponse= 1099; + GetSystemInfoResponseMessage getSystemInfoResponse= 1101; } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index 9751e28dc9..512ad3b5b7 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -818,6 +818,16 @@ message GetConnectionsResponseMessage{ RPCError error = 1000; } +message GetSystemInfoRequestMessage{ +} + +message GetSystemInfoResponseMessage{ + uint32 coreNum = 1; + uint64 totalMemory = 2; + uint32 fdLimit = 3; + RPCError error = 1000; +} + message GetMetricsRequestMessage{ bool processMetrics = 1; bool connectionMetrics = 2; diff --git a/rpc/grpc/core/src/convert/kaspad.rs b/rpc/grpc/core/src/convert/kaspad.rs index 1a4028f0b2..67b37d47f7 100644 --- a/rpc/grpc/core/src/convert/kaspad.rs +++ b/rpc/grpc/core/src/convert/kaspad.rs @@ -55,6 +55,7 @@ pub mod kaspad_request_convert { impl_into_kaspad_request!(Ping); impl_into_kaspad_request!(GetMetrics); impl_into_kaspad_request!(GetConnections); + impl_into_kaspad_request!(GetSystemInfo); impl_into_kaspad_request!(GetServerInfo); impl_into_kaspad_request!(GetSyncStatus); impl_into_kaspad_request!(GetDaaScoreTimestampEstimate); @@ -187,6 +188,7 @@ pub mod kaspad_response_convert { impl_into_kaspad_response!(Ping); impl_into_kaspad_response!(GetMetrics); impl_into_kaspad_response!(GetConnections); + impl_into_kaspad_response!(GetSystemInfo); impl_into_kaspad_response!(GetServerInfo); impl_into_kaspad_response!(GetSyncStatus); impl_into_kaspad_response!(GetDaaScoreTimestampEstimate); diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index 8a3f0cd2e3..e703fa723c 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -432,6 +432,16 @@ from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetCo } }); +from!(&kaspa_rpc_core::GetSystemInfoRequest, protowire::GetSystemInfoRequestMessage); +from!(item: RpcResult<&kaspa_rpc_core::GetSystemInfoResponse>, protowire::GetSystemInfoResponseMessage, { + Self { + total_memory : item.total_memory, + core_num : item.cpu_physical_cores as u32, + fd_limit : item.fd_limit, + error: None, + } +}); + from!(&kaspa_rpc_core::GetServerInfoRequest, protowire::GetServerInfoRequestMessage); from!(item: RpcResult<&kaspa_rpc_core::GetServerInfoResponse>, protowire::GetServerInfoResponseMessage, { Self { @@ -844,6 +854,15 @@ try_from!(item: &protowire::GetConnectionsResponseMessage, RpcResult, { + Self { + total_memory: item.total_memory, + cpu_physical_cores: item.core_num as u16, + fd_limit: item.fd_limit, + } +}); + try_from!(&protowire::GetServerInfoRequestMessage, kaspa_rpc_core::GetServerInfoRequest); try_from!(item: &protowire::GetServerInfoResponseMessage, RpcResult, { Self { diff --git a/rpc/grpc/core/src/ops.rs b/rpc/grpc/core/src/ops.rs index 5a16a1f29b..fc07988f33 100644 --- a/rpc/grpc/core/src/ops.rs +++ b/rpc/grpc/core/src/ops.rs @@ -79,6 +79,7 @@ pub enum KaspadPayloadOps { Ping, GetMetrics, GetConnections, + GetSystemInfo, GetServerInfo, GetSyncStatus, GetDaaScoreTimestampEstimate, diff --git a/rpc/grpc/server/src/request_handler/factory.rs b/rpc/grpc/server/src/request_handler/factory.rs index 49a4b86b47..722ad19e97 100644 --- a/rpc/grpc/server/src/request_handler/factory.rs +++ b/rpc/grpc/server/src/request_handler/factory.rs @@ -73,6 +73,7 @@ impl Factory { Ping, GetMetrics, GetConnections, + GetSystemInfo, GetServerInfo, GetSyncStatus, GetDaaScoreTimestampEstimate, diff --git a/rpc/grpc/server/src/tests/rpc_core_mock.rs b/rpc/grpc/server/src/tests/rpc_core_mock.rs index e37c4ea189..4f3c2a5653 100644 --- a/rpc/grpc/server/src/tests/rpc_core_mock.rs +++ b/rpc/grpc/server/src/tests/rpc_core_mock.rs @@ -98,6 +98,14 @@ impl RpcApi for RpcCoreMock { Err(RpcError::NotImplemented) } + async fn get_system_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSystemInfoRequest, + ) -> RpcResult { + Err(RpcError::NotImplemented) + } + async fn get_server_info_call( &self, _connection: Option<&DynRpcConnection>, diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index b7f4f19d28..1c5d0cf444 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -62,13 +62,14 @@ use kaspa_rpc_core::{ Notification, RpcError, RpcResult, }; use kaspa_txscript::{extract_script_pub_key_address, pay_to_address_script}; +use kaspa_utils::sysinfo::SystemInfo; use kaspa_utils::{channel::Channel, triggers::SingleTrigger}; use kaspa_utils_tower::counters::TowerConnectionCounters; use kaspa_utxoindex::api::UtxoIndexProxy; use std::{ collections::HashMap, iter::once, - sync::{atomic::Ordering, Arc}, + sync::{atomic::Ordering, Arc, OnceLock}, vec, }; use tokio::join; @@ -972,6 +973,23 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(response) } + async fn get_system_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSystemInfoRequest, + ) -> RpcResult { + static SYSTEM_INFO: OnceLock = OnceLock::new(); + + let system_info = SYSTEM_INFO.get_or_init(SystemInfo::default); + + let SystemInfo { cpu_physical_cores, total_memory, fd_limit } = system_info; + + let response = + GetSystemInfoResponse { cpu_physical_cores: *cpu_physical_cores, total_memory: *total_memory, fd_limit: *fd_limit }; + + Ok(response) + } + async fn get_server_info_call( &self, _connection: Option<&DynRpcConnection>, diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index ba45f0f80b..d93f165807 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -604,6 +604,7 @@ impl RpcApi for KaspaRpcClient { GetConnectedPeerInfo, GetDaaScoreTimestampEstimate, GetServerInfo, + GetSystemInfo, GetCurrentNetwork, GetHeaders, GetInfo, diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index 7210c5b34e..4e40c09df0 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -10,7 +10,7 @@ use workflow_core::runtime; use workflow_http::get_json; const DEFAULT_VERSION: usize = 2; -const RESOLVERS_PER_FQDN: usize = 16; +const RESOLVERS_PER_FQDN: usize = 8; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResolverRecord { diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs index 992511bc4b..3db80e25ef 100644 --- a/testing/integration/src/rpc_tests.rs +++ b/testing/integration/src/rpc_tests.rs @@ -547,6 +547,13 @@ async fn sanity_test() { }) } + KaspadPayloadOps::GetSystemInfo => { + let rpc_client = client.clone(); + tst!(op, { + let _response = rpc_client.get_system_info_call(None, GetSystemInfoRequest {}).await.unwrap(); + }) + } + KaspadPayloadOps::GetServerInfo => { let rpc_client = client.clone(); tst!(op, { diff --git a/utils/Cargo.toml b/utils/Cargo.toml index a3002afabd..c686a2b227 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -10,7 +10,6 @@ license.workspace = true repository.workspace = true [dependencies] -parking_lot.workspace = true async-channel.workspace = true borsh.workspace = true cfg-if.workspace = true @@ -18,12 +17,15 @@ event-listener.workspace = true faster-hex.workspace = true ipnet.workspace = true itertools.workspace = true +log.workspace = true +num_cpus.workspace = true +parking_lot.workspace = true serde.workspace = true smallvec.workspace = true +sysinfo.workspace = true thiserror.workspace = true triggered.workspace = true uuid.workspace = true -log.workspace = true wasm-bindgen.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/utils/src/lib.rs b/utils/src/lib.rs index bd3143719e..2ca954b046 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -9,6 +9,7 @@ pub mod mem_size; pub mod networking; pub mod option; pub mod refs; +pub mod sysinfo; pub mod as_slice; diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs new file mode 100644 index 0000000000..a410b8f9f7 --- /dev/null +++ b/utils/src/sysinfo.rs @@ -0,0 +1,20 @@ +use crate::fd_budget; + +pub struct SystemInfo { + pub cpu_physical_cores: u16, + pub total_memory: u64, + pub fd_limit: u32, +} + +impl Default for SystemInfo { + fn default() -> Self { + let mut system = sysinfo::System::new(); + system.refresh_memory(); + let cpu_physical_cores = num_cpus::get() as u16; + let total_memory = system.total_memory(); + + let fd_limit = fd_budget::limit() as u32; + + SystemInfo { cpu_physical_cores, total_memory, fd_limit } + } +} diff --git a/wallet/core/src/tests/rpc_core_mock.rs b/wallet/core/src/tests/rpc_core_mock.rs index ecc01300e9..8f6d39b76f 100644 --- a/wallet/core/src/tests/rpc_core_mock.rs +++ b/wallet/core/src/tests/rpc_core_mock.rs @@ -123,6 +123,14 @@ impl RpcApi for RpcCoreMock { Err(RpcError::NotImplemented) } + async fn get_system_info_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: GetSystemInfoRequest, + ) -> RpcResult { + Err(RpcError::NotImplemented) + } + async fn get_sync_status_call( &self, _connection: Option<&DynRpcConnection>, From 1be100201b92b35d4b8504176dc5729ecab082fa Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 9 Jul 2024 23:58:49 +0300 Subject: [PATCH 075/158] make priorityEntries optional in the TS interface definition --- wallet/core/src/wasm/tx/generator/generator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/core/src/wasm/tx/generator/generator.rs b/wallet/core/src/wasm/tx/generator/generator.rs index 3c7830f9bf..a110174bd6 100644 --- a/wallet/core/src/wasm/tx/generator/generator.rs +++ b/wallet/core/src/wasm/tx/generator/generator.rs @@ -72,7 +72,7 @@ interface IGeneratorSettingsObject { * overflows, these inputs will be consumed into a batch/sweep transaction * where the destination if the `changeAddress`. */ - priorityEntries: IUtxoEntry[], + priorityEntries?: IUtxoEntry[], /** * Optional number of signature operations in the transaction. */ From 6361ce628b4f90607adeea316f66e2af3e216af5 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 10 Jul 2024 01:24:18 +0300 Subject: [PATCH 076/158] merge cli-resolver --- Cargo.lock | 42 +++++++++++++++++++++--------------------- Cargo.toml | 24 ++++++++++++------------ 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09a48a2e00..a16a82e90a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6948,7 +6948,7 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.13.3" +version = "0.14.0" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6961,7 +6961,7 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.13.3" +version = "0.14.0" dependencies = [ "async-channel 2.3.1", "async-std", @@ -6991,7 +6991,7 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.13.3" +version = "0.14.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7006,7 +7006,7 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.13.3" +version = "0.14.0" dependencies = [ "futures", "js-sys", @@ -7022,7 +7022,7 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.13.3" +version = "0.14.0" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -7036,7 +7036,7 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.13.3" +version = "0.14.0" dependencies = [ "cfg-if 1.0.0", "console", @@ -7050,7 +7050,7 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.13.3" +version = "0.14.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7061,7 +7061,7 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.13.3" +version = "0.14.0" dependencies = [ "borsh", "futures", @@ -7080,7 +7080,7 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.13.3" +version = "0.14.0" dependencies = [ "ahash", "async-trait", @@ -7102,7 +7102,7 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.13.3" +version = "0.14.0" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7125,7 +7125,7 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.13.3" +version = "0.14.0" dependencies = [ "ahash", "async-std", @@ -7153,7 +7153,7 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.13.3" +version = "0.14.0" dependencies = [ "parse-variants", "proc-macro-error", @@ -7164,7 +7164,7 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.13.3" +version = "0.14.0" dependencies = [ "ahash", "borsh", @@ -7173,7 +7173,7 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.13.3" +version = "0.14.0" dependencies = [ "async-std", "base64 0.22.1", @@ -7199,7 +7199,7 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.13.3" +version = "0.14.0" dependencies = [ "futures", "thiserror", @@ -7209,7 +7209,7 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.13.3" +version = "0.14.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7223,7 +7223,7 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.13.3" +version = "0.14.0" dependencies = [ "async-std", "async-trait", @@ -7250,7 +7250,7 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.13.3" +version = "0.14.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7264,7 +7264,7 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.13.3" +version = "0.14.0" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7283,7 +7283,7 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.13.3" +version = "0.14.0" dependencies = [ "js-sys", "proc-macro-error", @@ -7295,7 +7295,7 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.13.3" +version = "0.14.0" dependencies = [ "ahash", "async-channel 2.3.1", diff --git a/Cargo.toml b/Cargo.toml index 366d24e7f6..e3522b04bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -276,18 +276,18 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -# workflow-core = { version = "0.13.2" } -# workflow-d3 = { version = "0.13.2" } -# workflow-dom = { version = "0.13.2" } -# workflow-http = { version = "0.13.2" } -# workflow-log = { version = "0.13.2" } -# workflow-node = { version = "0.13.2" } -# workflow-nw = { version = "0.13.2" } -# workflow-rpc = { version = "0.13.2" } -# workflow-serializer = { version = "0.13.2" } -# workflow-store = { version = "0.13.2" } -# workflow-terminal = { version = "0.13.2" } -# workflow-wasm = { version = "0.13.2" } +# workflow-core = { version = "0.14.0" } +# workflow-d3 = { version = "0.14.0" } +# workflow-dom = { version = "0.14.0" } +# workflow-http = { version = "0.14.0" } +# workflow-log = { version = "0.14.0" } +# workflow-node = { version = "0.14.0" } +# workflow-nw = { version = "0.14.0" } +# workflow-rpc = { version = "0.14.0" } +# workflow-serializer = { version = "0.14.0" } +# workflow-store = { version = "0.14.0" } +# workflow-terminal = { version = "0.14.0" } +# workflow-wasm = { version = "0.14.0" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs From f6fd1dab79dda3bb508e3e8bf5390aa7845e63ca Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 10 Jul 2024 02:21:44 +0300 Subject: [PATCH 077/158] remove resolver crate from workspace (move to https://github.com/aspectron/kaspa-resolver) --- Cargo.lock | 185 +------------------- Cargo.toml | 2 - rpc/wrpc/resolver/Cargo.toml | 41 ----- rpc/wrpc/resolver/src/args.rs | 54 ------ rpc/wrpc/resolver/src/connection.rs | 262 ---------------------------- rpc/wrpc/resolver/src/error.rs | 53 ------ rpc/wrpc/resolver/src/imports.rs | 28 --- rpc/wrpc/resolver/src/log.rs | 44 ----- rpc/wrpc/resolver/src/main.rs | 41 ----- rpc/wrpc/resolver/src/monitor.rs | 241 ------------------------- rpc/wrpc/resolver/src/node.rs | 75 -------- rpc/wrpc/resolver/src/panic.rs | 10 -- rpc/wrpc/resolver/src/params.rs | 146 ---------------- rpc/wrpc/resolver/src/result.rs | 1 - rpc/wrpc/resolver/src/server.rs | 149 ---------------- rpc/wrpc/resolver/src/transport.rs | 8 - 16 files changed, 3 insertions(+), 1337 deletions(-) delete mode 100644 rpc/wrpc/resolver/Cargo.toml delete mode 100644 rpc/wrpc/resolver/src/args.rs delete mode 100644 rpc/wrpc/resolver/src/connection.rs delete mode 100644 rpc/wrpc/resolver/src/error.rs delete mode 100644 rpc/wrpc/resolver/src/imports.rs delete mode 100644 rpc/wrpc/resolver/src/log.rs delete mode 100644 rpc/wrpc/resolver/src/main.rs delete mode 100644 rpc/wrpc/resolver/src/monitor.rs delete mode 100644 rpc/wrpc/resolver/src/node.rs delete mode 100644 rpc/wrpc/resolver/src/panic.rs delete mode 100644 rpc/wrpc/resolver/src/params.rs delete mode 100644 rpc/wrpc/resolver/src/result.rs delete mode 100644 rpc/wrpc/resolver/src/server.rs delete mode 100644 rpc/wrpc/resolver/src/transport.rs diff --git a/Cargo.lock b/Cargo.lock index a16a82e90a..14021b0ac1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,7 +443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core 0.3.4", + "axum-core", "bitflags 1.3.2", "bytes", "futures-util", @@ -464,40 +464,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "axum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" -dependencies = [ - "async-trait", - "axum-core 0.4.3", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "hyper 1.4.1", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "axum-core" version = "0.3.4" @@ -515,27 +481,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "axum-core" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "backtrace" version = "0.3.73" @@ -2145,7 +2090,6 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "httparse", - "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -3192,36 +3136,6 @@ dependencies = [ "workflow-wasm", ] -[[package]] -name = "kaspa-resolver" -version = "0.14.1" -dependencies = [ - "ahash", - "axum 0.7.5", - "cfg-if 1.0.0", - "clap 4.5.9", - "console", - "convert_case 0.6.0", - "futures", - "kaspa-consensus-core", - "kaspa-rpc-core", - "kaspa-utils", - "kaspa-wrpc-client", - "mime", - "serde", - "serde_json", - "thiserror", - "tokio", - "toml", - "tower", - "tower-http 0.5.2", - "tracing-subscriber", - "workflow-core", - "workflow-http", - "workflow-log", - "xxhash-rust", -] - [[package]] name = "kaspa-rpc-core" version = "0.14.1" @@ -3446,7 +3360,7 @@ dependencies = [ "pin-project-lite", "tokio", "tower", - "tower-http 0.4.4", + "tower-http", ] [[package]] @@ -4356,16 +4270,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num" version = "0.4.3" @@ -4601,12 +4505,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "pad" version = "0.1.6" @@ -5519,16 +5417,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_path_to_error" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" -dependencies = [ - "itoa", - "serde", -] - [[package]] name = "serde_repr" version = "0.1.19" @@ -5636,15 +5524,6 @@ dependencies = [ "keccak", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shlex" version = "1.3.0" @@ -5983,16 +5862,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if 1.0.0", - "once_cell", -] - [[package]] name = "time" version = "0.3.36" @@ -6212,7 +6081,7 @@ checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ "async-stream", "async-trait", - "axum 0.6.20", + "axum", "base64 0.21.7", "bytes", "flate2", @@ -6286,22 +6155,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags 2.6.0", - "bytes", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "pin-project-lite", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.2" @@ -6344,32 +6197,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", ] [[package]] @@ -6532,12 +6359,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "value-bag" version = "1.9.0" diff --git a/Cargo.toml b/Cargo.toml index e3522b04bd..029ab951ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ members = [ "rpc/grpc/core", "rpc/grpc/client", "rpc/grpc/server", - "rpc/wrpc/resolver", "rpc/wrpc/server", "rpc/wrpc/client", "rpc/wrpc/proxy", @@ -84,7 +83,6 @@ include = [ kaspa-addresses = { version = "0.14.1", path = "crypto/addresses" } kaspa-addressmanager = { version = "0.14.1", path = "components/addressmanager" } kaspa-bip32 = { version = "0.14.1", path = "wallet/bip32" } -kaspa-resolver = { version = "0.14.1", path = "rpr/wrpc/resolver" } kaspa-cli = { version = "0.14.1", path = "cli" } kaspa-connectionmanager = { version = "0.14.1", path = "components/connectionmanager" } kaspa-consensus = { version = "0.14.1", path = "consensus" } diff --git a/rpc/wrpc/resolver/Cargo.toml b/rpc/wrpc/resolver/Cargo.toml deleted file mode 100644 index cb28d82bf8..0000000000 --- a/rpc/wrpc/resolver/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "kaspa-resolver" -description = "Kaspa wRPC endpoint resolver and monitor" -version.workspace = true -edition.workspace = true -authors.workspace = true -include.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] - - -ahash.workspace = true -cfg-if.workspace = true -clap.workspace = true -convert_case.workspace = true -futures.workspace = true -kaspa-consensus-core.workspace = true -kaspa-rpc-core.workspace = true -kaspa-utils.workspace = true -kaspa-wrpc-client.workspace = true -serde_json.workspace = true -serde.workspace = true -thiserror.workspace = true -tokio.workspace = true -toml.workspace = true -workflow-core.workspace = true -workflow-http.workspace = true -workflow-log.workspace = true -xxhash-rust = { workspace = true } - -# these are temporarily localized to prevent -# conflicts with other workspace dependencies -# as tower is used in gRPC-related crates. -axum = "0.7.4" -console = "0.15.8" -mime = "0.3.16" -tower = { version = "0.4.13", features = ["buffer","limit"] } -tower-http = { version = "0.5.1", features = ["cors"] } -tracing-subscriber = "0.3.18" diff --git a/rpc/wrpc/resolver/src/args.rs b/rpc/wrpc/resolver/src/args.rs deleted file mode 100644 index 7a526b99b1..0000000000 --- a/rpc/wrpc/resolver/src/args.rs +++ /dev/null @@ -1,54 +0,0 @@ -pub use clap::Parser; -use std::str::FromStr; - -#[derive(Default, Parser, Debug)] -#[command(version, about, long_about = None)] -pub struct Args { - /// HTTP server port - #[arg(long, default_value = "127.0.0.1:8888")] - pub listen: String, - - /// Optional rate limit in the form `:`, where `requests` is the number of requests allowed per specified number of `seconds` - #[arg(long = "rate-limit", value_name = "REQUESTS:SECONDS")] - pub rate_limit: Option, - - /// Verbose mode - #[arg(short, long, default_value = "false")] - pub verbose: bool, - - /// Show node data on each election - #[arg(short, long, default_value = "false")] - pub election: bool, - - /// Enable resolver status access via `/status` - #[arg(long, default_value = "false")] - pub status: bool, -} - -#[derive(Clone, Debug)] -pub struct RateLimit { - pub requests: u64, - pub period: u64, -} - -impl FromStr for RateLimit { - type Err = String; - - fn from_str(s: &str) -> Result { - let parts = s.split_once(':'); - let (requests, period) = match parts { - None | Some(("", _)) | Some((_, "")) => { - return Err("invalid rate limit, must be `:`".to_string()); - } - Some(x) => x, - }; - let requests = requests - .parse() - .map_err(|_| format!("Unable to parse number of requests, the value must be an integer, supplied: {:?}", requests))?; - let period = period.parse().map_err(|_| { - format!("Unable to parse period, the value must be an integer specifying number of seconds, supplied: {:?}", period) - })?; - - Ok(RateLimit { requests, period }) - } -} diff --git a/rpc/wrpc/resolver/src/connection.rs b/rpc/wrpc/resolver/src/connection.rs deleted file mode 100644 index 88528bc810..0000000000 --- a/rpc/wrpc/resolver/src/connection.rs +++ /dev/null @@ -1,262 +0,0 @@ -use crate::imports::*; - -const BIAS_SCALE: u64 = 1_000_000; - -#[derive(Debug, Clone)] -pub struct Descriptor { - pub connection: Arc, - pub json: String, -} - -impl From<&Arc> for Descriptor { - fn from(connection: &Arc) -> Self { - Self { connection: connection.clone(), json: serde_json::to_string(&Output::from(connection)).unwrap() } - } -} - -impl fmt::Display for Connection { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}: [{:>3}] {}", self.node.id_string, self.clients(), self.node.address) - } -} - -#[derive(Debug)] -pub struct Connection { - pub node: Arc, - bias: u64, - descriptor: RwLock>, - sender: Sender, - client: KaspaRpcClient, - shutdown_ctl: DuplexChannel<()>, - is_connected: Arc, - is_synced: Arc, - is_online: Arc, - clients: Arc, - args: Arc, -} - -impl Connection { - pub fn try_new(node: Arc, sender: Sender, args: &Arc) -> Result { - let client = KaspaRpcClient::new(node.encoding, Some(&node.address), None, None, None)?; - let descriptor = RwLock::default(); - let shutdown_ctl = DuplexChannel::oneshot(); - let is_connected = Arc::new(AtomicBool::new(false)); - let is_synced = Arc::new(AtomicBool::new(true)); - let is_online = Arc::new(AtomicBool::new(false)); - let clients = Arc::new(AtomicU64::new(0)); - let bias = (node.bias.unwrap_or(1.0) * BIAS_SCALE as f64) as u64; - let args = args.clone(); - Ok(Self { node, descriptor, sender, client, shutdown_ctl, is_connected, is_synced, is_online, clients, bias, args }) - } - - pub fn verbose(&self) -> bool { - self.args.verbose - } - - pub fn score(&self) -> u64 { - self.clients.load(Ordering::Relaxed) * self.bias / BIAS_SCALE - } - - pub fn connected(&self) -> bool { - self.is_connected.load(Ordering::Relaxed) - } - - pub fn online(&self) -> bool { - self.is_online.load(Ordering::Relaxed) - } - - pub fn is_synced(&self) -> bool { - self.is_synced.load(Ordering::Relaxed) - } - - pub fn clients(&self) -> u64 { - self.clients.load(Ordering::Relaxed) - } - - pub fn status(&self) -> &'static str { - if self.connected() { - if self.is_synced() { - "online" - } else { - "syncing" - } - } else { - "offline" - } - } - - pub fn descriptor(&self) -> Option { - self.descriptor.read().unwrap().clone() - } - - async fn connect(&self) -> Result<()> { - let options = ConnectOptions { block_async_connect: false, strategy: ConnectStrategy::Retry, ..Default::default() }; - - self.client.connect(Some(options)).await?; - Ok(()) - } - - async fn task(self: Arc) -> Result<()> { - self.connect().await?; - let rpc_ctl_channel = self.client.rpc_ctl().multiplexer().channel(); - let shutdown_ctl_receiver = self.shutdown_ctl.request.receiver.clone(); - let shutdown_ctl_sender = self.shutdown_ctl.response.sender.clone(); - - let interval = workflow_core::task::interval(Duration::from_secs(5)); - pin_mut!(interval); - - loop { - select! { - _ = interval.next().fuse() => { - if self.is_connected.load(Ordering::Relaxed) { - let previous = self.is_online.load(Ordering::Relaxed); - let online = self.update_metrics().await.is_ok(); - self.is_online.store(online, Ordering::Relaxed); - if online != previous { - if self.verbose() { - log_error!("Offline","{}", self.node.address); - } - self.update(online).await?; - } - } - } - - msg = rpc_ctl_channel.receiver.recv().fuse() => { - match msg { - Ok(msg) => { - - // handle wRPC channel connection and disconnection events - match msg { - RpcState::Connected => { - log_success!("Connected","{}",self.node.address); - self.is_connected.store(true, Ordering::Relaxed); - if self.update_metrics().await.is_ok() { - self.is_online.store(true, Ordering::Relaxed); - self.update(true).await?; - } else { - self.is_online.store(false, Ordering::Relaxed); - } - }, - RpcState::Disconnected => { - self.is_connected.store(false, Ordering::Relaxed); - self.is_online.store(false, Ordering::Relaxed); - self.update(false).await?; - log_error!("Disconnected","{}",self.node.address); - } - } - } - Err(err) => { - println!("Monitor: error while receiving rpc_ctl_channel message: {err}"); - break; - } - } - } - - _ = shutdown_ctl_receiver.recv().fuse() => { - break; - }, - - } - } - - shutdown_ctl_sender.send(()).await.unwrap(); - - Ok(()) - } - - pub fn start(self: &Arc) -> Result<()> { - let this = self.clone(); - spawn(async move { - if let Err(error) = this.task().await { - println!("NodeConnection task error: {:?}", error); - } - }); - - Ok(()) - } - - pub async fn stop(self: &Arc) -> Result<()> { - self.shutdown_ctl.signal(()).await.expect("NodeConnection shutdown signal error"); - Ok(()) - } - - async fn update_metrics(self: &Arc) -> Result { - match self.client.get_sync_status().await { - Ok(is_synced) => { - let previous_sync = self.is_synced.load(Ordering::Relaxed); - self.is_synced.store(is_synced, Ordering::Relaxed); - - if is_synced { - match self.client.get_metrics(false, true, false, false, false, false).await { - Ok(metrics) => { - if let Some(connection_metrics) = metrics.connection_metrics { - // update - let previous = self.clients.load(Ordering::Relaxed); - let clients = - connection_metrics.borsh_live_connections as u64 + connection_metrics.json_live_connections as u64; - self.clients.store(clients, Ordering::Relaxed); - if clients != previous { - if self.verbose() { - log_success!("Clients", "{self}"); - } - Ok(true) - } else { - Ok(false) - } - } else { - log_error!("Metrics", "{self} - failure"); - Err(Error::ConnectionMetrics) - } - } - Err(err) => { - log_error!("Metrics", "{self}"); - log_error!("RPC", "{err}"); - Err(Error::Metrics) - } - } - } else { - if is_synced != previous_sync { - log_error!("Syncing", "{self}"); - } - Err(Error::Sync) - } - } - Err(err) => { - log_error!("RPC", "{self}"); - log_error!("RPC", "{err}"); - Err(Error::Status) - } - } - } - - pub async fn update(self: &Arc, online: bool) -> Result<()> { - *self.descriptor.write().unwrap() = online.then_some(self.into()); - self.sender.try_send(self.node.params())?; - Ok(()) - } -} - -#[derive(Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct Output<'a> { - pub id: &'a str, - pub url: &'a str, - - #[serde(skip_serializing_if = "Option::is_none")] - pub provider_name: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - pub provider_url: Option<&'a str>, -} - -impl<'a> From<&'a Arc> for Output<'a> { - fn from(connection: &'a Arc) -> Self { - let id = connection.node.id_string.as_str(); - let url = connection.node.address.as_str(); - let provider_name = connection.node.provider.as_ref().map(|provider| provider.name.as_str()); - let provider_url = connection.node.provider.as_ref().map(|provider| provider.url.as_str()); - - // let provider_name = connection.node.provider.as_deref(); - // let provider_url = connection.node.link.as_deref(); - Self { id, url, provider_name, provider_url } - } -} diff --git a/rpc/wrpc/resolver/src/error.rs b/rpc/wrpc/resolver/src/error.rs deleted file mode 100644 index 4f390c3ce5..0000000000 --- a/rpc/wrpc/resolver/src/error.rs +++ /dev/null @@ -1,53 +0,0 @@ -use kaspa_wrpc_client::error::Error as RpcError; -use thiserror::Error; -use toml::de::Error as TomlError; - -#[derive(Error, Debug)] -pub enum Error { - #[error("{0}")] - Custom(String), - - #[error("RPC error: {0}")] - Rpc(#[from] RpcError), - - #[error("TOML error: {0}")] - Toml(#[from] TomlError), - - #[error("IO Error: {0}")] - Io(#[from] std::io::Error), - - #[error(transparent)] - Serde(#[from] serde_json::Error), - - #[error("Connection Metrics")] - ConnectionMetrics, - #[error("Metrics")] - Metrics, - #[error("Sync")] - Sync, - #[error("Status")] - Status, - - #[error("Channel send error")] - ChannelSend, - #[error("Channel try send error")] - TryChannelSend, -} - -impl Error { - pub fn custom(msg: T) -> Self { - Error::Custom(msg.to_string()) - } -} - -impl From> for Error { - fn from(_: workflow_core::channel::SendError) -> Self { - Error::ChannelSend - } -} - -impl From> for Error { - fn from(_: workflow_core::channel::TrySendError) -> Self { - Error::TryChannelSend - } -} diff --git a/rpc/wrpc/resolver/src/imports.rs b/rpc/wrpc/resolver/src/imports.rs deleted file mode 100644 index 29c86a4813..0000000000 --- a/rpc/wrpc/resolver/src/imports.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub use crate::args::Args; -pub use crate::error::Error; -pub use crate::log::*; -pub use crate::node::Node; -pub use crate::params::{PathParams, QueryParams}; -pub use crate::result::Result; -pub use crate::transport::Transport; -pub use ahash::AHashMap; -pub use cfg_if::cfg_if; -pub use futures::{pin_mut, select, FutureExt, StreamExt}; -pub use kaspa_consensus_core::network::NetworkId; -pub use kaspa_rpc_core::api::ctl::RpcState; -pub use kaspa_rpc_core::api::rpc::RpcApi; -pub use kaspa_utils::hashmap::GroupExtension; -pub use kaspa_wrpc_client::{ - client::{ConnectOptions, ConnectStrategy}, - KaspaRpcClient, WrpcEncoding, -}; -pub use serde::{de::DeserializeOwned, Deserialize, Serialize}; -pub use std::collections::HashMap; -pub use std::fmt; -pub use std::path::Path; -pub use std::sync::atomic::AtomicBool; -pub use std::sync::atomic::{AtomicU64, Ordering}; -pub use std::sync::{Arc, Mutex, OnceLock, RwLock}; -pub use std::time::Duration; -pub use workflow_core::channel::*; -pub use workflow_core::task::spawn; diff --git a/rpc/wrpc/resolver/src/log.rs b/rpc/wrpc/resolver/src/log.rs deleted file mode 100644 index 5f66416a0a..0000000000 --- a/rpc/wrpc/resolver/src/log.rs +++ /dev/null @@ -1,44 +0,0 @@ -pub mod impls { - use console::style; - use std::fmt; - - pub fn log_success(source: &str, args: &fmt::Arguments<'_>) { - println!("{:>12} {}", style(source).green().bold(), args); - } - - pub fn log_warn(source: &str, args: &fmt::Arguments<'_>) { - println!("{:>12} {}", style(source).yellow().bold(), args); - } - - pub fn log_error(source: &str, args: &fmt::Arguments<'_>) { - println!("{:>12} {}", style(source).red().bold(), args); - } -} - -#[macro_export] -macro_rules! log_success { - ($target:expr, $($t:tt)*) => ( - $crate::log::impls::log_success($target, &format_args!($($t)*)) - ) -} - -pub use log_success; - -#[macro_export] -macro_rules! log_warn { - - ($target:expr, $($t:tt)*) => ( - $crate::log::impls::log_warn($target, &format_args!($($t)*)) - ) -} - -pub use log_warn; - -#[macro_export] -macro_rules! log_error { - ($target:expr, $($t:tt)*) => ( - $crate::log::impls::log_error($target, &format_args!($($t)*)) - ) -} - -pub use log_error; diff --git a/rpc/wrpc/resolver/src/main.rs b/rpc/wrpc/resolver/src/main.rs deleted file mode 100644 index f071b12874..0000000000 --- a/rpc/wrpc/resolver/src/main.rs +++ /dev/null @@ -1,41 +0,0 @@ -mod args; -mod connection; -mod error; -pub mod imports; -mod log; -mod monitor; -mod node; -mod panic; -mod params; -mod result; -mod server; -mod transport; - -use args::*; -use result::Result; -use std::sync::Arc; - -#[tokio::main] -async fn main() { - if let Err(error) = run().await { - eprintln!("Error: {}", error); - std::process::exit(1); - } -} - -async fn run() -> Result<()> { - let args = Arc::new(Args::parse()); - - workflow_log::set_log_level(workflow_log::LevelFilter::Info); - panic::init_ungraceful_panic_handler(); - - println!(); - println!("Kaspa wRPC Resolver v{} starting...", env!("CARGO_PKG_VERSION")); - - monitor::init(&args); - let (listener, app) = server::server(&args).await?; - monitor::start().await?; - axum::serve(listener, app).await?; - monitor::stop().await?; - Ok(()) -} diff --git a/rpc/wrpc/resolver/src/monitor.rs b/rpc/wrpc/resolver/src/monitor.rs deleted file mode 100644 index 748a5148f3..0000000000 --- a/rpc/wrpc/resolver/src/monitor.rs +++ /dev/null @@ -1,241 +0,0 @@ -use crate::connection::{Connection, Descriptor}; -use crate::imports::*; - -static MONITOR: OnceLock> = OnceLock::new(); - -pub fn init(args: &Arc) { - MONITOR.set(Arc::new(Monitor::new(args))).unwrap(); -} - -pub fn monitor() -> &'static Arc { - MONITOR.get().unwrap() -} - -pub async fn start() -> Result<()> { - monitor().start().await -} - -pub async fn stop() -> Result<()> { - monitor().stop().await -} - -/// Monitor receives updates from [Connection] monitoring tasks -/// and updates the descriptors for each [Params] based on the -/// connection store (number of connections * bias). -pub struct Monitor { - args: Arc, - connections: RwLock>>>, - descriptors: RwLock>, - channel: Channel, - shutdown_ctl: DuplexChannel<()>, -} - -impl Default for Monitor { - fn default() -> Self { - Self { - args: Arc::new(Args::default()), - connections: Default::default(), - descriptors: Default::default(), - channel: Channel::unbounded(), - shutdown_ctl: DuplexChannel::oneshot(), - } - } -} - -impl fmt::Debug for Monitor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Monitor") - .field("verbose", &self.verbose()) - .field("connections", &self.connections) - .field("descriptors", &self.descriptors) - .finish() - } -} - -impl Monitor { - pub fn new(args: &Arc) -> Self { - Self { args: args.clone(), ..Default::default() } - } - - pub fn verbose(&self) -> bool { - self.args.verbose - } - - pub fn connections(&self) -> AHashMap>> { - self.connections.read().unwrap().clone() - } - - /// Process an update to `Server.toml` removing or adding node connections accordingly. - pub async fn update_nodes(&self, nodes: Vec>) -> Result<()> { - let mut connections = self.connections(); - - for params in PathParams::iter() { - let nodes = nodes.iter().filter(|node| node.params() == params).collect::>(); - - let list = connections.entry(params).or_default(); - - let create: Vec<_> = nodes.iter().filter(|node| !list.iter().any(|connection| connection.node == ***node)).collect(); - - let remove: Vec<_> = - list.iter().filter(|connection| !nodes.iter().any(|node| connection.node == **node)).cloned().collect(); - - for node in create { - let created = Arc::new(Connection::try_new((*node).clone(), self.channel.sender.clone(), &self.args)?); - created.start()?; - list.push(created); - } - - for removed in remove { - removed.stop().await?; - list.retain(|c| c.node != removed.node); - } - } - - *self.connections.write().unwrap() = connections; - - // flush all params to the update channel to refresh selected descriptors - PathParams::iter().for_each(|param| self.channel.sender.try_send(param).unwrap()); - - Ok(()) - } - - pub async fn start(self: &Arc) -> Result<()> { - let toml = std::fs::read_to_string(Path::new("Servers.toml"))?; - let nodes = crate::node::try_parse_nodes(toml.as_str())?; - - let this = self.clone(); - spawn(async move { - if let Err(error) = this.task().await { - println!("NodeConnection task error: {:?}", error); - } - }); - - self.update_nodes(nodes).await?; - - Ok(()) - } - - pub async fn stop(&self) -> Result<()> { - self.shutdown_ctl.signal(()).await.expect("Monitor shutdown signal error"); - Ok(()) - } - - async fn task(self: Arc) -> Result<()> { - let receiver = self.channel.receiver.clone(); - let shutdown_ctl_receiver = self.shutdown_ctl.request.receiver.clone(); - let shutdown_ctl_sender = self.shutdown_ctl.response.sender.clone(); - - loop { - select! { - - msg = receiver.recv().fuse() => { - match msg { - Ok(params) => { - - // run node elections - - let mut connections = self.connections() - .get(¶ms) - .expect("Monitor: expecting existing connection params") - .clone() - .into_iter() - .filter(|connection|connection.online()) - .collect::>(); - if connections.is_empty() { - self.descriptors.write().unwrap().remove(¶ms); - } else { - connections.sort_by_key(|connection| connection.score()); - - if self.args.election { - log_success!("",""); - connections.iter().for_each(|connection| { - log_warn!("Node","{}", connection); - }); - } - - if let Some(descriptor) = connections.first().unwrap().descriptor() { - let mut descriptors = self.descriptors.write().unwrap(); - - // extra debug output & monitoring - if self.args.verbose || self.args.election { - if let Some(current) = descriptors.get(¶ms) { - if current.connection.node.id != descriptor.connection.node.id { - log_success!("Election","{}", descriptor.connection); - descriptors.insert(params,descriptor); - } else { - log_success!("Keep","{}", descriptor.connection); - } - } else { - log_success!("Default","{}", descriptor.connection); - descriptors.insert(params,descriptor); - } - } else { - descriptors.insert(params,descriptor); - } - } - - if self.args.election && self.args.verbose { - log_success!("",""); - } - } - } - Err(err) => { - println!("Monitor: error while receiving update message: {err}"); - } - } - - } - _ = shutdown_ctl_receiver.recv().fuse() => { - break; - }, - - } - } - - shutdown_ctl_sender.send(()).await.unwrap(); - - Ok(()) - } - - /// Get the status of all nodes as a JSON string (available via `/status` endpoint if enabled). - pub fn get_all_json(&self) -> String { - let connections = self.connections(); - let nodes = connections.values().flatten().map(Status::from).collect::>(); - serde_json::to_string(&nodes).unwrap() - } - - /// Get JSON string representing node information (id, url, provider, link) - pub fn get_json(&self, params: &PathParams) -> Option { - self.descriptors.read().unwrap().get(params).cloned().map(|descriptor| descriptor.json) - } -} - -#[derive(Serialize)] -pub struct Status<'a> { - pub id: &'a str, - pub url: &'a str, - #[serde(skip_serializing_if = "Option::is_none")] - pub provider_name: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - pub provider_url: Option<&'a str>, - pub transport: Transport, - pub encoding: WrpcEncoding, - pub network: NetworkId, - pub online: bool, - pub status: &'static str, -} - -impl<'a> From<&'a Arc> for Status<'a> { - fn from(connection: &'a Arc) -> Self { - let url = connection.node.address.as_str(); - let provider_name = connection.node.provider.as_ref().map(|provider| provider.name.as_str()); - let provider_url = connection.node.provider.as_ref().map(|provider| provider.url.as_str()); - let id = connection.node.id_string.as_str(); - let transport = connection.node.transport; - let encoding = connection.node.encoding; - let network = connection.node.network; - let status = connection.status(); - let online = connection.online(); - Self { id, url, provider_name, provider_url, transport, encoding, network, status, online } - } -} diff --git a/rpc/wrpc/resolver/src/node.rs b/rpc/wrpc/resolver/src/node.rs deleted file mode 100644 index d0968966cf..0000000000 --- a/rpc/wrpc/resolver/src/node.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::imports::*; -use xxhash_rust::xxh3::xxh3_64; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Provider { - pub name: String, - pub url: String, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Node { - #[serde(skip)] - pub id: u64, - #[serde(skip)] - pub id_string: String, - - pub name: Option, - pub location: Option, - pub address: String, - pub transport: Transport, - pub encoding: WrpcEncoding, - pub network: NetworkId, - pub enable: Option, - pub bias: Option, - pub version: Option, - pub provider: Option, -} - -impl Eq for Node {} - -impl PartialEq for Node { - fn eq(&self, other: &Self) -> bool { - self.address == other.address - } -} - -impl std::fmt::Display for Node { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let title = self.name.clone().unwrap_or(self.address.to_string()); - write!(f, "{}", title) - } -} - -impl Node { - pub fn params(&self) -> PathParams { - PathParams::new(self.encoding, self.network) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NodeConfig { - #[serde(rename = "node")] - nodes: Vec, -} - -pub fn try_parse_nodes(toml: &str) -> Result>> { - let nodes: Vec> = toml::from_str::(toml)? - .nodes - .into_iter() - .filter_map(|mut node| { - let id = xxh3_64(node.address.as_bytes()); - let id_string = format!("{id:x}"); - node.id = id; - node.id_string = id_string.chars().take(8).collect(); - node.enable.unwrap_or(true).then_some(node).map(Arc::new) - }) - .collect::>(); - Ok(nodes) -} - -impl AsRef for Node { - fn as_ref(&self) -> &Node { - self - } -} diff --git a/rpc/wrpc/resolver/src/panic.rs b/rpc/wrpc/resolver/src/panic.rs deleted file mode 100644 index 7e6d78a1ff..0000000000 --- a/rpc/wrpc/resolver/src/panic.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::panic; - -pub fn init_ungraceful_panic_handler() { - let default_hook = panic::take_hook(); - panic::set_hook(Box::new(move |panic_info| { - default_hook(panic_info); - println!("Exiting..."); - std::process::exit(1); - })); -} diff --git a/rpc/wrpc/resolver/src/params.rs b/rpc/wrpc/resolver/src/params.rs deleted file mode 100644 index 7e31b69e70..0000000000 --- a/rpc/wrpc/resolver/src/params.rs +++ /dev/null @@ -1,146 +0,0 @@ -use serde::{de, Deserializer, Serializer}; - -use crate::imports::*; -use std::{fmt, str::FromStr}; -// use convert_case::{Case, Casing}; - -#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)] -pub struct PathParams { - pub encoding: WrpcEncoding, - pub network: NetworkId, -} - -impl PathParams { - pub fn new(encoding: WrpcEncoding, network: NetworkId) -> Self { - Self { encoding, network } - } - - pub fn iter() -> impl Iterator { - NetworkId::iter().flat_map(move |network_id| WrpcEncoding::iter().map(move |encoding| PathParams::new(*encoding, network_id))) - } -} - -impl fmt::Display for PathParams { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}:{}", self.encoding.to_string().to_lowercase(), self.network) - } -} - -// --- - -#[derive(Debug, Deserialize)] -pub struct QueryParams { - // Accessible via a query string like "?access=utxo-index+tx-index+block-dag+metrics+visualizer+mining" - pub access: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)] -#[serde(rename_all = "kebab-case")] -pub enum AccessType { - Transact, // UTXO and TX index, submit transaction, single mempool entry - Mempool, // Full mempool data access - BlockDag, // Access to Blocks - Network, // Network data access (peers, ban, etc.) - Metrics, // Access to Metrics - Visualizer, // Access to Visualization data feeds - Mining, // Access to submit block, GBT, etc. -} - -impl AccessType { - pub fn iter() -> impl Iterator { - [ - AccessType::Transact, - AccessType::Mempool, - AccessType::BlockDag, - AccessType::Network, - AccessType::Metrics, - AccessType::Visualizer, - AccessType::Mining, - ] - .into_iter() - } -} - -impl fmt::Display for AccessType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - AccessType::Transact => "transact", - AccessType::Mempool => "mempool", - AccessType::BlockDag => "block-dag", - AccessType::Network => "network", - AccessType::Metrics => "metrics", - AccessType::Visualizer => "visualizer", - AccessType::Mining => "mining", - }; - write!(f, "{s}") - } -} - -impl FromStr for AccessType { - type Err = String; - fn from_str(s: &str) -> std::result::Result { - match s { - "transact" => Ok(AccessType::Transact), - "mempool" => Ok(AccessType::Mempool), - "block-dag" => Ok(AccessType::BlockDag), - "network" => Ok(AccessType::Network), - "metrics" => Ok(AccessType::Metrics), - "visualizer" => Ok(AccessType::Visualizer), - "mining" => Ok(AccessType::Mining), - _ => Err(format!("Invalid access type: {}", s)), - } - } -} - -#[derive(Debug, Clone)] -pub struct AccessList { - pub access: Vec, -} - -impl std::fmt::Display for AccessList { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.access.iter().map(|access| access.to_string()).collect::>().join(" ")) - } -} - -impl FromStr for AccessList { - type Err = String; - - fn from_str(s: &str) -> std::result::Result { - let access = s.split(' ').map(|s| s.parse::()).collect::, _>>()?; - Ok(AccessList { access }) - } -} - -impl Serialize for AccessList { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -struct AccessListVisitor; -impl<'de> de::Visitor<'de> for AccessListVisitor { - type Value = AccessList; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a string containing list of permissions separated by a '+'") - } - - fn visit_str(self, value: &str) -> std::result::Result - where - E: de::Error, - { - AccessList::from_str(value).map_err(|err| de::Error::custom(err.to_string())) - } -} - -impl<'de> Deserialize<'de> for AccessList { - fn deserialize(deserializer: D) -> std::result::Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(AccessListVisitor) - } -} diff --git a/rpc/wrpc/resolver/src/result.rs b/rpc/wrpc/resolver/src/result.rs deleted file mode 100644 index 605dc25cfc..0000000000 --- a/rpc/wrpc/resolver/src/result.rs +++ /dev/null @@ -1 +0,0 @@ -pub type Result = std::result::Result; diff --git a/rpc/wrpc/resolver/src/server.rs b/rpc/wrpc/resolver/src/server.rs deleted file mode 100644 index 3717a6ebf4..0000000000 --- a/rpc/wrpc/resolver/src/server.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::imports::*; -use crate::monitor::monitor; -use axum::{ - async_trait, - extract::{path::ErrorKind, rejection::PathRejection, FromRequestParts, Query}, - http::{header, request::Parts, HeaderValue, StatusCode}, - response::IntoResponse, - routing::get, - // Json, - Router, -}; -use tokio::net::TcpListener; - -use axum::{error_handling::HandleErrorLayer, BoxError}; -use std::time::Duration; -use tower::{buffer::BufferLayer, limit::RateLimitLayer, ServiceBuilder}; -use tower_http::cors::{Any, CorsLayer}; - -pub async fn server(args: &Args) -> Result<(TcpListener, Router)> { - // initialize tracing - tracing_subscriber::fmt::init(); - - let app = Router::new().route("/v1/wrpc/:encoding/:network", get(get_elected_node)); - - let app = if args.status { - log_warn!("Routes", "Enabling `/status` route"); - app.route("/status", get(get_status_all_nodes)) - } else { - log_success!("Routes", "Disabling `/status` route"); - app - }; - - let app = if let Some(rate_limit) = args.rate_limit.as_ref() { - log_success!("Limits", "Setting rate limit to: {} requests per {} seconds", rate_limit.requests, rate_limit.period); - app.layer( - ServiceBuilder::new() - .layer(HandleErrorLayer::new(|err: BoxError| async move { - (StatusCode::INTERNAL_SERVER_ERROR, format!("Unhandled error: {}", err)) - })) - .layer(BufferLayer::new(1024)) - .layer(RateLimitLayer::new(rate_limit.requests, Duration::from_secs(rate_limit.period))), - ) - } else { - log_warn!("Limits", "Rate limit is disabled"); - app - }; - - let app = app.layer(CorsLayer::new().allow_origin(Any)); - - log_success!("Server", "Listening on http://{}", args.listen.as_str()); - let listener = tokio::net::TcpListener::bind(args.listen.as_str()).await.unwrap(); - Ok((listener, app)) -} - -// respond with a JSON object containing the status of all nodes -async fn get_status_all_nodes() -> impl IntoResponse { - let json = monitor().get_all_json(); - (StatusCode::OK, [(header::CONTENT_TYPE, HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()))], json).into_response() -} - -// respond with a JSON object containing the elected node -async fn get_elected_node(Query(_query): Query, Path(params): Path) -> impl IntoResponse { - // println!("params: {:?}", params); - // println!("query: {:?}", query); - - if let Some(json) = monitor().get_json(¶ms) { - ([(header::CONTENT_TYPE, HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()))], json).into_response() - } else { - ( - StatusCode::NOT_FOUND, - [(header::CONTENT_TYPE, HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()))], - "NOT FOUND".to_string(), - ) - .into_response() - } -} - -// We define our own `Path` extractor that customizes the error from `axum::extract::Path` -struct Path(T); - -#[async_trait] -impl FromRequestParts for Path -where - // these trait bounds are copied from `impl FromRequest for axum::extract::path::Path` - T: DeserializeOwned + Send, - S: Send + Sync, -{ - type Rejection = (StatusCode, axum::Json); - - async fn from_request_parts(parts: &mut Parts, state: &S) -> std::result::Result { - match axum::extract::Path::::from_request_parts(parts, state).await { - Ok(value) => Ok(Self(value.0)), - Err(rejection) => { - let (status, body) = match rejection { - PathRejection::FailedToDeserializePathParams(inner) => { - let mut status = StatusCode::BAD_REQUEST; - - let kind = inner.into_kind(); - let body = match &kind { - ErrorKind::WrongNumberOfParameters { .. } => PathError { message: kind.to_string(), location: None }, - - ErrorKind::ParseErrorAtKey { key, .. } => { - PathError { message: kind.to_string(), location: Some(key.clone()) } - } - - ErrorKind::ParseErrorAtIndex { index, .. } => { - PathError { message: kind.to_string(), location: Some(index.to_string()) } - } - - ErrorKind::ParseError { .. } => PathError { message: kind.to_string(), location: None }, - - ErrorKind::InvalidUtf8InPathParam { key } => { - PathError { message: kind.to_string(), location: Some(key.clone()) } - } - - ErrorKind::UnsupportedType { .. } => { - // this error is caused by the programmer using an unsupported type - // (such as nested maps) so respond with `500` instead - status = StatusCode::INTERNAL_SERVER_ERROR; - PathError { message: kind.to_string(), location: None } - } - - ErrorKind::Message(msg) => PathError { message: msg.clone(), location: None }, - - _ => PathError { message: format!("Unhandled deserialization error: {kind}"), location: None }, - }; - - (status, body) - } - PathRejection::MissingPathParams(error) => { - (StatusCode::INTERNAL_SERVER_ERROR, PathError { message: error.to_string(), location: None }) - } - _ => ( - StatusCode::INTERNAL_SERVER_ERROR, - PathError { message: format!("Unhandled path rejection: {rejection}"), location: None }, - ), - }; - - Err((status, axum::Json(body))) - } - } - } -} - -#[derive(Serialize)] -struct PathError { - message: String, - location: Option, -} diff --git a/rpc/wrpc/resolver/src/transport.rs b/rpc/wrpc/resolver/src/transport.rs deleted file mode 100644 index ccfd6dee73..0000000000 --- a/rpc/wrpc/resolver/src/transport.rs +++ /dev/null @@ -1,8 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum Transport { - Grpc, - Wrpc, -} From 4253fb94273a7f299e5a1d7c8a7161ed3ae073d8 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 10 Jul 2024 04:11:40 +0300 Subject: [PATCH 078/158] WRS 0.14.0 --- Cargo.lock | 42 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14021b0ac1..0a3f5aab7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6770,6 +6770,8 @@ dependencies = [ [[package]] name = "workflow-chrome" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d24f8060d11a8eeeb25f0af9c229a94e0998d14a7a1932eb25bf9662e0775ed" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6783,6 +6785,8 @@ dependencies = [ [[package]] name = "workflow-core" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d889e6327e09b88827953e6bc6b13cb883b2b19b04185c75a6a05eede517fb1e" dependencies = [ "async-channel 2.3.1", "async-std", @@ -6813,6 +6817,8 @@ dependencies = [ [[package]] name = "workflow-core-macros" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba00e4a92112229e904826d366b4f518068be9c6a7f7c2d3f9f847ec917cf6b" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6828,6 +6834,8 @@ dependencies = [ [[package]] name = "workflow-dom" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2981dac7b9c3148c70e98959f90d878de28ad3546c496825faa6821122ab76" dependencies = [ "futures", "js-sys", @@ -6844,6 +6852,8 @@ dependencies = [ [[package]] name = "workflow-http" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb140b523579932d9233a73f7574d73a8e09f5e719cdb7121137116ef82820ac" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6858,6 +6868,8 @@ dependencies = [ [[package]] name = "workflow-log" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b383ae3a35a179259b67b5e57ff80f545a86472c56c538ba7ee64934426906" dependencies = [ "cfg-if 1.0.0", "console", @@ -6872,6 +6884,8 @@ dependencies = [ [[package]] name = "workflow-macro-tools" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059b3f3d8acfd2863ccebbd18096166ba45391f69f3825014968ea92bd96a9df" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6883,6 +6897,8 @@ dependencies = [ [[package]] name = "workflow-node" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b310a6999a7e6d725316cb4869819fa0a0894c2cf05f7d93cc6e3a8ad081061" dependencies = [ "borsh", "futures", @@ -6902,6 +6918,8 @@ dependencies = [ [[package]] name = "workflow-nw" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916aeb34a69402bf2d9aac05da191272ad09882f5f1e952da1e6de5071446fdb" dependencies = [ "ahash", "async-trait", @@ -6924,6 +6942,8 @@ dependencies = [ [[package]] name = "workflow-panic-hook" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273864a1c98165527a7291ca419c7637e3c42bbe24aa1821010d1bb12d1b28d4" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -6947,6 +6967,8 @@ dependencies = [ [[package]] name = "workflow-rpc" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35df84239b442e2a4bc7f012aeb99bf56b2ce177bfc1fb511f39295284d8432" dependencies = [ "ahash", "async-std", @@ -6975,6 +6997,8 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe36cbf1e1a1a33122ed499dc91d367abc4358e86c9f9126db8829a7085b711" dependencies = [ "parse-variants", "proc-macro-error", @@ -6986,6 +7010,8 @@ dependencies = [ [[package]] name = "workflow-serializer" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a489dfd457e5128c7342efaee4d95479cb91bba3850b8138f6a2c1bc5ac8b3" dependencies = [ "ahash", "borsh", @@ -6995,6 +7021,8 @@ dependencies = [ [[package]] name = "workflow-store" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de6a4827802926626f1b6359b180ff4540ae241b1844c0379aa8d567428f5f98" dependencies = [ "async-std", "base64 0.22.1", @@ -7021,6 +7049,8 @@ dependencies = [ [[package]] name = "workflow-task" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6549f54b7444b10514cc22193381ca4645f6f2d0c3513026c6fb79a05e4f097" dependencies = [ "futures", "thiserror", @@ -7031,6 +7061,8 @@ dependencies = [ [[package]] name = "workflow-task-macros" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b2bb83b731dae95ddb3478f57aa8afb9a6a30ce78b11ff319a84df9296db112" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7045,6 +7077,8 @@ dependencies = [ [[package]] name = "workflow-terminal" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aee5b190b532d7761728be9b473425c3420798daf341be44c99102adbf13f9a" dependencies = [ "async-std", "async-trait", @@ -7072,6 +7106,8 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "315bd4768689d133902dc2f8a1b4cb969e8e9875bcfc7703b2bf6ba42f294cc2" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7086,6 +7122,8 @@ dependencies = [ [[package]] name = "workflow-wasm" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc78269a2f7384e99bdcde6bd9a32ef28c20e6610d6ba7d817e9d57e7494038" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7105,6 +7143,8 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7af80a4b7508805932d80d7c8678374694a0f65cc60e0356597144128f6ee33e" dependencies = [ "js-sys", "proc-macro-error", @@ -7117,6 +7157,8 @@ dependencies = [ [[package]] name = "workflow-websocket" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c781fb394b573bf46c574703e4d08600f2947185c39d58839d6fe7ecd0d375b" dependencies = [ "ahash", "async-channel 2.3.1", diff --git a/Cargo.toml b/Cargo.toml index 029ab951ce..f04595844b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,34 +274,34 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -# workflow-core = { version = "0.14.0" } -# workflow-d3 = { version = "0.14.0" } -# workflow-dom = { version = "0.14.0" } -# workflow-http = { version = "0.14.0" } -# workflow-log = { version = "0.14.0" } -# workflow-node = { version = "0.14.0" } -# workflow-nw = { version = "0.14.0" } -# workflow-rpc = { version = "0.14.0" } -# workflow-serializer = { version = "0.14.0" } -# workflow-store = { version = "0.14.0" } -# workflow-terminal = { version = "0.14.0" } -# workflow-wasm = { version = "0.14.0" } +workflow-core = { version = "0.14.0" } +workflow-d3 = { version = "0.14.0" } +workflow-dom = { version = "0.14.0" } +workflow-http = { version = "0.14.0" } +workflow-log = { version = "0.14.0" } +workflow-node = { version = "0.14.0" } +workflow-nw = { version = "0.14.0" } +workflow-rpc = { version = "0.14.0" } +workflow-serializer = { version = "0.14.0" } +workflow-store = { version = "0.14.0" } +workflow-terminal = { version = "0.14.0" } +workflow-wasm = { version = "0.14.0" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs # into a sibling folder from https://github.com/workflow-rs/workflow-rs -workflow-core = { path = "../workflow-rs/core" } -workflow-d3 = { path = "../workflow-rs/d3" } -workflow-dom = { path = "../workflow-rs/dom" } -workflow-http = { path = "../workflow-rs/http" } -workflow-log = { path = "../workflow-rs/log" } -workflow-node = { path = "../workflow-rs/node" } -workflow-nw = { path = "../workflow-rs/nw" } -workflow-rpc = { path = "../workflow-rs/rpc" } -workflow-serializer = { path = "../workflow-rs/serializer" } -workflow-store = { path = "../workflow-rs/store" } -workflow-terminal = { path = "../workflow-rs/terminal" } -workflow-wasm = { path = "../workflow-rs/wasm" } +# workflow-core = { path = "../workflow-rs/core" } +# workflow-d3 = { path = "../workflow-rs/d3" } +# workflow-dom = { path = "../workflow-rs/dom" } +# workflow-http = { path = "../workflow-rs/http" } +# workflow-log = { path = "../workflow-rs/log" } +# workflow-node = { path = "../workflow-rs/node" } +# workflow-nw = { path = "../workflow-rs/nw" } +# workflow-rpc = { path = "../workflow-rs/rpc" } +# workflow-serializer = { path = "../workflow-rs/serializer" } +# workflow-store = { path = "../workflow-rs/store" } +# workflow-terminal = { path = "../workflow-rs/terminal" } +# workflow-wasm = { path = "../workflow-rs/wasm" } # --- # workflow-core = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } From 1d37b63c95c2fee25789657e4a3fc0f12f425c29 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Wed, 10 Jul 2024 04:32:24 +0300 Subject: [PATCH 079/158] fix merge mishap --- wallet/core/src/account/variants/bip32watch.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/wallet/core/src/account/variants/bip32watch.rs b/wallet/core/src/account/variants/bip32watch.rs index eb2c0f01a5..cfadb745df 100644 --- a/wallet/core/src/account/variants/bip32watch.rs +++ b/wallet/core/src/account/variants/bip32watch.rs @@ -203,6 +203,7 @@ impl Account for Bip32Watch { BIP32_WATCH_ACCOUNT_KIND.into(), *self.id(), self.name(), + self.balance(), AssocPrvKeyDataIds::None, self.receive_address().ok(), self.change_address().ok(), From 2c521859dd064028954113c9887d227fab68fffd Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 16 Jul 2024 16:26:44 +0300 Subject: [PATCH 080/158] refactor systeminfo + update resolver target generation --- Cargo.lock | 22 ++++++ Cargo.toml | 1 + kaspad/src/daemon.rs | 4 + rpc/core/src/model/message.rs | 5 +- rpc/core/src/model/tests.rs | 2 +- rpc/grpc/core/proto/rpc.proto | 7 +- rpc/grpc/core/src/convert/message.rs | 3 + rpc/service/Cargo.toml | 2 +- rpc/service/src/service.rs | 19 ++--- rpc/wrpc/client/Resolvers.toml | 31 +++++--- rpc/wrpc/client/src/resolver.rs | 108 +++++++++++++++++++-------- utils/Cargo.toml | 2 + utils/src/lib.rs | 4 +- utils/src/sysinfo.rs | 45 +++++++++-- 14 files changed, 191 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a3f5aab7a..44dbe0c45c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3334,12 +3334,14 @@ dependencies = [ "ipnet", "itertools 0.11.0", "log", + "mac_address", "num_cpus", "parking_lot", "rand 0.8.5", "rlimit", "serde", "serde_json", + "sha2", "smallvec", "sysinfo", "thiserror", @@ -3971,6 +3973,16 @@ dependencies = [ "libc", ] +[[package]] +name = "mac_address" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8836fae9d0d4be2c8b4efcdd79e828a2faa058a90d005abf42f91cac5493a08e" +dependencies = [ + "nix", + "winapi", +] + [[package]] name = "mach" version = "0.3.2" @@ -4090,6 +4102,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "mimalloc" version = "0.1.43" @@ -4236,6 +4257,7 @@ dependencies = [ "cfg-if 1.0.0", "cfg_aliases 0.1.1", "libc", + "memoffset", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f04595844b..a0d9924502 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -197,6 +197,7 @@ lazy_static = "1.4.0" local-ip-address = "0.5.6" log = "0.4.20" log4rs = "1.2.0" +mac_address = "1.1.7" malachite-base = "0.4.4" malachite-nz = "0.4.4" md-5 = "0.10.6" diff --git a/kaspad/src/daemon.rs b/kaspad/src/daemon.rs index 0950ad8fab..4a6d60efc5 100644 --- a/kaspad/src/daemon.rs +++ b/kaspad/src/daemon.rs @@ -14,6 +14,7 @@ use kaspa_notify::{address::tracker::Tracker, subscription::context::Subscriptio use kaspa_rpc_service::service::RpcCoreService; use kaspa_txscript::caches::TxScriptCacheCounters; use kaspa_utils::networking::ContextualNetAddress; +use kaspa_utils::sysinfo::SystemInfo; use kaspa_utils_tower::counters::TowerConnectionCounters; use kaspa_addressmanager::AddressManager; @@ -402,6 +403,8 @@ do you confirm? (answer y/n or pass --yes to the Kaspad command line to confirm Arc::new(perf_monitor_builder.build()) }; + let system_info = SystemInfo::default(); + let notify_service = Arc::new(NotifyService::new(notification_root.clone(), notification_recv, subscription_context.clone())); let index_service: Option> = if args.utxoindex { // Use only a single thread for none-consensus databases @@ -465,6 +468,7 @@ do you confirm? (answer y/n or pass --yes to the Kaspad command line to confirm perf_monitor.clone(), p2p_tower_counters.clone(), grpc_tower_counters.clone(), + system_info, )); let grpc_service_broadcasters: usize = 3; // TODO: add a command line argument or derive from other arg/config/host-related fields let grpc_service = if !args.disable_grpc { diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 9745a37392..adfbd961ce 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1806,6 +1806,7 @@ impl Deserializer for GetSystemInfoRequest { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSystemInfoResponse { + pub system_id: Vec, pub cpu_physical_cores: u16, pub total_memory: u64, pub fd_limit: u32, @@ -1814,6 +1815,7 @@ pub struct GetSystemInfoResponse { impl Serializer for GetSystemInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; + store!(Vec, &self.system_id, writer)?; store!(u16, &self.cpu_physical_cores, writer)?; store!(u64, &self.total_memory, writer)?; store!(u32, &self.fd_limit, writer)?; @@ -1825,11 +1827,12 @@ impl Serializer for GetSystemInfoResponse { impl Deserializer for GetSystemInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; + let system_id = load!(Vec, reader)?; let cpu_physical_cores = load!(u16, reader)?; let total_memory = load!(u64, reader)?; let fd_limit = load!(u32, reader)?; - Ok(Self { cpu_physical_cores, total_memory, fd_limit }) + Ok(Self { system_id, cpu_physical_cores, total_memory, fd_limit }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index fba3e7a71d..758e5c883a 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -932,7 +932,7 @@ mod mockery { impl Mock for GetSystemInfoResponse { fn mock() -> Self { - GetSystemInfoResponse { cpu_physical_cores: mock(), total_memory: mock(), fd_limit: mock() } + GetSystemInfoResponse { system_id: mock(), cpu_physical_cores: mock(), total_memory: mock(), fd_limit: mock() } } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index 512ad3b5b7..b7f66d32ad 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -822,9 +822,10 @@ message GetSystemInfoRequestMessage{ } message GetSystemInfoResponseMessage{ - uint32 coreNum = 1; - uint64 totalMemory = 2; - uint32 fdLimit = 3; + string systemId = 1; + uint32 coreNum = 2; + uint64 totalMemory = 3; + uint32 fdLimit = 4; RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index e703fa723c..5a4a6b5b0c 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -26,6 +26,7 @@ use kaspa_rpc_core::{ RpcContextualPeerAddress, RpcError, RpcExtraData, RpcHash, RpcIpAddress, RpcNetworkType, RpcPeerAddress, RpcResult, SubmitBlockRejectReason, SubmitBlockReport, }; +use kaspa_utils::hex::*; use std::str::FromStr; macro_rules! from { @@ -435,6 +436,7 @@ from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetCo from!(&kaspa_rpc_core::GetSystemInfoRequest, protowire::GetSystemInfoRequestMessage); from!(item: RpcResult<&kaspa_rpc_core::GetSystemInfoResponse>, protowire::GetSystemInfoResponseMessage, { Self { + system_id : item.system_id.to_hex(), total_memory : item.total_memory, core_num : item.cpu_physical_cores as u32, fd_limit : item.fd_limit, @@ -857,6 +859,7 @@ try_from!(item: &protowire::GetConnectionsResponseMessage, RpcResult, { Self { + system_id: FromHex::from_hex(&item.system_id)?, total_memory: item.total_memory, cpu_physical_cores: item.core_num as u16, fd_limit: item.fd_limit, diff --git a/rpc/service/Cargo.toml b/rpc/service/Cargo.toml index d606d51533..54e9764088 100644 --- a/rpc/service/Cargo.toml +++ b/rpc/service/Cargo.toml @@ -33,4 +33,4 @@ async-trait.workspace = true log.workspace = true tokio.workspace = true triggered.workspace = true -workflow-rpc.workspace = true +workflow-rpc.workspace = true \ No newline at end of file diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index 1c5d0cf444..4c2028a960 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -69,7 +69,7 @@ use kaspa_utxoindex::api::UtxoIndexProxy; use std::{ collections::HashMap, iter::once, - sync::{atomic::Ordering, Arc, OnceLock}, + sync::{atomic::Ordering, Arc}, vec, }; use tokio::join; @@ -111,6 +111,7 @@ pub struct RpcCoreService { perf_monitor: Arc>>, p2p_tower_counters: Arc, grpc_tower_counters: Arc, + system_info: SystemInfo, } const RPC_CORE: &str = "rpc-core"; @@ -135,6 +136,7 @@ impl RpcCoreService { perf_monitor: Arc>>, p2p_tower_counters: Arc, grpc_tower_counters: Arc, + system_info: SystemInfo, ) -> Self { // This notifier UTXOs subscription granularity to index-processor or consensus notifier let policies = match index_notifier { @@ -210,6 +212,7 @@ impl RpcCoreService { perf_monitor, p2p_tower_counters, grpc_tower_counters, + system_info, } } @@ -978,14 +981,12 @@ NOTE: This error usually indicates an RPC conversion error between the node and _connection: Option<&DynRpcConnection>, _request: GetSystemInfoRequest, ) -> RpcResult { - static SYSTEM_INFO: OnceLock = OnceLock::new(); - - let system_info = SYSTEM_INFO.get_or_init(SystemInfo::default); - - let SystemInfo { cpu_physical_cores, total_memory, fd_limit } = system_info; - - let response = - GetSystemInfoResponse { cpu_physical_cores: *cpu_physical_cores, total_memory: *total_memory, fd_limit: *fd_limit }; + let response = GetSystemInfoResponse { + system_id: self.system_info.system_id.clone(), + cpu_physical_cores: self.system_info.cpu_physical_cores, + total_memory: self.system_info.total_memory, + fd_limit: self.system_info.fd_limit, + }; Ok(response) } diff --git a/rpc/wrpc/client/Resolvers.toml b/rpc/wrpc/client/Resolvers.toml index 8879512efe..29556d3e5e 100644 --- a/rpc/wrpc/client/Resolvers.toml +++ b/rpc/wrpc/client/Resolvers.toml @@ -1,15 +1,28 @@ -[[resolver]] -url = "https://*.kaspa-ng.org" -enable = true +# [[group]] +# enable = true +# template = "https://*.kaspa-ng.org" +# nodes = ["jack", "john", "paul", "luke"] -[[resolver]] -url = "https://*.kaspa-ng.io" -enable = true +# [[group]] +# enable = true +# template = "https://*.kaspa-ng.io" +# nodes = ["mark", "noah", "liam", "ryan"] -[[resolver]] -url = "https://*.kaspa-ng.net" +# [[group]] +# enable = true +# template = "https://*.kaspa-ng.net" +# nodes = ["max", "evan", "alex", "eric"] + +[[group]] enable = true +template = "https://*.kaspa.stream" +nodes = ["adam", "owen", "sean", "kyle"] + +# [[group]] +# enable = false +# template = "https://*." +# nodes = ["jake", "jace", "cody", "troy"] [[resolver]] -url = "http://127.0.0.1:8888" enable = false +address = "http://127.0.0.1:8888" diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index 4e40c09df0..beb0911ede 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -9,41 +9,49 @@ use rand::thread_rng; use workflow_core::runtime; use workflow_http::get_json; -const DEFAULT_VERSION: usize = 2; -const RESOLVERS_PER_FQDN: usize = 8; +const CURRENT_VERSION: usize = 2; +const RESOLVER_CONFIG: &str = include_str!("../Resolvers.toml"); #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResolverRecord { - pub url: String, + pub address: String, + pub enable: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ResolverGroup { + pub template: String, + pub nodes: Vec, pub enable: Option, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ResolverConfig { - resolver: Vec, + #[serde(rename = "group")] + groups: Vec, + #[serde(rename = "resolver")] + resolvers: Vec, } -fn try_parse_resolvers(toml: &str) -> Result> { - Ok(toml::from_str::(toml)? - .resolver +fn try_parse_resolvers(toml: &str) -> Result>> { + let config = toml::from_str::(toml)?; + + let mut resolvers = config + .resolvers .into_iter() - .filter_map(|resolver| resolver.enable.unwrap_or(true).then_some(resolver.url)) - .collect::>()) -} + .filter_map(|resolver| resolver.enable.unwrap_or(true).then_some(resolver.address)) + .collect::>(); -fn transform(source_list: Vec) -> Vec> { - let mut resolvers = vec![]; - for url in source_list.into_iter() { - if url.contains('*') { - for n in 0..RESOLVERS_PER_FQDN { - resolvers.push(Arc::new(url.replace('*', &format!("r{n}")))); - } - } else { - resolvers.push(Arc::new(url)); + let groups = config.groups.into_iter().filter(|group| group.enable.unwrap_or(true)).collect::>(); + + for group in groups { + let ResolverGroup { template, nodes, .. } = group; + for node in nodes { + resolvers.push(template.replace('*', &node)); } } - resolvers + Ok(resolvers.into_iter().map(Arc::new).collect::>()) } #[derive(Debug)] @@ -67,9 +75,8 @@ pub struct Resolver { impl Default for Resolver { fn default() -> Self { - let toml = include_str!("../Resolvers.toml"); - let urls = try_parse_resolvers(toml).expect("TOML: Unable to parse RPC Resolver list"); - Self { inner: Arc::new(Inner::new(transform(urls))) } + let urls = try_parse_resolvers(RESOLVER_CONFIG).expect("TOML: Unable to parse RPC Resolver list"); + Self { inner: Arc::new(Inner::new(urls)) } } } @@ -87,26 +94,27 @@ impl Resolver { } fn make_url(&self, url: &str, encoding: Encoding, network_id: NetworkId) -> String { - static SSL: OnceLock = OnceLock::new(); + static TLS: OnceLock<&'static str> = OnceLock::new(); - let ssl = *SSL.get_or_init(|| { + let tls = *TLS.get_or_init(|| { if runtime::is_web() { - js_sys::Reflect::get(&js_sys::global(), &"location".into()) + let tls = js_sys::Reflect::get(&js_sys::global(), &"location".into()) .and_then(|location| js_sys::Reflect::get(&location, &"protocol".into())) .ok() .and_then(|protocol| protocol.as_string()) .map(|protocol| protocol.starts_with("https")) - .unwrap_or(false) + .unwrap_or(false); + if tls { + "tls" + } else { + "any" + } } else { - false + "tls" } }); - if ssl { - format!("{}/v{}/rk/wrpc/tls/{}/{}", url, DEFAULT_VERSION, encoding, network_id) - } else { - format!("{}/v{}/rk/wrpc/any/{}/{}", url, DEFAULT_VERSION, encoding, network_id) - } + format!("{url}/v{CURRENT_VERSION}/kaspa/{network_id}/wrpc/{tls}/{encoding}") } async fn fetch_node_info(&self, url: &str, encoding: Encoding, network_id: NetworkId) -> Result { @@ -160,3 +168,37 @@ impl Resolver { Ok(nodes.url.clone()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_resolver_config_1() { + let toml = r#" + [[group]] + enable = true + template = "https://*.example.org" + nodes = ["alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta"] + + [[group]] + enable = true + template = "https://*.example.com" + nodes = ["iota", "kappa", "lambda", "mu", "nu", "xi", "omicron", "pi"] + + [[resolver]] + enable = true + address = "http://127.0.0.1:8888" + "#; + + let urls = try_parse_resolvers(toml).expect("TOML: Unable to parse RPC Resolver list"); + // println!("{:#?}", urls); + assert_eq!(urls.len(), 17); + } + + #[test] + fn test_resolver_config_2() { + let _urls = try_parse_resolvers(RESOLVER_CONFIG).expect("TOML: Unable to parse RPC Resolver list"); + // println!("{:#?}", urls); + } +} diff --git a/utils/Cargo.toml b/utils/Cargo.toml index c686a2b227..b4de1788e4 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -21,6 +21,7 @@ log.workspace = true num_cpus.workspace = true parking_lot.workspace = true serde.workspace = true +sha2.workspace = true smallvec.workspace = true sysinfo.workspace = true thiserror.workspace = true @@ -29,6 +30,7 @@ uuid.workspace = true wasm-bindgen.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +mac_address.workspace = true rlimit.workspace = true [dev-dependencies] diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 2ca954b046..8df30e745a 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -9,7 +9,6 @@ pub mod mem_size; pub mod networking; pub mod option; pub mod refs; -pub mod sysinfo; pub mod as_slice; @@ -192,4 +191,7 @@ pub mod sync; pub mod triggers; pub mod vec; +#[cfg(not(target_arch = "wasm32"))] pub mod fd_budget; +#[cfg(not(target_arch = "wasm32"))] +pub mod sysinfo; diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index a410b8f9f7..215afbc36d 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -1,20 +1,53 @@ use crate::fd_budget; +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::io::Read; +use std::sync::OnceLock; +#[derive(Debug, Clone)] pub struct SystemInfo { + pub system_id: Vec, pub cpu_physical_cores: u16, pub total_memory: u64, pub fd_limit: u32, } +static SYSTEM_INFO: OnceLock = OnceLock::new(); + impl Default for SystemInfo { fn default() -> Self { - let mut system = sysinfo::System::new(); - system.refresh_memory(); - let cpu_physical_cores = num_cpus::get() as u16; - let total_memory = system.total_memory(); + let system_info = SYSTEM_INFO.get_or_init(|| { + let mut system = sysinfo::System::new(); + system.refresh_memory(); + let cpu_physical_cores = num_cpus::get() as u16; + let total_memory = system.total_memory(); + // ~ + let fd_limit = fd_budget::limit() as u32; + // ~ + let some_id = if let Ok(mut file) = File::open("/etc/machine-id") { + // fetch the system id from /etc/machine-id + let mut machine_id = String::new(); + file.read_to_string(&mut machine_id).ok(); + machine_id + } else if let Ok(Some(mac)) = mac_address::get_mac_address() { + // fallback on the mac address + mac.to_string() + } else { + // fallback on a random value - should never happen + uuid::Uuid::new_v4().to_string() + }; + let mut sha256 = Sha256::default(); + sha256.update(some_id.as_bytes()); + let system_id = sha256.finalize().to_vec(); - let fd_limit = fd_budget::limit() as u32; + SystemInfo { system_id, cpu_physical_cores, total_memory, fd_limit } + }); + (*system_info).clone() + } +} - SystemInfo { cpu_physical_cores, total_memory, fd_limit } +impl AsRef for SystemInfo { + fn as_ref(&self) -> &SystemInfo { + self } } From 4c0b148bb231d5c2b16592b334c261d5629e7313 Mon Sep 17 00:00:00 2001 From: KaffinPX <73744616+KaffinPX@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:05:49 +0300 Subject: [PATCH 081/158] Custom input signatureScript ability (#69) * Refactorize some addresses and Script related parts * A ScriptBuilder example on TypeScript * addOps Opcodes are now BinaryT * Move txscript bindings to relevant folders * Sort lines of deps and imports * Lint * Prototype of custom sighash operations * Experimental * Add SighashType enum and option on SignInput * Format and a small fix * Clippy --- consensus/core/src/hashing/mod.rs | 2 + consensus/core/src/hashing/wasm.rs | 28 +++++++++++++ wallet/core/src/account/mod.rs | 2 +- wallet/core/src/tx/generator/pending.rs | 42 ++++++++++++++++++-- wallet/core/src/wasm/tx/generator/pending.rs | 19 ++++++++- 5 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 consensus/core/src/hashing/wasm.rs diff --git a/consensus/core/src/hashing/mod.rs b/consensus/core/src/hashing/mod.rs index aa0ac2d9b6..edcca1d034 100644 --- a/consensus/core/src/hashing/mod.rs +++ b/consensus/core/src/hashing/mod.rs @@ -5,6 +5,8 @@ pub mod header; pub mod sighash; pub mod sighash_type; pub mod tx; +#[cfg(feature = "wasm32-sdk")] +pub mod wasm; pub trait HasherExtensions { /// Writes the len as u64 little endian bytes diff --git a/consensus/core/src/hashing/wasm.rs b/consensus/core/src/hashing/wasm.rs new file mode 100644 index 0000000000..1c3b51a7a3 --- /dev/null +++ b/consensus/core/src/hashing/wasm.rs @@ -0,0 +1,28 @@ +use super::sighash_type::{self, SigHashType}; +use wasm_bindgen::prelude::*; + +/// Kaspa Sighash types allowed by consensus +/// @see {@link signInput} +/// @category Consensus +#[wasm_bindgen] +pub enum SighashType { + All, + None, + Single, + AllAnyOneCanPay, + NoneAnyOneCanPay, + SingleAnyOneCanPay, +} + +impl From for SigHashType { + fn from(sighash_type: SighashType) -> SigHashType { + match sighash_type { + SighashType::All => sighash_type::SIG_HASH_ALL, + SighashType::None => sighash_type::SIG_HASH_NONE, + SighashType::Single => sighash_type::SIG_HASH_SINGLE, + SighashType::AllAnyOneCanPay => sighash_type::SIG_HASH_ANY_ONE_CAN_PAY, + SighashType::NoneAnyOneCanPay => SigHashType(sighash_type::SIG_HASH_NONE.0 | sighash_type::SIG_HASH_ANY_ONE_CAN_PAY.0), + SighashType::SingleAnyOneCanPay => SigHashType(sighash_type::SIG_HASH_SINGLE.0 | sighash_type::SIG_HASH_ANY_ONE_CAN_PAY.0), + } + } +} diff --git a/wallet/core/src/account/mod.rs b/wallet/core/src/account/mod.rs index 56abdc787d..325ead81c6 100644 --- a/wallet/core/src/account/mod.rs +++ b/wallet/core/src/account/mod.rs @@ -538,7 +538,7 @@ pub trait DerivationCapableAccount: Account { let mut stream = generator.stream(); while let Some(transaction) = stream.try_next().await? { - transaction.try_sign_with_keys(&keys)?; + transaction.try_sign_with_keys(&keys, None)?; let id = transaction.try_submit(&rpc).await?; if let Some(notifier) = notifier { notifier(index, aggregate_utxo_count, balance, Some(id)); diff --git a/wallet/core/src/tx/generator/pending.rs b/wallet/core/src/tx/generator/pending.rs index cd757e54b6..7d07af74e4 100644 --- a/wallet/core/src/tx/generator/pending.rs +++ b/wallet/core/src/tx/generator/pending.rs @@ -8,7 +8,9 @@ use crate::result::Result; use crate::rpc::DynRpcApi; use crate::tx::{DataKind, Generator}; use crate::utxo::{UtxoContext, UtxoEntryId, UtxoEntryReference}; -use kaspa_consensus_core::sign::sign_with_multiple_v2; +use kaspa_consensus_core::hashing::sighash::{calc_schnorr_signature_hash, SigHashReusedValues}; +use kaspa_consensus_core::hashing::sighash_type::SigHashType; +use kaspa_consensus_core::sign::{sign_with_multiple_v2, Signed}; use kaspa_consensus_core::tx::{SignableTransaction, Transaction, TransactionId}; use kaspa_rpc_core::{RpcTransaction, RpcTransactionId}; @@ -223,9 +225,43 @@ impl PendingTransaction { Ok(()) } - pub fn try_sign_with_keys(&self, privkeys: &[[u8; 32]]) -> Result<()> { + pub fn sign_input(&self, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Result> { + // TODO: Move to sign.rs let mutable_tx = self.inner.signable_tx.lock()?.clone(); - let signed_tx = sign_with_multiple_v2(mutable_tx, privkeys).fully_signed()?; + let mut reused_values = SigHashReusedValues::new(); + + let hash = calc_schnorr_signature_hash(&mutable_tx.as_verifiable(), input_index, hash_type, &mut reused_values); + let msg = secp256k1::Message::from_digest_slice(hash.as_bytes().as_slice()).unwrap(); + let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key).unwrap(); + let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref(); + + // This represents OP_DATA_65 (since signature length is 64 bytes and SIGHASH_TYPE is one byte) + Ok(std::iter::once(65u8).chain(sig).chain([hash_type.to_u8()]).collect()) + } + + pub fn fill_input(&self, input_index: usize, signature_script: Vec) -> Result<()> { + let mut mutable_tx = self.inner.signable_tx.lock()?.clone(); + mutable_tx.tx.inputs[input_index].signature_script = signature_script; + *self.inner.signable_tx.lock().unwrap() = mutable_tx; + + Ok(()) + } + + pub fn try_sign_with_keys(&self, privkeys: &[[u8; 32]], check_fully_signed: Option) -> Result<()> { + let mutable_tx = self.inner.signable_tx.lock()?.clone(); + let signed = sign_with_multiple_v2(mutable_tx, privkeys); + + let signed_tx = match signed { + Signed::Fully(tx) => tx, + Signed::Partially(_) => { + if check_fully_signed.unwrap_or(true) { + signed.fully_signed()? + } else { + signed.unwrap() + } + } + }; + *self.inner.signable_tx.lock().unwrap() = signed_tx; Ok(()) } diff --git a/wallet/core/src/wasm/tx/generator/pending.rs b/wallet/core/src/wasm/tx/generator/pending.rs index 8de697e557..2257440d9a 100644 --- a/wallet/core/src/wasm/tx/generator/pending.rs +++ b/wallet/core/src/wasm/tx/generator/pending.rs @@ -4,7 +4,9 @@ use crate::tx::generator as native; use crate::wasm::PrivateKeyArrayT; use kaspa_consensus_client::{numeric, string}; use kaspa_consensus_client::{Transaction, TransactionT}; +use kaspa_consensus_core::hashing::wasm::SighashType; use kaspa_wallet_keys::privatekey::PrivateKey; +use kaspa_wasm_core::types::{BinaryT, HexString}; use kaspa_wrpc_wasm::RpcClient; /// @category Wallet SDK @@ -70,16 +72,29 @@ impl PendingTransaction { self.inner.utxo_entries().values().map(|utxo_entry| JsValue::from(utxo_entry.clone())).collect() } + #[wasm_bindgen(js_name = signInput)] + pub fn sign_input(&self, input_index: u8, private_key: &PrivateKey, sighash_type: Option) -> Result { + let signature = + self.inner.sign_input(input_index.into(), &private_key.secret_bytes(), sighash_type.unwrap_or(SighashType::All).into())?; + + Ok(signature.to_hex().into()) + } + + #[wasm_bindgen(js_name = fillInput)] + pub fn fill_input(&self, input_index: u8, signature_script: BinaryT) -> Result<()> { + self.inner.fill_input(input_index.into(), signature_script.try_as_vec_u8()?) + } + /// Sign transaction with supplied [`Array`] or [`PrivateKey`] or an array of /// raw private key bytes (encoded as `Uint8Array` or as hex strings) - pub fn sign(&self, js_value: PrivateKeyArrayT) -> Result<()> { + pub fn sign(&self, js_value: PrivateKeyArrayT, check_fully_signed: Option) -> Result<()> { if let Ok(keys) = js_value.dyn_into::() { let keys = keys .iter() .map(PrivateKey::try_cast_from) .collect::, kaspa_wallet_keys::error::Error>>()?; let mut keys = keys.iter().map(|key| key.as_ref().secret_bytes()).collect::>(); - self.inner.try_sign_with_keys(&keys)?; + self.inner.try_sign_with_keys(&keys, check_fully_signed)?; keys.zeroize(); Ok(()) } else { From 4a8f2cdfbf7ff4ecd97321e6884f3bf6b83056fc Mon Sep 17 00:00:00 2001 From: aspect Date: Tue, 16 Jul 2024 19:10:02 +0300 Subject: [PATCH 082/158] hex view for ScriptBuilder (#67) --- Cargo.lock | 3 + Cargo.toml | 5 +- crypto/txscript/Cargo.toml | 1 + crypto/txscript/src/script_builder.rs | 11 ++ crypto/txscript/src/wasm/builder.rs | 10 ++ wasm/core/Cargo.toml | 2 + wasm/core/src/hex.rs | 152 ++++++++++++++++++++++++++ wasm/core/src/lib.rs | 3 +- wasm/core/src/types.rs | 2 +- 9 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 wasm/core/src/hex.rs diff --git a/Cargo.lock b/Cargo.lock index dcaef44db0..6fbf15dc74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3377,6 +3377,7 @@ dependencies = [ "cfg-if 1.0.0", "criterion", "hex", + "hexplay", "indexmap 2.2.6", "itertools 0.11.0", "kaspa-addresses", @@ -3675,8 +3676,10 @@ name = "kaspa-wasm-core" version = "0.14.1" dependencies = [ "faster-hex 0.6.1", + "hexplay", "js-sys", "wasm-bindgen", + "workflow-wasm", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ee054e4f2a..ec54a70d22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -178,14 +178,13 @@ faster-hex = "0.6.1" # TODO "0.8.1" - fails unit tests fixedstr = { version = "0.5.4", features = ["serde"] } flate2 = "1.0.28" futures = { version = "0.3.29" } -futures-util = { version = "0.3.29", default-features = false, features = [ - "alloc", -] } +futures-util = { version = "0.3.29", default-features = false, features = ["alloc"] } getrandom = { version = "0.2.10", features = ["js"] } h2 = "0.3.21" heapless = "0.7.16" hex = { version = "0.4.3", features = ["serde"] } hex-literal = "0.4.1" +hexplay = "0.3.0" hmac = { version = "0.12.1", default-features = false } home = "0.5.5" igd-next = { version = "0.14.2", features = ["aio_tokio"] } diff --git a/crypto/txscript/Cargo.toml b/crypto/txscript/Cargo.toml index c7f352e1b5..dace708fd1 100644 --- a/crypto/txscript/Cargo.toml +++ b/crypto/txscript/Cargo.toml @@ -16,6 +16,7 @@ wasm32-sdk = [] blake2b_simd.workspace = true borsh.workspace = true cfg-if.workspace = true +hexplay.workspace = true indexmap.workspace = true itertools.workspace = true kaspa-addresses.workspace = true diff --git a/crypto/txscript/src/script_builder.rs b/crypto/txscript/src/script_builder.rs index f1d7d716a9..731c47680e 100644 --- a/crypto/txscript/src/script_builder.rs +++ b/crypto/txscript/src/script_builder.rs @@ -5,6 +5,7 @@ use crate::{ opcodes::{codes::*, OP_1_NEGATE_VAL, OP_DATA_MAX_VAL, OP_DATA_MIN_VAL, OP_SMALL_INT_MAX_VAL}, MAX_SCRIPTS_SIZE, MAX_SCRIPT_ELEMENT_SIZE, }; +use hexplay::{HexView, HexViewBuilder}; use thiserror::Error; /// DEFAULT_SCRIPT_ALLOC is the default size used for the backing array @@ -248,6 +249,16 @@ impl ScriptBuilder { let trimmed = &buffer[0..trimmed_size]; self.add_data(trimmed) } + + /// Return [`HexViewBuilder`] for the script + pub fn hex_view_builder(&self) -> HexViewBuilder<'_> { + HexViewBuilder::new(&self.script) + } + + /// Return ready to use [`HexView`] for the script + pub fn hex_view(&self, offset: usize, width: usize) -> HexView<'_> { + HexViewBuilder::new(&self.script).address_offset(offset).row_width(width).finish() + } } impl Default for ScriptBuilder { diff --git a/crypto/txscript/src/wasm/builder.rs b/crypto/txscript/src/wasm/builder.rs index ce5d3ba3c7..8a6688fd91 100644 --- a/crypto/txscript/src/wasm/builder.rs +++ b/crypto/txscript/src/wasm/builder.rs @@ -2,6 +2,7 @@ use crate::result::Result; use crate::{script_builder as native, standard}; use kaspa_consensus_core::tx::ScriptPublicKey; use kaspa_utils::hex::ToHex; +use kaspa_wasm_core::hex::{HexViewConfig, HexViewConfigT}; use kaspa_wasm_core::types::{BinaryT, HexString}; use std::cell::{Ref, RefCell, RefMut}; use std::rc::Rc; @@ -168,4 +169,13 @@ impl ScriptBuilder { Ok(generated_script.to_hex().into()) } + + #[wasm_bindgen(js_name = "hexView")] + pub fn hex_view(&self, args: Option) -> Result { + let inner = self.inner(); + let script = inner.script(); + + let config = args.map(HexViewConfig::try_from).transpose()?.unwrap_or_default(); + Ok(config.build(script).to_string()) + } } diff --git a/wasm/core/Cargo.toml b/wasm/core/Cargo.toml index 7ecda22a9a..4c765e9bd6 100644 --- a/wasm/core/Cargo.toml +++ b/wasm/core/Cargo.toml @@ -15,6 +15,8 @@ wasm32-sdk = [] wasm-bindgen.workspace = true js-sys.workspace = true faster-hex.workspace = true +hexplay.workspace = true +workflow-wasm.workspace = true [lints] workspace = true diff --git a/wasm/core/src/hex.rs b/wasm/core/src/hex.rs new file mode 100644 index 0000000000..8c187e03f5 --- /dev/null +++ b/wasm/core/src/hex.rs @@ -0,0 +1,152 @@ +//! +//! Hex module provides a way to display binary data in a human-readable format. +//! + +use hexplay::{ + color::{Color, Spec}, + HexView, HexViewBuilder, +}; +use std::ops::Range; +use std::str::FromStr; +use wasm_bindgen::prelude::*; +use workflow_wasm::prelude::*; + +type Result = std::result::Result; + +#[derive(Default)] +pub struct HexViewConfig { + pub offset: Option, + pub replace_char: Option, + pub width: Option, + pub colors: Option)>>, +} + +impl HexViewConfig { + pub fn build(self, slice: &[u8]) -> HexView<'_> { + let mut builder = HexViewBuilder::new(slice); + + if let Some(offset) = self.offset { + builder = builder.address_offset(offset); + } + + if let Some(replace_char) = self.replace_char { + builder = builder.replacement_character(replace_char); + } + + if let Some(width) = self.width { + builder = builder.row_width(width); + } + + if let Some(colors) = self.colors { + if !colors.is_empty() { + builder = builder.add_colors(colors); + } + } + + builder.finish() + } +} + +pub struct ColorRange { + pub color: Option, + pub background: Option, + pub range: Range, +} + +impl ColorRange { + fn new(color: Option, background: Option, range: Range) -> Self { + Self { color, background, range } + } + + fn into_tuple(self) -> (Spec, Range) { + let mut spec = Spec::new(); + spec.set_fg(self.color); + spec.set_bg(self.background); + + (spec, self.range) + } +} + +#[wasm_bindgen(typescript_custom_section)] +const TS_HEX_VIEW: &'static str = r#" +/** + * Color range configuration for Hex View. + * + * @category General + */ +export interface IHexViewColor { + start: number; + end: number; + color?: string; + background?: string; +} + +/** + * Configuration interface for Hex View. + * + * @category General + */ +export interface IHexViewConfig { + offset? : number; + replacementCharacter? : string; + width? : number; + colors? : IHexViewColor[]; +} +"#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "IHexViewColor")] + pub type HexViewColorT; + #[wasm_bindgen(extends = js_sys::Array, typescript_type = "IHexViewColor[]")] + pub type HexViewColorArrayT; + #[wasm_bindgen(typescript_type = "IHexViewConfig")] + pub type HexViewConfigT; +} + +impl TryFrom for ColorRange { + type Error = JsValue; + fn try_from(js_value: JsValue) -> Result { + if let Some(object) = js_sys::Object::try_from(&js_value) { + let start = object.get_u32("start")? as usize; + let end = object.get_u32("end")? as usize; + + let color = object.get_string("color").ok(); + let color = + color.map(|color| Color::from_str(color.as_str()).map_err(|e| JsValue::from_str(&e.to_string()))).transpose()?; + + let background = object.get_string("background").ok(); + let background = background + .map(|background| Color::from_str(background.as_str()).map_err(|e| JsValue::from_str(&e.to_string()))) + .transpose()?; + + Ok(ColorRange::new(color, background, start..end)) + } else { + Err(JsValue::from_str("color range must be an object")) + } + } +} + +pub fn try_to_color_vec(js_value: JsValue) -> Result)>> { + if js_value.is_array() { + let list = js_sys::Array::from(&js_value).iter().map(TryFrom::try_from).collect::>>()?; + Ok(list.into_iter().map(ColorRange::into_tuple).collect::>()) + } else { + let tuple = ColorRange::try_from(js_value).map(ColorRange::into_tuple)?; + Ok(vec![tuple]) + } +} + +impl TryFrom for HexViewConfig { + type Error = JsValue; + fn try_from(js_value: HexViewConfigT) -> Result { + let object = js_sys::Object::try_from(&js_value).ok_or_else(|| JsValue::from_str("HexView config must be an object"))?; + + let offset = object.get_u32("offset").ok().map(|v| v as usize); + let replace_char = object.get_string("replacementCharacter").ok().map(|s| s.chars().next().unwrap_or(' ')); + let width = object.get_u32("width").ok().map(|v| v as usize); + let colors = object.get_value("colors").ok().map(try_to_color_vec).transpose()?; + + Ok(HexViewConfig { offset, replace_char, width, colors }) + } +} diff --git a/wasm/core/src/lib.rs b/wasm/core/src/lib.rs index 1710c11fea..d99f297310 100644 --- a/wasm/core/src/lib.rs +++ b/wasm/core/src/lib.rs @@ -1,4 +1,3 @@ pub mod events; +pub mod hex; pub mod types; - -// pub use types::*; diff --git a/wasm/core/src/types.rs b/wasm/core/src/types.rs index fc898ee8b0..7e8e29335e 100644 --- a/wasm/core/src/types.rs +++ b/wasm/core/src/types.rs @@ -30,7 +30,7 @@ impl From for HexString { impl TryFrom for String { type Error = &'static str; - fn try_from(value: HexString) -> Result { + fn try_from(value: HexString) -> std::result::Result { value.as_string().ok_or("Supplied value is not a string") } } From dc217dfbeb9a99ab2a907cad035806eb804da3b6 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 18 Jul 2024 14:20:36 +0300 Subject: [PATCH 083/158] add git_hash to system_id --- rpc/core/src/model/message.rs | 11 ++-- rpc/core/src/model/tests.rs | 8 ++- rpc/grpc/core/proto/rpc.proto | 7 +-- rpc/grpc/core/src/convert/message.rs | 6 ++- rpc/service/src/service.rs | 1 + rpc/wrpc/client/src/node.rs | 6 --- rpc/wrpc/wasm/src/client.rs | 22 ++++---- utils/src/sysinfo.rs | 79 +++++++++++++++++++++------- 8 files changed, 93 insertions(+), 47 deletions(-) diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index adfbd961ce..0661a7af5b 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1806,7 +1806,8 @@ impl Deserializer for GetSystemInfoRequest { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSystemInfoResponse { - pub system_id: Vec, + pub system_id: Option>, + pub git_hash: Option>, pub cpu_physical_cores: u16, pub total_memory: u64, pub fd_limit: u32, @@ -1815,7 +1816,8 @@ pub struct GetSystemInfoResponse { impl Serializer for GetSystemInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(Vec, &self.system_id, writer)?; + store!(Option>, &self.system_id, writer)?; + store!(Option>, &self.git_hash, writer)?; store!(u16, &self.cpu_physical_cores, writer)?; store!(u64, &self.total_memory, writer)?; store!(u32, &self.fd_limit, writer)?; @@ -1827,12 +1829,13 @@ impl Serializer for GetSystemInfoResponse { impl Deserializer for GetSystemInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let system_id = load!(Vec, reader)?; + let system_id = load!(Option>, reader)?; + let git_hash = load!(Option>, reader)?; let cpu_physical_cores = load!(u16, reader)?; let total_memory = load!(u64, reader)?; let fd_limit = load!(u32, reader)?; - Ok(Self { system_id, cpu_physical_cores, total_memory, fd_limit }) + Ok(Self { system_id, git_hash, cpu_physical_cores, total_memory, fd_limit }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index 758e5c883a..950703a58b 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -932,7 +932,13 @@ mod mockery { impl Mock for GetSystemInfoResponse { fn mock() -> Self { - GetSystemInfoResponse { system_id: mock(), cpu_physical_cores: mock(), total_memory: mock(), fd_limit: mock() } + GetSystemInfoResponse { + system_id: mock(), + git_hash: mock(), + cpu_physical_cores: mock(), + total_memory: mock(), + fd_limit: mock(), + } } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index b7f66d32ad..9c59d95d48 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -823,9 +823,10 @@ message GetSystemInfoRequestMessage{ message GetSystemInfoResponseMessage{ string systemId = 1; - uint32 coreNum = 2; - uint64 totalMemory = 3; - uint32 fdLimit = 4; + string gitHash = 2; + uint32 coreNum = 3; + uint64 totalMemory = 4; + uint32 fdLimit = 5; RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index 5a4a6b5b0c..d1f02f1e4c 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -436,7 +436,8 @@ from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetCo from!(&kaspa_rpc_core::GetSystemInfoRequest, protowire::GetSystemInfoRequestMessage); from!(item: RpcResult<&kaspa_rpc_core::GetSystemInfoResponse>, protowire::GetSystemInfoResponseMessage, { Self { - system_id : item.system_id.to_hex(), + system_id : item.system_id.as_ref().map(|system_id|system_id.to_hex()).unwrap_or_default(), + git_hash : item.git_hash.as_ref().map(|git_hash|git_hash.to_hex()).unwrap_or_default(), total_memory : item.total_memory, core_num : item.cpu_physical_cores as u32, fd_limit : item.fd_limit, @@ -859,7 +860,8 @@ try_from!(item: &protowire::GetConnectionsResponseMessage, RpcResult, { Self { - system_id: FromHex::from_hex(&item.system_id)?, + system_id: (!item.system_id.is_empty()).then(|| FromHex::from_hex(&item.system_id)).transpose()?, + git_hash: (!item.git_hash.is_empty()).then(|| FromHex::from_hex(&item.git_hash)).transpose()?, total_memory: item.total_memory, cpu_physical_cores: item.core_num as u16, fd_limit: item.fd_limit, diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index 4c2028a960..402bbaa1c6 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -983,6 +983,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and ) -> RpcResult { let response = GetSystemInfoResponse { system_id: self.system_info.system_id.clone(), + git_hash: self.system_info.git_hash.clone(), cpu_physical_cores: self.system_info.cpu_physical_cores, total_memory: self.system_info.total_memory, fd_limit: self.system_info.fd_limit, diff --git a/rpc/wrpc/client/src/node.rs b/rpc/wrpc/client/src/node.rs index 4afb0f1b7a..7af7ad1568 100644 --- a/rpc/wrpc/client/src/node.rs +++ b/rpc/wrpc/client/src/node.rs @@ -15,12 +15,6 @@ pub struct NodeDescriptor { /// The URL of the node WebSocket (wRPC URL). #[wasm_bindgen(getter_with_clone)] pub url: String, - /// Optional name of the node provider. - #[wasm_bindgen(getter_with_clone)] - pub provider_name: Option, - /// Optional site URL of the node provider. - #[wasm_bindgen(getter_with_clone)] - pub provider_url: Option, } impl Eq for NodeDescriptor {} diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index bb52f98740..ae5c409894 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -367,17 +367,17 @@ impl RpcClient { self.inner.client.node_descriptor().map(|node| node.id.clone()) } - /// Optional: public node provider name. - #[wasm_bindgen(getter, js_name = "providerName")] - pub fn resolver_node_provider_name(&self) -> Option { - self.inner.client.node_descriptor().and_then(|node| node.provider_name.clone()) - } - - /// Optional: public node provider URL. - #[wasm_bindgen(getter, js_name = "providerUrl")] - pub fn resolver_node_provider_url(&self) -> Option { - self.inner.client.node_descriptor().and_then(|node| node.provider_url.clone()) - } + // /// Optional: public node provider name. + // #[wasm_bindgen(getter, js_name = "providerName")] + // pub fn resolver_node_provider_name(&self) -> Option { + // self.inner.client.node_descriptor().and_then(|node| node.provider_name.clone()) + // } + + // /// Optional: public node provider URL. + // #[wasm_bindgen(getter, js_name = "providerUrl")] + // pub fn resolver_node_provider_url(&self) -> Option { + // self.inner.client.node_descriptor().and_then(|node| node.provider_url.clone()) + // } /// Connect to the Kaspa RPC server. This function starts a background /// task that connects and reconnects to the server if the connection diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index 215afbc36d..0add0b5fb2 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -1,4 +1,5 @@ use crate::fd_budget; +use crate::hex::FromHex; use sha2::{Digest, Sha256}; use std::fs::File; use std::io::Read; @@ -6,7 +7,8 @@ use std::sync::OnceLock; #[derive(Debug, Clone)] pub struct SystemInfo { - pub system_id: Vec, + pub system_id: Option>, + pub git_hash: Option>, pub cpu_physical_cores: u16, pub total_memory: u64, pub fd_limit: u32, @@ -21,31 +23,68 @@ impl Default for SystemInfo { system.refresh_memory(); let cpu_physical_cores = num_cpus::get() as u16; let total_memory = system.total_memory(); - // ~ let fd_limit = fd_budget::limit() as u32; - // ~ - let some_id = if let Ok(mut file) = File::open("/etc/machine-id") { - // fetch the system id from /etc/machine-id - let mut machine_id = String::new(); - file.read_to_string(&mut machine_id).ok(); - machine_id - } else if let Ok(Some(mac)) = mac_address::get_mac_address() { - // fallback on the mac address - mac.to_string() - } else { - // fallback on a random value - should never happen - uuid::Uuid::new_v4().to_string() - }; - let mut sha256 = Sha256::default(); - sha256.update(some_id.as_bytes()); - let system_id = sha256.finalize().to_vec(); - - SystemInfo { system_id, cpu_physical_cores, total_memory, fd_limit } + let system_id = Self::try_system_id(); + let git_hash = Self::try_git_hash_as_vec(); + + SystemInfo { system_id, git_hash, cpu_physical_cores, total_memory, fd_limit } }); (*system_info).clone() } } +impl SystemInfo { + /// Obtain a unique system (machine) identifier. + fn try_system_id() -> Option> { + let some_id = if let Ok(mut file) = File::open("/etc/machine-id") { + // fetch the system id from /etc/machine-id + let mut machine_id = String::new(); + file.read_to_string(&mut machine_id).ok(); + machine_id + } else if let Ok(Some(mac)) = mac_address::get_mac_address() { + // fallback on the mac address + mac.to_string() + } else { + // 🤷 + return None; + }; + let mut sha256 = Sha256::default(); + sha256.update(some_id.as_bytes()); + Some(sha256.finalize().to_vec()) + } + + /// Check if the codebase is built under a Git repository + /// and return the hash of the current commit as `Vec`. + fn try_git_hash_as_vec() -> Option> { + Vec::::from_hex(&Self::try_git_hash_as_string()?).ok() + } + + /// Check if the codebase is built under Git repository + /// and return the hash of the current commit as `String`. + fn try_git_hash_as_string() -> Option { + let current_exe = std::env::current_exe().ok()?; + // assume `folder/target/release/binary`, cascade back to `folder/` + let path = current_exe.as_path().parent()?.parent()?.parent()?; + + let git_folder = path.join(".git"); + if git_folder.is_dir() { + let head = git_folder.join("HEAD"); + if head.is_file() { + let head = std::fs::read_to_string(head).ok()?; + if head.starts_with("ref: ") { + let head = head.trim_start_matches("ref: "); + let head = git_folder.join(head); + if head.is_file() { + return std::fs::read_to_string(head).ok(); + } + } + } + } + + None + } +} + impl AsRef for SystemInfo { fn as_ref(&self) -> &SystemInfo { self From 94cb8df11ffe453414d694aa56042be38bf3eacd Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 18 Jul 2024 14:22:51 +0300 Subject: [PATCH 084/158] add git_hash to system_id --- rpc/core/src/model/message.rs | 11 ++-- rpc/core/src/model/tests.rs | 8 ++- rpc/grpc/core/proto/rpc.proto | 7 +-- rpc/grpc/core/src/convert/message.rs | 6 ++- rpc/service/src/service.rs | 1 + rpc/wrpc/client/src/node.rs | 6 --- rpc/wrpc/wasm/src/client.rs | 22 ++++---- utils/src/sysinfo.rs | 79 +++++++++++++++++++++------- 8 files changed, 93 insertions(+), 47 deletions(-) diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index adfbd961ce..0661a7af5b 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1806,7 +1806,8 @@ impl Deserializer for GetSystemInfoRequest { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSystemInfoResponse { - pub system_id: Vec, + pub system_id: Option>, + pub git_hash: Option>, pub cpu_physical_cores: u16, pub total_memory: u64, pub fd_limit: u32, @@ -1815,7 +1816,8 @@ pub struct GetSystemInfoResponse { impl Serializer for GetSystemInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - store!(Vec, &self.system_id, writer)?; + store!(Option>, &self.system_id, writer)?; + store!(Option>, &self.git_hash, writer)?; store!(u16, &self.cpu_physical_cores, writer)?; store!(u64, &self.total_memory, writer)?; store!(u32, &self.fd_limit, writer)?; @@ -1827,12 +1829,13 @@ impl Serializer for GetSystemInfoResponse { impl Deserializer for GetSystemInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let system_id = load!(Vec, reader)?; + let system_id = load!(Option>, reader)?; + let git_hash = load!(Option>, reader)?; let cpu_physical_cores = load!(u16, reader)?; let total_memory = load!(u64, reader)?; let fd_limit = load!(u32, reader)?; - Ok(Self { system_id, cpu_physical_cores, total_memory, fd_limit }) + Ok(Self { system_id, git_hash, cpu_physical_cores, total_memory, fd_limit }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index 758e5c883a..950703a58b 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -932,7 +932,13 @@ mod mockery { impl Mock for GetSystemInfoResponse { fn mock() -> Self { - GetSystemInfoResponse { system_id: mock(), cpu_physical_cores: mock(), total_memory: mock(), fd_limit: mock() } + GetSystemInfoResponse { + system_id: mock(), + git_hash: mock(), + cpu_physical_cores: mock(), + total_memory: mock(), + fd_limit: mock(), + } } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index b7f66d32ad..9c59d95d48 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -823,9 +823,10 @@ message GetSystemInfoRequestMessage{ message GetSystemInfoResponseMessage{ string systemId = 1; - uint32 coreNum = 2; - uint64 totalMemory = 3; - uint32 fdLimit = 4; + string gitHash = 2; + uint32 coreNum = 3; + uint64 totalMemory = 4; + uint32 fdLimit = 5; RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index 5a4a6b5b0c..d1f02f1e4c 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -436,7 +436,8 @@ from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetCo from!(&kaspa_rpc_core::GetSystemInfoRequest, protowire::GetSystemInfoRequestMessage); from!(item: RpcResult<&kaspa_rpc_core::GetSystemInfoResponse>, protowire::GetSystemInfoResponseMessage, { Self { - system_id : item.system_id.to_hex(), + system_id : item.system_id.as_ref().map(|system_id|system_id.to_hex()).unwrap_or_default(), + git_hash : item.git_hash.as_ref().map(|git_hash|git_hash.to_hex()).unwrap_or_default(), total_memory : item.total_memory, core_num : item.cpu_physical_cores as u32, fd_limit : item.fd_limit, @@ -859,7 +860,8 @@ try_from!(item: &protowire::GetConnectionsResponseMessage, RpcResult, { Self { - system_id: FromHex::from_hex(&item.system_id)?, + system_id: (!item.system_id.is_empty()).then(|| FromHex::from_hex(&item.system_id)).transpose()?, + git_hash: (!item.git_hash.is_empty()).then(|| FromHex::from_hex(&item.git_hash)).transpose()?, total_memory: item.total_memory, cpu_physical_cores: item.core_num as u16, fd_limit: item.fd_limit, diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index 4c2028a960..402bbaa1c6 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -983,6 +983,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and ) -> RpcResult { let response = GetSystemInfoResponse { system_id: self.system_info.system_id.clone(), + git_hash: self.system_info.git_hash.clone(), cpu_physical_cores: self.system_info.cpu_physical_cores, total_memory: self.system_info.total_memory, fd_limit: self.system_info.fd_limit, diff --git a/rpc/wrpc/client/src/node.rs b/rpc/wrpc/client/src/node.rs index 4afb0f1b7a..7af7ad1568 100644 --- a/rpc/wrpc/client/src/node.rs +++ b/rpc/wrpc/client/src/node.rs @@ -15,12 +15,6 @@ pub struct NodeDescriptor { /// The URL of the node WebSocket (wRPC URL). #[wasm_bindgen(getter_with_clone)] pub url: String, - /// Optional name of the node provider. - #[wasm_bindgen(getter_with_clone)] - pub provider_name: Option, - /// Optional site URL of the node provider. - #[wasm_bindgen(getter_with_clone)] - pub provider_url: Option, } impl Eq for NodeDescriptor {} diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index bb52f98740..ae5c409894 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -367,17 +367,17 @@ impl RpcClient { self.inner.client.node_descriptor().map(|node| node.id.clone()) } - /// Optional: public node provider name. - #[wasm_bindgen(getter, js_name = "providerName")] - pub fn resolver_node_provider_name(&self) -> Option { - self.inner.client.node_descriptor().and_then(|node| node.provider_name.clone()) - } - - /// Optional: public node provider URL. - #[wasm_bindgen(getter, js_name = "providerUrl")] - pub fn resolver_node_provider_url(&self) -> Option { - self.inner.client.node_descriptor().and_then(|node| node.provider_url.clone()) - } + // /// Optional: public node provider name. + // #[wasm_bindgen(getter, js_name = "providerName")] + // pub fn resolver_node_provider_name(&self) -> Option { + // self.inner.client.node_descriptor().and_then(|node| node.provider_name.clone()) + // } + + // /// Optional: public node provider URL. + // #[wasm_bindgen(getter, js_name = "providerUrl")] + // pub fn resolver_node_provider_url(&self) -> Option { + // self.inner.client.node_descriptor().and_then(|node| node.provider_url.clone()) + // } /// Connect to the Kaspa RPC server. This function starts a background /// task that connects and reconnects to the server if the connection diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index 215afbc36d..59c822e9bd 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -1,4 +1,5 @@ use crate::fd_budget; +use crate::hex::FromHex; use sha2::{Digest, Sha256}; use std::fs::File; use std::io::Read; @@ -6,7 +7,8 @@ use std::sync::OnceLock; #[derive(Debug, Clone)] pub struct SystemInfo { - pub system_id: Vec, + pub system_id: Option>, + pub git_hash: Option>, pub cpu_physical_cores: u16, pub total_memory: u64, pub fd_limit: u32, @@ -21,31 +23,68 @@ impl Default for SystemInfo { system.refresh_memory(); let cpu_physical_cores = num_cpus::get() as u16; let total_memory = system.total_memory(); - // ~ let fd_limit = fd_budget::limit() as u32; - // ~ - let some_id = if let Ok(mut file) = File::open("/etc/machine-id") { - // fetch the system id from /etc/machine-id - let mut machine_id = String::new(); - file.read_to_string(&mut machine_id).ok(); - machine_id - } else if let Ok(Some(mac)) = mac_address::get_mac_address() { - // fallback on the mac address - mac.to_string() - } else { - // fallback on a random value - should never happen - uuid::Uuid::new_v4().to_string() - }; - let mut sha256 = Sha256::default(); - sha256.update(some_id.as_bytes()); - let system_id = sha256.finalize().to_vec(); - - SystemInfo { system_id, cpu_physical_cores, total_memory, fd_limit } + let system_id = Self::try_system_id(); + let git_hash = Self::try_git_hash_as_vec(); + + SystemInfo { system_id, git_hash, cpu_physical_cores, total_memory, fd_limit } }); (*system_info).clone() } } +impl SystemInfo { + /// Obtain a unique system (machine) identifier. + fn try_system_id() -> Option> { + let some_id = if let Ok(mut file) = File::open("/etc/machine-id") { + // fetch the system id from /etc/machine-id + let mut machine_id = String::new(); + file.read_to_string(&mut machine_id).ok(); + machine_id + } else if let Ok(Some(mac)) = mac_address::get_mac_address() { + // fallback on the mac address + mac.to_string() + } else { + // 🤷 + return None; + }; + let mut sha256 = Sha256::default(); + sha256.update(some_id.as_bytes()); + Some(sha256.finalize().to_vec()) + } + + /// Check if the codebase is built under a Git repository + /// and return the hash of the current commit as `Vec`. + fn try_git_hash_as_vec() -> Option> { + Vec::::from_hex(&Self::try_git_hash_as_string()?).ok() + } + + /// Check if the codebase is built under Git repository + /// and return the hash of the current commit as `String`. + fn try_git_hash_as_string() -> Option { + let current_exe = std::env::current_exe().ok()?; + // assume `folder/target/release/binary`, cascade back to `folder/` + let path = current_exe.as_path().parent()?.parent()?.parent()?; + + let git_folder = path.join(".git"); + if git_folder.is_dir() { + let head = git_folder.join("HEAD"); + if head.is_file() { + let head = std::fs::read_to_string(head).ok()?; + if head.starts_with("ref: ") { + let head = head.trim_start_matches("ref: "); + let head = git_folder.join(head.trim()); + if head.is_file() { + return std::fs::read_to_string(head).ok(); + } + } + } + } + + None + } +} + impl AsRef for SystemInfo { fn as_ref(&self) -> &SystemInfo { self From 4da95a3aa8aa2f5e453aea411ed1aa9940e46c15 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 18 Jul 2024 15:56:15 +0300 Subject: [PATCH 085/158] wip --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5644e06dea..925a7c9479 100644 --- a/README.md +++ b/README.md @@ -415,4 +415,3 @@ Logging in `kaspad` and `simpa` can be [filtered](https://docs.rs/env_logger/0.1 - From c417edc21feb359aa183d8ea85cc52a950aac98b Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 18 Jul 2024 16:14:20 +0300 Subject: [PATCH 086/158] wip --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 925a7c9479..22b4ef7ca6 100644 --- a/README.md +++ b/README.md @@ -414,4 +414,3 @@ Logging in `kaspad` and `simpa` can be [filtered](https://docs.rs/env_logger/0.1 - From ad4ab9157562412a6db90c2b671b719b0132ca1b Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 18 Jul 2024 16:28:15 +0300 Subject: [PATCH 087/158] refactor git head fetch to use build.rs --- utils/build.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++ utils/src/sysinfo.rs | 29 +++------------------------ 2 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 utils/build.rs diff --git a/utils/build.rs b/utils/build.rs new file mode 100644 index 0000000000..b7b0da8db4 --- /dev/null +++ b/utils/build.rs @@ -0,0 +1,47 @@ +use std::env; +use std::path::*; + +struct GitHead { + head_path: String, + head_ref_path: String, + commit_hash: String, +} + +fn main() { + + if env::var("RUSTY_KASPA_NO_COMMIT_HASH").is_err() { + if let Some(GitHead { head_path, head_ref_path, commit_hash }) = try_git_head() { + println!("cargo::rerun-if-changed={head_path}"); + println!("cargo::rerun-if-changed={head_ref_path}"); + println!("cargo:rustc-env=RUSTY_KASPA_GIT_COMMIT_HASH={commit_hash}"); + } + } +} + +fn try_git_head() -> Option { + let cargo_manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let path = cargo_manifest_dir.as_path().parent()?; + + let git_folder = path.join(".git"); + if git_folder.is_dir() { + let head_path = git_folder.join("HEAD"); + if head_path.is_file() { + let head = std::fs::read_to_string(&head_path).ok()?; + if head.starts_with("ref: ") { + let head_ref_path = head.trim_start_matches("ref: "); + let head_ref_path = git_folder.join(head_ref_path.trim()); + if head_ref_path.is_file() { + if let Ok(commit_hash) = std::fs::read_to_string(&head_ref_path) { + return Some(GitHead { + head_path: head_path.to_str().unwrap().to_string(), + head_ref_path: head_ref_path.to_str().unwrap().to_string(), + commit_hash : commit_hash.trim().to_string() + }); + } + } + } + } + } + + None +} \ No newline at end of file diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index 59c822e9bd..b11b394d7f 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -16,6 +16,8 @@ pub struct SystemInfo { static SYSTEM_INFO: OnceLock = OnceLock::new(); +const GIT_HASH: &str = env!("RUSTY_KASPA_GIT_COMMIT_HASH"); + impl Default for SystemInfo { fn default() -> Self { let system_info = SYSTEM_INFO.get_or_init(|| { @@ -56,32 +58,7 @@ impl SystemInfo { /// Check if the codebase is built under a Git repository /// and return the hash of the current commit as `Vec`. fn try_git_hash_as_vec() -> Option> { - Vec::::from_hex(&Self::try_git_hash_as_string()?).ok() - } - - /// Check if the codebase is built under Git repository - /// and return the hash of the current commit as `String`. - fn try_git_hash_as_string() -> Option { - let current_exe = std::env::current_exe().ok()?; - // assume `folder/target/release/binary`, cascade back to `folder/` - let path = current_exe.as_path().parent()?.parent()?.parent()?; - - let git_folder = path.join(".git"); - if git_folder.is_dir() { - let head = git_folder.join("HEAD"); - if head.is_file() { - let head = std::fs::read_to_string(head).ok()?; - if head.starts_with("ref: ") { - let head = head.trim_start_matches("ref: "); - let head = git_folder.join(head.trim()); - if head.is_file() { - return std::fs::read_to_string(head).ok(); - } - } - } - } - - None + FromHex::from_hex(GIT_HASH).ok() } } From 28c6b97cdda989752a2ad0a8a86bea30c3c0ceec Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 18 Jul 2024 16:40:13 +0300 Subject: [PATCH 088/158] comment --- utils/src/sysinfo.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index b11b394d7f..4ce6a37626 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -16,6 +16,7 @@ pub struct SystemInfo { static SYSTEM_INFO: OnceLock = OnceLock::new(); +// generates by `build.rs` const GIT_HASH: &str = env!("RUSTY_KASPA_GIT_COMMIT_HASH"); impl Default for SystemInfo { From 3b80a8bb9d4b5706094877bb1e338c24af2b9cbf Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 19 Jul 2024 11:24:25 +0300 Subject: [PATCH 089/158] split utils/sysinfo into utils/git, refactor utils/build.rs to run git to obtain hashes (in addition to file check) --- Cargo.lock | 33 ++++++++++++++++++++++++++++++++ kaspad/src/daemon.rs | 3 ++- utils/Cargo.toml | 3 +++ utils/build.rs | 41 ++++++++++++++++++++++++++++++++-------- utils/src/git.rs | 43 ++++++++++++++++++++++++++++++++++++++++++ utils/src/lib.rs | 2 ++ utils/src/sysinfo.rs | 13 ++----------- wallet/core/src/lib.rs | 5 +++++ 8 files changed, 123 insertions(+), 20 deletions(-) create mode 100644 utils/src/git.rs diff --git a/Cargo.lock b/Cargo.lock index ac630f1669..0e33d48f67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1415,6 +1415,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "duct" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ab5718d1224b63252cd0c6f74f6480f9ffeb117438a2e0f5cf6d9a4798929c" +dependencies = [ + "libc", + "once_cell", + "os_pipe", + "shared_child", +] + [[package]] name = "duration-string" version = "0.3.0" @@ -3329,6 +3341,7 @@ dependencies = [ "borsh", "cfg-if 1.0.0", "criterion", + "duct", "event-listener 2.5.3", "faster-hex 0.6.1", "futures-util", @@ -4530,6 +4543,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_pipe" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "pad" version = "0.1.6" @@ -5549,6 +5572,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "shared_child" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "shlex" version = "1.3.0" diff --git a/kaspad/src/daemon.rs b/kaspad/src/daemon.rs index 4a6d60efc5..20c44f2429 100644 --- a/kaspad/src/daemon.rs +++ b/kaspad/src/daemon.rs @@ -13,6 +13,7 @@ use kaspa_grpc_server::service::GrpcService; use kaspa_notify::{address::tracker::Tracker, subscription::context::SubscriptionContext}; use kaspa_rpc_service::service::RpcCoreService; use kaspa_txscript::caches::TxScriptCacheCounters; +use kaspa_utils::git; use kaspa_utils::networking::ContextualNetAddress; use kaspa_utils::sysinfo::SystemInfo; use kaspa_utils_tower::counters::TowerConnectionCounters; @@ -228,7 +229,7 @@ pub fn create_core_with_runtime(runtime: &Runtime, args: &Args, fd_total_budget: let db_dir = app_dir.join(network.to_prefixed()).join(DEFAULT_DATA_DIR); // Print package name and version - info!("{} v{}", env!("CARGO_PKG_NAME"), version()); + info!("{} v{}", env!("CARGO_PKG_NAME"), git::with_short_hash(version())); assert!(!db_dir.to_str().unwrap().is_empty()); info!("Application directory: {}", app_dir.display()); diff --git a/utils/Cargo.toml b/utils/Cargo.toml index b4de1788e4..efc1661681 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -9,6 +9,9 @@ include.workspace = true license.workspace = true repository.workspace = true +[build-dependencies] +duct = "0.13.7" + [dependencies] async-channel.workspace = true borsh.workspace = true diff --git a/utils/build.rs b/utils/build.rs index b7b0da8db4..2d97cd7080 100644 --- a/utils/build.rs +++ b/utils/build.rs @@ -1,19 +1,21 @@ +use duct::cmd; use std::env; use std::path::*; struct GitHead { head_path: String, head_ref_path: String, - commit_hash: String, + full_hash: String, + short_hash: String, } fn main() { - if env::var("RUSTY_KASPA_NO_COMMIT_HASH").is_err() { - if let Some(GitHead { head_path, head_ref_path, commit_hash }) = try_git_head() { + if let Some(GitHead { head_path, head_ref_path, full_hash, short_hash }) = try_git_head() { println!("cargo::rerun-if-changed={head_path}"); println!("cargo::rerun-if-changed={head_ref_path}"); - println!("cargo:rustc-env=RUSTY_KASPA_GIT_COMMIT_HASH={commit_hash}"); + println!("cargo:rustc-env=RUSTY_KASPA_GIT_FULL_COMMIT_HASH={full_hash}"); + println!("cargo:rustc-env=RUSTY_KASPA_GIT_SHORT_COMMIT_HASH={short_hash}"); } } } @@ -22,6 +24,10 @@ fn try_git_head() -> Option { let cargo_manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let path = cargo_manifest_dir.as_path().parent()?; + let full_hash = cmd!("git", "rev-parse", "HEAD").dir(path).read().ok().map(|full_hash| full_hash.trim().to_string()); + + let short_hash = cmd!("git", "rev-parse", "--short", "HEAD").dir(path).read().ok().map(|short_hash| short_hash.trim().to_string()); + let git_folder = path.join(".git"); if git_folder.is_dir() { let head_path = git_folder.join("HEAD"); @@ -31,11 +37,30 @@ fn try_git_head() -> Option { let head_ref_path = head.trim_start_matches("ref: "); let head_ref_path = git_folder.join(head_ref_path.trim()); if head_ref_path.is_file() { - if let Ok(commit_hash) = std::fs::read_to_string(&head_ref_path) { + if let (Some(full_hash), Some(short_hash)) = (full_hash, short_hash) { + return Some(GitHead { + head_path: head_path.to_str().unwrap().to_string(), + head_ref_path: head_ref_path.to_str().unwrap().to_string(), + full_hash, + short_hash, + }); + } else if let Ok(full_hash) = std::fs::read_to_string(&head_ref_path) { + let full_hash = full_hash.trim().to_string(); + let short_hash = if full_hash.len() >= 7 { + // this is not actually correct as short hash has a variable + // length based on commit short hash collisions (which is) + // why we attempt to use `git rev-parse` above. But since this + // is for reference purposes only, we can live with it. + full_hash[0..7].to_string() + } else { + full_hash.to_string() + }; + return Some(GitHead { head_path: head_path.to_str().unwrap().to_string(), - head_ref_path: head_ref_path.to_str().unwrap().to_string(), - commit_hash : commit_hash.trim().to_string() + head_ref_path: head_ref_path.to_str().unwrap().to_string(), + full_hash, + short_hash, }); } } @@ -44,4 +69,4 @@ fn try_git_head() -> Option { } None -} \ No newline at end of file +} diff --git a/utils/src/git.rs b/utils/src/git.rs new file mode 100644 index 0000000000..3a2b099b0f --- /dev/null +++ b/utils/src/git.rs @@ -0,0 +1,43 @@ +use crate::hex::FromHex; +use std::fmt::Display; + +// generates by `build.rs` +const FULL_HASH: &str = env!("RUSTY_KASPA_GIT_FULL_COMMIT_HASH"); +const SHORT_HASH: &str = env!("RUSTY_KASPA_GIT_SHORT_COMMIT_HASH"); + +/// Check if the codebase is built under a Git repository +/// and return the hash of the current commit as `Vec`. +pub fn hash() -> Option> { + FromHex::from_hex(FULL_HASH).ok() +} + +pub fn short_hash() -> Option> { + FromHex::from_hex(SHORT_HASH).ok() +} + +pub fn hash_str() -> Option<&'static str> { + #[allow(clippy::const_is_empty)] + (!FULL_HASH.is_empty()).then_some(FULL_HASH) +} + +pub fn short_hash_str() -> Option<&'static str> { + #[allow(clippy::const_is_empty)] + (!SHORT_HASH.is_empty()).then_some(SHORT_HASH) +} + +pub fn with_short_hash(version: V) -> impl Display +where + V: Display, +{ + if let Some(short_hash) = short_hash_str() { + format!("{version}-{short_hash}") + } else { + version.to_string() + } +} + +#[test] +fn test_git_hash() { + println!("FULL_HASH: {:?}", hash_str()); + println!("SHORT_HASH: {:?}", short_hash_str()); +} diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 8df30e745a..e8c72e669f 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -191,6 +191,8 @@ pub mod sync; pub mod triggers; pub mod vec; +pub mod git; + #[cfg(not(target_arch = "wasm32"))] pub mod fd_budget; #[cfg(not(target_arch = "wasm32"))] diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index 4ce6a37626..3d47b43ed9 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -1,5 +1,5 @@ use crate::fd_budget; -use crate::hex::FromHex; +use crate::git; use sha2::{Digest, Sha256}; use std::fs::File; use std::io::Read; @@ -16,9 +16,6 @@ pub struct SystemInfo { static SYSTEM_INFO: OnceLock = OnceLock::new(); -// generates by `build.rs` -const GIT_HASH: &str = env!("RUSTY_KASPA_GIT_COMMIT_HASH"); - impl Default for SystemInfo { fn default() -> Self { let system_info = SYSTEM_INFO.get_or_init(|| { @@ -28,7 +25,7 @@ impl Default for SystemInfo { let total_memory = system.total_memory(); let fd_limit = fd_budget::limit() as u32; let system_id = Self::try_system_id(); - let git_hash = Self::try_git_hash_as_vec(); + let git_hash = git::hash(); SystemInfo { system_id, git_hash, cpu_physical_cores, total_memory, fd_limit } }); @@ -55,12 +52,6 @@ impl SystemInfo { sha256.update(some_id.as_bytes()); Some(sha256.finalize().to_vec()) } - - /// Check if the codebase is built under a Git repository - /// and return the hash of the current commit as `Vec`. - fn try_git_hash_as_vec() -> Option> { - FromHex::from_hex(GIT_HASH).ok() - } } impl AsRef for SystemInfo { diff --git a/wallet/core/src/lib.rs b/wallet/core/src/lib.rs index 6029d6c4c4..bca89d4e7e 100644 --- a/wallet/core/src/lib.rs +++ b/wallet/core/src/lib.rs @@ -119,5 +119,10 @@ pub fn version() -> String { env!("CARGO_PKG_VERSION").to_string() } +/// Returns the version of the Wallet framework combined with short git hash. +pub fn version_with_git_hash() -> String { + kaspa_utils::git::with_short_hash(env!("CARGO_PKG_VERSION")).to_string() +} + #[cfg(test)] pub mod tests; From fa870c13e45a499e924ddc1758ee9782082b8a26 Mon Sep 17 00:00:00 2001 From: surinder singh Date: Fri, 19 Jul 2024 23:13:34 +0530 Subject: [PATCH 090/158] using WalletGuard, account guard (#73) 1) private context issue on legacy accounts 2) optional url option handling on rpc client connect method 3) using `WalletGuard` type instead of `AsyncMutexGuard` --- Cargo.lock | 749 ++++++++++++++++-------------- cli/src/extensions/transaction.rs | 13 +- rpc/wrpc/client/src/client.rs | 6 +- wallet/core/src/account/mod.rs | 2 +- wallet/core/src/wallet/mod.rs | 25 +- 5 files changed, 419 insertions(+), 376 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e33d48f67..baaa434f04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,21 +4,21 @@ version = 3 [[package]] name = "accessory" -version = "1.3.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87537f9ae7cfa78d5b8ebd1a1db25959f5e737126be4d8eb44a5452fc4b63cde" +checksum = "850bb534b9dc04744fbbb71d30ad6d25a7e4cf6dc33e223c81ef3a92ebab4e0b" dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "addr2line" -version = "0.22.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -57,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", - "getrandom 0.2.15", + "getrandom 0.2.14", "once_cell", "version_check", "zerocopy", @@ -145,9 +145,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "approx" @@ -230,21 +230,22 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", - "event-listener-strategy", + "event-listener 5.3.0", + "event-listener-strategy 0.5.2", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.12.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ "async-task", "concurrent-queue", @@ -259,10 +260,10 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-executor", - "async-io 2.3.3", - "async-lock 3.4.0", + "async-io 2.3.2", + "async-lock 3.3.0", "blocking", "futures-lite 2.3.0", "once_cell", @@ -290,17 +291,17 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.3" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ - "async-lock 3.4.0", + "async-lock 3.3.0", "cfg-if 1.0.0", "concurrent-queue", "futures-io", "futures-lite 2.3.0", "parking", - "polling 3.7.2", + "polling 3.7.0", "rustix 0.38.34", "slab", "tracing", @@ -318,12 +319,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "event-listener 5.3.1", - "event-listener-strategy", + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -373,7 +374,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -384,13 +385,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -449,7 +450,7 @@ dependencies = [ "futures-util", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.30", + "hyper 0.14.28", "itoa", "matchit", "memchr", @@ -483,9 +484,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -564,7 +565,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -575,9 +576,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "blake2" @@ -610,11 +611,12 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", + "async-lock 3.3.0", "async-task", "futures-io", "futures-lite 2.3.0", @@ -641,7 +643,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", "syn_derive", ] @@ -686,9 +688,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.7" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" dependencies = [ "serde", ] @@ -724,9 +726,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.0" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" dependencies = [ "jobserver", "libc", @@ -823,7 +825,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -866,9 +868,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", @@ -892,9 +894,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.9" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -902,9 +904,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -914,21 +916,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" @@ -1022,9 +1024,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if 1.0.0", ] @@ -1038,7 +1040,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.9", + "clap 4.5.4", "criterion-plot", "is-terminal", "itertools 0.10.5", @@ -1071,9 +1073,9 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -1099,9 +1101,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crossterm" @@ -1109,7 +1111,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "crossterm_winapi", "libc", "mio", @@ -1188,14 +1190,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if 1.0.0", "cpufeatures", "curve25519-dalek-derive", "fiat-crypto", + "platforms", "rustc_version", "subtle", "zeroize", @@ -1209,14 +1212,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "darling" -version = "0.20.10" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ "darling_core", "darling_macro", @@ -1224,27 +1227,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.70", + "strsim 0.10.0", + "syn 2.0.60", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -1275,7 +1278,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -1317,7 +1320,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -1327,20 +1330,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", - "syn 2.0.70", + "syn 1.0.109", ] [[package]] @@ -1351,9 +1354,9 @@ checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" [[package]] name = "deunicode" -version = "1.6.0" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" +checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e" [[package]] name = "dhat" @@ -1435,9 +1438,9 @@ checksum = "6fcc1d9ae294a15ed05aeae8e11ee5f2b3fe971c077d45a42fb20825fba6ee13" [[package]] name = "either" -version = "1.13.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encode_unicode" @@ -1486,9 +1489,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1502,22 +1505,43 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.3.1" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.3.0", "pin-project-lite", ] @@ -1539,7 +1563,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -1574,9 +1598,9 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" [[package]] name = "filetime" @@ -1598,9 +1622,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "fixedstr" -version = "0.5.7" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e049f021908beff8f8c430a99f5c136d3be69f1667346e581f446b173bc012" +checksum = "5f4e4dfef7b590ab7d11e531d602fdfb6a3413b09924db1428902bbc4410a9a8" dependencies = [ "serde", ] @@ -1729,7 +1753,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -1795,9 +1819,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1808,9 +1832,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -1936,12 +1960,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - [[package]] name = "hex" version = "0.4.3" @@ -2030,12 +2048,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", - "futures-util", + "futures-core", "http 1.1.0", "http-body 1.0.0", "pin-project-lite", @@ -2049,9 +2067,9 @@ checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" -version = "1.9.4" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -2067,9 +2085,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -2091,9 +2109,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", "futures-channel", @@ -2117,7 +2135,7 @@ checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.4.1", + "hyper 1.3.1", "hyper-util", "rustls 0.23.11", "rustls-pki-types", @@ -2132,7 +2150,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.30", + "hyper 0.14.28", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -2146,7 +2164,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper 1.3.1", "hyper-util", "native-tls", "tokio", @@ -2156,16 +2174,16 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.0", - "hyper 1.4.1", + "hyper 1.3.1", "pin-project-lite", "socket2 0.5.7", "tokio", @@ -2224,7 +2242,7 @@ dependencies = [ "bytes", "futures", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.28", "log", "rand 0.8.5", "tokio", @@ -2282,9 +2300,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.13" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2454,7 +2472,7 @@ dependencies = [ "borsh", "bs58", "faster-hex 0.6.1", - "getrandom 0.2.15", + "getrandom 0.2.14", "hmac", "js-sys", "kaspa-consensus-core", @@ -2541,7 +2559,7 @@ name = "kaspa-consensus" version = "0.14.1" dependencies = [ "arc-swap", - "async-channel 2.3.1", + "async-channel 2.2.1", "bincode", "criterion", "crossbeam-channel", @@ -2618,7 +2636,7 @@ dependencies = [ "criterion", "faster-hex 0.6.1", "futures-util", - "getrandom 0.2.15", + "getrandom 0.2.14", "itertools 0.11.0", "js-sys", "kaspa-addresses", @@ -2649,7 +2667,7 @@ dependencies = [ name = "kaspa-consensus-notify" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "cfg-if 1.0.0", "derive_more", "futures", @@ -2772,7 +2790,7 @@ dependencies = [ name = "kaspa-grpc-client" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-stream", "async-trait", "faster-hex 0.6.1", @@ -2803,7 +2821,7 @@ dependencies = [ name = "kaspa-grpc-core" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-stream", "async-trait", "faster-hex 0.6.1", @@ -2832,7 +2850,7 @@ dependencies = [ name = "kaspa-grpc-server" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-stream", "async-trait", "faster-hex 0.6.1", @@ -2888,7 +2906,7 @@ dependencies = [ name = "kaspa-index-core" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-trait", "derive_more", "futures", @@ -2907,7 +2925,7 @@ dependencies = [ name = "kaspa-index-processor" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-trait", "derive_more", "futures", @@ -3026,7 +3044,7 @@ dependencies = [ name = "kaspa-notify" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-trait", "borsh", "criterion", @@ -3152,7 +3170,7 @@ dependencies = [ name = "kaspa-rpc-core" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-trait", "borsh", "cfg-if 1.0.0", @@ -3235,11 +3253,11 @@ dependencies = [ name = "kaspa-testing-integration" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-trait", "bincode", "chrono", - "clap 4.5.9", + "clap 4.5.4", "criterion", "crossbeam-channel", "dhat", @@ -3335,7 +3353,7 @@ dependencies = [ name = "kaspa-utils" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-trait", "bincode", "borsh", @@ -3371,7 +3389,7 @@ version = "0.14.1" dependencies = [ "cfg-if 1.0.0", "futures", - "hyper 0.14.30", + "hyper 0.14.28", "log", "pin-project-lite", "tokio", @@ -3433,7 +3451,7 @@ dependencies = [ "aes", "ahash", "argon2", - "async-channel 2.3.1", + "async-channel 2.2.1", "async-std", "async-trait", "base64 0.21.7", @@ -3665,7 +3683,7 @@ name = "kaspa-wrpc-proxy" version = "0.14.1" dependencies = [ "async-trait", - "clap 4.5.9", + "clap 4.5.4", "kaspa-consensus-core", "kaspa-grpc-client", "kaspa-rpc-core", @@ -3740,8 +3758,8 @@ dependencies = [ name = "kaspad" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", - "clap 4.5.9", + "async-channel 2.2.1", + "clap 4.5.4", "dhat", "dirs", "futures-util", @@ -3801,9 +3819,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" @@ -3813,18 +3831,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -3835,9 +3853,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libmimalloc-sys" -version = "0.1.39" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44" +checksum = "81eb4061c0582dedea1cbc7aff2240300dd6982e0239d1c99e65c1dbf4a30ba7" dependencies = [ "cc", "libc", @@ -3849,7 +3867,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "libc", ] @@ -3871,9 +3889,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.18" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", "pkg-config", @@ -3908,9 +3926,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "local-ip-address" @@ -3936,9 +3954,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" dependencies = [ "serde", "value-bag", @@ -3981,9 +3999,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.5" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9764018d143cc854c9f17f0b907de70f14393b1f502da6375dce70f00514eb3" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" dependencies = [ "cc", "libc", @@ -4028,7 +4046,7 @@ dependencies = [ "cfg-if 1.0.0", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -4039,7 +4057,7 @@ checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -4052,14 +4070,14 @@ dependencies = [ "macroific_core", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "malachite-base" -version = "0.4.14" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c817c713ff9f16e06cfdc23baa3fecf1b71eaaac714816a98a560f4e350aa6" +checksum = "d073a3d1e4e037975af5ef176a2632672e25e8ddbe8e1811745c2e0726b6ad94" dependencies = [ "hashbrown 0.14.5", "itertools 0.11.0", @@ -4069,9 +4087,9 @@ dependencies = [ [[package]] name = "malachite-nz" -version = "0.4.14" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "603729facf62429736ac17a9fc9fe1bf7e0eb8bde3da3b18cc2b6153150464d5" +checksum = "2546fc6ae29728079e87a2a0f011509e6060713b65e62ee46ba5d413b495ebc7" dependencies = [ "itertools 0.11.0", "libm", @@ -4114,9 +4132,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" @@ -4129,9 +4147,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.43" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633" +checksum = "9f41a2280ded0da56c8cf898babb86e8f10651a34adcfff190ae9a1159c6908d" dependencies = [ "libmimalloc-sys", ] @@ -4150,9 +4168,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -4210,10 +4228,11 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ + "lazy_static", "libc", "log", "openssl", @@ -4269,7 +4288,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "cfg-if 1.0.0", "cfg_aliases 0.1.1", "libc", @@ -4310,24 +4329,25 @@ dependencies = [ [[package]] name = "num" -version = "0.4.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" dependencies = [ "num-bigint", - "num-complex 0.4.6", + "num-complex 0.4.5", "num-integer", "num-iter", - "num-rational 0.4.2", + "num-rational 0.4.1", "num-traits", ] [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ + "autocfg", "num-integer", "num-traits", ] @@ -4344,9 +4364,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] @@ -4390,10 +4410,11 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ + "autocfg", "num-bigint", "num-integer", "num-traits", @@ -4449,9 +4470,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -4464,9 +4485,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" -version = "11.1.4" +version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "opaque-debug" @@ -4480,7 +4501,7 @@ version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "cfg-if 1.0.0", "foreign-types", "libc", @@ -4497,7 +4518,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -4508,9 +4529,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.3.1+3.3.1" +version = "300.2.3+3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91" +checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" dependencies = [ "cc", ] @@ -4570,9 +4591,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -4586,9 +4607,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.5.2", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -4608,7 +4629,7 @@ checksum = "70df726c43c645ef1dde24c7ae14692036ebe5457c92c5f0ec4cfceb99634ff6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -4624,9 +4645,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -4652,9 +4673,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" -version = "0.6.5" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", "indexmap 2.2.6", @@ -4677,7 +4698,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -4694,9 +4715,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", "fastrand 2.1.0", @@ -4709,6 +4730,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + [[package]] name = "polling" version = "2.8.0" @@ -4727,13 +4754,13 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.2" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if 1.0.0", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi 0.3.9", "pin-project-lite", "rustix 0.38.34", "tracing", @@ -4771,12 +4798,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ "proc-macro2", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -4813,18 +4840,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.12.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", "prost-derive", @@ -4832,9 +4859,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", "heck", @@ -4847,28 +4874,28 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.70", + "syn 2.0.60", "tempfile", ] [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "prost-types" -version = "0.12.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ "prost", ] @@ -4941,7 +4968,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.14", ] [[package]] @@ -5009,11 +5036,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", ] [[package]] @@ -5022,16 +5049,16 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.14", "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -5041,9 +5068,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -5052,9 +5079,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" @@ -5071,7 +5098,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "http-body-util", - "hyper 1.4.1", + "hyper 1.3.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -5107,7 +5134,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if 1.0.0", - "getrandom 0.2.15", + "getrandom 0.2.14", "libc", "spin", "untrusted", @@ -5146,8 +5173,8 @@ dependencies = [ name = "rothschild" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", - "clap 4.5.9", + "async-channel 2.2.1", + "clap 4.5.4", "criterion", "faster-hex 0.6.1", "itertools 0.11.0", @@ -5168,9 +5195,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -5207,10 +5234,10 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys 0.4.13", "windows-sys 0.52.0", ] @@ -5287,15 +5314,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "salsa20" @@ -5372,7 +5399,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -5391,9 +5418,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] @@ -5415,9 +5442,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.204" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] @@ -5445,20 +5472,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -5473,14 +5500,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -5499,9 +5526,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.8.3" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e73139bc5ec2d45e6c5fd85be5a46949c1c39a4c18e56915f5eb4c12f975e377" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" dependencies = [ "base64 0.22.1", "chrono", @@ -5517,14 +5544,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.3" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b80d3d6b56b64335c0180e5ffde23b3c5e08c14c585b51a15bd0e95393f46703" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -5622,8 +5649,8 @@ dependencies = [ name = "simpa" version = "0.14.1" dependencies = [ - "async-channel 2.3.1", - "clap 4.5.9", + "async-channel 2.2.1", + "clap 4.5.4", "dhat", "futures", "futures-util", @@ -5749,6 +5776,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" @@ -5757,9 +5790,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.6.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -5774,9 +5807,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.70" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -5792,7 +5825,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -5886,22 +5919,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -5965,9 +5998,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -5980,9 +6013,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -6008,13 +6041,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -6088,21 +6121,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.15", + "toml_edit 0.22.12", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -6120,15 +6153,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.15" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.13", + "winnow 0.6.7", ] [[package]] @@ -6146,7 +6179,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.30", + "hyper 0.14.28", "hyper-timeout", "percent-encoding", "pin-project", @@ -6172,7 +6205,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -6201,7 +6234,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "bytes", "futures-core", "futures-util", @@ -6245,7 +6278,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -6338,9 +6371,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "universal-hash" @@ -6375,9 +6408,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -6392,9 +6425,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" @@ -6402,7 +6435,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.14", ] [[package]] @@ -6411,7 +6444,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.14", "rand 0.8.5", "serde", "wasm-bindgen", @@ -6437,9 +6470,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "vergen" -version = "8.3.2" +version = "8.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" +checksum = "e27d6bdd219887a9eadd19e1c34f32e47fa332301184935c6d9bca26f3cca525" dependencies = [ "anyhow", "cargo_metadata", @@ -6458,9 +6491,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "waker-fn" -version = "1.2.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" @@ -6516,7 +6549,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -6550,7 +6583,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6583,7 +6616,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] @@ -6646,7 +6679,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core", - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -6655,7 +6688,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -6673,7 +6706,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -6693,18 +6726,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -6715,9 +6748,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -6727,9 +6760,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -6739,15 +6772,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -6757,9 +6790,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -6769,9 +6802,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -6781,9 +6814,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -6793,9 +6826,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -6808,9 +6841,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ "memchr", ] @@ -6846,7 +6879,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d889e6327e09b88827953e6bc6b13cb883b2b19b04185c75a6a05eede517fb1e" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.2.1", "async-std", "borsh", "bs58", @@ -6855,7 +6888,7 @@ dependencies = [ "dirs", "faster-hex 0.9.0", "futures", - "getrandom 0.2.15", + "getrandom 0.2.14", "instant", "js-sys", "rand 0.8.5", @@ -7035,7 +7068,7 @@ dependencies = [ "downcast-rs", "futures", "futures-util", - "getrandom 0.2.15", + "getrandom 0.2.14", "manual_future", "rand 0.8.5", "serde", @@ -7219,7 +7252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c781fb394b573bf46c574703e4d08600f2947185c39d58839d6fe7ecd0d375b" dependencies = [ "ahash", - "async-channel 2.3.1", + "async-channel 2.2.1", "async-std", "async-trait", "cfg-if 1.0.0", @@ -7258,41 +7291,41 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.11" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63658493314859b4dfdf3fb8c1defd61587839def09582db50b8a4e93afca6bb" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.60", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", diff --git a/cli/src/extensions/transaction.rs b/cli/src/extensions/transaction.rs index 0f88169bff..415aa7a34b 100644 --- a/cli/src/extensions/transaction.rs +++ b/cli/src/extensions/transaction.rs @@ -2,6 +2,7 @@ use crate::imports::*; use kaspa_consensus_core::tx::{TransactionInput, TransactionOutpoint}; use kaspa_wallet_core::storage::Binding; use kaspa_wallet_core::storage::{TransactionData, TransactionKind, TransactionRecord}; +use kaspa_wallet_core::wallet::WalletGuard; use workflow_log::style; pub trait TransactionTypeExtension { @@ -48,13 +49,13 @@ impl TransactionTypeExtension for TransactionKind { #[async_trait] pub trait TransactionExtension { - async fn format_transaction(&self, wallet: &Arc, include_utxos: bool, guard: &AsyncMutexGuard<'_, ()>) -> Vec; + async fn format_transaction(&self, wallet: &Arc, include_utxos: bool, guard: &WalletGuard) -> Vec; async fn format_transaction_with_state( &self, wallet: &Arc, state: Option<&str>, include_utxos: bool, - guard: &AsyncMutexGuard<'_, ()>, + guard: &WalletGuard, ) -> Vec; async fn format_transaction_with_args( &self, @@ -64,13 +65,13 @@ pub trait TransactionExtension { include_utxos: bool, history: bool, account: Option>, - guard: &AsyncMutexGuard<'_, ()>, + guard: &WalletGuard, ) -> Vec; } #[async_trait] impl TransactionExtension for TransactionRecord { - async fn format_transaction(&self, wallet: &Arc, include_utxos: bool, guard: &AsyncMutexGuard<'_, ()>) -> Vec { + async fn format_transaction(&self, wallet: &Arc, include_utxos: bool, guard: &WalletGuard) -> Vec { self.format_transaction_with_args(wallet, None, None, include_utxos, false, None, guard).await } @@ -79,7 +80,7 @@ impl TransactionExtension for TransactionRecord { wallet: &Arc, state: Option<&str>, include_utxos: bool, - guard: &AsyncMutexGuard<'_, ()>, + guard: &WalletGuard, ) -> Vec { self.format_transaction_with_args(wallet, state, None, include_utxos, false, None, guard).await } @@ -92,7 +93,7 @@ impl TransactionExtension for TransactionRecord { include_utxos: bool, history: bool, account: Option>, - guard: &AsyncMutexGuard<'_, ()>, + guard: &WalletGuard, ) -> Vec { let TransactionRecord { id, binding, block_daa_score, transaction_data, .. } = self; diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index 6875c7f6d7..326bd39d3b 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -408,10 +408,12 @@ impl KaspaRpcClient { pub async fn connect(&self, options: Option) -> ConnectResult { let _guard = self.inner.connect_guard.lock().await; - let mut options = options.unwrap_or_default(); + let options = options.unwrap_or_default(); let strategy = options.strategy; - self.set_url(options.url.take().as_deref())?; + if let Some(ref url) = options.url { + self.set_url(Some(url))?; + } // 1Gb message and frame size limits (on native and NodeJs platforms) let ws_config = WebSocketConfig { diff --git a/wallet/core/src/account/mod.rs b/wallet/core/src/account/mod.rs index 1a608c7b23..a377d33398 100644 --- a/wallet/core/src/account/mod.rs +++ b/wallet/core/src/account/mod.rs @@ -366,7 +366,7 @@ pub trait Account: AnySync + Send + Sync + 'static { payment_secret: Option, abortable: &Abortable, notifier: Option, - guard: &AsyncMutexGuard<'_, ()>, + guard: &WalletGuard, ) -> Result<(GeneratorSummary, Vec)> { let keydata = self.prv_key_data(wallet_secret).await?; let signer = Arc::new(Signer::new(self.clone().as_dyn_arc(), keydata, payment_secret)); diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index bcc83e4351..c905d368a3 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -96,6 +96,7 @@ pub struct Inner { retained_contexts: Mutex>>>, // Mutex used to protect concurrent access to accounts at the wallet api level guard: Arc>, + account_guard: Arc>, } /// @@ -161,6 +162,7 @@ impl Wallet { estimation_abortables: Mutex::new(HashMap::new()), retained_contexts: Mutex::new(HashMap::new()), guard: Arc::new(AsyncMutex::new(())), + account_guard: Arc::new(AsyncMutex::new(())), }), }; @@ -373,20 +375,25 @@ impl Wallet { None }; - let account_descriptors = accounts - .as_ref() - .map(|accounts| accounts.iter().map(|account| account.descriptor()).collect::>>()) - .transpose()?; - - if let Some(accounts) = accounts { - for account in accounts.into_iter() { + if let Some(accounts) = &accounts { + for account in accounts.iter() { if let Ok(legacy_account) = account.clone().as_legacy_account() { - self.legacy_accounts().insert(account); + // if let Ok(descriptor) = account.descriptor(){ + // legacy_account.create_private_context(wallet_secret, None, index) + // } + legacy_account.create_private_context(wallet_secret, None, None).await?; + log_info!("create_private_context: receive_address: {:?}", account.receive_address()); + self.legacy_accounts().insert(account.clone()); } } } + let account_descriptors = accounts + .as_ref() + .map(|accounts| accounts.iter().map(|account| account.descriptor()).collect::>>()) + .transpose()?; + self.notify(Events::WalletOpen { wallet_descriptor: wallet_name, account_descriptors: account_descriptors.clone() }).await?; let hint = self.store().get_user_hint().await?; @@ -947,7 +954,7 @@ impl Wallet { account_id: &AccountId, _guard: &WalletGuard<'_>, ) -> Result>> { - let _guard = self.inner.guard.lock().await; + let _guard = self.inner.account_guard.lock().await; if let Some(account) = self.active_accounts().get(account_id) { Ok(Some(account.clone())) From 5a9b06e38cc4c89d84d380aa49a73e5de3528be4 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 20 Jul 2024 11:34:41 +0300 Subject: [PATCH 091/158] add short hash to sysinfo, return short hash in GetSystemInfo --- rpc/service/src/service.rs | 2 +- utils/src/sysinfo.rs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index 402bbaa1c6..cd91829ce2 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -983,7 +983,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and ) -> RpcResult { let response = GetSystemInfoResponse { system_id: self.system_info.system_id.clone(), - git_hash: self.system_info.git_hash.clone(), + git_hash: self.system_info.git_short_hash.clone(), cpu_physical_cores: self.system_info.cpu_physical_cores, total_memory: self.system_info.total_memory, fd_limit: self.system_info.fd_limit, diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index 3d47b43ed9..1a8c808395 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -9,6 +9,7 @@ use std::sync::OnceLock; pub struct SystemInfo { pub system_id: Option>, pub git_hash: Option>, + pub git_short_hash: Option>, pub cpu_physical_cores: u16, pub total_memory: u64, pub fd_limit: u32, @@ -26,8 +27,9 @@ impl Default for SystemInfo { let fd_limit = fd_budget::limit() as u32; let system_id = Self::try_system_id(); let git_hash = git::hash(); + let git_short_hash = git::short_hash(); - SystemInfo { system_id, git_hash, cpu_physical_cores, total_memory, fd_limit } + SystemInfo { system_id, git_hash, git_short_hash, cpu_physical_cores, total_memory, fd_limit } }); (*system_info).clone() } @@ -40,10 +42,10 @@ impl SystemInfo { // fetch the system id from /etc/machine-id let mut machine_id = String::new(); file.read_to_string(&mut machine_id).ok(); - machine_id + machine_id.trim().to_string() } else if let Ok(Some(mac)) = mac_address::get_mac_address() { // fallback on the mac address - mac.to_string() + mac.to_string().trim().to_string() } else { // 🤷 return None; From ad6607d19ee5d9dc726470cfa8aabdf95c27fda1 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 20 Jul 2024 11:36:01 +0300 Subject: [PATCH 092/158] add contributor DNS seeders (gerri and H@H) --- consensus/core/src/config/params.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/consensus/core/src/config/params.rs b/consensus/core/src/config/params.rs index e2f2639a16..20c7123743 100644 --- a/consensus/core/src/config/params.rs +++ b/consensus/core/src/config/params.rs @@ -309,6 +309,10 @@ pub const MAINNET_PARAMS: Params = Params { "kaspadns.kaspacalc.net", // This DNS seeder is run by supertypo "n-mainnet.kaspa.ws", + // This DNS seeder is run by -gerri- + "dnsseeder-kaspa-mainnet.x-con.at", + // This DNS seeder is run by H@H + "ns-mainnet.kaspa-dnsseeder.net", ], net: NetworkId::new(NetworkType::Mainnet), genesis: GENESIS, @@ -368,6 +372,10 @@ pub const TESTNET_PARAMS: Params = Params { dns_seeders: &[ // This DNS seeder is run by Tiram "seeder1-testnet.kaspad.net", + // This DNS seeder is run by -gerri- + "dnsseeder-kaspa-testnet.x-con.at", + // This DNS seeder is run by H@H + "ns-testnet10.kaspa-dnsseeder.net", ], net: NetworkId::with_suffix(NetworkType::Testnet, 10), genesis: TESTNET_GENESIS, @@ -429,6 +437,10 @@ pub const TESTNET11_PARAMS: Params = Params { "seeder1-testnet-11.kaspad.net", // This DNS seeder is run by supertypo "n-testnet-11.kaspa.ws", + // This DNS seeder is run by -gerri- + "dnsseeder-kaspa-testnet11.x-con.at", + // This DNS seeder is run by H@H + "ns-testnet11.kaspa-dnsseeder.net", ], net: NetworkId::with_suffix(NetworkType::Testnet, 11), genesis: TESTNET11_GENESIS, From bacfd81a3bc24527c99a0071b84b7eb476a681e7 Mon Sep 17 00:00:00 2001 From: surinder singh Date: Sun, 21 Jul 2024 05:00:37 +0530 Subject: [PATCH 093/158] Update phrase.rs (#74) --- wallet/bip32/src/mnemonic/phrase.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/bip32/src/mnemonic/phrase.rs b/wallet/bip32/src/mnemonic/phrase.rs index 76450590a6..56693002dd 100644 --- a/wallet/bip32/src/mnemonic/phrase.rs +++ b/wallet/bip32/src/mnemonic/phrase.rs @@ -23,7 +23,7 @@ pub type Entropy32 = [u8; KEY_SIZE]; pub type Entropy16 = [u8; 16]; /// Word count for a BIP39 mnemonic phrase. Identifies mnemonic as 12 or 24 word variants. -#[derive(Default, Clone, Copy, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "kebab-case")] pub enum WordCount { #[default] From c340bcf1cb2c69c6173c764673a4d8d606e1049b Mon Sep 17 00:00:00 2001 From: 1bananagirl <168954040+1bananagirl@users.noreply.github.com> Date: Sun, 21 Jul 2024 01:31:26 +0200 Subject: [PATCH 094/158] Base implementation for PSKB and usage in core account with generator wrapper (#64) * Base implementation for PSKB and usage in core account with generator wrapper stream handling. * prune test file * prune test file * Converters for transanction and populated transaction. * Optional signature import in PSKT conversion. * PSKB wallet cli commands. * More PSKB wallet cli commands for custom script locks. * Serialization test case * Reviews patches, cli script debug command added. * Doc about fee per transaction for script unlocking UTXOS * Parameter changed to priority_fee_sompi_per_transaction, revert function renaming. * Error handling --- Cargo.lock | 30 +++ Cargo.toml | 1 + cli/Cargo.toml | 2 + cli/src/error.rs | 6 + cli/src/modules/mod.rs | 3 +- cli/src/modules/pskb.rs | 257 ++++++++++++++++++++++ cli/src/modules/pskb_redeem.json | 99 +++++++++ cli/src/modules/send.rs | 2 +- wallet/core/Cargo.toml | 1 + wallet/core/src/account/mod.rs | 67 ++++++ wallet/core/src/account/pskb.rs | 360 +++++++++++++++++++++++++++++++ wallet/core/src/error.rs | 6 + wallet/pskt/Cargo.toml | 5 + wallet/pskt/src/bundle.rs | 348 +++++++++++++++++++++++++++++- wallet/pskt/src/convert.rs | 66 +++++- wallet/pskt/src/error.rs | 45 +++- wallet/pskt/src/lib.rs | 1 + wallet/pskt/src/wasm/pskt.rs | 2 +- 18 files changed, 1286 insertions(+), 15 deletions(-) create mode 100644 cli/src/modules/pskb.rs create mode 100644 cli/src/modules/pskb_redeem.json create mode 100644 wallet/core/src/account/pskb.rs diff --git a/Cargo.lock b/Cargo.lock index d61da0a651..16c88cf908 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2537,6 +2537,7 @@ dependencies = [ "downcast", "faster-hex 0.6.1", "futures", + "hex", "js-sys", "kaspa-addresses", "kaspa-bip32", @@ -2548,6 +2549,7 @@ dependencies = [ "kaspa-utils", "kaspa-wallet-core", "kaspa-wallet-keys", + "kaspa-wallet-pskt", "kaspa-wrpc-client", "nw-sys", "pad", @@ -3538,6 +3540,7 @@ dependencies = [ "kaspa-utils", "kaspa-wallet-keys", "kaspa-wallet-macros", + "kaspa-wallet-pskt", "kaspa-wasm-core", "kaspa-wrpc-client", "kaspa-wrpc-wasm", @@ -3622,14 +3625,19 @@ dependencies = [ name = "kaspa-wallet-pskt" version = "0.14.1" dependencies = [ + "bincode", "derive_builder", + "futures", + "hex", "js-sys", + "kaspa-addresses", "kaspa-bip32", "kaspa-consensus-client", "kaspa-consensus-core", "kaspa-txscript", "kaspa-txscript-errors", "kaspa-utils", + "rmp-serde", "secp256k1", "serde", "serde-value", @@ -5179,6 +5187,28 @@ dependencies = [ "libc", ] +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rocksdb" version = "0.21.0" diff --git a/Cargo.toml b/Cargo.toml index 2789c15c1a..4918045f9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -220,6 +220,7 @@ regex = "1.10.2" ripemd = { version = "0.1.3", default-features = false } rlimit = "0.10.1" rocksdb = "0.21.0" +rmp-serde = "1.3.0" secp256k1 = { version = "0.28.2", features = [ "global-context", "rand-std", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f2c80a5fa3..c308360e67 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -33,6 +33,7 @@ downcast.workspace = true faster-hex.workspace = true futures.workspace = true js-sys.workspace = true +hex.workspace = true kaspa-addresses.workspace = true kaspa-bip32.workspace = true kaspa-consensus-core.workspace = true @@ -43,6 +44,7 @@ kaspa-rpc-core.workspace = true kaspa-utils.workspace = true kaspa-wallet-core.workspace = true kaspa-wallet-keys.workspace = true +kaspa-wallet-pskt.workspace = true kaspa-wrpc-client.workspace = true nw-sys.workspace = true pad.workspace = true diff --git a/cli/src/error.rs b/cli/src/error.rs index a1701be355..5103cf8715 100644 --- a/cli/src/error.rs +++ b/cli/src/error.rs @@ -122,6 +122,12 @@ pub enum Error { #[error(transparent)] KaspaWalletKeys(#[from] kaspa_wallet_keys::error::Error), + + #[error(transparent)] + PskbLockScriptSigError(#[from] kaspa_wallet_pskt::error::Error), + + #[error("To hex serialization error")] + PskbSerializeToHexError, } impl Error { diff --git a/cli/src/modules/mod.rs b/cli/src/modules/mod.rs index a6371814f2..7990a9d6e1 100644 --- a/cli/src/modules/mod.rs +++ b/cli/src/modules/mod.rs @@ -26,6 +26,7 @@ pub mod network; pub mod node; pub mod open; pub mod ping; +pub mod pskb; pub mod reload; pub mod rpc; pub mod select; @@ -57,7 +58,7 @@ pub fn register_handlers(cli: &Arc) -> Result<()> { cli.handlers(), [ account, address, close, connect, details, disconnect, estimate, exit, export, guide, help, history, rpc, list, miner, - message, monitor, mute, network, node, open, ping, reload, select, send, server, settings, sweep, track, transfer, + message, monitor, mute, network, node, open, ping, pskb, reload, select, send, server, settings, sweep, track, transfer, wallet, // halt, // theme, start, stop diff --git a/cli/src/modules/pskb.rs b/cli/src/modules/pskb.rs new file mode 100644 index 0000000000..5bff35a1e7 --- /dev/null +++ b/cli/src/modules/pskb.rs @@ -0,0 +1,257 @@ +use crate::imports::*; +use kaspa_consensus_core::tx::{TransactionOutpoint, UtxoEntry}; +use kaspa_wallet_core::account::pskb::finalize_pskt_one_or_more_sig_and_redeem_script; +use kaspa_wallet_pskt::prelude::{lock_script_sig_templating, script_sig_to_address, unlock_utxos_as_pskb, Bundle, Signer, PSKT}; + +#[derive(Default, Handler)] +#[help("Send a Kaspa transaction to a public address")] +pub struct Pskb; + +impl Pskb { + async fn main(self: Arc, ctx: &Arc, mut argv: Vec, _cmd: &str) -> Result<()> { + let ctx = ctx.clone().downcast_arc::()?; + + if !ctx.wallet().is_open() { + return Err(Error::WalletIsNotOpen); + } + + let account = ctx.wallet().account()?; + + if argv.is_empty() { + return self.display_help(ctx, argv).await; + } + + let action = argv.remove(0); + + match action.as_str() { + "create" => { + if argv.len() < 2 || argv.len() > 3 { + return self.display_help(ctx, argv).await; + } else { + let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; + let _ = ctx.notifier().show(Notification::Processing).await; + + let address = Address::try_from(argv.first().unwrap().as_str())?; + let amount_sompi = try_parse_required_nonzero_kaspa_as_sompi_u64(argv.get(1))?; + let outputs = PaymentOutputs::from((address, amount_sompi)); + let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.get(2))?.unwrap_or(0); + let abortable = Abortable::default(); + + let signer = account + .pskb_from_send_generator( + outputs.into(), + priority_fee_sompi.into(), + None, + wallet_secret.clone(), + payment_secret.clone(), + &abortable, + ) + .await?; + + match signer.to_hex() { + Ok(encoded) => tprintln!(ctx, "{encoded}"), + Err(e) => return Err(e.into()), + } + } + } + "script" => { + if argv.len() < 2 || argv.len() > 4 { + return self.display_help(ctx, argv).await; + } + + let subcommand = argv.remove(0); + let payload = argv.remove(0); + + let receive_address = account.receive_address()?; + let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; + let _ = ctx.notifier().show(Notification::Processing).await; + + let script_sig = match lock_script_sig_templating(payload.clone(), Some(&receive_address.payload)) { + Ok(value) => value, + Err(e) => { + terrorln!(ctx, "{}", e.to_string()); + return Err(e.into()); + } + }; + + let script_p2sh = match script_sig_to_address(&script_sig, ctx.wallet().address_prefix()?) { + Ok(p2sh) => p2sh, + Err(e) => { + terrorln!(ctx, "Error generating script address: {}", e.to_string()); + return Err(e.into()); + } + }; + + match subcommand.as_str() { + "lock" => { + let amount_sompi = try_parse_required_nonzero_kaspa_as_sompi_u64(argv.first())?; + let outputs = PaymentOutputs::from((script_p2sh, amount_sompi)); + let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.get(1))?.unwrap_or(0); + let abortable = Abortable::default(); + + let signer = account + .pskb_from_send_generator( + outputs.into(), + priority_fee_sompi.into(), + None, + wallet_secret.clone(), + payment_secret.clone(), + &abortable, + ) + .await?; + + match signer.to_hex() { + Ok(encoded) => tprintln!(ctx, "{encoded}"), + Err(e) => return Err(e.into()), + } + } + "unlock" => { + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } + + // Get locked UTXO set. + let spend_utxos: Vec = + ctx.wallet().rpc_api().get_utxos_by_addresses(vec![script_p2sh.clone()]).await?; + let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.first())?.unwrap_or(0) as u64; + + if spend_utxos.is_empty() { + twarnln!(ctx, "No locked UTXO set found."); + return Ok(()); + } + + let references: Vec<(UtxoEntry, TransactionOutpoint)> = + spend_utxos.iter().map(|entry| (entry.utxo_entry.clone(), entry.outpoint)).collect(); + + let total_locked_sompi: u64 = spend_utxos.iter().map(|entry| entry.utxo_entry.amount).sum(); + + tprintln!( + ctx, + "{} locked UTXO{} found with total amount of {} KAS", + spend_utxos.len(), + if spend_utxos.len() == 1 { "" } else { "s" }, + sompi_to_kaspa(total_locked_sompi) + ); + + // Sweep UTXO set. + match unlock_utxos_as_pskb(references, &receive_address, script_sig, priority_fee_sompi as u64) { + Ok(pskb) => { + let pskb_hex = pskb.to_hex()?; + tprintln!(ctx, "{pskb_hex}"); + } + Err(e) => tprintln!(ctx, "Error generating unlock PSKB: {}", e.to_string()), + } + } + "sign" => { + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + + // Sign PSKB using the account's receiver address. + match account.pskb_sign(&pskb, wallet_secret.clone(), payment_secret.clone(), Some(&receive_address)).await { + Ok(signed_pskb) => { + let pskb_pack = String::try_from(signed_pskb)?; + tprintln!(ctx, "{pskb_pack}"); + } + Err(e) => terrorln!(ctx, "{}", e.to_string()), + } + } + "address" => { + tprintln!(ctx, "\r\nP2SH address: {}", script_p2sh); + } + v => { + terrorln!(ctx, "unknown command: '{v}'\r\n"); + return self.display_help(ctx, argv).await; + } + } + } + "sign" => { + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } else { + let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + + match account.pskb_sign(&pskb, wallet_secret.clone(), payment_secret.clone(), None).await { + Ok(signed_pskb) => { + let pskb_pack = String::try_from(signed_pskb)?; + tprintln!(ctx, "{pskb_pack}"); + } + Err(e) => terrorln!(ctx, "{}", e.to_string()), + } + } + } + "send" => { + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } else { + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + + match account.pskb_broadcast(&pskb).await { + Ok(sent) => tprintln!(ctx, "Sent transactions {:?}", sent), + Err(e) => terrorln!(ctx, "Send error {:?}", e), + } + } + } + "debug" => { + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + + // Debug bundle view. + tprintln!(ctx, "{:?}", pskb); + + match pskb.inner_list.first() { + Some(bundle_inner) => { + let pskt: PSKT = PSKT::::from(bundle_inner.to_owned()); + let mut fin = pskt.finalizer(); + + fin = finalize_pskt_one_or_more_sig_and_redeem_script(fin).expect("Finalized PSKT"); + + // Verify if extraction is possible. + match fin.extractor() { + Ok(ex) => match ex.extract_tx() { + Ok(_) => tprintln!( + ctx, + "Transaction extracted successfuly meaning it is finalized with a valid script signature." + ), + Err(e) => terrorln!(ctx, "Transaction extraction error: {}", e.to_string()), + }, + Err(_) => twarnln!(ctx, "PSKB not finalized"), + } + } + None => { + twarnln!(ctx, "Debugging an empty PSKB"); + } + } + } + v => { + tprintln!(ctx, "unknown command: '{v}'\r\n"); + return self.display_help(ctx, argv).await; + } + } + Ok(()) + } + + fn parse_input_pskb(input: &str) -> Result { + match Bundle::try_from(input) { + Ok(bundle) => Ok(bundle), + Err(e) => Err(Error::custom(format!("Error while parsing input PSKB {}", e))), + } + } + + async fn display_help(self: Arc, ctx: Arc, _argv: Vec) -> Result<()> { + ctx.term().help( + &[ + ("pskb create
", "Create a PSKB from single send transaction"), + ("pskb sign ", "Sign given PSKB"), + ("pskb send ", "Broadcast bundled transactions"), + ("pskb debug ", "Print PSKB debug view"), + ("pskb script lock [priority fee]", "Generate a PSKB with one send transaction to given P2SH payload. Optional public key placeholder in payload: {{pubkey}}"), + ("pskb script unlock ", "Generate a PSKB to unlock UTXOS one by one from given P2SH payload. Fee amount will be applied to every spent UTXO, meaning every transaction. Optional public key placeholder in payload: {{pubkey}}"), + ("pskb script sign ", "Sign all PSKB's P2SH locked inputs"), + ("pskb script sign ", "Sign all PSKB's P2SH locked inputs"), + ("pskb script address ", "Prints P2SH address"), + ], + None, + )?; + + Ok(()) + } +} diff --git a/cli/src/modules/pskb_redeem.json b/cli/src/modules/pskb_redeem.json new file mode 100644 index 0000000000..9f3e00d654 --- /dev/null +++ b/cli/src/modules/pskb_redeem.json @@ -0,0 +1,99 @@ +{ + "inner_list": [ + { + "global": { + "version": 0, + "tx_version": 0, + "fallback_lock_time": null, + "inputs_modifiable": false, + "outputs_modifiable": false, + "input_count": 1, + "output_count": 0, + "xpubs": {}, + "id": null, + "proprietaries": {} + }, + "inputs": [ + { + "utxo_entry": { + "amount": 3300000000, + "scriptPublicKey": "000020f2b9984ea0bf9464b028534a51c84dca8d8e8037499ce9a01f43d717dc4b157cac", + "blockDaaScore": 160987746, + "isCoinbase": false + }, + "previous_outpoint": { + "transactionId": "6323bee26238b82f926176c08b8919c42cdb204a14c17fd35c0e2664326133ff", + "index": 0 + }, + "sequence": null, + "min_time": null, + "partial_sigs": {}, + "sighash_type": 1, + "redeem_script": null, + "sig_op_count": 1, + "bip32_derivations": {}, + "final_script_sig": null, + "proprietaries": {} + }, + { + "utxo_entry": { + "amount": 33210791745569, + "scriptPublicKey": "0000200e6227c375a68b02da57c58f8eaf837a003c59e957890baf1cf18aaf28aca76dac", + "blockDaaScore": 160987746, + "isCoinbase": false + }, + "previous_outpoint": { + "transactionId": "6323bee26238b82f926176c08b8919c42cdb204a14c17fd35c0e2664326133ff", + "index": 1 + }, + "sequence": null, + "min_time": null, + "partial_sigs": {}, + "sighash_type": 1, + "redeem_script": null, + "sig_op_count": 1, + "bip32_derivations": {}, + "final_script_sig": null, + "proprietaries": {} + }, + { + "utxo_entry": { + "amount": 3300000000, + "scriptPublicKey": "000020f2b9984ea0bf9464b028534a51c84dca8d8e8037499ce9a01f43d717dc4b157cac", + "blockDaaScore": 160987746, + "isCoinbase": false + }, + "previous_outpoint": { + "transactionId": "w", + "index": 0 + }, + "sequence": null, + "min_time": null, + "partial_sigs": {}, + "sighash_type": 1, + "redeem_script": "ac", + "sig_op_count": 1, + "bip32_derivations": {}, + "final_script_sig": null, + "proprietaries": {} + } + ], + "outputs": [ + { + "amount": 6400000000, + "script_public_key": "000020f2b9984ea0bf9464b028534a51c84dca8d8e8037499ce9a01f43d717dc4b157cac", + "redeem_script": null, + "bip32_derivations": {}, + "proprietaries": {} + }, + { + "amount": 33207691742259, + "script_public_key": "0000200e6227c375a68b02da57c58f8eaf837a003c59e957890baf1cf18aaf28aca76dac", + "redeem_script": null, + "bip32_derivations": {}, + "proprietaries": {} + } + ] + } + ] +} \ No newline at end of file diff --git a/cli/src/modules/send.rs b/cli/src/modules/send.rs index d9f35d994d..773861dd4a 100644 --- a/cli/src/modules/send.rs +++ b/cli/src/modules/send.rs @@ -39,7 +39,7 @@ impl Send { .await?; tprintln!(ctx, "Send - {summary}"); - // tprintln!(ctx, "\nSending {} KAS to {address}, tx ids:", sompi_to_kaspa_string(amount_sompi)); + tprintln!(ctx, "\nSending {} KAS to {address}, tx ids:", sompi_to_kaspa_string(amount_sompi)); // tprintln!(ctx, "{}\n", ids.into_iter().map(|a| a.to_string()).collect::>().join("\n")); Ok(()) diff --git a/wallet/core/Cargo.toml b/wallet/core/Cargo.toml index fb31afb310..d97f84cd7d 100644 --- a/wallet/core/Cargo.toml +++ b/wallet/core/Cargo.toml @@ -71,6 +71,7 @@ kaspa-txscript.workspace = true kaspa-utils.workspace = true kaspa-wallet-keys.workspace = true kaspa-wallet-macros.workspace = true +kaspa-wallet-pskt.workspace = true kaspa-wasm-core.workspace = true kaspa-wrpc-client.workspace = true kaspa-wrpc-wasm.workspace = true diff --git a/wallet/core/src/account/mod.rs b/wallet/core/src/account/mod.rs index b921bf4914..010bbeb0dd 100644 --- a/wallet/core/src/account/mod.rs +++ b/wallet/core/src/account/mod.rs @@ -5,8 +5,15 @@ pub mod descriptor; pub mod kind; +pub mod pskb; pub mod variants; +use kaspa_hashes::Hash; +use kaspa_wallet_pskt::bundle::Bundle; pub use kind::*; +use pskb::{ + bundle_from_pskt_generator, bundle_to_finalizer_stream, pskb_signer_for_address, pskt_to_pending_transaction, PSKBSigner, + PSKTGenerator, +}; pub use variants::*; use crate::derivation::build_derivate_paths; @@ -348,6 +355,66 @@ pub trait Account: AnySync + Send + Sync + 'static { Ok((generator.summary(), ids)) } + async fn pskb_from_send_generator( + self: Arc, + destination: PaymentDestination, + priority_fee_sompi: Fees, + payload: Option>, + wallet_secret: Secret, + payment_secret: Option, + abortable: &Abortable, + ) -> Result { + let settings = GeneratorSettings::try_new_with_account(self.clone().as_dyn_arc(), destination, priority_fee_sompi, payload)?; + let keydata = self.prv_key_data(wallet_secret).await?; + let signer = Arc::new(PSKBSigner::new(self.clone().as_dyn_arc(), keydata, payment_secret)); + let generator = Generator::try_new(settings, None, Some(abortable))?; + let pskt_generator = PSKTGenerator::new(generator, signer, self.wallet().address_prefix()?); + bundle_from_pskt_generator(pskt_generator).await + } + + async fn pskb_sign( + self: Arc, + bundle: &Bundle, + wallet_secret: Secret, + payment_secret: Option, + sign_for_address: Option<&Address>, + ) -> Result { + let keydata = self.prv_key_data(wallet_secret).await?; + let signer = Arc::new(PSKBSigner::new(self.clone().as_dyn_arc(), keydata.clone(), payment_secret.clone())); + + let network_id = self.wallet().clone().network_id()?; + let derivation = self.as_derivation_capable()?; + + let (derivation_path, _) = + build_derivate_paths(&derivation.account_kind(), derivation.account_index(), derivation.cosigner_index())?; + + let key_fingerprint = keydata.get_xprv(payment_secret.clone().as_ref())?.public_key().fingerprint(); + + match pskb_signer_for_address(bundle, signer, network_id, sign_for_address, derivation_path, key_fingerprint).await { + Ok(signer) => Ok(signer), + Err(e) => Err(Error::from(e.to_string())), + } + } + + async fn pskb_broadcast(self: Arc, bundle: &Bundle) -> Result, Error> { + let mut ids = Vec::new(); + let mut stream = bundle_to_finalizer_stream(bundle); + + while let Some(result) = stream.next().await { + match result { + Ok(pskt) => { + let change = self.wallet().account()?.change_address()?; + let transaction = pskt_to_pending_transaction(pskt, self.wallet().network_id()?, change)?; + ids.push(transaction.try_submit(&self.wallet().rpc_api()).await?); + } + Err(e) => { + eprintln!("Error processing a PSKT from bundle: {:?}", e); + } + } + } + Ok(ids) + } + /// Execute a transfer to another wallet account. async fn transfer( self: Arc, diff --git a/wallet/core/src/account/pskb.rs b/wallet/core/src/account/pskb.rs new file mode 100644 index 0000000000..b30682dddf --- /dev/null +++ b/wallet/core/src/account/pskb.rs @@ -0,0 +1,360 @@ +pub use crate::error::Error; +use crate::imports::*; +use crate::tx::PaymentOutputs; +use futures::stream; +use kaspa_bip32::{DerivationPath, KeyFingerprint, PrivateKey}; +use kaspa_consensus_client::UtxoEntry as ClientUTXO; +use kaspa_consensus_core::hashing::sighash::{calc_schnorr_signature_hash, SigHashReusedValues}; +use kaspa_consensus_core::tx::VerifiableTransaction; +use kaspa_consensus_core::tx::{TransactionInput, UtxoEntry}; +use kaspa_txscript::extract_script_pub_key_address; +use kaspa_txscript::opcodes::codes::OpData65; +use kaspa_txscript::script_builder::ScriptBuilder; +use kaspa_wallet_core::tx::{Generator, GeneratorSettings, PaymentDestination, PendingTransaction}; +pub use kaspa_wallet_pskt::bundle::Bundle; +use kaspa_wallet_pskt::prelude::KeySource; +use kaspa_wallet_pskt::prelude::{Finalizer, Inner, SignInputOk, Signature, Signer}; +pub use kaspa_wallet_pskt::pskt::{Creator, PSKT}; +use secp256k1::schnorr; +use secp256k1::{Message, PublicKey}; +use std::iter; + +struct PSKBSignerInner { + keydata: PrvKeyData, + account: Arc, + payment_secret: Option, + keys: Mutex>, +} + +pub struct PSKBSigner { + inner: Arc, +} + +impl PSKBSigner { + pub fn new(account: Arc, keydata: PrvKeyData, payment_secret: Option) -> Self { + Self { inner: Arc::new(PSKBSignerInner { keydata, account, payment_secret, keys: Mutex::new(AHashMap::new()) }) } + } + + pub fn ingest(&self, addresses: &[Address]) -> Result<()> { + let mut keys = self.inner.keys.lock()?; + + // Skip addresses that are already present in the key map. + let addresses = addresses.iter().filter(|a| !keys.contains_key(a)).collect::>(); + if !addresses.is_empty() { + let account = self.inner.account.clone().as_derivation_capable().expect("expecting derivation capable account"); + let (receive, change) = account.derivation().addresses_indexes(&addresses)?; + let private_keys = account.create_private_keys(&self.inner.keydata, &self.inner.payment_secret, &receive, &change)?; + for (address, private_key) in private_keys { + keys.insert(address.clone(), private_key.to_bytes()); + } + } + Ok(()) + } + + fn public_key(&self, for_address: &Address) -> Result { + let keys = self.inner.keys.lock()?; + match keys.get(for_address) { + Some(private_key) => { + let kp = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key)?; + Ok(kp.public_key()) + } + None => Err(Error::from("PSKBSigner address coverage error")), + } + } + + fn sign_schnorr(&self, for_address: &Address, message: Message) -> Result { + let keys = self.inner.keys.lock()?; + match keys.get(for_address) { + Some(private_key) => { + let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key)?; + Ok(schnorr_key.sign_schnorr(message)) + } + None => Err(Error::from("PSKBSigner address coverage error")), + } + } +} + +pub struct PSKTGenerator { + generator: Generator, + signer: Arc, + prefix: Prefix, +} + +impl PSKTGenerator { + pub fn new(generator: Generator, signer: Arc, prefix: Prefix) -> Self { + Self { generator, signer, prefix } + } + + pub fn stream(&self) -> impl Stream, Error>> { + PSKTStream::new(self.generator.clone(), self.signer.clone(), self.prefix) + } +} + +struct PSKTStream { + generator_stream: Pin> + Send>>, + signer: Arc, + prefix: Prefix, +} + +impl PSKTStream { + fn new(generator: Generator, signer: Arc, prefix: Prefix) -> Self { + let generator_stream = generator.stream().map_err(Error::from); + Self { generator_stream: Box::pin(generator_stream), signer, prefix } + } +} + +impl Stream for PSKTStream { + type Item = Result, Error>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.as_ref(); // Access the mutable reference to self + + let _prefix = this.prefix; + let _signer = this.signer.clone(); + + match self.get_mut().generator_stream.as_mut().poll_next(cx) { + Poll::Ready(Some(Ok(pending_tx))) => { + let pskt = convert_pending_tx_to_pskt(pending_tx); + Poll::Ready(Some(pskt)) + } + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +fn convert_pending_tx_to_pskt(pending_tx: PendingTransaction) -> Result, Error> { + let signable_tx = pending_tx.signable_transaction(); + let verifiable_tx = signable_tx.as_verifiable(); + let populated_inputs: Vec<(&TransactionInput, &UtxoEntry)> = verifiable_tx.populated_inputs().collect(); + let pskt_inner = Inner::try_from((pending_tx.transaction(), populated_inputs.to_owned()))?; + Ok(PSKT::::from(pskt_inner)) +} + +pub async fn bundle_from_pskt_generator(generator: PSKTGenerator) -> Result { + let mut bundle: Bundle = Bundle::new(); + let mut stream = generator.stream(); + + while let Some(pskt_result) = stream.next().await { + match pskt_result { + Ok(pskt) => bundle.add_pskt(pskt), + Err(e) => return Err(e), + } + } + + Ok(bundle) +} + +pub async fn pskb_signer_for_address( + bundle: &Bundle, + signer: Arc, + network_id: NetworkId, + sign_for_address: Option<&Address>, + derivation_path: DerivationPath, + key_fingerprint: KeyFingerprint, +) -> Result { + let mut signed_bundle = Bundle::new(); + let mut reused_values = SigHashReusedValues::new(); + + // If set, sign-for address is used for signing. + // Else, all addresses from inputs are. + let addresses: Vec
= match sign_for_address { + Some(signer) => vec![signer.clone()], + None => bundle + .inner_list + .iter() + .flat_map(|inner| { + inner.inputs + .iter() + .filter_map(|input| input.utxo_entry.as_ref()) // Filter out None and get a reference to UtxoEntry if it exists + .filter_map(|utxo_entry| { + extract_script_pub_key_address(&utxo_entry.script_public_key.clone(), network_id.into()).ok() + }) + .collect::>() + }) + .collect(), + }; + + // Prepare the signer. + signer.ingest(addresses.as_ref())?; + + for pskt_inner in bundle.inner_list.clone() { + let pskt: PSKT = PSKT::from(pskt_inner); + + let mut sign = |signer_pskt: PSKT| { + signer_pskt + .pass_signature_sync(|tx, sighash| -> Result, String> { + tx.tx + .inputs + .iter() + .enumerate() + .map(|(idx, _input)| { + let hash = calc_schnorr_signature_hash(&tx.as_verifiable(), idx, sighash[idx], &mut reused_values); + let msg = secp256k1::Message::from_digest_slice(hash.as_bytes().as_slice()).unwrap(); + + // When address represents a lock utxo, no private key is available. + // Instead, use the account receive address private key. + let address: &Address = match sign_for_address { + Some(address) => address, + None => addresses.get(idx).expect("Input indexed address"), + }; + + let public_key = signer.public_key(address).expect("Public key for input indexed address"); + + Ok(SignInputOk { + signature: Signature::Schnorr(signer.sign_schnorr(address, msg).unwrap()), + pub_key: public_key, + key_source: Some(KeySource { key_fingerprint, derivation_path: derivation_path.clone() }), + }) + }) + .collect() + }) + .unwrap() + }; + signed_bundle.add_pskt(sign(pskt.clone())); + } + Ok(signed_bundle) +} + +pub fn finalize_pskt_one_or_more_sig_and_redeem_script(pskt: PSKT) -> Result, Error> { + let result = pskt.finalize_sync(|inner: &Inner| -> Result>, String> { + Ok(inner + .inputs + .iter() + .map(|input| -> Vec { + let signatures: Vec<_> = input + .partial_sigs + .clone() + .into_iter() + .flat_map(|(_, signature)| iter::once(OpData65).chain(signature.into_bytes()).chain([input.sighash_type.to_u8()])) + .collect(); + + signatures + .into_iter() + .chain( + input + .redeem_script + .as_ref() + .map(|redeem_script| ScriptBuilder::new().add_data(redeem_script.as_slice()).unwrap().drain().to_vec()) + .unwrap_or_default(), + ) + .collect() + }) + .collect()) + }); + + match result { + Ok(finalized_pskt) => Ok(finalized_pskt), + Err(e) => Err(Error::from(e.to_string())), + } +} + +pub fn finalize_pskt_no_sig_and_redeem_script(pskt: PSKT) -> Result, Error> { + let result = pskt.finalize_sync(|inner: &Inner| -> Result>, String> { + Ok(inner + .inputs + .iter() + .map(|input| -> Vec { + input + .redeem_script + .as_ref() + .map(|redeem_script| ScriptBuilder::new().add_data(redeem_script.as_slice()).unwrap().drain().to_vec()) + .unwrap_or_default() + }) + .collect()) + }); + + match result { + Ok(finalized_pskt) => Ok(finalized_pskt), + Err(e) => Err(Error::from(e.to_string())), + } +} + +pub fn bundle_to_finalizer_stream(bundle: &Bundle) -> impl Stream, Error>> + Send { + stream::iter(bundle.inner_list.clone()).map(move |pskt_inner| { + let pskt: PSKT = PSKT::from(pskt_inner); + let pskt_finalizer = pskt.constructor().updater().signer().finalizer(); + finalize_pskt_one_or_more_sig_and_redeem_script(pskt_finalizer) + }) +} + +pub fn pskt_to_pending_transaction( + finalized_pskt: PSKT, + network_id: NetworkId, + change_address: Address, +) -> Result { + let mass = 10; + let (signed_tx, _) = match finalized_pskt.clone().extractor() { + Ok(extractor) => match extractor.extract_tx() { + Ok(once_mass) => once_mass(mass), + Err(e) => return Err(Error::PendingTransactionFromPSKTError(e.to_string())), + }, + Err(e) => return Err(Error::PendingTransactionFromPSKTError(e.to_string())), + }; + + let inner_pskt = finalized_pskt.deref().clone(); + + let utxo_entries_ref: Vec = inner_pskt + .inputs + .iter() + .filter_map(|input| { + if let Some(ue) = input.clone().utxo_entry { + return Some(UtxoEntryReference { + utxo: Arc::new(ClientUTXO { + address: Some(extract_script_pub_key_address(&ue.script_public_key, network_id.into()).unwrap()), + amount: ue.amount, + outpoint: input.previous_outpoint.into(), + script_public_key: ue.script_public_key, + block_daa_score: ue.block_daa_score, + is_coinbase: ue.is_coinbase, + }), + }); + } + None + }) + .collect(); + + let output: Vec = signed_tx.outputs.clone(); + let recipient = extract_script_pub_key_address(&output[0].script_public_key, network_id.into())?; + let fee_u: u64 = 0; + + let utxo_iterator: Box + Send + Sync + 'static> = + Box::new(utxo_entries_ref.clone().into_iter()); + + let final_transaction_destination = PaymentDestination::PaymentOutputs(PaymentOutputs::from((recipient.clone(), output[0].value))); + + let settings = GeneratorSettings { + network_id, + multiplexer: None, + sig_op_count: 1, + minimum_signatures: 1, + change_address, + utxo_iterator, + source_utxo_context: None, + destination_utxo_context: None, + final_transaction_priority_fee: fee_u.into(), + final_transaction_destination, + final_transaction_payload: None, + }; + + // Create the Generator + let generator = Generator::try_new(settings, None, None)?; + + // Create PendingTransaction + let pending_tx = PendingTransaction::try_new( + &generator, + signed_tx.clone(), + utxo_entries_ref.clone(), + vec![], + None, + 0, + 0, + 0, + 0, + 0, + kaspa_wallet_core::tx::DataKind::Final, + )?; + + Ok(pending_tx) +} diff --git a/wallet/core/src/error.rs b/wallet/core/src/error.rs index a89b1dcf00..ee71761eec 100644 --- a/wallet/core/src/error.rs +++ b/wallet/core/src/error.rs @@ -326,6 +326,12 @@ pub enum Error { #[error(transparent)] Metrics(#[from] kaspa_metrics_core::error::Error), + + #[error(transparent)] + Pskt(#[from] kaspa_wallet_pskt::error::Error), + + #[error("Error generating pending transaction from PSKT: {0}")] + PendingTransactionFromPSKTError(String), } impl From for Error { diff --git a/wallet/pskt/Cargo.toml b/wallet/pskt/Cargo.toml index c9376fe061..3e45dbadbe 100644 --- a/wallet/pskt/Cargo.toml +++ b/wallet/pskt/Cargo.toml @@ -19,6 +19,7 @@ wasm32-sdk = ["kaspa-consensus-client/wasm32-sdk"] wasm32-types = ["kaspa-consensus-client/wasm32-types"] [dependencies] +kaspa-addresses.workspace = true kaspa-bip32.workspace = true kaspa-consensus-client.workspace = true kaspa-consensus-core.workspace = true @@ -26,9 +27,13 @@ kaspa-txscript-errors.workspace = true kaspa-txscript.workspace = true kaspa-utils.workspace = true +bincode.workspace = true derive_builder.workspace = true js-sys.workspace = true +futures.workspace = true +hex.workspace = true secp256k1.workspace = true +rmp-serde.workspace = true serde_repr.workspace = true serde-value.workspace = true serde.workspace = true diff --git a/wallet/pskt/src/bundle.rs b/wallet/pskt/src/bundle.rs index 1443ad600b..750b06a9ce 100644 --- a/wallet/pskt/src/bundle.rs +++ b/wallet/pskt/src/bundle.rs @@ -1,9 +1,347 @@ -// use crate::role::*; +use crate::error::Error; +use crate::prelude::*; +use crate::pskt::{Inner as PSKTInner, PSKT}; +use crate::wasm::result; + +use kaspa_addresses::Address; +use kaspa_consensus_core::tx::{ScriptPublicKey, TransactionOutpoint, UtxoEntry}; + +use hex; +use kaspa_txscript::{extract_script_pub_key_address, pay_to_address_script, pay_to_script_hash_script}; use serde::{Deserialize, Serialize}; -use std::marker::PhantomData; +use std::ops::Deref; #[derive(Debug, Serialize, Deserialize)] -pub struct Bundle { - #[serde(skip_serializing, default)] - role: PhantomData, +pub struct Bundle { + pub inner_list: Vec, +} + +impl From> for Bundle { + fn from(pskt: PSKT) -> Self { + Bundle { inner_list: vec![pskt.deref().clone()] } + } +} + +impl From>> for Bundle { + fn from(pskts: Vec>) -> Self { + let inner_list = pskts.into_iter().map(|pskt| pskt.deref().clone()).collect(); + Bundle { inner_list } + } +} + +impl Bundle { + pub fn new() -> Self { + Self { inner_list: Vec::new() } + } + + /// Adds an Inner instance to the bundle + pub fn add_inner(&mut self, inner: PSKTInner) { + self.inner_list.push(inner); + } + + /// Adds a PSKT instance to the bundle + pub fn add_pskt(&mut self, pskt: PSKT) { + self.inner_list.push(pskt.deref().clone()); + } + + /// Merges another bundle into the current bundle + pub fn merge(&mut self, other: Bundle) { + for inner in other.inner_list { + self.inner_list.push(inner); + } + } + + pub fn to_hex(&self) -> Result { + match TypeMarked::new(self, Marker::Pskb) { + Ok(type_marked) => match serde_json::to_string(&type_marked) { + Ok(result) => Ok(hex::encode(result)), + Err(e) => Err(Error::PskbSerializeToHexError(e.to_string())), + }, + Err(e) => Err(Error::PskbSerializeToHexError(e.to_string())), + } + } + + pub fn from_hex(hex_data: &str) -> Result { + let bundle: TypeMarked = serde_json::from_slice(hex::decode(hex_data)?.as_slice())?; + Ok(bundle.data) + } +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +enum Marker { + Pskb, +} + +impl Marker { + fn as_str(&self) -> &str { + match self { + Marker::Pskb => "pskb", + } + } + + fn from_str(marker: &str) -> Result { + match marker { + "pskb" => Ok(Marker::Pskb), + _ => Err("Invalid pskb type marker".into()), + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +struct TypeMarked { + type_marker: String, + #[serde(flatten)] + data: T, +} + +impl TypeMarked { + fn new(data: T, marker: Marker) -> Result { + let type_marker = marker.as_str().to_string(); + if Marker::from_str(&type_marker)? == marker { + Ok(Self { type_marker, data }) + } else { + Err("Invalid pskb type marker".into()) + } + } +} + +impl TryFrom for Bundle { + type Error = Error; + fn try_from(value: String) -> Result { + Bundle::from_hex(&value) + } +} + +impl TryFrom<&str> for Bundle { + type Error = Error; + fn try_from(value: &str) -> Result { + Bundle::from_hex(value) + } +} +impl TryFrom for String { + type Error = Error; + fn try_from(value: Bundle) -> Result { + match Bundle::to_hex(&value) { + Ok(output) => Ok(output.to_owned()), + Err(e) => Err(Error::PskbSerializeError(e.to_string())), + } + } +} + +impl Default for Bundle { + fn default() -> Self { + Self::new() + } +} + +pub fn lock_script_sig_templating(payload: String, pubkey_bytes: Option<&[u8]>) -> Result, Error> { + let mut payload_bytes: Vec = hex::decode(payload)?; + + if let Some(pubkey) = pubkey_bytes { + let placeholder = b"{{pubkey}}"; + + // Search for the placeholder in payload bytes to be replaced by public key. + if let Some(pos) = payload_bytes.windows(placeholder.len()).position(|window| window == placeholder) { + payload_bytes.splice(pos..pos + placeholder.len(), pubkey.iter().cloned()); + } + } + Ok(payload_bytes) +} + +pub fn script_sig_to_address(script_sig: &[u8], prefix: kaspa_addresses::Prefix) -> Result { + extract_script_pub_key_address(&pay_to_script_hash_script(script_sig), prefix).map_err(Error::P2SHExtractError) +} + +pub fn unlock_utxos_as_pskb( + utxo_references: Vec<(UtxoEntry, TransactionOutpoint)>, + recipient: &Address, + script_sig: Vec, + priority_fee_sompi_per_transaction: u64, +) -> Result { + // Fee per transaction. + // Check if each UTXO's amounts can cover priority fee. + utxo_references + .iter() + .map(|(entry, _)| { + if entry.amount <= priority_fee_sompi_per_transaction { + return Err(Error::ExcessUnlockFeeError); + } + Ok(()) + }) + .collect::, _>>()?; + + let recipient_spk = pay_to_address_script(recipient); + let (successes, errors): (Vec<_>, Vec<_>) = utxo_references + .into_iter() + .map(|(utxo_entry, outpoint)| { + unlock_utxo(&utxo_entry, &outpoint, &recipient_spk, &script_sig, priority_fee_sompi_per_transaction) + }) + .partition(Result::is_ok); + + let successful_bundles: Vec<_> = successes.into_iter().filter_map(Result::ok).collect(); + let error_list: Vec<_> = errors.into_iter().filter_map(Result::err).collect(); + + if !error_list.is_empty() { + return Err(Error::MultipleUnlockUtxoError(error_list)); + } + + let merged_bundle = successful_bundles.into_iter().fold(None, |acc: Option, bundle| match acc { + Some(mut merged_bundle) => { + merged_bundle.merge(bundle); + Some(merged_bundle) + } + None => Some(bundle), + }); + + match merged_bundle { + None => Err("Generating an empty PSKB".into()), + Some(bundle) => Ok(bundle), + } +} + +pub fn unlock_utxo( + utxo_entry: &UtxoEntry, + outpoint: &TransactionOutpoint, + script_public_key: &ScriptPublicKey, + script_sig: &[u8], + priority_fee_sompi: u64, +) -> Result { + let input = InputBuilder::default() + .utxo_entry(utxo_entry.to_owned()) + .previous_outpoint(outpoint.to_owned()) + .sig_op_count(1) + .redeem_script(script_sig.to_vec()) + .build()?; + + let output = OutputBuilder::default() + .amount(utxo_entry.amount - priority_fee_sompi) + .script_public_key(script_public_key.clone()) + .build()?; + + let pskt: PSKT = PSKT::::default().constructor().input(input).output(output); + Ok(pskt.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::prelude::*; + use crate::role::Creator; + use crate::role::*; + // hashing::sighash::{calc_schnorr_signature_hash, SigHashReusedValues}, + use kaspa_consensus_core::tx::{TransactionId, TransactionOutpoint, UtxoEntry}; + use kaspa_txscript::{multisig_redeem_script, pay_to_script_hash_script}; + // use kaspa_txscript::{multisig_redeem_script, opcodes::codes::OpData65, pay_to_script_hash_script, script_builder::ScriptBuilder}; + use rmp_serde::{decode, encode}; + use secp256k1::Secp256k1; + use secp256k1::{rand::thread_rng, Keypair}; + use std::str::FromStr; + use std::sync::Once; + + static INIT: Once = Once::new(); + static mut CONTEXT: Option)>> = None; + + fn mock_context() -> &'static ([Keypair; 2], Vec) { + unsafe { + INIT.call_once(|| { + let kps = [Keypair::new(&Secp256k1::new(), &mut thread_rng()), Keypair::new(&Secp256k1::new(), &mut thread_rng())]; + let redeem_script: Vec = multisig_redeem_script(kps.iter().map(|pk| pk.x_only_public_key().0.serialize()), 2) + .expect("Test multisig redeem script"); + + CONTEXT = Some(Box::new((kps, redeem_script))); + }); + + CONTEXT.as_ref().unwrap() + } + } + + // Mock multisig PSKT from example + fn mock_pskt_constructor() -> PSKT { + let (_, redeem_script) = mock_context(); + let pskt = PSKT::::default().inputs_modifiable().outputs_modifiable(); + let input_0 = InputBuilder::default() + .utxo_entry(UtxoEntry { + amount: 12793000000000, + script_public_key: pay_to_script_hash_script(redeem_script), + block_daa_score: 36151168, + is_coinbase: false, + }) + .previous_outpoint(TransactionOutpoint { + transaction_id: TransactionId::from_str("63020db736215f8b1105a9281f7bcbb6473d965ecc45bb2fb5da59bd35e6ff84").unwrap(), + index: 0, + }) + .sig_op_count(2) + .redeem_script(redeem_script.to_owned()) + .build() + .expect("Mock PSKT constructor"); + + pskt.constructor().input(input_0) + } + + #[test] + fn test_serialization() { + let constructor = mock_pskt_constructor(); + let bundle = Bundle::from(constructor.clone()); + + // Serialize to MessagePack + let mut buf = Vec::new(); + encode::write(&mut buf, &bundle).expect("Serialize PSKB"); + println!("Serialized: {:?}", buf); + + assert!(!bundle.inner_list.is_empty()); + + // todo: discuss why deserializing from MessagePack errors + match decode::from_slice::(&buf) { + Ok(bundle_constructor_deser) => { + println!("Deserialized: {:?}", bundle_constructor_deser); + let pskt_constructor_deser: Option> = + bundle_constructor_deser.inner_list.first().map(|inner| PSKT::from(inner.clone())); + match pskt_constructor_deser { + Some(_) => println!("PSKT deserialized successfully"), + None => println!("No elements in inner_list to deserialize"), + } + } + Err(e) => { + eprintln!("Failed to deserialize: {}", e); + panic!() + } + } + } + + #[test] + fn test_bundle_creation() { + let bundle = Bundle::new(); + assert!(bundle.inner_list.is_empty()); + } + + #[test] + fn test_new_with_pskt() { + let pskt = PSKT::::default(); + let bundle = Bundle::from(pskt); + assert_eq!(bundle.inner_list.len(), 1); + } + + #[test] + fn test_add_pskt() { + let mut bundle = Bundle::new(); + let pskt = PSKT::::default(); + bundle.add_pskt(pskt); + assert_eq!(bundle.inner_list.len(), 1); + } + + #[test] + fn test_merge_bundles() { + let mut bundle1 = Bundle::new(); + let mut bundle2 = Bundle::new(); + + let inner1 = PSKTInner::default(); + let inner2 = PSKTInner::default(); + + bundle1.add_inner(inner1.clone()); + bundle2.add_inner(inner2.clone()); + + bundle1.merge(bundle2); + + assert_eq!(bundle1.inner_list.len(), 2); + } } diff --git a/wallet/pskt/src/convert.rs b/wallet/pskt/src/convert.rs index 17edc0847f..18acf94ed9 100644 --- a/wallet/pskt/src/convert.rs +++ b/wallet/pskt/src/convert.rs @@ -1,14 +1,14 @@ use crate::error::Error; use crate::input::{Input, InputBuilder}; use crate::output::{Output, OutputBuilder}; -use crate::pskt::Inner; +use crate::pskt::{Global, Inner}; use kaspa_consensus_client::{Transaction, TransactionInput, TransactionInputInner, TransactionOutput, TransactionOutputInner}; +use kaspa_consensus_core::tx as cctx; -impl From for Inner { - fn from(_transaction: Transaction) -> Inner { - // Self::Transaction(transaction) - - todo!() +impl TryFrom for Inner { + type Error = Error; + fn try_from(_transaction: Transaction) -> Result { + Inner::try_from(cctx::Transaction::from(&_transaction)) } } @@ -53,3 +53,57 @@ impl TryFrom for Output { Ok(output) } } + +impl TryFrom<(cctx::Transaction, Vec<(&cctx::TransactionInput, &cctx::UtxoEntry)>)> for Inner { + type Error = Error; // Define your error type + + fn try_from( + (transaction, populated_inputs): (cctx::Transaction, Vec<(&cctx::TransactionInput, &cctx::UtxoEntry)>), + ) -> Result { + let inputs: Result, Self::Error> = populated_inputs + .into_iter() + .map(|(input, utxo)| { + InputBuilder::default() + .utxo_entry(utxo.to_owned().clone()) + .previous_outpoint(input.previous_outpoint) + .sig_op_count(input.sig_op_count) + .build() + .map_err(Error::TxToInnerConversionInputBuildingError) + // Handle the error + }) + .collect::>(); + + let outputs: Result, Self::Error> = transaction + .outputs + .iter() + .map(|output| { + Output::try_from(TransactionOutput::from(output.to_owned())).map_err(|e| Error::TxToInnerConversionError(Box::new(e))) + }) + .collect::>(); + + Ok(Inner { global: Global::default(), inputs: inputs?, outputs: outputs? }) + } +} + +impl TryFrom for Inner { + type Error = Error; + fn try_from(transaction: cctx::Transaction) -> Result { + let inputs = transaction + .inputs + .iter() + .map(|input| { + Input::try_from(TransactionInput::from(input.to_owned())).map_err(|e| Error::TxToInnerConversionError(Box::new(e))) + }) + .collect::>()?; + + let outputs = transaction + .outputs + .iter() + .map(|output| { + Output::try_from(TransactionOutput::from(output.to_owned())).map_err(|e| Error::TxToInnerConversionError(Box::new(e))) + }) + .collect::>()?; + + Ok(Inner { global: Global::default(), inputs, outputs }) + } +} diff --git a/wallet/pskt/src/error.rs b/wallet/pskt/src/error.rs index 2ce546d230..4441bd231a 100644 --- a/wallet/pskt/src/error.rs +++ b/wallet/pskt/src/error.rs @@ -1,17 +1,42 @@ +use kaspa_txscript_errors::TxScriptError; + +use crate::input::InputBuilderError; + #[derive(thiserror::Error, Debug)] pub enum Error { + #[error("{0}")] + Custom(String), #[error(transparent)] ConstructorError(#[from] ConstructorError), #[error("OutputNotModifiable")] OutOfBounds, #[error("Missing UTXO entry")] MissingUtxoEntry, + #[error("Missing redeem script")] + MissingRedeemScript, #[error(transparent)] InputBuilder(#[from] crate::input::InputBuilderError), #[error(transparent)] OutputBuilder(#[from] crate::output::OutputBuilderError), + #[error("Serialization error: {0}")] + HexDecodeError(#[from] hex::FromHexError), + #[error("Json deserialize error: {0}")] + JsonDeserializeError(#[from] serde_json::Error), + #[error("Serialize error")] + PskbSerializeError(String), + #[error("Unlock utxo error")] + MultipleUnlockUtxoError(Vec), + #[error("Unlock fees exceed available amount")] + ExcessUnlockFeeError, + #[error("Transaction output to output conversion error")] + TxToInnerConversionError(#[source] Box), + #[error("Transaction input building error in conversion")] + TxToInnerConversionInputBuildingError(#[source] InputBuilderError), + #[error("P2SH extraction error")] + P2SHExtractError(#[source] TxScriptError), + #[error("PSKB hex serialization error: {0}")] + PskbSerializeToHexError(String), } - #[derive(thiserror::Error, Debug)] pub enum ConstructorError { #[error("InputNotModifiable")] @@ -19,3 +44,21 @@ pub enum ConstructorError { #[error("OutputNotModifiable")] OutputNotModifiable, } + +impl From for Error { + fn from(err: String) -> Self { + Self::Custom(err) + } +} + +impl From<&str> for Error { + fn from(err: &str) -> Self { + Self::Custom(err.to_string()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ConversionError { + #[error("Invalid output conversion")] + InvalidOutput, +} diff --git a/wallet/pskt/src/lib.rs b/wallet/pskt/src/lib.rs index a7e423cd47..ed57e9b1ed 100644 --- a/wallet/pskt/src/lib.rs +++ b/wallet/pskt/src/lib.rs @@ -18,6 +18,7 @@ mod convert; mod utils; pub mod prelude { + pub use crate::bundle::Bundle; pub use crate::bundle::*; pub use crate::global::Global; pub use crate::input::Input; diff --git a/wallet/pskt/src/wasm/pskt.rs b/wallet/pskt/src/wasm/pskt.rs index b940ff5181..e12f512b5b 100644 --- a/wallet/pskt/src/wasm/pskt.rs +++ b/wallet/pskt/src/wasm/pskt.rs @@ -96,7 +96,7 @@ impl TryCastFromJs for PSKT { let pskt_inner: Inner = serde_json::from_str(&data).map_err(|_| Error::InvalidPayload)?; Ok(PSKT::from(State::NoOp(Some(pskt_inner)))) } else if let Ok(transaction) = Transaction::try_owned_from(&value) { - let pskt_inner: Inner = transaction.into(); + let pskt_inner: Inner = transaction.try_into()?; Ok(PSKT::from(State::NoOp(Some(pskt_inner)))) } else { Err(Error::InvalidPayload) From 97b777960f15a8f19a2bcb5cf58e40c8bad27c90 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 21 Jul 2024 05:05:23 +0300 Subject: [PATCH 095/158] fix missing RPC refs --- rpc/wrpc/client/src/resolver.rs | 2 +- rpc/wrpc/server/src/router.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index beb0911ede..dad7542758 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -114,7 +114,7 @@ impl Resolver { } }); - format!("{url}/v{CURRENT_VERSION}/kaspa/{network_id}/wrpc/{tls}/{encoding}") + format!("{url}/v{CURRENT_VERSION}/kaspa/{network_id}/{tls}/wrpc/{encoding}") } async fn fetch_node_info(&self, url: &str, encoding: Encoding, network_id: NetworkId) -> Result { diff --git a/rpc/wrpc/server/src/router.rs b/rpc/wrpc/server/src/router.rs index c170e16ddf..000b57a62c 100644 --- a/rpc/wrpc/server/src/router.rs +++ b/rpc/wrpc/server/src/router.rs @@ -61,6 +61,7 @@ impl Router { GetSink, GetSubnetwork, GetSyncStatus, + GetSystemInfo, GetUtxosByAddresses, GetSinkBlueScore, GetVirtualChainFromBlock, From a49071fb9904b9c7557f28d4cfc109d01bbc445b Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 21 Jul 2024 15:44:59 +0300 Subject: [PATCH 096/158] Update resolver config (WIP) --- rpc/wrpc/client/Resolvers.toml | 37 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/rpc/wrpc/client/Resolvers.toml b/rpc/wrpc/client/Resolvers.toml index 29556d3e5e..4580fc7586 100644 --- a/rpc/wrpc/client/Resolvers.toml +++ b/rpc/wrpc/client/Resolvers.toml @@ -1,28 +1,41 @@ +[[resolver]] +enable = false +address = "http://127.0.0.1:8888" + +[[group]] +template = "https://*.kaspa.stream" +nodes = ["eric","maxim","sean","troy"] + +# [[group]] +# template = "https://*.kaspa.red" +# nodes = ["john", "mike", "paul", "alex"] + +# [[group]] +# template = "https://*.kaspa.green" +# nodes = ["jake", "mark", "adam", "liam"] + +# [[group]] +# template = "https://*.kaspa.blue" +# nodes = ["noah", "ryan", "jack", "luke"] + # [[group]] # enable = true # template = "https://*.kaspa-ng.org" -# nodes = ["jack", "john", "paul", "luke"] +# nodes = ["cole", "ivan", "oscar", "zane"] # [[group]] # enable = true # template = "https://*.kaspa-ng.io" -# nodes = ["mark", "noah", "liam", "ryan"] +# nodes = ["gary", "hugo", "finn", "evan"] # [[group]] # enable = true # template = "https://*.kaspa-ng.net" -# nodes = ["max", "evan", "alex", "eric"] +# nodes = ["neil", "dave", "kyle", "toby"] -[[group]] -enable = true -template = "https://*.kaspa.stream" -nodes = ["adam", "owen", "sean", "kyle"] +# --- # [[group]] # enable = false # template = "https://*." -# nodes = ["jake", "jace", "cody", "troy"] - -[[resolver]] -enable = false -address = "http://127.0.0.1:8888" +# nodes = ["rudy", "todd", "clay", "walt"] From 2eb1dfd39a44e0184e3ba887961f92a8cd140f69 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 22 Jul 2024 02:34:53 +0300 Subject: [PATCH 097/158] add version to GetSystemInfoResponse --- rpc/core/src/model/message.rs | 5 ++++- rpc/core/src/model/tests.rs | 1 + rpc/grpc/core/proto/rpc.proto | 11 ++++++----- rpc/grpc/core/src/convert/message.rs | 2 ++ rpc/service/src/service.rs | 1 + utils/src/git.rs | 6 ++++++ utils/src/sysinfo.rs | 4 +++- 7 files changed, 23 insertions(+), 7 deletions(-) diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 0661a7af5b..ec15d4ed52 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1806,6 +1806,7 @@ impl Deserializer for GetSystemInfoRequest { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSystemInfoResponse { + pub version: String, pub system_id: Option>, pub git_hash: Option>, pub cpu_physical_cores: u16, @@ -1816,6 +1817,7 @@ pub struct GetSystemInfoResponse { impl Serializer for GetSystemInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; + store!(String, &self.version, writer)?; store!(Option>, &self.system_id, writer)?; store!(Option>, &self.git_hash, writer)?; store!(u16, &self.cpu_physical_cores, writer)?; @@ -1829,13 +1831,14 @@ impl Serializer for GetSystemInfoResponse { impl Deserializer for GetSystemInfoResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; + let version = load!(String, reader)?; let system_id = load!(Option>, reader)?; let git_hash = load!(Option>, reader)?; let cpu_physical_cores = load!(u16, reader)?; let total_memory = load!(u64, reader)?; let fd_limit = load!(u32, reader)?; - Ok(Self { system_id, git_hash, cpu_physical_cores, total_memory, fd_limit }) + Ok(Self { version, system_id, git_hash, cpu_physical_cores, total_memory, fd_limit }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index 950703a58b..ba28b0c1c9 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -933,6 +933,7 @@ mod mockery { impl Mock for GetSystemInfoResponse { fn mock() -> Self { GetSystemInfoResponse { + version: "1.2.3".to_string(), system_id: mock(), git_hash: mock(), cpu_physical_cores: mock(), diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index 9c59d95d48..6ce249c448 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -822,11 +822,12 @@ message GetSystemInfoRequestMessage{ } message GetSystemInfoResponseMessage{ - string systemId = 1; - string gitHash = 2; - uint32 coreNum = 3; - uint64 totalMemory = 4; - uint32 fdLimit = 5; + string version = 1; + string systemId = 2; + string gitHash = 3; + uint32 coreNum = 4; + uint64 totalMemory = 5; + uint32 fdLimit = 6; RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index d1f02f1e4c..ea29778ae5 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -436,6 +436,7 @@ from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetCo from!(&kaspa_rpc_core::GetSystemInfoRequest, protowire::GetSystemInfoRequestMessage); from!(item: RpcResult<&kaspa_rpc_core::GetSystemInfoResponse>, protowire::GetSystemInfoResponseMessage, { Self { + version : item.version.clone(), system_id : item.system_id.as_ref().map(|system_id|system_id.to_hex()).unwrap_or_default(), git_hash : item.git_hash.as_ref().map(|git_hash|git_hash.to_hex()).unwrap_or_default(), total_memory : item.total_memory, @@ -860,6 +861,7 @@ try_from!(item: &protowire::GetConnectionsResponseMessage, RpcResult, { Self { + version: item.version.clone(), system_id: (!item.system_id.is_empty()).then(|| FromHex::from_hex(&item.system_id)).transpose()?, git_hash: (!item.git_hash.is_empty()).then(|| FromHex::from_hex(&item.git_hash)).transpose()?, total_memory: item.total_memory, diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index cd91829ce2..dbd03804b9 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -982,6 +982,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and _request: GetSystemInfoRequest, ) -> RpcResult { let response = GetSystemInfoResponse { + version: self.system_info.version.clone(), system_id: self.system_info.system_id.clone(), git_hash: self.system_info.git_short_hash.clone(), cpu_physical_cores: self.system_info.cpu_physical_cores, diff --git a/utils/src/git.rs b/utils/src/git.rs index 3a2b099b0f..ebee717b15 100644 --- a/utils/src/git.rs +++ b/utils/src/git.rs @@ -1,6 +1,8 @@ use crate::hex::FromHex; use std::fmt::Display; +const VERSION: &str = env!("CARGO_PKG_VERSION"); + // generates by `build.rs` const FULL_HASH: &str = env!("RUSTY_KASPA_GIT_FULL_COMMIT_HASH"); const SHORT_HASH: &str = env!("RUSTY_KASPA_GIT_SHORT_COMMIT_HASH"); @@ -25,6 +27,10 @@ pub fn short_hash_str() -> Option<&'static str> { (!SHORT_HASH.is_empty()).then_some(SHORT_HASH) } +pub fn version() -> String { + format!("v{VERSION}-{SHORT_HASH}") +} + pub fn with_short_hash(version: V) -> impl Display where V: Display, diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index 1a8c808395..3127dc0da5 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -10,6 +10,7 @@ pub struct SystemInfo { pub system_id: Option>, pub git_hash: Option>, pub git_short_hash: Option>, + pub version: String, pub cpu_physical_cores: u16, pub total_memory: u64, pub fd_limit: u32, @@ -28,8 +29,9 @@ impl Default for SystemInfo { let system_id = Self::try_system_id(); let git_hash = git::hash(); let git_short_hash = git::short_hash(); + let version = git::version(); - SystemInfo { system_id, git_hash, git_short_hash, cpu_physical_cores, total_memory, fd_limit } + SystemInfo { system_id, git_hash, git_short_hash, version, cpu_physical_cores, total_memory, fd_limit } }); (*system_info).clone() } From 0f699f2bdf88b0e3d02720c7abf9ca835c95d424 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 22 Jul 2024 04:48:26 +0300 Subject: [PATCH 098/158] fix git version handling --- utils/src/git.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/src/git.rs b/utils/src/git.rs index ebee717b15..14de9e21bc 100644 --- a/utils/src/git.rs +++ b/utils/src/git.rs @@ -28,7 +28,11 @@ pub fn short_hash_str() -> Option<&'static str> { } pub fn version() -> String { - format!("v{VERSION}-{SHORT_HASH}") + if let Some(short_hash) = short_hash_str() { + format!("v{VERSION}-{short_hash}") + } else { + format!("v{VERSION}") + } } pub fn with_short_hash(version: V) -> impl Display From e0fd3d195f12fdc3c142afbee9e8cb3faca3644d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 22 Jul 2024 10:29:26 +0300 Subject: [PATCH 099/158] update client-side resolver properties to match current structs --- cli/src/imports.rs | 1 + cli/src/modules/connect.rs | 19 ++++++++++++------- cli/src/modules/rpc.rs | 2 +- rpc/wrpc/client/src/node.rs | 2 +- rpc/wrpc/client/src/resolver.rs | 21 --------------------- rpc/wrpc/wasm/src/client.rs | 14 +------------- 6 files changed, 16 insertions(+), 43 deletions(-) diff --git a/cli/src/imports.rs b/cli/src/imports.rs index 17a4169c51..24de4b0ddd 100644 --- a/cli/src/imports.rs +++ b/cli/src/imports.rs @@ -14,6 +14,7 @@ pub use kaspa_utils::hex::*; pub use kaspa_wallet_core::compat::*; pub use kaspa_wallet_core::prelude::*; pub use kaspa_wallet_core::settings::{DefaultSettings, SettingsStore, WalletSettings}; +pub use kaspa_wrpc_client::prelude::*; pub use pad::PadStr; pub use regex::Regex; pub use separator::Separatable; diff --git a/cli/src/modules/connect.rs b/cli/src/modules/connect.rs index 826956f0f6..92a3c676ba 100644 --- a/cli/src/modules/connect.rs +++ b/cli/src/modules/connect.rs @@ -11,21 +11,21 @@ impl Connect { let network_id = ctx.wallet().network_id()?; let arg_or_server_address = argv.first().cloned().or_else(|| ctx.wallet().settings().get(WalletSettings::Server)); - let url = match arg_or_server_address.as_deref() { - Some("public") | Some("resolver") => { + let (is_public, url) = match arg_or_server_address.as_deref() { + Some("public") => { tprintln!(ctx, "Connecting to a public node"); - None + (true, Resolver::default().fetch(WrpcEncoding::Borsh, network_id).await.map_err(|e| e.to_string())?.url) } None => { tprintln!(ctx, "No server set, connecting to a public node"); - None + (true, Resolver::default().fetch(WrpcEncoding::Borsh, network_id).await.map_err(|e| e.to_string())?.url) } Some(url) => { - Some(wrpc_client.parse_url_with_network_type(url.to_string(), network_id.into()).map_err(|e| e.to_string())?) + (false, wrpc_client.parse_url_with_network_type(url.to_string(), network_id.into()).map_err(|e| e.to_string())?) } }; - if url.is_none() { + if is_public { tpara!( ctx, "Please note that public node infrastructure is community-operated and \ @@ -35,7 +35,12 @@ impl Connect { ); } - let options = ConnectOptions { block_async_connect: true, strategy: ConnectStrategy::Fallback, url, ..Default::default() }; + let options = ConnectOptions { + block_async_connect: true, + strategy: ConnectStrategy::Fallback, + url: Some(url), + ..Default::default() + }; wrpc_client.connect(Some(options)).await.map_err(|e| e.to_string())?; } else { terrorln!(ctx, "Unable to connect with non-wRPC client"); diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index da11746c88..dd33d4fed8 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -1,6 +1,6 @@ use crate::imports::*; use convert_case::{Case, Casing}; -use kaspa_rpc_core::{api::ops::RpcApiOps, *}; +use kaspa_rpc_core::api::ops::RpcApiOps; #[derive(Default, Handler)] #[help("Execute RPC commands against the connected Kaspa node")] diff --git a/rpc/wrpc/client/src/node.rs b/rpc/wrpc/client/src/node.rs index 7af7ad1568..ca7e19c879 100644 --- a/rpc/wrpc/client/src/node.rs +++ b/rpc/wrpc/client/src/node.rs @@ -11,7 +11,7 @@ use crate::imports::*; pub struct NodeDescriptor { /// The unique identifier of the node. #[wasm_bindgen(getter_with_clone)] - pub id: String, + pub uid: String, /// The URL of the node WebSocket (wRPC URL). #[wasm_bindgen(getter_with_clone)] pub url: String, diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index dad7542758..558db86c4e 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -138,27 +138,6 @@ impl Resolver { Err(Error::Custom(format!("Failed to connect: {:?}", errors))) } - pub async fn fetch_all(&self, encoding: Encoding, network_id: NetworkId) -> Result> { - let futures = self.inner.urls.iter().map(|url| self.fetch_node_info(url, encoding, network_id)).collect::>(); - let mut errors = Vec::default(); - let result = join_all(futures) - .await - .into_iter() - .filter_map(|result| match result { - Ok(node) => Some(node), - Err(error) => { - errors.push(format!("{:?}", error)); - None - } - }) - .collect::>(); - if result.is_empty() { - Err(Error::Custom(format!("Failed to connect: {:?}", errors))) - } else { - Ok(result) - } - } - pub async fn get_node(&self, encoding: Encoding, network_id: NetworkId) -> Result { self.fetch(encoding, network_id).await } diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index ae5c409894..4d2cecc8f2 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -364,21 +364,9 @@ impl RpcClient { /// Optional: Resolver node id. #[wasm_bindgen(getter, js_name = "nodeId")] pub fn resolver_node_id(&self) -> Option { - self.inner.client.node_descriptor().map(|node| node.id.clone()) + self.inner.client.node_descriptor().map(|node| node.uid.clone()) } - // /// Optional: public node provider name. - // #[wasm_bindgen(getter, js_name = "providerName")] - // pub fn resolver_node_provider_name(&self) -> Option { - // self.inner.client.node_descriptor().and_then(|node| node.provider_name.clone()) - // } - - // /// Optional: public node provider URL. - // #[wasm_bindgen(getter, js_name = "providerUrl")] - // pub fn resolver_node_provider_url(&self) -> Option { - // self.inner.client.node_descriptor().and_then(|node| node.provider_url.clone()) - // } - /// Connect to the Kaspa RPC server. This function starts a background /// task that connects and reconnects to the server if the connection /// is terminated. Use [`disconnect()`](Self::disconnect()) to From 4be40b1a557cf65f2dbe475d8c8b1f603ef804bd Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 22 Jul 2024 18:11:33 +0300 Subject: [PATCH 100/158] update resolvers --- rpc/wrpc/client/Resolvers.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/rpc/wrpc/client/Resolvers.toml b/rpc/wrpc/client/Resolvers.toml index 4580fc7586..cb8b436527 100644 --- a/rpc/wrpc/client/Resolvers.toml +++ b/rpc/wrpc/client/Resolvers.toml @@ -6,17 +6,17 @@ address = "http://127.0.0.1:8888" template = "https://*.kaspa.stream" nodes = ["eric","maxim","sean","troy"] -# [[group]] -# template = "https://*.kaspa.red" -# nodes = ["john", "mike", "paul", "alex"] +[[group]] +template = "https://*.kaspa.red" +nodes = ["john", "mike", "paul", "alex"] -# [[group]] -# template = "https://*.kaspa.green" -# nodes = ["jake", "mark", "adam", "liam"] +[[group]] +template = "https://*.kaspa.green" +nodes = ["jake", "mark", "adam", "liam"] -# [[group]] -# template = "https://*.kaspa.blue" -# nodes = ["noah", "ryan", "jack", "luke"] +[[group]] +template = "https://*.kaspa.blue" +nodes = ["noah", "ryan", "jack", "luke"] # [[group]] # enable = true From d66301343a87809db0981aa105d2f67a4c1d102d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 23 Jul 2024 03:24:36 +0300 Subject: [PATCH 101/158] fix kaspa-utils/build.rs to always produce git related env vars. --- utils/build.rs | 12 +++++++++++- utils/src/git.rs | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/utils/build.rs b/utils/build.rs index 2d97cd7080..705e922962 100644 --- a/utils/build.rs +++ b/utils/build.rs @@ -10,13 +10,23 @@ struct GitHead { } fn main() { - if env::var("RUSTY_KASPA_NO_COMMIT_HASH").is_err() { + let success = if env::var("RUSTY_KASPA_NO_COMMIT_HASH").is_err() { if let Some(GitHead { head_path, head_ref_path, full_hash, short_hash }) = try_git_head() { println!("cargo::rerun-if-changed={head_path}"); println!("cargo::rerun-if-changed={head_ref_path}"); println!("cargo:rustc-env=RUSTY_KASPA_GIT_FULL_COMMIT_HASH={full_hash}"); println!("cargo:rustc-env=RUSTY_KASPA_GIT_SHORT_COMMIT_HASH={short_hash}"); + true + } else { + false } + } else { + false + }; + + if !success { + println!("cargo:rustc-env=RUSTY_KASPA_GIT_FULL_COMMIT_HASH="); + println!("cargo:rustc-env=RUSTY_KASPA_GIT_SHORT_COMMIT_HASH="); } } diff --git a/utils/src/git.rs b/utils/src/git.rs index 14de9e21bc..ca62da8a7c 100644 --- a/utils/src/git.rs +++ b/utils/src/git.rs @@ -3,7 +3,7 @@ use std::fmt::Display; const VERSION: &str = env!("CARGO_PKG_VERSION"); -// generates by `build.rs` +// generated by `build.rs` const FULL_HASH: &str = env!("RUSTY_KASPA_GIT_FULL_COMMIT_HASH"); const SHORT_HASH: &str = env!("RUSTY_KASPA_GIT_SHORT_COMMIT_HASH"); From c831c53a1964a262025865339d5a27f745077871 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 23 Jul 2024 10:59:27 +0300 Subject: [PATCH 102/158] add git commit hash to WASM32 SDK artifacts during CI build --- .github/workflows/ci.yaml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8a412c054c..a81bfe93ef 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -274,12 +274,16 @@ jobs: run: cargo clippy -p kaspa-wasm --target wasm32-unknown-unknown build-wasm32: - name: Build Wasm32 + name: Build WASM32 SDK runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Setup Environment + shell: bash + run: echo "SHORT_SHA=`git rev-parse --short HEAD`" >> $GITHUB_ENV + - name: Install Protoc uses: arduino/setup-protoc@v3 with: @@ -337,13 +341,16 @@ jobs: key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Build wasm release - run: cd wasm && bash build-release + run: | + cd wasm && bash build-release + mv wasm/release/kaspa-wasm32-sdk.zip wasm/release/kaspa-wasm32-sdk-${{ env.SHORT_SHA }}.zip - - name: Upload wasm binary to GitHub + - name: Upload WASM build to GitHub uses: actions/upload-artifact@v4 with: - name: kaspa-wasm32-sdk - path: wasm/release/kaspa-wasm32-sdk.zip + name: kaspa-wasm32-sdk-${{ env.SHORT_SHA }}.zip + path: wasm/release/kaspa-wasm32-sdk-${{ env.SHORT_SHA }}.zip + build-release: name: Build Ubuntu Release runs-on: ubuntu-latest From 0812a7af07bce821d5daf924adbdf4fa152bbf9d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 23 Jul 2024 11:48:22 +0300 Subject: [PATCH 103/158] fix WASM32 CI build (testing) --- .github/workflows/ci.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a81bfe93ef..27fe1376a0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -211,7 +211,7 @@ jobs: check-wasm32: - name: Check Wasm32 + name: Check WASM32 runs-on: ubuntu-latest steps: - name: Checkout sources @@ -342,7 +342,10 @@ jobs: - name: Build wasm release run: | - cd wasm && bash build-release + pushd . + cd wasm + bash build-release + popd mv wasm/release/kaspa-wasm32-sdk.zip wasm/release/kaspa-wasm32-sdk-${{ env.SHORT_SHA }}.zip - name: Upload WASM build to GitHub From a55905b6cbc43955cf1db29ab0219c9737403e43 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 23 Jul 2024 12:41:40 +0300 Subject: [PATCH 104/158] fix the default url handling in wRPC client --- rpc/wrpc/client/src/client.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index 326bd39d3b..f15bccbf11 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -34,7 +34,14 @@ struct Inner { connect_guard: AsyncMutex<()>, disconnect_guard: AsyncMutex<()>, // --- + // The permanent url passed in the constructor + // (dominant, overrides Resolver if supplied). + ctor_url: Mutex>, + // The url passed in the connect() method + // (overrides default URL and the Resolver). default_url: Mutex>, + // The current url wRPC is connected to + // (possibly acquired via the Resolver). current_url: Mutex>, resolver: Mutex>, network_id: Mutex>, @@ -104,7 +111,8 @@ impl Inner { connect_guard: async_std::sync::Mutex::new(()), disconnect_guard: async_std::sync::Mutex::new(()), // --- - default_url: Mutex::new(url.map(|s| s.to_string())), + ctor_url: Mutex::new(url.map(|s| s.to_string())), + default_url: Mutex::new(None), current_url: Mutex::new(None), resolver: Mutex::new(resolver), network_id: Mutex::new(network_id), @@ -133,6 +141,10 @@ impl Inner { Ok(()) } + fn ctor_url(&self) -> Option { + self.ctor_url.lock().unwrap().clone() + } + fn default_url(&self) -> Option { self.default_url.lock().unwrap().clone() } @@ -214,7 +226,7 @@ impl SubscriptionManager for Inner { #[async_trait] impl RpcResolver for Inner { async fn resolve_url(&self) -> ResolverResult { - let url = if let Some(url) = self.default_url() { + let url = if let Some(url) = self.default_url().or(self.ctor_url()) { url } else if let Some(resolver) = self.resolver().as_ref() { let network_id = self.network_id().expect("Resolver requires network id in RPC client configuration"); @@ -255,7 +267,11 @@ impl Debug for KaspaRpcClient { } impl KaspaRpcClient { - /// Create a new `KaspaRpcClient` with the given Encoding and URL + /// Create a new `KaspaRpcClient` with the given Encoding, and an optional url or a Resolver. + /// Please note that if you pass the url to the constructor, it will force the KaspaRpcClient + /// to always use this url. If you want to have the ability to switch between urls, + /// you must pass [`Option::None`] as the `url` argument and then supply your own url to the `connect()` + /// function each time you connect. pub fn new( encoding: Encoding, url: Option<&str>, @@ -411,9 +427,7 @@ impl KaspaRpcClient { let options = options.unwrap_or_default(); let strategy = options.strategy; - if let Some(ref url) = options.url { - self.set_url(Some(url))?; - } + self.inner.set_default_url(options.url.as_deref()); // 1Gb message and frame size limits (on native and NodeJs platforms) let ws_config = WebSocketConfig { From e180c0b4eed76488441fc86efe5ac7f19fb06f0c Mon Sep 17 00:00:00 2001 From: aspect Date: Wed, 24 Jul 2024 01:34:15 +0300 Subject: [PATCH 105/158] Key attributes (make XPrv and XPub inspectable) (#77) * getters for XPrv and XPub attributes * fmt --- wallet/keys/src/xprv.rs | 51 ++++++++++++++++++++++++++++++++++++++++- wallet/keys/src/xpub.rs | 42 +++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/wallet/keys/src/xprv.rs b/wallet/keys/src/xprv.rs index e431a5ee34..c9a6bf18df 100644 --- a/wallet/keys/src/xprv.rs +++ b/wallet/keys/src/xprv.rs @@ -1,3 +1,5 @@ +use kaspa_bip32::{ChainCode, KeyFingerprint, PrivateKey}; + use crate::imports::*; /// @@ -13,7 +15,7 @@ use crate::imports::*; /// #[derive(Clone, CastFromJs)] -#[wasm_bindgen] +#[wasm_bindgen(inspectable)] pub struct XPrv { inner: ExtendedPrivateKey, } @@ -70,6 +72,53 @@ impl XPrv { let public_key = self.inner.public_key(); Ok(public_key.into()) } + + // ~~~~ Getters ~~~~ + + #[wasm_bindgen(getter)] + pub fn xprv(&self) -> Result { + let str = self.inner.to_extended_key("kprv".try_into()?).to_string(); + Ok(str) + } + + #[wasm_bindgen(getter, js_name = "privateKey")] + pub fn private_key_as_hex_string(&self) -> String { + self.inner.private_key().to_bytes().to_vec().to_hex() + } + + #[wasm_bindgen(getter)] + pub fn depth(&self) -> u8 { + self.inner.attrs().depth + } + + #[wasm_bindgen(getter, js_name = parentFingerprint)] + pub fn parent_fingerprint_as_hex_string(&self) -> String { + self.inner.attrs().parent_fingerprint.to_vec().to_hex() + } + + #[wasm_bindgen(getter, js_name = childNumber)] + pub fn child_number(&self) -> u32 { + self.inner.attrs().child_number.into() + } + + #[wasm_bindgen(getter, js_name = chainCode)] + pub fn chain_code_as_hex_string(&self) -> String { + self.inner.attrs().chain_code.to_vec().to_hex() + } +} + +impl XPrv { + pub fn private_key(&self) -> &SecretKey { + self.inner.private_key() + } + + pub fn parent_fingerprint(&self) -> KeyFingerprint { + self.inner.attrs().parent_fingerprint + } + + pub fn chain_code(&self) -> ChainCode { + self.inner.attrs().chain_code + } } impl<'a> From<&'a XPrv> for &'a ExtendedPrivateKey { diff --git a/wallet/keys/src/xpub.rs b/wallet/keys/src/xpub.rs index e91df41c0e..623136c988 100644 --- a/wallet/keys/src/xpub.rs +++ b/wallet/keys/src/xpub.rs @@ -1,4 +1,4 @@ -use kaspa_bip32::Prefix; +use kaspa_bip32::{ChainCode, KeyFingerprint, Prefix}; use std::{fmt, str::FromStr}; use crate::imports::*; @@ -15,7 +15,7 @@ use crate::imports::*; /// @category Wallet SDK /// #[derive(Clone, CastFromJs)] -#[wasm_bindgen] +#[wasm_bindgen(inspectable)] pub struct XPub { inner: ExtendedPublicKey, } @@ -58,6 +58,44 @@ impl XPub { pub fn public_key(&self) -> PublicKey { self.inner.public_key().into() } + + // ~~~~ Getters ~~~~ + + #[wasm_bindgen(getter)] + pub fn xpub(&self) -> Result { + let str = self.inner.to_extended_key("kpub".try_into()?).to_string(); + Ok(str) + } + + #[wasm_bindgen(getter)] + pub fn depth(&self) -> u8 { + self.inner.attrs().depth + } + + #[wasm_bindgen(getter, js_name = parentFingerprint)] + pub fn parent_fingerprint_as_hex_string(&self) -> String { + self.inner.attrs().parent_fingerprint.to_vec().to_hex() + } + + #[wasm_bindgen(getter, js_name = childNumber)] + pub fn child_number(&self) -> u32 { + self.inner.attrs().child_number.into() + } + + #[wasm_bindgen(getter, js_name = chainCode)] + pub fn chain_code_as_hex_string(&self) -> String { + self.inner.attrs().chain_code.to_vec().to_hex() + } +} + +impl XPub { + pub fn parent_fingerprint(&self) -> KeyFingerprint { + self.inner.attrs().parent_fingerprint + } + + pub fn chain_code(&self) -> ChainCode { + self.inner.attrs().chain_code + } } impl From> for XPub { From 54a724e3aec183bbafdc65ecf09c5c3af36b4b16 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 25 Jul 2024 22:22:05 +0300 Subject: [PATCH 106/158] post merge fixes --- rpc/core/src/api/rpc.rs | 3 ++- rpc/core/src/model/message.rs | 40 ++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 8e3adde798..0251aa7fc9 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -216,10 +216,11 @@ pub trait RpcApi: Sync + Send + AnySync { /// /// Returns the ID of the inserted transaction and the transaction the submission replaced in the mempool. async fn submit_transaction_replacement(&self, transaction: RpcTransaction) -> RpcResult { - self.submit_transaction_replacement_call(SubmitTransactionReplacementRequest { transaction }).await + self.submit_transaction_replacement_call(None, SubmitTransactionReplacementRequest { transaction }).await } async fn submit_transaction_replacement_call( &self, + connection: Option<&DynRpcConnection>, request: SubmitTransactionReplacementRequest, ) -> RpcResult; diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 15e5d2d3fb..2631a67018 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -742,7 +742,25 @@ impl SubmitTransactionReplacementRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +impl Serializer for SubmitTransactionReplacementRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + serialize!(RpcTransaction, &self.transaction, writer)?; + + Ok(()) + } +} + +impl Deserializer for SubmitTransactionReplacementRequest { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + let transaction = deserialize!(RpcTransaction, reader)?; + + Ok(Self { transaction }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubmitTransactionReplacementResponse { pub transaction_id: RpcTransactionId, @@ -755,6 +773,26 @@ impl SubmitTransactionReplacementResponse { } } +impl Serializer for SubmitTransactionReplacementResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + store!(RpcTransactionId, &self.transaction_id, writer)?; + serialize!(RpcTransaction, &self.replaced_transaction, writer)?; + + Ok(()) + } +} + +impl Deserializer for SubmitTransactionReplacementResponse { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + let transaction_id = load!(RpcTransactionId, reader)?; + let replaced_transaction = deserialize!(RpcTransaction, reader)?; + + Ok(Self { transaction_id, replaced_transaction }) + } +} + #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "camelCase")] pub struct GetSubnetworkRequest { From 090fb86af4f13f4c2128baf75add056461737877 Mon Sep 17 00:00:00 2001 From: aspect Date: Thu, 25 Jul 2024 22:22:55 +0300 Subject: [PATCH 107/158] Merge RBF (#80) * Replace by fee on mempool (#499) * Replace by fee on mempool with tests * Add a custom RemovalReason -- ReplacedByFee * Let `MinerManager` handle replace by fee (RBF) for RPC and P2P * Refines success conditions and process of RBF policies * Add an RPC `submit_transaction_replacement` method * fix fmt * Fix CLI Build WASM32 error * Avoid breaking wRPC * Extend some tests coverage to all priority, orphan & RBF policy combinations * Let RBF fail early or at least before checking transaction scripts in consensus * Cleaning * More cleaning * Use contextual instead of compute mass in RBF eviction rule * Avoid collision with another PR * Avoid collision with another PR (2) * Extended test coverage of RBF * Extended test coverage of RBF (2) * Rename `TransactionBatchValidationArgs` to `TransactionValidationBatchArgs` * Add comments * Assert instead of condition * Add an `RbfPolicy` parameter to mining manager tx validate_and_insert_... fns * Infer RBF policy from unorphaned transaction * Apply the RBF policy to all orphan-related cases * In Rbf allowed mode, check feerate threshold vs all double spends (i.e., compare to the max) * Minor hashset optimization * Rename: fee_per_mass -> feerate * Use rbf policy arg for post processing step as well * Renames and comments * Relaxation: fail gracefully if rbf replaced tx is missing (also fixes an edge case where mempool duplication resulted in this scenario) * Tx id is appended by the caller anyway --------- Co-authored-by: Tiram <18632023+tiram88@users.noreply.github.com> Co-authored-by: Michael Sutton * post merge fixes --------- Co-authored-by: KaffinPX <73744616+KaffinPX@users.noreply.github.com> Co-authored-by: Tiram <18632023+tiram88@users.noreply.github.com> Co-authored-by: Michael Sutton --- consensus/core/src/api/args.rs | 47 + consensus/core/src/api/mod.rs | 14 +- consensus/core/src/errors/tx.rs | 5 + consensus/core/src/tx.rs | 13 + consensus/src/consensus/mod.rs | 21 +- .../pipeline/virtual_processor/processor.rs | 22 +- .../virtual_processor/utxo_validation.rs | 6 +- .../transaction_validator_populated.rs | 21 +- crypto/txscript/src/standard.rs | 42 +- mining/errors/src/mempool.rs | 12 +- mining/src/block_template/selector.rs | 6 +- mining/src/manager.rs | 142 ++- mining/src/manager_tests.rs | 814 ++++++++++++++---- mining/src/mempool/mod.rs | 48 ++ mining/src/mempool/model/transactions_pool.rs | 22 +- mining/src/mempool/model/tx.rs | 53 +- mining/src/mempool/model/utxo_set.rs | 32 +- .../populate_entries_and_try_validate.rs | 20 +- mining/src/mempool/replace_by_fee.rs | 149 ++++ .../validate_and_insert_transaction.rs | 47 +- mining/src/model/mod.rs | 1 + mining/src/model/tx_insert.rs | 14 + mining/src/testutils/consensus_mock.rs | 17 +- protocol/flows/src/flow_context.rs | 40 +- protocol/flows/src/v5/txrelay/flow.rs | 4 +- rothschild/src/main.rs | 4 +- rpc/core/src/api/ops.rs | 3 + rpc/core/src/api/rpc.rs | 12 + rpc/core/src/model/message.rs | 63 ++ rpc/grpc/client/src/lib.rs | 1 + rpc/grpc/core/proto/messages.proto | 12 +- rpc/grpc/core/proto/rpc.proto | 21 +- rpc/grpc/core/src/convert/kaspad.rs | 2 + rpc/grpc/core/src/convert/message.rs | 27 + rpc/grpc/core/src/ops.rs | 1 + .../server/src/request_handler/factory.rs | 1 + rpc/grpc/server/src/tests/rpc_core_mock.rs | 8 + rpc/service/src/service.rs | 17 + rpc/wrpc/client/src/client.rs | 1 + rpc/wrpc/server/src/router.rs | 1 + testing/integration/src/common/utils.rs | 4 +- testing/integration/src/rpc_tests.rs | 11 + wallet/core/src/tests/rpc_core_mock.rs | 8 + 43 files changed, 1500 insertions(+), 309 deletions(-) create mode 100644 consensus/core/src/api/args.rs create mode 100644 mining/src/mempool/replace_by_fee.rs create mode 100644 mining/src/model/tx_insert.rs diff --git a/consensus/core/src/api/args.rs b/consensus/core/src/api/args.rs new file mode 100644 index 0000000000..ebc76d97d0 --- /dev/null +++ b/consensus/core/src/api/args.rs @@ -0,0 +1,47 @@ +use std::collections::HashMap; + +use crate::tx::TransactionId; + +/// A struct provided to consensus for transaction validation processing calls +#[derive(Clone, Debug, Default)] +pub struct TransactionValidationArgs { + /// Optional fee/mass threshold above which a bound transaction in not rejected + pub feerate_threshold: Option, +} + +impl TransactionValidationArgs { + pub fn new(feerate_threshold: Option) -> Self { + Self { feerate_threshold } + } +} + +/// A struct provided to consensus for transactions validation batch processing calls +pub struct TransactionValidationBatchArgs { + tx_args: HashMap, +} + +impl TransactionValidationBatchArgs { + const DEFAULT_ARGS: TransactionValidationArgs = TransactionValidationArgs { feerate_threshold: None }; + + pub fn new() -> Self { + Self { tx_args: HashMap::new() } + } + + /// Set some fee/mass threshold for transaction `transaction_id`. + pub fn set_feerate_threshold(&mut self, transaction_id: TransactionId, feerate_threshold: f64) { + self.tx_args + .entry(transaction_id) + .and_modify(|x| x.feerate_threshold = Some(feerate_threshold)) + .or_insert(TransactionValidationArgs::new(Some(feerate_threshold))); + } + + pub fn get(&self, transaction_id: &TransactionId) -> &TransactionValidationArgs { + self.tx_args.get(transaction_id).unwrap_or(&Self::DEFAULT_ARGS) + } +} + +impl Default for TransactionValidationBatchArgs { + fn default() -> Self { + Self::new() + } +} diff --git a/consensus/core/src/api/mod.rs b/consensus/core/src/api/mod.rs index 23e7abb530..4d8fbc3b4d 100644 --- a/consensus/core/src/api/mod.rs +++ b/consensus/core/src/api/mod.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use crate::{ acceptance_data::AcceptanceData, + api::args::{TransactionValidationArgs, TransactionValidationBatchArgs}, block::{Block, BlockTemplate, TemplateBuildMode, TemplateTransactionSelector, VirtualStateApproxId}, blockstatus::BlockStatus, coinbase::MinerData, @@ -25,6 +26,7 @@ use kaspa_hashes::Hash; pub use self::stats::{BlockCount, ConsensusStats}; +pub mod args; pub mod counters; pub mod stats; @@ -62,14 +64,18 @@ pub trait ConsensusApi: Send + Sync { } /// Populates the mempool transaction with maximally found UTXO entry data and proceeds to full transaction - /// validation if all are found. If validation is successful, also [`transaction.calculated_fee`] is expected to be populated. - fn validate_mempool_transaction(&self, transaction: &mut MutableTransaction) -> TxResult<()> { + /// validation if all are found. If validation is successful, also `transaction.calculated_fee` is expected to be populated. + fn validate_mempool_transaction(&self, transaction: &mut MutableTransaction, args: &TransactionValidationArgs) -> TxResult<()> { unimplemented!() } /// Populates the mempool transactions with maximally found UTXO entry data and proceeds to full transactions - /// validation if all are found. If validation is successful, also [`transaction.calculated_fee`] is expected to be populated. - fn validate_mempool_transactions_in_parallel(&self, transactions: &mut [MutableTransaction]) -> Vec> { + /// validation if all are found. If validation is successful, also `transaction.calculated_fee` is expected to be populated. + fn validate_mempool_transactions_in_parallel( + &self, + transactions: &mut [MutableTransaction], + args: &TransactionValidationBatchArgs, + ) -> Vec> { unimplemented!() } diff --git a/consensus/core/src/errors/tx.rs b/consensus/core/src/errors/tx.rs index e1936d37aa..ec1899faad 100644 --- a/consensus/core/src/errors/tx.rs +++ b/consensus/core/src/errors/tx.rs @@ -88,6 +88,11 @@ pub enum TxRuleError { #[error("calculated contextual mass (including storage mass) {0} is not equal to the committed mass field {1}")] WrongMass(u64, u64), + + /// [`TxRuleError::FeerateTooLow`] is not a consensus error but a mempool error triggered by the + /// fee/mass RBF validation rule + #[error("fee rate per contextual mass gram is not greater than the fee rate of the replaced transaction")] + FeerateTooLow, } pub type TxResult = std::result::Result; diff --git a/consensus/core/src/tx.rs b/consensus/core/src/tx.rs index 399985b3b9..3b48681d07 100644 --- a/consensus/core/src/tx.rs +++ b/consensus/core/src/tx.rs @@ -407,6 +407,19 @@ impl> MutableTransaction { *entry = None; } } + + /// Returns the calculated feerate. The feerate is calculated as the amount of fee + /// this transactions pays per gram of the full contextual (compute & storage) mass. The + /// function returns a value when calculated fee exists and the contextual mass is greater + /// than zero, otherwise `None` is returned. + pub fn calculated_feerate(&self) -> Option { + let contextual_mass = self.tx.as_ref().mass(); + if contextual_mass > 0 { + self.calculated_fee.map(|fee| fee as f64 / contextual_mass as f64) + } else { + None + } + } } impl> AsRef for MutableTransaction { diff --git a/consensus/src/consensus/mod.rs b/consensus/src/consensus/mod.rs index 80babbef0f..fb86d0dab1 100644 --- a/consensus/src/consensus/mod.rs +++ b/consensus/src/consensus/mod.rs @@ -40,7 +40,11 @@ use crate::{ }; use kaspa_consensus_core::{ acceptance_data::AcceptanceData, - api::{stats::BlockCount, BlockValidationFutures, ConsensusApi, ConsensusStats}, + api::{ + args::{TransactionValidationArgs, TransactionValidationBatchArgs}, + stats::BlockCount, + BlockValidationFutures, ConsensusApi, ConsensusStats, + }, block::{Block, BlockTemplate, TemplateBuildMode, TemplateTransactionSelector, VirtualStateApproxId}, blockhash::BlockHashExtensions, blockstatus::BlockStatus, @@ -49,9 +53,10 @@ use kaspa_consensus_core::{ errors::{ coinbase::CoinbaseResult, consensus::{ConsensusError, ConsensusResult}, + difficulty::DifficultyError, + pruning::PruningImportError, tx::TxResult, }, - errors::{difficulty::DifficultyError, pruning::PruningImportError}, header::Header, muhash::MuHashExtensions, network::NetworkType, @@ -418,13 +423,17 @@ impl ConsensusApi for Consensus { BlockValidationFutures { block_task: Box::pin(block_task), virtual_state_task: Box::pin(virtual_state_task) } } - fn validate_mempool_transaction(&self, transaction: &mut MutableTransaction) -> TxResult<()> { - self.virtual_processor.validate_mempool_transaction(transaction)?; + fn validate_mempool_transaction(&self, transaction: &mut MutableTransaction, args: &TransactionValidationArgs) -> TxResult<()> { + self.virtual_processor.validate_mempool_transaction(transaction, args)?; Ok(()) } - fn validate_mempool_transactions_in_parallel(&self, transactions: &mut [MutableTransaction]) -> Vec> { - self.virtual_processor.validate_mempool_transactions_in_parallel(transactions) + fn validate_mempool_transactions_in_parallel( + &self, + transactions: &mut [MutableTransaction], + args: &TransactionValidationBatchArgs, + ) -> Vec> { + self.virtual_processor.validate_mempool_transactions_in_parallel(transactions, args) } fn populate_mempool_transaction(&self, transaction: &mut MutableTransaction) -> TxResult<()> { diff --git a/consensus/src/pipeline/virtual_processor/processor.rs b/consensus/src/pipeline/virtual_processor/processor.rs index ded0622514..bb1132fea0 100644 --- a/consensus/src/pipeline/virtual_processor/processor.rs +++ b/consensus/src/pipeline/virtual_processor/processor.rs @@ -48,6 +48,7 @@ use crate::{ }; use kaspa_consensus_core::{ acceptance_data::AcceptanceData, + api::args::{TransactionValidationArgs, TransactionValidationBatchArgs}, block::{BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector}, blockstatus::BlockStatus::{StatusDisqualifiedFromChain, StatusUTXOValid}, coinbase::MinerData, @@ -757,23 +758,28 @@ impl VirtualStateProcessor { virtual_utxo_view: &impl UtxoView, virtual_daa_score: u64, virtual_past_median_time: u64, + args: &TransactionValidationArgs, ) -> TxResult<()> { self.transaction_validator.validate_tx_in_isolation(&mutable_tx.tx)?; self.transaction_validator.utxo_free_tx_validation(&mutable_tx.tx, virtual_daa_score, virtual_past_median_time)?; - self.validate_mempool_transaction_in_utxo_context(mutable_tx, virtual_utxo_view, virtual_daa_score)?; + self.validate_mempool_transaction_in_utxo_context(mutable_tx, virtual_utxo_view, virtual_daa_score, args)?; Ok(()) } - pub fn validate_mempool_transaction(&self, mutable_tx: &mut MutableTransaction) -> TxResult<()> { + pub fn validate_mempool_transaction(&self, mutable_tx: &mut MutableTransaction, args: &TransactionValidationArgs) -> TxResult<()> { let virtual_read = self.virtual_stores.read(); let virtual_state = virtual_read.state.get().unwrap(); let virtual_utxo_view = &virtual_read.utxo_set; let virtual_daa_score = virtual_state.daa_score; let virtual_past_median_time = virtual_state.past_median_time; - self.validate_mempool_transaction_impl(mutable_tx, virtual_utxo_view, virtual_daa_score, virtual_past_median_time) + self.validate_mempool_transaction_impl(mutable_tx, virtual_utxo_view, virtual_daa_score, virtual_past_median_time, args) } - pub fn validate_mempool_transactions_in_parallel(&self, mutable_txs: &mut [MutableTransaction]) -> Vec> { + pub fn validate_mempool_transactions_in_parallel( + &self, + mutable_txs: &mut [MutableTransaction], + args: &TransactionValidationBatchArgs, + ) -> Vec> { let virtual_read = self.virtual_stores.read(); let virtual_state = virtual_read.state.get().unwrap(); let virtual_utxo_view = &virtual_read.utxo_set; @@ -784,7 +790,13 @@ impl VirtualStateProcessor { mutable_txs .par_iter_mut() .map(|mtx| { - self.validate_mempool_transaction_impl(mtx, &virtual_utxo_view, virtual_daa_score, virtual_past_median_time) + self.validate_mempool_transaction_impl( + mtx, + &virtual_utxo_view, + virtual_daa_score, + virtual_past_median_time, + args.get(&mtx.id()), + ) }) .collect::>>() }) diff --git a/consensus/src/pipeline/virtual_processor/utxo_validation.rs b/consensus/src/pipeline/virtual_processor/utxo_validation.rs index 1129762945..16f355a451 100644 --- a/consensus/src/pipeline/virtual_processor/utxo_validation.rs +++ b/consensus/src/pipeline/virtual_processor/utxo_validation.rs @@ -15,6 +15,7 @@ use crate::{ }; use kaspa_consensus_core::{ acceptance_data::{AcceptedTxEntry, MergesetBlockAcceptanceData}, + api::args::TransactionValidationArgs, coinbase::*, hashing, header::Header, @@ -248,7 +249,7 @@ impl VirtualStateProcessor { } } let populated_tx = PopulatedTransaction::new(transaction, entries); - let res = self.transaction_validator.validate_populated_transaction_and_get_fee(&populated_tx, pov_daa_score, flags); + let res = self.transaction_validator.validate_populated_transaction_and_get_fee(&populated_tx, pov_daa_score, flags, None); match res { Ok(calculated_fee) => Ok(ValidatedTransaction::new(populated_tx, calculated_fee)), Err(tx_rule_error) => { @@ -290,6 +291,7 @@ impl VirtualStateProcessor { mutable_tx: &mut MutableTransaction, utxo_view: &impl UtxoView, pov_daa_score: u64, + args: &TransactionValidationArgs, ) -> TxResult<()> { self.populate_mempool_transaction_in_utxo_context(mutable_tx, utxo_view)?; @@ -308,10 +310,12 @@ impl VirtualStateProcessor { mutable_tx.tx.set_mass(contextual_mass); // At this point we know all UTXO entries are populated, so we can safely pass the tx as verifiable + let mass_and_feerate_threshold = args.feerate_threshold.map(|threshold| (contextual_mass, threshold)); let calculated_fee = self.transaction_validator.validate_populated_transaction_and_get_fee( &mutable_tx.as_verifiable(), pov_daa_score, TxValidationFlags::SkipMassCheck, // we can skip the mass check since we just set it + mass_and_feerate_threshold, )?; mutable_tx.calculated_fee = Some(calculated_fee); Ok(()) diff --git a/consensus/src/processes/transaction_validator/transaction_validator_populated.rs b/consensus/src/processes/transaction_validator/transaction_validator_populated.rs index 696b9a9d49..1835ba61e4 100644 --- a/consensus/src/processes/transaction_validator/transaction_validator_populated.rs +++ b/consensus/src/processes/transaction_validator/transaction_validator_populated.rs @@ -27,10 +27,12 @@ impl TransactionValidator { tx: &impl VerifiableTransaction, pov_daa_score: u64, flags: TxValidationFlags, + mass_and_feerate_threshold: Option<(u64, f64)>, ) -> TxResult { self.check_transaction_coinbase_maturity(tx, pov_daa_score)?; let total_in = self.check_transaction_input_amounts(tx)?; let total_out = Self::check_transaction_output_values(tx, total_in)?; + let fee = total_in - total_out; if flags != TxValidationFlags::SkipMassCheck && pov_daa_score > self.storage_mass_activation_daa_score { // Storage mass hardfork was activated self.check_mass_commitment(tx)?; @@ -40,6 +42,11 @@ impl TransactionValidator { } } Self::check_sequence_lock(tx, pov_daa_score)?; + + // The following call is not a consensus check (it could not be one in the first place since it uses floating number) + // but rather a mempool Replace by Fee validation rule. It was placed here purposely for avoiding unneeded script checks. + Self::check_feerate_threshold(fee, mass_and_feerate_threshold)?; + match flags { TxValidationFlags::Full | TxValidationFlags::SkipMassCheck => { Self::check_sig_op_counts(tx)?; @@ -47,7 +54,19 @@ impl TransactionValidator { } TxValidationFlags::SkipScriptChecks => {} } - Ok(total_in - total_out) + Ok(fee) + } + + fn check_feerate_threshold(fee: u64, mass_and_feerate_threshold: Option<(u64, f64)>) -> TxResult<()> { + // An actual check can only occur if some mass and threshold are provided, + // otherwise, the check does not verify anything and exits successfully. + if let Some((contextual_mass, feerate_threshold)) = mass_and_feerate_threshold { + assert!(contextual_mass > 0); + if fee as f64 / contextual_mass as f64 <= feerate_threshold { + return Err(TxRuleError::FeerateTooLow); + } + } + Ok(()) } fn check_transaction_coinbase_maturity(&self, tx: &impl VerifiableTransaction, pov_daa_score: u64) -> TxResult<()> { diff --git a/crypto/txscript/src/standard.rs b/crypto/txscript/src/standard.rs index fb7eb455a3..3c0f12a181 100644 --- a/crypto/txscript/src/standard.rs +++ b/crypto/txscript/src/standard.rs @@ -100,9 +100,9 @@ pub mod test_helpers { (script_public_key, redeem_script) } - // Creates a transaction that spends the first output of provided transaction. - // Assumes that the output being spent has opTrueScript as it's scriptPublicKey. - // Creates the value of the spent output minus provided `fee` (in sompi). + /// Creates a transaction that spends the first output of provided transaction. + /// Assumes that the output being spent has opTrueScript as its scriptPublicKey. + /// Creates the value of the spent output minus provided `fee` (in sompi). pub fn create_transaction(tx_to_spend: &Transaction, fee: u64) -> Transaction { let (script_public_key, redeem_script) = op_true_script(); let signature_script = pay_to_script_hash_signature_script(redeem_script, vec![]).expect("the script is canonical"); @@ -111,6 +111,42 @@ pub mod test_helpers { let output = TransactionOutput::new(tx_to_spend.outputs[0].value - fee, script_public_key); Transaction::new(TX_VERSION, vec![input], vec![output], 0, SUBNETWORK_ID_NATIVE, 0, vec![]) } + + /// Creates a transaction that spends the outputs of specified indexes (if they exist) of every provided transaction and returns an optional change. + /// Assumes that the outputs being spent have opTrueScript as their scriptPublicKey. + /// + /// If some change is provided, creates two outputs, first one with the value of the spent outputs minus `change` + /// and `fee` (in sompi) and second one of `change` amount. + /// + /// If no change is provided, creates only one output with the value of the spent outputs minus and `fee` (in sompi) + pub fn create_transaction_with_change<'a>( + txs_to_spend: impl Iterator, + output_indexes: Vec, + change: Option, + fee: u64, + ) -> Transaction { + let (script_public_key, redeem_script) = op_true_script(); + let signature_script = pay_to_script_hash_signature_script(redeem_script, vec![]).expect("the script is canonical"); + let mut inputs_value: u64 = 0; + let mut inputs = vec![]; + for tx_to_spend in txs_to_spend { + for i in output_indexes.iter().copied() { + if i < tx_to_spend.outputs.len() { + let previous_outpoint = TransactionOutpoint::new(tx_to_spend.id(), i as u32); + inputs.push(TransactionInput::new(previous_outpoint, signature_script.clone(), MAX_TX_IN_SEQUENCE_NUM, 1)); + inputs_value += tx_to_spend.outputs[i].value; + } + } + } + let outputs = match change { + Some(change) => vec![ + TransactionOutput::new(inputs_value - fee - change, script_public_key.clone()), + TransactionOutput::new(change, script_public_key), + ], + None => vec![TransactionOutput::new(inputs_value - fee, script_public_key.clone())], + }; + Transaction::new(TX_VERSION, inputs, outputs, 0, SUBNETWORK_ID_NATIVE, 0, vec![]) + } } #[cfg(test)] diff --git a/mining/errors/src/mempool.rs b/mining/errors/src/mempool.rs index e33737f9df..be8ff389a9 100644 --- a/mining/errors/src/mempool.rs +++ b/mining/errors/src/mempool.rs @@ -4,7 +4,7 @@ use kaspa_consensus_core::{ }; use thiserror::Error; -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum RuleError { /// A consensus transaction rule error /// @@ -24,9 +24,15 @@ pub enum RuleError { #[error("transaction {0} is already in the mempool")] RejectDuplicate(TransactionId), - #[error("output {0} already spent by transaction {1} in the memory pool")] + #[error("output {0} already spent by transaction {1} in the mempool")] RejectDoubleSpendInMempool(TransactionOutpoint, TransactionId), + #[error("replace by fee found no double spending transaction in the mempool")] + RejectRbfNoDoubleSpend, + + #[error("replace by fee found more than one double spending transaction in the mempool")] + RejectRbfTooManyDoubleSpendingTransactions, + /// New behavior: a transaction is rejected if the mempool is full #[error("number of high-priority transactions in mempool ({0}) has reached the maximum allowed ({1})")] RejectMempoolIsFull(usize, u64), @@ -95,7 +101,7 @@ impl From for RuleError { pub type RuleResult = std::result::Result; -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum NonStandardError { #[error("transaction version {1} is not in the valid range of {2}-{3}")] RejectVersion(TransactionId, u16, u16, u16), diff --git a/mining/src/block_template/selector.rs b/mining/src/block_template/selector.rs index b65126caf6..a55ecb93db 100644 --- a/mining/src/block_template/selector.rs +++ b/mining/src/block_template/selector.rs @@ -182,11 +182,7 @@ impl TransactionsSelector { self.total_mass += selected_tx.calculated_mass; self.total_fees += selected_tx.calculated_fee; - trace!( - "Adding tx {0} (fee per megagram: {1})", - selected_tx.tx.id(), - selected_tx.calculated_fee * 1_000_000 / selected_tx.calculated_mass - ); + trace!("Adding tx {0} (fee per gram: {1})", selected_tx.tx.id(), selected_tx.calculated_fee / selected_tx.calculated_mass); // Mark for deletion selected_candidate.is_marked_for_deletion = true; diff --git a/mining/src/manager.rs b/mining/src/manager.rs index 5743901220..c3bf612618 100644 --- a/mining/src/manager.rs +++ b/mining/src/manager.rs @@ -4,24 +4,28 @@ use crate::{ errors::MiningManagerResult, mempool::{ config::Config, - model::tx::{MempoolTransaction, TxRemovalReason}, + model::tx::{MempoolTransaction, TransactionPostValidation, TransactionPreValidation, TxRemovalReason}, populate_entries_and_try_validate::{ populate_mempool_transactions_in_parallel, validate_mempool_transaction, validate_mempool_transactions_in_parallel, }, - tx::{Orphan, Priority}, + tx::{Orphan, Priority, RbfPolicy}, Mempool, }, model::{ candidate_tx::CandidateTransaction, owner_txs::{GroupedOwnerTransactions, ScriptPublicKeySet}, topological_sort::IntoIterTopologically, + tx_insert::TransactionInsertion, tx_query::TransactionQuery, }, MempoolCountersSnapshot, MiningCounters, P2pTxCountSample, }; use itertools::Itertools; use kaspa_consensus_core::{ - api::ConsensusApi, + api::{ + args::{TransactionValidationArgs, TransactionValidationBatchArgs}, + ConsensusApi, + }, block::{BlockTemplate, TemplateBuildMode}, coinbase::MinerData, errors::{block::RuleError as BlockRuleError, tx::TxRuleError}, @@ -212,47 +216,58 @@ impl MiningManager { /// adds it to the set of known transactions that have not yet been /// added to any block. /// - /// The returned transactions are clones of objects owned by the mempool. + /// The validation is constrained by a Replace by fee policy applied + /// to double spends in the mempool. For more information, see [`RbfPolicy`]. + /// + /// On success, returns transactions that where unorphaned following the insertion + /// of the provided transaction. + /// + /// The returned transactions are references of objects owned by the mempool. pub fn validate_and_insert_transaction( &self, consensus: &dyn ConsensusApi, transaction: Transaction, priority: Priority, orphan: Orphan, - ) -> MiningManagerResult>> { - self.validate_and_insert_mutable_transaction(consensus, MutableTransaction::from_tx(transaction), priority, orphan) + rbf_policy: RbfPolicy, + ) -> MiningManagerResult { + self.validate_and_insert_mutable_transaction(consensus, MutableTransaction::from_tx(transaction), priority, orphan, rbf_policy) } - /// Exposed only for tests. Ordinary users should call `validate_and_insert_transaction` instead - pub fn validate_and_insert_mutable_transaction( + /// Exposed for tests only + /// + /// See `validate_and_insert_transaction` + pub(crate) fn validate_and_insert_mutable_transaction( &self, consensus: &dyn ConsensusApi, transaction: MutableTransaction, priority: Priority, orphan: Orphan, - ) -> MiningManagerResult>> { + rbf_policy: RbfPolicy, + ) -> MiningManagerResult { // read lock on mempool - let mut transaction = self.mempool.read().pre_validate_and_populate_transaction(consensus, transaction)?; + let TransactionPreValidation { mut transaction, feerate_threshold } = + self.mempool.read().pre_validate_and_populate_transaction(consensus, transaction, rbf_policy)?; + let args = TransactionValidationArgs::new(feerate_threshold); // no lock on mempool - let validation_result = validate_mempool_transaction(consensus, &mut transaction); + let validation_result = validate_mempool_transaction(consensus, &mut transaction, &args); // write lock on mempool let mut mempool = self.mempool.write(); - if let Some(accepted_transaction) = - mempool.post_validate_and_insert_transaction(consensus, validation_result, transaction, priority, orphan)? - { - let unorphaned_transactions = mempool.get_unorphaned_transactions_after_accepted_transaction(&accepted_transaction); - drop(mempool); - - // The capacity used here may be exceeded since accepted unorphaned transaction may themselves unorphan other transactions. - let mut accepted_transactions = Vec::with_capacity(unorphaned_transactions.len() + 1); - // We include the original accepted transaction as well - accepted_transactions.push(accepted_transaction); - accepted_transactions.extend(self.validate_and_insert_unorphaned_transactions(consensus, unorphaned_transactions)); - self.counters.increase_tx_counts(1, priority); - - Ok(accepted_transactions) - } else { - Ok(vec![]) + match mempool.post_validate_and_insert_transaction(consensus, validation_result, transaction, priority, orphan, rbf_policy)? { + TransactionPostValidation { removed, accepted: Some(accepted_transaction) } => { + let unorphaned_transactions = mempool.get_unorphaned_transactions_after_accepted_transaction(&accepted_transaction); + drop(mempool); + + // The capacity used here may be exceeded since accepted unorphaned transaction may themselves unorphan other transactions. + let mut accepted_transactions = Vec::with_capacity(unorphaned_transactions.len() + 1); + // We include the original accepted transaction as well + accepted_transactions.push(accepted_transaction); + accepted_transactions.extend(self.validate_and_insert_unorphaned_transactions(consensus, unorphaned_transactions)); + self.counters.increase_tx_counts(1, priority); + + Ok(TransactionInsertion::new(removed, accepted_transactions)) + } + TransactionPostValidation { removed, accepted: None } => Ok(TransactionInsertion::new(removed, vec![])), } } @@ -263,6 +278,9 @@ impl MiningManager { ) -> Vec> { // The capacity used here may be exceeded (see next comment). let mut accepted_transactions = Vec::with_capacity(incoming_transactions.len()); + // The validation args map is immutably empty since unorphaned transactions do not require pre processing so there + // are no feerate thresholds to use. Instead, we rely on this being checked during post processing. + let args = TransactionValidationBatchArgs::new(); // We loop as long as incoming unorphaned transactions do unorphan other transactions when they // get validated and inserted into the mempool. while !incoming_transactions.is_empty() { @@ -277,8 +295,11 @@ impl MiningManager { let mut validation_results = Vec::with_capacity(transactions.len()); while let Some(upper_bound) = self.next_transaction_chunk_upper_bound(&transactions, lower_bound) { assert!(lower_bound < upper_bound, "the chunk is never empty"); - validation_results - .extend(validate_mempool_transactions_in_parallel(consensus, &mut transactions[lower_bound..upper_bound])); + validation_results.extend(validate_mempool_transactions_in_parallel( + consensus, + &mut transactions[lower_bound..upper_bound], + &args, + )); lower_bound = upper_bound; } assert_eq!(transactions.len(), validation_results.len(), "every transaction should have a matching validation result"); @@ -291,19 +312,21 @@ impl MiningManager { .zip(validation_results) .flat_map(|((transaction, priority), validation_result)| { let orphan_id = transaction.id(); + let rbf_policy = Mempool::get_orphan_transaction_rbf_policy(priority); match mempool.post_validate_and_insert_transaction( consensus, validation_result, transaction, priority, Orphan::Forbidden, + rbf_policy, ) { - Ok(Some(accepted_transaction)) => { + Ok(TransactionPostValidation { removed: _, accepted: Some(accepted_transaction) }) => { accepted_transactions.push(accepted_transaction.clone()); self.counters.increase_tx_counts(1, priority); mempool.get_unorphaned_transactions_after_accepted_transaction(&accepted_transaction) } - Ok(None) => vec![], + Ok(TransactionPostValidation { removed: _, accepted: None }) => vec![], Err(err) => { debug!("Failed to unorphan transaction {0} due to rule error: {1}", orphan_id, err); vec![] @@ -319,14 +342,18 @@ impl MiningManager { /// Validates a batch of transactions, handling iteratively only the independent ones, and /// adds those to the set of known transactions that have not yet been added to any block. /// + /// The validation is constrained by a Replace by fee policy applied + /// to double spends in the mempool. For more information, see [`RbfPolicy`]. + /// /// Returns transactions that where unorphaned following the insertion of the provided - /// transactions. The returned transactions are clones of objects owned by the mempool. + /// transactions. The returned transactions are references of objects owned by the mempool. pub fn validate_and_insert_transaction_batch( &self, consensus: &dyn ConsensusApi, transactions: Vec, priority: Priority, orphan: Orphan, + rbf_policy: RbfPolicy, ) -> Vec>> { const TRANSACTION_CHUNK_SIZE: usize = 250; @@ -340,12 +367,18 @@ impl MiningManager { // read lock on mempool // Here, we simply log and drop all erroneous transactions since the caller doesn't care about those anyway let mut transactions = Vec::with_capacity(sorted_transactions.len()); + let mut args = TransactionValidationBatchArgs::new(); for chunk in &sorted_transactions.chunks(TRANSACTION_CHUNK_SIZE) { let mempool = self.mempool.read(); let txs = chunk.filter_map(|tx| { let transaction_id = tx.id(); - match mempool.pre_validate_and_populate_transaction(consensus, tx) { - Ok(tx) => Some(tx), + match mempool.pre_validate_and_populate_transaction(consensus, tx, rbf_policy) { + Ok(TransactionPreValidation { transaction, feerate_threshold }) => { + if let Some(threshold) = feerate_threshold { + args.set_feerate_threshold(transaction.id(), threshold); + } + Some(transaction) + } Err(RuleError::RejectAlreadyAccepted(transaction_id)) => { debug!("Ignoring already accepted transaction {}", transaction_id); None @@ -374,8 +407,11 @@ impl MiningManager { let mut validation_results = Vec::with_capacity(transactions.len()); while let Some(upper_bound) = self.next_transaction_chunk_upper_bound(&transactions, lower_bound) { assert!(lower_bound < upper_bound, "the chunk is never empty"); - validation_results - .extend(validate_mempool_transactions_in_parallel(consensus, &mut transactions[lower_bound..upper_bound])); + validation_results.extend(validate_mempool_transactions_in_parallel( + consensus, + &mut transactions[lower_bound..upper_bound], + &args, + )); lower_bound = upper_bound; } assert_eq!(transactions.len(), validation_results.len(), "every transaction should have a matching validation result"); @@ -386,13 +422,20 @@ impl MiningManager { let mut mempool = self.mempool.write(); let txs = chunk.flat_map(|(transaction, validation_result)| { let transaction_id = transaction.id(); - match mempool.post_validate_and_insert_transaction(consensus, validation_result, transaction, priority, orphan) { - Ok(Some(accepted_transaction)) => { + match mempool.post_validate_and_insert_transaction( + consensus, + validation_result, + transaction, + priority, + orphan, + rbf_policy, + ) { + Ok(TransactionPostValidation { removed: _, accepted: Some(accepted_transaction) }) => { insert_results.push(Ok(accepted_transaction.clone())); self.counters.increase_tx_counts(1, priority); mempool.get_unorphaned_transactions_after_accepted_transaction(&accepted_transaction) } - Ok(None) => { + Ok(TransactionPostValidation { removed: _, accepted: None }) | Err(RuleError::RejectDuplicate(_)) => { // Either orphaned or already existing in the mempool vec![] } @@ -759,32 +802,43 @@ impl MiningManagerProxy { /// Validates a transaction and adds it to the set of known transactions that have not yet been /// added to any block. /// - /// The returned transactions are clones of objects owned by the mempool. + /// The validation is constrained by a Replace by fee policy applied + /// to double spends in the mempool. For more information, see [`RbfPolicy`]. + /// + /// The returned transactions are references of objects owned by the mempool. pub async fn validate_and_insert_transaction( self, consensus: &ConsensusProxy, transaction: Transaction, priority: Priority, orphan: Orphan, - ) -> MiningManagerResult>> { - consensus.clone().spawn_blocking(move |c| self.inner.validate_and_insert_transaction(c, transaction, priority, orphan)).await + rbf_policy: RbfPolicy, + ) -> MiningManagerResult { + consensus + .clone() + .spawn_blocking(move |c| self.inner.validate_and_insert_transaction(c, transaction, priority, orphan, rbf_policy)) + .await } /// Validates a batch of transactions, handling iteratively only the independent ones, and /// adds those to the set of known transactions that have not yet been added to any block. /// + /// The validation is constrained by a Replace by fee policy applied + /// to double spends in the mempool. For more information, see [`RbfPolicy`]. + /// /// Returns transactions that where unorphaned following the insertion of the provided - /// transactions. The returned transactions are clones of objects owned by the mempool. + /// transactions. The returned transactions are references of objects owned by the mempool. pub async fn validate_and_insert_transaction_batch( self, consensus: &ConsensusProxy, transactions: Vec, priority: Priority, orphan: Orphan, + rbf_policy: RbfPolicy, ) -> Vec>> { consensus .clone() - .spawn_blocking(move |c| self.inner.validate_and_insert_transaction_batch(c, transactions, priority, orphan)) + .spawn_blocking(move |c| self.inner.validate_and_insert_transaction_batch(c, transactions, priority, orphan, rbf_policy)) .await } diff --git a/mining/src/manager_tests.rs b/mining/src/manager_tests.rs index 5301170945..a308e56575 100644 --- a/mining/src/manager_tests.rs +++ b/mining/src/manager_tests.rs @@ -7,19 +7,20 @@ mod tests { mempool::{ config::{Config, DEFAULT_MINIMUM_RELAY_TRANSACTION_FEE}, errors::RuleError, - tx::{Orphan, Priority}, + tx::{Orphan, Priority, RbfPolicy}, }, model::{candidate_tx::CandidateTransaction, tx_query::TransactionQuery}, testutils::consensus_mock::ConsensusMock, MiningCounters, }; + use itertools::Itertools; use kaspa_addresses::{Address, Prefix, Version}; use kaspa_consensus_core::{ api::ConsensusApi, block::TemplateBuildMode, coinbase::MinerData, constants::{MAX_TX_IN_SEQUENCE_NUM, SOMPI_PER_KASPA, TX_VERSION}, - errors::tx::{TxResult, TxRuleError}, + errors::tx::TxRuleError, mass::transaction_estimated_serialized_size, subnets::SUBNETWORK_ID_NATIVE, tx::{ @@ -28,11 +29,12 @@ mod tests { }, }; use kaspa_hashes::Hash; + use kaspa_mining_errors::mempool::RuleResult; use kaspa_txscript::{ pay_to_address_script, pay_to_script_hash_signature_script, - test_helpers::{create_transaction, op_true_script}, + test_helpers::{create_transaction, create_transaction_with_change, op_true_script}, }; - use std::sync::Arc; + use std::{iter::once, sync::Arc}; use tokio::sync::mpsc::{error::TryRecvError, unbounded_channel}; const TARGET_TIME_PER_BLOCK: u64 = 1_000; @@ -42,72 +44,106 @@ mod tests { #[test] fn test_validate_and_insert_transaction() { const TX_COUNT: u32 = 10; - let consensus = Arc::new(ConsensusMock::new()); - let counters = Arc::new(MiningCounters::default()); - let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); - let transactions_to_insert = (0..TX_COUNT).map(|i| create_transaction_with_utxo_entry(i, 0)).collect::>(); - for transaction in transactions_to_insert.iter() { - let result = mining_manager.validate_and_insert_mutable_transaction( - consensus.as_ref(), - transaction.clone(), - Priority::Low, - Orphan::Allowed, - ); - assert!(result.is_ok(), "inserting a valid transaction failed"); - } - // The UtxoEntry was filled manually for those transactions, so the transactions won't be considered orphans. - // Therefore, all the transactions expected to be contained in the mempool. - let (transactions_from_pool, _) = mining_manager.get_all_transactions(TransactionQuery::TransactionsOnly); - assert_eq!( - transactions_to_insert.len(), - transactions_from_pool.len(), - "wrong number of transactions in mempool: expected: {}, got: {}", - transactions_to_insert.len(), - transactions_from_pool.len() - ); - transactions_to_insert.iter().for_each(|tx_to_insert| { - let found_exact_match = transactions_from_pool.contains(tx_to_insert); - let tx_from_pool = transactions_from_pool.iter().find(|tx_from_pool| tx_from_pool.id() == tx_to_insert.id()); - let found_transaction_id = tx_from_pool.is_some(); - if found_transaction_id && !found_exact_match { - let tx = tx_from_pool.unwrap(); - assert_eq!( - tx_to_insert.calculated_fee.unwrap(), - tx.calculated_fee.unwrap(), - "wrong fee in transaction {}: expected: {}, got: {}", - tx.id(), - tx_to_insert.calculated_fee.unwrap(), - tx.calculated_fee.unwrap() - ); - assert_eq!( - tx_to_insert.calculated_compute_mass.unwrap(), - tx.calculated_compute_mass.unwrap(), - "wrong mass in transaction {}: expected: {}, got: {}", - tx.id(), - tx_to_insert.calculated_compute_mass.unwrap(), - tx.calculated_compute_mass.unwrap() - ); + for (priority, orphan, rbf_policy) in all_priority_orphan_rbf_policy_combinations() { + let consensus = Arc::new(ConsensusMock::new()); + let counters = Arc::new(MiningCounters::default()); + let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); + let transactions_to_insert = (0..TX_COUNT).map(|i| create_transaction_with_utxo_entry(i, 0)).collect::>(); + for transaction in transactions_to_insert.iter() { + let result = into_mempool_result(mining_manager.validate_and_insert_mutable_transaction( + consensus.as_ref(), + transaction.clone(), + priority, + orphan, + rbf_policy, + )); + match rbf_policy { + RbfPolicy::Forbidden | RbfPolicy::Allowed => { + assert!(result.is_ok(), "({priority:?}, {orphan:?}, {rbf_policy:?}) inserting a valid transaction failed"); + } + RbfPolicy::Mandatory => { + assert!(result.is_err(), "({priority:?}, {orphan:?}, {rbf_policy:?}) replacing a valid transaction without replacement in mempool should fail"); + let err = result.unwrap_err(); + assert_eq!( + RuleError::RejectRbfNoDoubleSpend, + err, + "({priority:?}, {orphan:?}, {rbf_policy:?}) wrong error: expected {} got: {}", + RuleError::RejectRbfNoDoubleSpend, + err, + ); + } + } } - assert!(found_exact_match, "missing transaction {} in the mempool, no exact match", tx_to_insert.id()); - }); - // The parent's transaction was inserted into the consensus, so we want to verify that - // the child transaction is not considered an orphan and inserted into the mempool. - let transaction_not_an_orphan = create_child_and_parent_txs_and_add_parent_to_consensus(&consensus); - let result = mining_manager.validate_and_insert_transaction( - consensus.as_ref(), - transaction_not_an_orphan.clone(), - Priority::Low, - Orphan::Allowed, - ); - assert!(result.is_ok(), "inserting the child transaction {} into the mempool failed", transaction_not_an_orphan.id()); - let (transactions_from_pool, _) = mining_manager.get_all_transactions(TransactionQuery::TransactionsOnly); - assert!( - contained_by(transaction_not_an_orphan.id(), &transactions_from_pool), - "missing transaction {} in the mempool", - transaction_not_an_orphan.id() - ); + // The UtxoEntry was filled manually for those transactions, so the transactions won't be considered orphans. + // Therefore, all the transactions expected to be contained in the mempool if replace by fee policy allowed it. + let (transactions_from_pool, _) = mining_manager.get_all_transactions(TransactionQuery::TransactionsOnly); + let transactions_inserted = match rbf_policy { + RbfPolicy::Forbidden | RbfPolicy::Allowed => transactions_to_insert.clone(), + RbfPolicy::Mandatory => { + vec![] + } + }; + assert_eq!( + transactions_inserted.len(), + transactions_from_pool.len(), + "({priority:?}, {orphan:?}, {rbf_policy:?}) wrong number of transactions in mempool: expected: {}, got: {}", + transactions_inserted.len(), + transactions_from_pool.len() + ); + transactions_inserted.iter().for_each(|tx_to_insert| { + let found_exact_match = transactions_from_pool.contains(tx_to_insert); + let tx_from_pool = transactions_from_pool.iter().find(|tx_from_pool| tx_from_pool.id() == tx_to_insert.id()); + let found_transaction_id = tx_from_pool.is_some(); + if found_transaction_id && !found_exact_match { + let tx = tx_from_pool.unwrap(); + assert_eq!( + tx_to_insert.calculated_fee.unwrap(), + tx.calculated_fee.unwrap(), + "({priority:?}, {orphan:?}, {rbf_policy:?}) wrong fee in transaction {}: expected: {}, got: {}", + tx.id(), + tx_to_insert.calculated_fee.unwrap(), + tx.calculated_fee.unwrap() + ); + assert_eq!( + tx_to_insert.calculated_compute_mass.unwrap(), + tx.calculated_compute_mass.unwrap(), + "({priority:?}, {orphan:?}, {rbf_policy:?}) wrong mass in transaction {}: expected: {}, got: {}", + tx.id(), + tx_to_insert.calculated_compute_mass.unwrap(), + tx.calculated_compute_mass.unwrap() + ); + } + assert!( + found_exact_match, + "({priority:?}, {orphan:?}, {rbf_policy:?}) missing transaction {} in the mempool, no exact match", + tx_to_insert.id() + ); + }); + + // The parent's transaction was inserted into the consensus, so we want to verify that + // the child transaction is not considered an orphan and inserted into the mempool. + let transaction_not_an_orphan = create_child_and_parent_txs_and_add_parent_to_consensus(&consensus); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + transaction_not_an_orphan.clone(), + priority, + orphan, + RbfPolicy::Forbidden, + ); + assert!( + result.is_ok(), + "({priority:?}, {orphan:?}, {rbf_policy:?}) inserting the child transaction {} into the mempool failed", + transaction_not_an_orphan.id() + ); + let (transactions_from_pool, _) = mining_manager.get_all_transactions(TransactionQuery::TransactionsOnly); + assert!( + contained_by(transaction_not_an_orphan.id(), &transactions_from_pool), + "({priority:?}, {orphan:?}, {rbf_policy:?}) missing transaction {} in the mempool", + transaction_not_an_orphan.id() + ); + } } /// test_simulated_error_in_consensus verifies that a predefined result is actually @@ -115,127 +151,397 @@ mod tests { /// insert a transaction. #[test] fn test_simulated_error_in_consensus() { - let consensus = Arc::new(ConsensusMock::new()); - let counters = Arc::new(MiningCounters::default()); - let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); - - // Build an invalid transaction with some gas and inform the consensus mock about the result it should return - // when the mempool will submit this transaction for validation. - let mut transaction = create_transaction_with_utxo_entry(0, 1); - Arc::make_mut(&mut transaction.tx).gas = 1000; - let status = Err(TxRuleError::TxHasGas); - consensus.set_status(transaction.id(), status.clone()); - - // Try validate and insert the transaction into the mempool - let result = into_status(mining_manager.validate_and_insert_transaction( - consensus.as_ref(), - transaction.tx.as_ref().clone(), - Priority::Low, - Orphan::Allowed, - )); + for (priority, orphan, rbf_policy) in all_priority_orphan_rbf_policy_combinations() { + let consensus = Arc::new(ConsensusMock::new()); + let counters = Arc::new(MiningCounters::default()); + let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); + + // Build an invalid transaction with some gas and inform the consensus mock about the result it should return + // when the mempool will submit this transaction for validation. + let mut transaction = create_transaction_with_utxo_entry(0, 1); + Arc::make_mut(&mut transaction.tx).gas = 1000; + let tx_err = TxRuleError::TxHasGas; + let expected = match rbf_policy { + RbfPolicy::Forbidden | RbfPolicy::Allowed => Err(RuleError::from(tx_err.clone())), + RbfPolicy::Mandatory => Err(RuleError::RejectRbfNoDoubleSpend), + }; + consensus.set_status(transaction.id(), Err(tx_err)); + + // Try validate and insert the transaction into the mempool + let result = into_mempool_result(mining_manager.validate_and_insert_mutable_transaction( + consensus.as_ref(), + transaction.clone(), + priority, + orphan, + rbf_policy, + )); - assert_eq!( - status, result, - "Unexpected result when trying to insert an invalid transaction: expected: {status:?}, got: {result:?}", - ); - let pool_tx = mining_manager.get_transaction(&transaction.id(), TransactionQuery::All); - assert!(pool_tx.is_none(), "Mempool contains a transaction that should have been rejected"); + assert_eq!( + expected, result, + "({priority:?}, {orphan:?}, {rbf_policy:?}) unexpected result when trying to insert an invalid transaction: expected: {expected:?}, got: {result:?}", + ); + let pool_tx = mining_manager.get_transaction(&transaction.id(), TransactionQuery::All); + assert!( + pool_tx.is_none(), + "({priority:?}, {orphan:?}, {rbf_policy:?}) mempool contains a transaction that should have been rejected" + ); + } } /// test_insert_double_transactions_to_mempool verifies that an attempt to insert a transaction /// more than once into the mempool will result in raising an appropriate error. #[test] fn test_insert_double_transactions_to_mempool() { - let consensus = Arc::new(ConsensusMock::new()); - let counters = Arc::new(MiningCounters::default()); - let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); + for (priority, orphan, rbf_policy) in all_priority_orphan_rbf_policy_combinations() { + let consensus = Arc::new(ConsensusMock::new()); + let counters = Arc::new(MiningCounters::default()); + let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); - let transaction = create_transaction_with_utxo_entry(0, 0); + let transaction = create_transaction_with_utxo_entry(0, 0); - // submit the transaction to the mempool - let result = mining_manager.validate_and_insert_mutable_transaction( - consensus.as_ref(), - transaction.clone(), - Priority::Low, - Orphan::Allowed, - ); - assert!(result.is_ok(), "mempool should have accepted a valid transaction but did not"); - - // submit the same transaction again to the mempool - let result = mining_manager.validate_and_insert_transaction( - consensus.as_ref(), - transaction.tx.as_ref().clone(), - Priority::Low, - Orphan::Allowed, - ); - assert!(result.is_err(), "mempool should refuse a double submit of the same transaction but accepts it"); - if let Err(MiningManagerError::MempoolError(RuleError::RejectDuplicate(transaction_id))) = result { - assert_eq!( - transaction.id(), - transaction_id, - "the error returned by the mempool should include id {} but provides {}", - transaction.id(), - transaction_id + // submit the transaction to the mempool + let result = mining_manager.validate_and_insert_mutable_transaction( + consensus.as_ref(), + transaction.clone(), + priority, + orphan, + rbf_policy.for_insert(), ); - } else { - panic!( - "the nested error returned by the mempool should be variant RuleError::RejectDuplicate but is {:?}", - result.err().unwrap() + assert!( + result.is_ok(), + "({priority:?}, {orphan:?}, {rbf_policy:?}) mempool should have accepted a valid transaction but did not" ); + + // submit the same transaction again to the mempool + let result = into_mempool_result(mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + transaction.tx.as_ref().clone(), + priority, + orphan, + rbf_policy, + )); + match result { + Err(RuleError::RejectDuplicate(transaction_id)) => { + assert_eq!( + transaction.id(), + transaction_id, + "({priority:?}, {orphan:?}, {rbf_policy:?}) the error returned by the mempool should include transaction id {} but provides {}", + transaction.id(), + transaction_id + ); + } + Err(err) => { + panic!( + "({priority:?}, {orphan:?}, {rbf_policy:?}) the error returned by the mempool should be {:?} but is {err:?}", + RuleError::RejectDuplicate(transaction.id()) + ); + } + Ok(()) => { + panic!("({priority:?}, {orphan:?}, {rbf_policy:?}) mempool should refuse a double submit of the same transaction but accepts it"); + } + } } } - // test_double_spend_in_mempool verifies that an attempt to insert a transaction double-spending - // another transaction already in the mempool will result in raising an appropriate error. + /// test_double_spend_in_mempool verifies that an attempt to insert a transaction double-spending + /// another transaction already in the mempool will result in raising an appropriate error. #[test] fn test_double_spend_in_mempool() { - let consensus = Arc::new(ConsensusMock::new()); - let counters = Arc::new(MiningCounters::default()); - let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); + for (priority, orphan, rbf_policy) in all_priority_orphan_rbf_policy_combinations() { + let consensus = Arc::new(ConsensusMock::new()); + let counters = Arc::new(MiningCounters::default()); + let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); - let transaction = create_child_and_parent_txs_and_add_parent_to_consensus(&consensus); - assert!( - consensus.can_finance_transaction(&MutableTransaction::from_tx(transaction.clone())), - "the consensus mock should have spendable UTXOs for the newly created transaction {}", - transaction.id() - ); + let transaction = create_child_and_parent_txs_and_add_parent_to_consensus(&consensus); + assert!( + consensus.can_finance_transaction(&MutableTransaction::from_tx(transaction.clone())), + "({priority:?}, {orphan:?}, {rbf_policy:?}) the consensus mock should have spendable UTXOs for the newly created transaction {}", + transaction.id() + ); - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), transaction.clone(), Priority::Low, Orphan::Allowed); - assert!(result.is_ok(), "the mempool should accept a valid transaction when it is able to populate its UTXO entries"); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + transaction.clone(), + priority, + orphan, + RbfPolicy::Forbidden, + ); + assert!(result.is_ok(), "({priority:?}, {orphan:?}, {rbf_policy:?}) the mempool should accept a valid transaction when it is able to populate its UTXO entries"); - let mut double_spending_transaction = transaction.clone(); - double_spending_transaction.outputs[0].value -= 1; // do some minor change so that txID is different - double_spending_transaction.finalize(); - assert_ne!( - transaction.id(), - double_spending_transaction.id(), - "two transactions differing by only one output value should have different ids" - ); - let result = mining_manager.validate_and_insert_transaction( - consensus.as_ref(), - double_spending_transaction.clone(), - Priority::Low, - Orphan::Allowed, - ); - assert!(result.is_err(), "mempool should refuse a double spend transaction but accepts it"); - if let Err(MiningManagerError::MempoolError(RuleError::RejectDoubleSpendInMempool(_, transaction_id))) = result { - assert_eq!( + let mut double_spending_transaction = transaction.clone(); + double_spending_transaction.outputs[0].value += 1; // do some minor change so that txID is different while not increasing fee + double_spending_transaction.finalize(); + assert_ne!( transaction.id(), - transaction_id, - "the error returned by the mempool should include id {} but provides {}", - transaction.id(), - transaction_id - ); - } else { - panic!( - "the nested error returned by the mempool should be variant RuleError::RejectDoubleSpendInMempool but is {:?}", - result.err().unwrap() + double_spending_transaction.id(), + "({priority:?}, {orphan:?}, {rbf_policy:?}) two transactions differing by only one output value should have different ids" ); + let result = into_mempool_result(mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + double_spending_transaction.clone(), + priority, + orphan, + rbf_policy, + )); + match result { + Err(RuleError::RejectDoubleSpendInMempool(_, transaction_id)) => { + assert_eq!( + transaction.id(), + transaction_id, + "({priority:?}, {orphan:?}, {rbf_policy:?}) the error returned by the mempool should include id {} but provides {}", + transaction.id(), + transaction_id + ); + } + Err(err) => { + panic!("({priority:?}, {orphan:?}, {rbf_policy:?}) the error returned by the mempool should be RuleError::RejectDoubleSpendInMempool but is {err:?}"); + } + Ok(()) => { + panic!("({priority:?}, {orphan:?}, {rbf_policy:?}) mempool should refuse a double spend transaction ineligible to RBF but accepts it"); + } + } + } + } + + /// test_replace_by_fee_in_mempool verifies that an attempt to insert a double-spending transaction + /// will cause or not the transaction(s) double spending in the mempool to be replaced/removed, + /// depending on varying factors. + #[test] + fn test_replace_by_fee_in_mempool() { + const BASE_FEE: u64 = DEFAULT_MINIMUM_RELAY_TRANSACTION_FEE; + + struct TxOp { + /// Funding transaction indexes + tx: Vec, + /// Funding transaction output indexes + output: Vec, + /// Add a change output to the transaction + change: bool, + /// Transaction fee + fee: u64, + /// Children binary tree depth + depth: usize, + } + + impl TxOp { + fn change(&self) -> Option { + self.change.then_some(900 * SOMPI_PER_KASPA) + } + } + + struct Test { + name: &'static str, + /// Initial transactions in the mempool + starts: Vec, + /// Replacement transaction submitted to the mempool + replacement: TxOp, + /// Expected RBF result for the 3 policies [Forbidden, Allowed, Mandatory] + expected: [bool; 3], + } + + impl Test { + fn run_rbf(&self, rbf_policy: RbfPolicy, expected: bool) { + let consensus = Arc::new(ConsensusMock::new()); + let counters = Arc::new(MiningCounters::default()); + let mining_manager = MiningManager::new(TARGET_TIME_PER_BLOCK, false, MAX_BLOCK_MASS, None, counters); + let funding_transactions = create_and_add_funding_transactions(&consensus, 10); + + // RPC submit the initial transactions + let (transactions, children): (Vec<_>, Vec<_>) = + self.starts + .iter() + .map(|tx_op| { + let transaction = create_funded_transaction( + select_transactions(&funding_transactions, &tx_op.tx), + tx_op.output.clone(), + tx_op.change(), + tx_op.fee, + ); + assert!( + consensus.can_finance_transaction(&MutableTransaction::from_tx(transaction.clone())), + "[{}, {:?}] the consensus should have spendable UTXOs for the newly created transaction {}", + self.name, rbf_policy, transaction.id() + ); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + transaction.clone(), + Priority::High, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); + assert!( + result.is_ok(), + "[{}, {:?}] the mempool should accept a valid transaction when it is able to populate its UTXO entries", + self.name, rbf_policy, + ); + let children = create_children_tree(&transaction, tx_op.depth); + let children_count = (2_usize.pow(tx_op.depth as u32) - 1) * transaction.outputs.len(); + assert_eq!( + children.len(), children_count, + "[{}, {:?}] a parent transaction with {} output(s) should generate a binary children tree of depth {} with {} children but got {}", + self.name, rbf_policy, transaction.outputs.len(), tx_op.depth, children_count, children.len(), + ); + validate_and_insert_transactions( + &mining_manager, + consensus.as_ref(), + children.iter(), + Priority::High, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); + (transaction, children) + }) + .unzip(); + + // RPC submit transaction replacement + let transaction_replacement = create_funded_transaction( + select_transactions(&funding_transactions, &self.replacement.tx), + self.replacement.output.clone(), + self.replacement.change(), + self.replacement.fee, + ); + assert!( + consensus.can_finance_transaction(&MutableTransaction::from_tx(transaction_replacement.clone())), + "[{}, {:?}] the consensus should have spendable UTXOs for the newly created transaction {}", + self.name, + rbf_policy, + transaction_replacement.id() + ); + let tx_count = mining_manager.transaction_count(TransactionQuery::TransactionsOnly); + let expected_tx_count = match expected { + true => tx_count + 1 - transactions.len() - children.iter().map(|x| x.len()).sum::(), + false => tx_count, + }; + let priority = match rbf_policy { + RbfPolicy::Forbidden | RbfPolicy::Mandatory => Priority::High, + RbfPolicy::Allowed => Priority::Low, + }; + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + transaction_replacement.clone(), + priority, + Orphan::Forbidden, + rbf_policy, + ); + if expected { + assert!(result.is_ok(), "[{}, {:?}] mempool should accept a RBF transaction", self.name, rbf_policy,); + let tx_insertion = result.unwrap(); + assert_eq!( + tx_insertion.removed.as_ref().unwrap().id(), + transactions[0].id(), + "[{}, {:?}] RBF should return the removed transaction", + self.name, + rbf_policy, + ); + transactions.iter().for_each(|x| { + assert!( + !mining_manager.has_transaction(&x.id(), TransactionQuery::All), + "[{}, {:?}] RBF replaced transaction should no longer be in the mempool", + self.name, + rbf_policy, + ); + }); + assert_transaction_count( + &mining_manager, + expected_tx_count, + &format!( + "[{}, {:?}] RBF should remove all chained transactions of the removed mempool transaction(s)", + self.name, rbf_policy + ), + ); + } else { + assert!(result.is_err(), "[{}, {:?}] mempool should reject the RBF transaction", self.name, rbf_policy); + transactions.iter().for_each(|x| { + assert!( + mining_manager.has_transaction(&x.id(), TransactionQuery::All), + "[{}, {:?}] RBF transaction target is no longer in the mempool", + self.name, + rbf_policy + ); + }); + assert_transaction_count( + &mining_manager, + expected_tx_count, + &format!("[{}, {:?}] a failing RBF should leave the mempool unchanged", self.name, rbf_policy), + ); + } + } + + fn run(&self) { + [RbfPolicy::Forbidden, RbfPolicy::Allowed, RbfPolicy::Mandatory].iter().copied().enumerate().for_each( + |(i, rbf_policy)| { + self.run_rbf(rbf_policy, self.expected[i]); + }, + ) + } + } + + let tests = vec![ + Test { + name: "1 input, 1 output <=> 1 input, 1 output, constant fee", + starts: vec![TxOp { tx: vec![0], output: vec![0], change: false, fee: BASE_FEE, depth: 0 }], + replacement: TxOp { tx: vec![0], output: vec![0], change: false, fee: BASE_FEE, depth: 0 }, + expected: [false, false, false], + }, + Test { + name: "1 input, 1 output <=> 1 input, 1 output, increased fee", + starts: vec![TxOp { tx: vec![0], output: vec![0], change: false, fee: BASE_FEE, depth: 0 }], + replacement: TxOp { tx: vec![0], output: vec![0], change: false, fee: BASE_FEE * 2, depth: 0 }, + expected: [false, true, true], + }, + Test { + name: "2 inputs, 2 outputs <=> 2 inputs, 2 outputs, increased fee", + starts: vec![TxOp { tx: vec![0, 1], output: vec![0], change: true, fee: BASE_FEE, depth: 2 }], + replacement: TxOp { tx: vec![0, 1], output: vec![0], change: true, fee: BASE_FEE * 2, depth: 0 }, + expected: [false, true, true], + }, + Test { + name: "4 inputs, 2 outputs <=> 2 inputs, 2 outputs, constant fee", + starts: vec![TxOp { tx: vec![0, 1], output: vec![0, 1], change: true, fee: BASE_FEE, depth: 2 }], + replacement: TxOp { tx: vec![0, 1], output: vec![0], change: true, fee: BASE_FEE, depth: 0 }, + expected: [false, true, true], + }, + Test { + name: "2 inputs, 2 outputs <=> 2 inputs, 1 output, constant fee", + starts: vec![TxOp { tx: vec![0, 1], output: vec![0], change: true, fee: BASE_FEE, depth: 2 }], + replacement: TxOp { tx: vec![0, 1], output: vec![0], change: false, fee: BASE_FEE, depth: 0 }, + expected: [false, true, true], + }, + Test { + name: "2 inputs, 2 outputs <=> 4 inputs, 2 output, constant fee (MUST FAIL on fee/mass)", + starts: vec![TxOp { tx: vec![0, 1], output: vec![0], change: true, fee: BASE_FEE, depth: 2 }], + replacement: TxOp { tx: vec![0, 1], output: vec![0, 1], change: true, fee: BASE_FEE, depth: 0 }, + expected: [false, false, false], + }, + Test { + name: "2 inputs, 1 output <=> 4 inputs, 2 output, increased fee (MUST FAIL on fee/mass)", + starts: vec![TxOp { tx: vec![0, 1], output: vec![0], change: false, fee: BASE_FEE, depth: 2 }], + replacement: TxOp { tx: vec![0, 1], output: vec![0, 1], change: true, fee: BASE_FEE + 10, depth: 0 }, + expected: [false, false, false], + }, + Test { + name: "2 inputs, 2 outputs <=> 2 inputs, 1 output, constant fee, partial double spend overlap", + starts: vec![TxOp { tx: vec![0, 1], output: vec![0], change: true, fee: BASE_FEE, depth: 2 }], + replacement: TxOp { tx: vec![0, 2], output: vec![0], change: false, fee: BASE_FEE, depth: 0 }, + expected: [false, true, true], + }, + Test { + name: "(2 inputs, 2 outputs) * 2 <=> 4 inputs, 2 outputs, increased fee, 2 double spending mempool transactions (MUST FAIL on Mandatory)", + starts: vec![ + TxOp { tx: vec![0, 1], output: vec![0], change: true, fee: BASE_FEE, depth: 2 }, + TxOp { tx: vec![0, 1], output: vec![1], change: true, fee: BASE_FEE, depth: 2 }, + ], + replacement: TxOp { tx: vec![0, 1], output: vec![0, 1], change: true, fee: BASE_FEE * 2, depth: 0 }, + expected: [false, true, false], + }, + ]; + + for test in tests { + test.run(); } } - // test_handle_new_block_transactions verifies that all the transactions in the block were successfully removed from the mempool. + /// test_handle_new_block_transactions verifies that all the transactions in the block were successfully removed from the mempool. #[test] fn test_handle_new_block_transactions() { let consensus = Arc::new(ConsensusMock::new()); @@ -250,6 +556,7 @@ mod tests { transaction.tx.as_ref().clone(), Priority::Low, Orphan::Allowed, + RbfPolicy::Forbidden, ); assert!(result.is_ok(), "the insertion of a new valid transaction in the mempool failed"); } @@ -295,8 +602,8 @@ mod tests { } #[test] - // test_double_spend_with_block verifies that any transactions which are now double spends as a result of the block's new transactions - // will be removed from the mempool. + /// test_double_spend_with_block verifies that any transactions which are now double spends as a result of the block's new transactions + /// will be removed from the mempool. fn test_double_spend_with_block() { let consensus = Arc::new(ConsensusMock::new()); let counters = Arc::new(MiningCounters::default()); @@ -308,6 +615,7 @@ mod tests { transaction_in_the_mempool.tx.as_ref().clone(), Priority::Low, Orphan::Allowed, + RbfPolicy::Forbidden, ); assert!(result.is_ok()); @@ -326,7 +634,7 @@ mod tests { ); } - // test_orphan_transactions verifies that a transaction could be a part of a new block template only if it's not an orphan. + /// test_orphan_transactions verifies that a transaction could be a part of a new block template only if it's not an orphan. #[test] fn test_orphan_transactions() { let consensus = Arc::new(ConsensusMock::new()); @@ -340,8 +648,13 @@ mod tests { assert_eq!(parent_txs.len(), TX_PAIRS_COUNT); assert_eq!(child_txs.len(), TX_PAIRS_COUNT); for orphan in child_txs.iter() { - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), orphan.clone(), Priority::Low, Orphan::Allowed); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + orphan.clone(), + Priority::Low, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); assert!(result.is_ok(), "the mempool should accept the valid orphan transaction {}", orphan.id()); } let (populated_txs, orphans) = mining_manager.get_all_transactions(TransactionQuery::All); @@ -485,10 +798,15 @@ mod tests { ); // Add the remaining parent transaction into the mempool - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), parent_txs[0].clone(), Priority::Low, Orphan::Allowed); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + parent_txs[0].clone(), + Priority::Low, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); assert!(result.is_ok(), "the insertion of the remaining parent transaction in the mempool failed"); - let unorphaned_txs = result.unwrap(); + let unorphaned_txs = result.unwrap().accepted; let (populated_txs, orphans) = mining_manager.get_all_transactions(TransactionQuery::All); assert_eq!( unorphaned_txs.len(), SKIPPED_TXS + 1, @@ -592,8 +910,13 @@ mod tests { // Try submit children while rejecting orphans for (tx, test) in child_txs.iter().zip(tests.iter()) { - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), tx.clone(), test.priority, Orphan::Forbidden); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + tx.clone(), + test.priority, + Orphan::Forbidden, + RbfPolicy::Forbidden, + ); assert!(result.is_err(), "mempool should reject an orphan transaction with {:?} when asked to do so", test.priority); if let Err(MiningManagerError::MempoolError(RuleError::RejectDisallowedOrphan(transaction_id))) = result { assert_eq!( @@ -613,8 +936,13 @@ mod tests { // Try submit children while accepting orphans for (tx, test) in child_txs.iter().zip(tests.iter()) { - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), tx.clone(), test.priority, Orphan::Allowed); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + tx.clone(), + test.priority, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); assert_eq!( test.should_enter_orphan_pool, result.is_ok(), @@ -623,7 +951,7 @@ mod tests { test.insert_result() ); if let Ok(unorphaned_txs) = result { - assert!(unorphaned_txs.is_empty(), "mempool should unorphan no transaction since it only contains orphans"); + assert!(unorphaned_txs.accepted.is_empty(), "mempool should unorphan no transaction since it only contains orphans"); } else if let Err(MiningManagerError::MempoolError(RuleError::RejectOrphanPoolIsFull(pool_len, config_len))) = result { assert_eq!( (config.maximum_orphan_transaction_count as usize, config.maximum_orphan_transaction_count), @@ -642,10 +970,15 @@ mod tests { // Submit all the parents for (i, (tx, test)) in parent_txs.iter().zip(tests.iter()).enumerate() { - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), tx.clone(), test.priority, Orphan::Allowed); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + tx.clone(), + test.priority, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); assert!(result.is_ok(), "mempool should accept a valid transaction with {:?} when asked to do so", test.priority,); - let unorphaned_txs = result.as_ref().unwrap(); + let unorphaned_txs = &result.as_ref().unwrap().accepted; assert_eq!( test.should_unorphan, unorphaned_txs.len() > 1, @@ -682,8 +1015,13 @@ mod tests { // Add to mempool a transaction that spends child_tx_2 (as high priority) let spending_tx = create_transaction(&child_tx_2, 1_000); - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), spending_tx.clone(), Priority::High, Orphan::Allowed); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + spending_tx.clone(), + Priority::High, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); assert!(result.is_ok(), "the insertion in the mempool of the spending transaction failed"); // Revalidate, to make sure spending_tx is still valid @@ -725,7 +1063,7 @@ mod tests { assert!(orphan_txs.is_empty(), "orphan pool should be empty"); } - // test_modify_block_template verifies that modifying a block template changes coinbase data correctly. + /// test_modify_block_template verifies that modifying a block template changes coinbase data correctly. #[test] fn test_modify_block_template() { let consensus = Arc::new(ConsensusMock::new()); @@ -737,11 +1075,21 @@ mod tests { let (parent_txs, child_txs) = create_arrays_of_parent_and_children_transactions(&consensus, TX_PAIRS_COUNT); for (parent_tx, child_tx) in parent_txs.iter().zip(child_txs.iter()) { - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), parent_tx.clone(), Priority::Low, Orphan::Allowed); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + parent_tx.clone(), + Priority::Low, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); assert!(result.is_ok(), "the mempool should accept the valid parent transaction {}", parent_tx.id()); - let result = - mining_manager.validate_and_insert_transaction(consensus.as_ref(), child_tx.clone(), Priority::Low, Orphan::Allowed); + let result = mining_manager.validate_and_insert_transaction( + consensus.as_ref(), + child_tx.clone(), + Priority::Low, + Orphan::Allowed, + RbfPolicy::Forbidden, + ); assert!(result.is_ok(), "the mempool should accept the valid child transaction {}", parent_tx.id()); } @@ -933,6 +1281,68 @@ mod tests { mutable_tx } + fn create_and_add_funding_transactions(consensus: &Arc, count: usize) -> Vec { + // Make the funding amounts always different so that funding txs have different ids + (0..count) + .map(|i| { + let funding_tx = create_transaction_without_input(vec![1_000 * SOMPI_PER_KASPA, 2_500 * SOMPI_PER_KASPA + i as u64]); + consensus.add_transaction(funding_tx.clone(), 1); + funding_tx + }) + .collect_vec() + } + + fn select_transactions<'a>(transactions: &'a [Transaction], indexes: &'a [usize]) -> impl Iterator { + indexes.iter().map(|i| &transactions[*i]) + } + + fn create_funded_transaction<'a>( + txs_to_spend: impl Iterator, + output_indexes: Vec, + change: Option, + fee: u64, + ) -> Transaction { + create_transaction_with_change(txs_to_spend, output_indexes, change, fee) + } + + fn create_children_tree(parent: &Transaction, depth: usize) -> Vec { + let mut tree = vec![]; + let root = [parent.clone()]; + let mut parents = &root[..]; + let mut first_child = 0; + for _ in 0..depth { + let mut children = vec![]; + for parent in parents { + children.extend(parent.outputs.iter().enumerate().map(|(i, output)| { + create_transaction_with_change( + once(parent), + vec![i], + Some(output.value / 2), + DEFAULT_MINIMUM_RELAY_TRANSACTION_FEE, + ) + })); + } + tree.extend(children); + parents = &tree[first_child..]; + first_child = tree.len() + } + tree + } + + fn validate_and_insert_transactions<'a>( + mining_manager: &MiningManager, + consensus: &dyn ConsensusApi, + transactions: impl Iterator, + priority: Priority, + orphan: Orphan, + rbf_policy: RbfPolicy, + ) { + transactions.for_each(|transaction| { + let result = mining_manager.validate_and_insert_transaction(consensus, transaction.clone(), priority, orphan, rbf_policy); + assert!(result.is_ok(), "the mempool should accept a valid transaction when it is able to populate its UTXO entries"); + }); + } + fn create_arrays_of_parent_and_children_transactions( consensus: &Arc, count: usize, @@ -974,11 +1384,13 @@ mod tests { transactions.iter().any(|x| x.as_ref().id() == transaction_id) } - fn into_status(result: MiningManagerResult) -> TxResult<()> { + fn into_mempool_result(result: MiningManagerResult) -> RuleResult<()> { match result { Ok(_) => Ok(()), - Err(MiningManagerError::MempoolError(RuleError::RejectTxRule(err))) => Err(err), - _ => Ok(()), + Err(MiningManagerError::MempoolError(err)) => Err(err), + _ => { + panic!("result is an unsupported error"); + } } } @@ -1000,4 +1412,26 @@ mod tests { let script = pay_to_address_script(&address); MinerData::new(script, vec![]) } + + #[allow(dead_code)] + fn all_priority_orphan_combinations() -> impl Iterator { + [Priority::Low, Priority::High] + .iter() + .flat_map(|priority| [Orphan::Allowed, Orphan::Forbidden].iter().map(|orphan| (*priority, *orphan))) + } + + fn all_priority_orphan_rbf_policy_combinations() -> impl Iterator { + [Priority::Low, Priority::High].iter().flat_map(|priority| { + [Orphan::Allowed, Orphan::Forbidden].iter().flat_map(|orphan| { + [RbfPolicy::Forbidden, RbfPolicy::Allowed, RbfPolicy::Mandatory] + .iter() + .map(|rbf_policy| (*priority, *orphan, *rbf_policy)) + }) + }) + } + + fn assert_transaction_count(mining_manager: &MiningManager, expected_count: usize, message: &str) { + let count = mining_manager.transaction_count(TransactionQuery::TransactionsOnly); + assert_eq!(expected_count, count, "{message} mempool transaction count: expected {}, got {}", expected_count, count); + } } diff --git a/mining/src/mempool/mod.rs b/mining/src/mempool/mod.rs index a3e26e7c99..1f63a3f441 100644 --- a/mining/src/mempool/mod.rs +++ b/mining/src/mempool/mod.rs @@ -23,6 +23,7 @@ pub(crate) mod handle_new_block_transactions; pub(crate) mod model; pub(crate) mod populate_entries_and_try_validate; pub(crate) mod remove_transaction; +pub(crate) mod replace_by_fee; pub(crate) mod validate_and_insert_transaction; /// Mempool contains transactions intended to be inserted into a block and mined. @@ -158,4 +159,51 @@ pub mod tx { Forbidden, Allowed, } + + /// Replace by Fee (RBF) policy + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum RbfPolicy { + /// ### RBF is forbidden + /// + /// Inserts the incoming transaction. + /// + /// Conditions of success: + /// + /// - no double spend + /// + /// If conditions are not met, leaves the mempool unchanged and fails with a double spend error. + Forbidden, + + /// ### RBF may occur + /// + /// Identifies double spends in mempool and their owning transactions checking in order every input of the incoming + /// transaction. + /// + /// Removes all mempool transactions owning double spends and inserts the incoming transaction. + /// + /// Conditions of success: + /// + /// - on absence of double spends, always succeeds + /// - on double spends, the incoming transaction has a higher fee/mass ratio than the mempool transaction owning + /// the first double spend + /// + /// If conditions are not met, leaves the mempool unchanged and fails with a double spend or a tx fee/mass too low error. + Allowed, + + /// ### RBF must occur + /// + /// Identifies double spends in mempool and their owning transactions checking in order every input of the incoming + /// transaction. + /// + /// Removes the mempool transaction owning the double spends and inserts the incoming transaction. + /// + /// Conditions of success: + /// + /// - at least one double spend + /// - all double spends belong to the same mempool transaction + /// - the incoming transaction has a higher fee/mass ratio than the mempool double spending transaction. + /// + /// If conditions are not met, leaves the mempool unchanged and fails with a double spend or a tx fee/mass too low error. + Mandatory, + } } diff --git a/mining/src/mempool/model/transactions_pool.rs b/mining/src/mempool/model/transactions_pool.rs index cf70150df7..988da5ddd6 100644 --- a/mining/src/mempool/model/transactions_pool.rs +++ b/mining/src/mempool/model/transactions_pool.rs @@ -5,7 +5,7 @@ use crate::{ model::{ map::MempoolTransactionCollection, pool::{Pool, TransactionsEdges}, - tx::MempoolTransaction, + tx::{DoubleSpend, MempoolTransaction}, utxo_set::MempoolUtxoSet, }, tx::Priority, @@ -182,6 +182,7 @@ impl TransactionsPool { } false } + /// Returns the exceeding low-priority transactions having the lowest fee rates in order /// to have room for at least `free_slots` new transactions. The returned transactions /// are guaranteed to be unchained (no successor in mempool) and to not be parent of @@ -245,10 +246,29 @@ impl TransactionsPool { self.utxo_set.get_outpoint_owner_id(outpoint) } + /// Make sure no other transaction in the mempool is already spending an output which one of this transaction inputs spends pub(crate) fn check_double_spends(&self, transaction: &MutableTransaction) -> RuleResult<()> { self.utxo_set.check_double_spends(transaction) } + /// Returns the first double spend of every transaction in the mempool double spending on `transaction` + pub(crate) fn get_double_spend_transaction_ids(&self, transaction: &MutableTransaction) -> Vec { + self.utxo_set.get_double_spend_transaction_ids(transaction) + } + + pub(crate) fn get_double_spend_owner<'a>(&'a self, double_spend: &DoubleSpend) -> RuleResult<&'a MempoolTransaction> { + match self.get(&double_spend.owner_id) { + Some(transaction) => Ok(transaction), + None => { + // This case should never arise in the first place. + // Anyway, in case it does, if a double spent transaction id is found but the matching + // transaction cannot be located in the mempool a replacement is no longer possible + // so a double spend error is returned. + Err(double_spend.into()) + } + } + } + pub(crate) fn collect_expired_low_priority_transactions(&mut self, virtual_daa_score: u64) -> Vec { let now = unix_now(); if virtual_daa_score < self.last_expire_scan_daa_score + self.config.transaction_expire_scan_interval_daa_score diff --git a/mining/src/mempool/model/tx.rs b/mining/src/mempool/model/tx.rs index 1e549c9979..48f24b9f62 100644 --- a/mining/src/mempool/model/tx.rs +++ b/mining/src/mempool/model/tx.rs @@ -1,8 +1,10 @@ -use crate::mempool::tx::Priority; -use kaspa_consensus_core::{tx::MutableTransaction, tx::TransactionId}; +use crate::mempool::tx::{Priority, RbfPolicy}; +use kaspa_consensus_core::tx::{MutableTransaction, Transaction, TransactionId, TransactionOutpoint}; +use kaspa_mining_errors::mempool::RuleError; use std::{ cmp::Ordering, fmt::{Display, Formatter}, + sync::Arc, }; pub(crate) struct MempoolTransaction { @@ -53,6 +55,51 @@ impl PartialEq for MempoolTransaction { } } +impl RbfPolicy { + #[cfg(test)] + /// Returns an alternate policy accepting a transaction insertion in case the policy requires a replacement + pub(crate) fn for_insert(&self) -> RbfPolicy { + match self { + RbfPolicy::Forbidden | RbfPolicy::Allowed => *self, + RbfPolicy::Mandatory => RbfPolicy::Allowed, + } + } +} + +pub(crate) struct DoubleSpend { + pub outpoint: TransactionOutpoint, + pub owner_id: TransactionId, +} + +impl DoubleSpend { + pub fn new(outpoint: TransactionOutpoint, owner_id: TransactionId) -> Self { + Self { outpoint, owner_id } + } +} + +impl From for RuleError { + fn from(value: DoubleSpend) -> Self { + RuleError::RejectDoubleSpendInMempool(value.outpoint, value.owner_id) + } +} + +impl From<&DoubleSpend> for RuleError { + fn from(value: &DoubleSpend) -> Self { + RuleError::RejectDoubleSpendInMempool(value.outpoint, value.owner_id) + } +} + +pub(crate) struct TransactionPreValidation { + pub transaction: MutableTransaction, + pub feerate_threshold: Option, +} + +#[derive(Default)] +pub(crate) struct TransactionPostValidation { + pub removed: Option>, + pub accepted: Option>, +} + #[derive(PartialEq, Eq)] pub(crate) enum TxRemovalReason { Muted, @@ -63,6 +110,7 @@ pub(crate) enum TxRemovalReason { DoubleSpend, InvalidInBlockTemplate, RevalidationWithMissingOutpoints, + ReplacedByFee, } impl TxRemovalReason { @@ -76,6 +124,7 @@ impl TxRemovalReason { TxRemovalReason::DoubleSpend => "double spend", TxRemovalReason::InvalidInBlockTemplate => "invalid in block template", TxRemovalReason::RevalidationWithMissingOutpoints => "revalidation with missing outpoints", + TxRemovalReason::ReplacedByFee => "replaced by fee", } } diff --git a/mining/src/mempool/model/utxo_set.rs b/mining/src/mempool/model/utxo_set.rs index 38c2bcb4ee..808d674885 100644 --- a/mining/src/mempool/model/utxo_set.rs +++ b/mining/src/mempool/model/utxo_set.rs @@ -1,7 +1,9 @@ +use std::collections::HashSet; + use crate::{ mempool::{ - errors::{RuleError, RuleResult}, - model::map::OutpointIndex, + errors::RuleResult, + model::{map::OutpointIndex, tx::DoubleSpend}, }, model::TransactionIdSet, }; @@ -70,14 +72,36 @@ impl MempoolUtxoSet { /// Make sure no other transaction in the mempool is already spending an output which one of this transaction inputs spends pub(crate) fn check_double_spends(&self, transaction: &MutableTransaction) -> RuleResult<()> { + match self.get_first_double_spend(transaction) { + Some(double_spend) => Err(double_spend.into()), + None => Ok(()), + } + } + + pub(crate) fn get_first_double_spend(&self, transaction: &MutableTransaction) -> Option { let transaction_id = transaction.id(); for input in transaction.tx.inputs.iter() { if let Some(existing_transaction_id) = self.get_outpoint_owner_id(&input.previous_outpoint) { if *existing_transaction_id != transaction_id { - return Err(RuleError::RejectDoubleSpendInMempool(input.previous_outpoint, *existing_transaction_id)); + return Some(DoubleSpend::new(input.previous_outpoint, *existing_transaction_id)); + } + } + } + None + } + + /// Returns the first double spend of every transaction in the mempool double spending on `transaction` + pub(crate) fn get_double_spend_transaction_ids(&self, transaction: &MutableTransaction) -> Vec { + let transaction_id = transaction.id(); + let mut double_spends = vec![]; + let mut visited = HashSet::new(); + for input in transaction.tx.inputs.iter() { + if let Some(existing_transaction_id) = self.get_outpoint_owner_id(&input.previous_outpoint) { + if *existing_transaction_id != transaction_id && visited.insert(*existing_transaction_id) { + double_spends.push(DoubleSpend::new(input.previous_outpoint, *existing_transaction_id)); } } } - Ok(()) + double_spends } } diff --git a/mining/src/mempool/populate_entries_and_try_validate.rs b/mining/src/mempool/populate_entries_and_try_validate.rs index 0c0dcf9a1e..a5c1252805 100644 --- a/mining/src/mempool/populate_entries_and_try_validate.rs +++ b/mining/src/mempool/populate_entries_and_try_validate.rs @@ -1,5 +1,12 @@ use crate::mempool::{errors::RuleResult, model::pool::Pool, Mempool}; -use kaspa_consensus_core::{api::ConsensusApi, constants::UNACCEPTED_DAA_SCORE, tx::MutableTransaction, tx::UtxoEntry}; +use kaspa_consensus_core::{ + api::{ + args::{TransactionValidationArgs, TransactionValidationBatchArgs}, + ConsensusApi, + }, + constants::UNACCEPTED_DAA_SCORE, + tx::{MutableTransaction, UtxoEntry}, +}; use kaspa_mining_errors::mempool::RuleError; impl Mempool { @@ -14,15 +21,20 @@ impl Mempool { } } -pub(crate) fn validate_mempool_transaction(consensus: &dyn ConsensusApi, transaction: &mut MutableTransaction) -> RuleResult<()> { - Ok(consensus.validate_mempool_transaction(transaction)?) +pub(crate) fn validate_mempool_transaction( + consensus: &dyn ConsensusApi, + transaction: &mut MutableTransaction, + args: &TransactionValidationArgs, +) -> RuleResult<()> { + Ok(consensus.validate_mempool_transaction(transaction, args)?) } pub(crate) fn validate_mempool_transactions_in_parallel( consensus: &dyn ConsensusApi, transactions: &mut [MutableTransaction], + args: &TransactionValidationBatchArgs, ) -> Vec> { - consensus.validate_mempool_transactions_in_parallel(transactions).into_iter().map(|x| x.map_err(RuleError::from)).collect() + consensus.validate_mempool_transactions_in_parallel(transactions, args).into_iter().map(|x| x.map_err(RuleError::from)).collect() } pub(crate) fn populate_mempool_transactions_in_parallel( diff --git a/mining/src/mempool/replace_by_fee.rs b/mining/src/mempool/replace_by_fee.rs new file mode 100644 index 0000000000..6acd1a6188 --- /dev/null +++ b/mining/src/mempool/replace_by_fee.rs @@ -0,0 +1,149 @@ +use crate::mempool::{ + errors::{RuleError, RuleResult}, + model::tx::{DoubleSpend, MempoolTransaction, TxRemovalReason}, + tx::RbfPolicy, + Mempool, +}; +use kaspa_consensus_core::tx::{MutableTransaction, Transaction}; +use std::sync::Arc; + +impl Mempool { + /// Returns the replace by fee (RBF) constraint fee/mass threshold for an incoming transaction and a policy. + /// + /// Fails if the transaction does not meet some condition of the RBF policy, excluding the fee/mass condition. + /// + /// See [`RbfPolicy`] variants for details of each policy process and success conditions. + pub(super) fn get_replace_by_fee_constraint( + &self, + transaction: &MutableTransaction, + rbf_policy: RbfPolicy, + ) -> RuleResult> { + match rbf_policy { + RbfPolicy::Forbidden => { + // When RBF is forbidden, fails early on any double spend + self.transaction_pool.check_double_spends(transaction)?; + Ok(None) + } + + RbfPolicy::Allowed => { + // When RBF is allowed, never fails since both insertion and replacement are possible + let double_spends = self.transaction_pool.get_double_spend_transaction_ids(transaction); + if double_spends.is_empty() { + Ok(None) + } else { + let mut feerate_threshold = 0f64; + for double_spend in double_spends { + // We take the max over all double spends as the required threshold + feerate_threshold = feerate_threshold.max(self.get_double_spend_feerate(&double_spend)?); + } + Ok(Some(feerate_threshold)) + } + } + + RbfPolicy::Mandatory => { + // When RBF is mandatory, fails early if we do not have exactly one double spending transaction + let double_spends = self.transaction_pool.get_double_spend_transaction_ids(transaction); + match double_spends.len() { + 0 => Err(RuleError::RejectRbfNoDoubleSpend), + 1 => { + let feerate_threshold = self.get_double_spend_feerate(&double_spends[0])?; + Ok(Some(feerate_threshold)) + } + _ => Err(RuleError::RejectRbfTooManyDoubleSpendingTransactions), + } + } + } + } + + /// Executes replace by fee (RBF) for an incoming transaction and a policy. + /// + /// See [`RbfPolicy`] variants for details of each policy process and success conditions. + /// + /// On success, `transaction` is guaranteed to embed no double spend with the mempool. + /// + /// On success with the [`RbfPolicy::Mandatory`] policy, some removed transaction is always returned. + pub(super) fn execute_replace_by_fee( + &mut self, + transaction: &MutableTransaction, + rbf_policy: RbfPolicy, + ) -> RuleResult>> { + match rbf_policy { + RbfPolicy::Forbidden => { + self.transaction_pool.check_double_spends(transaction)?; + Ok(None) + } + + RbfPolicy::Allowed => { + let double_spends = self.transaction_pool.get_double_spend_transaction_ids(transaction); + match double_spends.is_empty() { + true => Ok(None), + false => { + let removed = self.validate_double_spending_transaction(transaction, &double_spends[0])?.mtx.tx.clone(); + for double_spend in double_spends.iter().skip(1) { + // Validate the feerate threshold is passed for all double spends + self.validate_double_spending_transaction(transaction, double_spend)?; + } + // We apply consequences such as removal only after we fully validate against all double spends + for double_spend in double_spends { + self.remove_transaction( + &double_spend.owner_id, + true, + TxRemovalReason::ReplacedByFee, + format!("by {}", transaction.id()).as_str(), + )?; + } + Ok(Some(removed)) + } + } + } + + RbfPolicy::Mandatory => { + let double_spends = self.transaction_pool.get_double_spend_transaction_ids(transaction); + match double_spends.len() { + 0 => Err(RuleError::RejectRbfNoDoubleSpend), + 1 => { + let removed = self.validate_double_spending_transaction(transaction, &double_spends[0])?.mtx.tx.clone(); + self.remove_transaction( + &double_spends[0].owner_id, + true, + TxRemovalReason::ReplacedByFee, + format!("by {}", transaction.id()).as_str(), + )?; + Ok(Some(removed)) + } + _ => Err(RuleError::RejectRbfTooManyDoubleSpendingTransactions), + } + } + } + } + + fn get_double_spend_feerate(&self, double_spend: &DoubleSpend) -> RuleResult { + let owner = self.transaction_pool.get_double_spend_owner(double_spend)?; + match owner.mtx.calculated_feerate() { + Some(double_spend_feerate) => Ok(double_spend_feerate), + // Getting here is unexpected since a mempool owned tx should be populated with fee + // and mass at this stage but nonetheless we fail gracefully + None => Err(double_spend.into()), + } + } + + fn validate_double_spending_transaction<'a>( + &'a self, + transaction: &MutableTransaction, + double_spend: &DoubleSpend, + ) -> RuleResult<&'a MempoolTransaction> { + let owner = self.transaction_pool.get_double_spend_owner(double_spend)?; + if let (Some(transaction_feerate), Some(double_spend_feerate)) = + (transaction.calculated_feerate(), owner.mtx.calculated_feerate()) + { + if transaction_feerate > double_spend_feerate { + return Ok(owner); + } else { + return Err(double_spend.into()); + } + } + // Getting here is unexpected since both txs should be populated with + // fee and mass at this stage but nonetheless we fail gracefully + Err(double_spend.into()) + } +} diff --git a/mining/src/mempool/validate_and_insert_transaction.rs b/mining/src/mempool/validate_and_insert_transaction.rs index 591fa5c4aa..bcfedc2dbe 100644 --- a/mining/src/mempool/validate_and_insert_transaction.rs +++ b/mining/src/mempool/validate_and_insert_transaction.rs @@ -2,9 +2,9 @@ use crate::mempool::{ errors::{RuleError, RuleResult}, model::{ pool::Pool, - tx::{MempoolTransaction, TxRemovalReason}, + tx::{MempoolTransaction, TransactionPostValidation, TransactionPreValidation, TxRemovalReason}, }, - tx::{Orphan, Priority}, + tx::{Orphan, Priority, RbfPolicy}, Mempool, }; use kaspa_consensus_core::{ @@ -13,21 +13,21 @@ use kaspa_consensus_core::{ tx::{MutableTransaction, Transaction, TransactionId, TransactionOutpoint, UtxoEntry}, }; use kaspa_core::{debug, info}; -use std::sync::Arc; impl Mempool { pub(crate) fn pre_validate_and_populate_transaction( &self, consensus: &dyn ConsensusApi, mut transaction: MutableTransaction, - ) -> RuleResult { + rbf_policy: RbfPolicy, + ) -> RuleResult { self.validate_transaction_unacceptance(&transaction)?; // Populate mass in the beginning, it will be used in multiple places throughout the validation and insertion. transaction.calculated_compute_mass = Some(consensus.calculate_transaction_compute_mass(&transaction.tx)); self.validate_transaction_in_isolation(&transaction)?; - self.transaction_pool.check_double_spends(&transaction)?; + let feerate_threshold = self.get_replace_by_fee_constraint(&transaction, rbf_policy)?; self.populate_mempool_entries(&mut transaction); - Ok(transaction) + Ok(TransactionPreValidation { transaction, feerate_threshold }) } pub(crate) fn post_validate_and_insert_transaction( @@ -37,7 +37,8 @@ impl Mempool { transaction: MutableTransaction, priority: Priority, orphan: Orphan, - ) -> RuleResult>> { + rbf_policy: RbfPolicy, + ) -> RuleResult { let transaction_id = transaction.id(); // First check if the transaction was not already added to the mempool. @@ -46,28 +47,29 @@ impl Mempool { // concurrently. if self.transaction_pool.has(&transaction_id) { debug!("Transaction {0} is not post validated since already in the mempool", transaction_id); - return Ok(None); + return Err(RuleError::RejectDuplicate(transaction_id)); } self.validate_transaction_unacceptance(&transaction)?; - // Re-check double spends since validate_and_insert_transaction is no longer atomic - self.transaction_pool.check_double_spends(&transaction)?; - match validation_result { Ok(_) => {} Err(RuleError::RejectMissingOutpoint) => { if orphan == Orphan::Forbidden { return Err(RuleError::RejectDisallowedOrphan(transaction_id)); } + let _ = self.get_replace_by_fee_constraint(&transaction, rbf_policy)?; self.orphan_pool.try_add_orphan(consensus.get_virtual_daa_score(), transaction, priority)?; - return Ok(None); + return Ok(TransactionPostValidation::default()); } Err(err) => { return Err(err); } } + // Check double spends and try to remove them if the RBF policy requires it + let removed_transaction = self.execute_replace_by_fee(&transaction, rbf_policy)?; + self.validate_transaction_in_context(&transaction)?; // Before adding the transaction, check if there is room in the pool @@ -78,7 +80,7 @@ impl Mempool { // Add the transaction to the mempool as a MempoolTransaction and return a clone of the embedded Arc let accepted_transaction = self.transaction_pool.add_transaction(transaction, consensus.get_virtual_daa_score(), priority)?.mtx.tx.clone(); - Ok(Some(accepted_transaction)) + Ok(TransactionPostValidation { removed: removed_transaction, accepted: Some(accepted_transaction) }) } /// Validates that the transaction wasn't already accepted into the DAG @@ -184,9 +186,26 @@ impl Mempool { // The one we just removed from the orphan pool. assert_eq!(transactions.len(), 1, "the list returned by remove_orphan is expected to contain exactly one transaction"); let transaction = transactions.pop().unwrap(); + let rbf_policy = Self::get_orphan_transaction_rbf_policy(transaction.priority); self.validate_transaction_unacceptance(&transaction.mtx)?; - self.transaction_pool.check_double_spends(&transaction.mtx)?; + let _ = self.get_replace_by_fee_constraint(&transaction.mtx, rbf_policy)?; Ok(transaction) } + + /// Returns the RBF policy to apply to an orphan/unorphaned transaction by inferring it from the transaction priority. + pub(crate) fn get_orphan_transaction_rbf_policy(priority: Priority) -> RbfPolicy { + // The RBF policy applied to an orphaned transaction is not recorded in the orphan pool + // but we can infer it from the priority: + // + // - high means a submitted tx via RPC which forbids RBF + // - low means a tx arrived via P2P which allows RBF + // + // Note that the RPC submit transaction replacement case, implying a mandatory RBF, forbids orphans + // so is excluded here. + match priority { + Priority::High => RbfPolicy::Forbidden, + Priority::Low => RbfPolicy::Allowed, + } + } } diff --git a/mining/src/model/mod.rs b/mining/src/model/mod.rs index 3f17a50c81..66c91cae58 100644 --- a/mining/src/model/mod.rs +++ b/mining/src/model/mod.rs @@ -5,6 +5,7 @@ pub(crate) mod candidate_tx; pub mod owner_txs; pub mod topological_index; pub mod topological_sort; +pub mod tx_insert; pub mod tx_query; /// A set of unique transaction ids diff --git a/mining/src/model/tx_insert.rs b/mining/src/model/tx_insert.rs new file mode 100644 index 0000000000..4c006fb991 --- /dev/null +++ b/mining/src/model/tx_insert.rs @@ -0,0 +1,14 @@ +use kaspa_consensus_core::tx::Transaction; +use std::sync::Arc; + +#[derive(Debug)] +pub struct TransactionInsertion { + pub removed: Option>, + pub accepted: Vec>, +} + +impl TransactionInsertion { + pub fn new(removed: Option>, accepted: Vec>) -> Self { + Self { removed, accepted } + } +} diff --git a/mining/src/testutils/consensus_mock.rs b/mining/src/testutils/consensus_mock.rs index 94d774c428..4ec37ec820 100644 --- a/mining/src/testutils/consensus_mock.rs +++ b/mining/src/testutils/consensus_mock.rs @@ -1,6 +1,9 @@ use super::coinbase_mock::CoinbaseManagerMock; use kaspa_consensus_core::{ - api::ConsensusApi, + api::{ + args::{TransactionValidationArgs, TransactionValidationBatchArgs}, + ConsensusApi, + }, block::{BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector, VirtualStateApproxId}, coinbase::MinerData, constants::BLOCK_VERSION, @@ -103,7 +106,7 @@ impl ConsensusApi for ConsensusMock { Ok(BlockTemplate::new(mutable_block, miner_data, coinbase.has_red_reward, now, 0, ZERO_HASH)) } - fn validate_mempool_transaction(&self, mutable_tx: &mut MutableTransaction) -> TxResult<()> { + fn validate_mempool_transaction(&self, mutable_tx: &mut MutableTransaction, _: &TransactionValidationArgs) -> TxResult<()> { // If a predefined status was registered to simulate an error, return it right away if let Some(status) = self.statuses.read().get(&mutable_tx.id()) { if status.is_err() { @@ -138,12 +141,16 @@ impl ConsensusApi for ConsensusMock { Ok(()) } - fn validate_mempool_transactions_in_parallel(&self, transactions: &mut [MutableTransaction]) -> Vec> { - transactions.iter_mut().map(|x| self.validate_mempool_transaction(x)).collect() + fn validate_mempool_transactions_in_parallel( + &self, + transactions: &mut [MutableTransaction], + _: &TransactionValidationBatchArgs, + ) -> Vec> { + transactions.iter_mut().map(|x| self.validate_mempool_transaction(x, &Default::default())).collect() } fn populate_mempool_transactions_in_parallel(&self, transactions: &mut [MutableTransaction]) -> Vec> { - transactions.iter_mut().map(|x| self.validate_mempool_transaction(x)).collect() + transactions.iter_mut().map(|x| self.validate_mempool_transaction(x, &Default::default())).collect() } fn calculate_transaction_compute_mass(&self, transaction: &Transaction) -> u64 { diff --git a/protocol/flows/src/flow_context.rs b/protocol/flows/src/flow_context.rs index 3d365c54f9..14d4168aca 100644 --- a/protocol/flows/src/flow_context.rs +++ b/protocol/flows/src/flow_context.rs @@ -25,8 +25,8 @@ use kaspa_core::{ }; use kaspa_core::{time::unix_now, warn}; use kaspa_hashes::Hash; -use kaspa_mining::manager::MiningManagerProxy; use kaspa_mining::mempool::tx::{Orphan, Priority}; +use kaspa_mining::{manager::MiningManagerProxy, mempool::tx::RbfPolicy}; use kaspa_notify::notifier::Notify; use kaspa_p2p_lib::{ common::ProtocolError, @@ -618,16 +618,48 @@ impl FlowContext { transaction: Transaction, orphan: Orphan, ) -> Result<(), ProtocolError> { - let accepted_transactions = - self.mining_manager().clone().validate_and_insert_transaction(consensus, transaction, Priority::High, orphan).await?; + let transaction_insertion = self + .mining_manager() + .clone() + .validate_and_insert_transaction(consensus, transaction, Priority::High, orphan, RbfPolicy::Forbidden) + .await?; self.broadcast_transactions( - accepted_transactions.iter().map(|x| x.id()), + transaction_insertion.accepted.iter().map(|x| x.id()), false, // RPC transactions are considered high priority, so we don't want to throttle them ) .await; Ok(()) } + /// Replaces the rpc-submitted transaction into the mempool and propagates it to peers. + /// + /// Returns the removed mempool transaction on successful replace by fee. + /// + /// Transactions submitted through rpc are considered high priority. This definition does not affect the tx selection algorithm + /// but only changes how we manage the lifetime of the tx. A high-priority tx does not expire and is repeatedly rebroadcasted to + /// peers + pub async fn submit_rpc_transaction_replacement( + &self, + consensus: &ConsensusProxy, + transaction: Transaction, + ) -> Result, ProtocolError> { + let transaction_insertion = self + .mining_manager() + .clone() + .validate_and_insert_transaction(consensus, transaction, Priority::High, Orphan::Forbidden, RbfPolicy::Mandatory) + .await?; + self.broadcast_transactions( + transaction_insertion.accepted.iter().map(|x| x.id()), + false, // RPC transactions are considered high priority, so we don't want to throttle them + ) + .await; + // The combination of args above of Orphan::Forbidden and RbfPolicy::Mandatory should always result + // in a removed transaction returned, however we prefer failing gracefully in case of future internal mempool changes + transaction_insertion.removed.ok_or(ProtocolError::Other( + "Replacement transaction was actually accepted but the *replaced* transaction was not returned from the mempool", + )) + } + /// Returns true if the time has come for running the task cleaning mempool transactions. async fn should_run_mempool_scanning_task(&self) -> bool { self.transactions_spread.write().await.should_run_mempool_scanning_task() diff --git a/protocol/flows/src/v5/txrelay/flow.rs b/protocol/flows/src/v5/txrelay/flow.rs index 6a177e57ff..af7e2b6c7d 100644 --- a/protocol/flows/src/v5/txrelay/flow.rs +++ b/protocol/flows/src/v5/txrelay/flow.rs @@ -10,7 +10,7 @@ use kaspa_mining::{ errors::MiningManagerError, mempool::{ errors::RuleError, - tx::{Orphan, Priority}, + tx::{Orphan, Priority, RbfPolicy}, }, model::tx_query::TransactionQuery, P2pTxCountSample, @@ -219,7 +219,7 @@ impl RelayTransactionsFlow { .ctx .mining_manager() .clone() - .validate_and_insert_transaction_batch(&consensus, transactions, Priority::Low, Orphan::Allowed) + .validate_and_insert_transaction_batch(&consensus, transactions, Priority::Low, Orphan::Allowed, RbfPolicy::Allowed) .await; for res in insert_results.iter() { diff --git a/rothschild/src/main.rs b/rothschild/src/main.rs index 2154cec5c7..d303f1da05 100644 --- a/rothschild/src/main.rs +++ b/rothschild/src/main.rs @@ -21,7 +21,7 @@ use secp256k1::{rand::thread_rng, Keypair}; use tokio::time::{interval, MissedTickBehavior}; const DEFAULT_SEND_AMOUNT: u64 = 10 * SOMPI_PER_KASPA; -const FEE_PER_MASS: u64 = 10; +const FEE_RATE: u64 = 10; const MILLIS_PER_TICK: u64 = 10; const ADDRESS_PREFIX: Prefix = Prefix::Testnet; const ADDRESS_VERSION: Version = Version::PubKey; @@ -438,7 +438,7 @@ fn clean_old_pending_outpoints(pending: &mut HashMap) } fn required_fee(num_utxos: usize, num_outs: u64) -> u64 { - FEE_PER_MASS * estimated_mass(num_utxos, num_outs) + FEE_RATE * estimated_mass(num_utxos, num_outs) } fn estimated_mass(num_utxos: usize, num_outs: u64) -> u64 { diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index 9b36c3b2dd..df3ab8a3a3 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -124,6 +124,9 @@ pub enum RpcApiOps { GetCoinSupply, /// Get DAA Score timestamp estimate GetDaaScoreTimestampEstimate, + + /// Extracts a transaction out of the request message and attempts to replace a matching transaction in the mempool with it, applying a mandatory Replace by Fee policy + SubmitTransactionReplacement, } impl RpcApiOps { diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 8857e1a62b..0251aa7fc9 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -212,6 +212,18 @@ pub trait RpcApi: Sync + Send + AnySync { request: SubmitTransactionRequest, ) -> RpcResult; + /// Submits a transaction replacement to the mempool, applying a mandatory Replace by Fee policy. + /// + /// Returns the ID of the inserted transaction and the transaction the submission replaced in the mempool. + async fn submit_transaction_replacement(&self, transaction: RpcTransaction) -> RpcResult { + self.submit_transaction_replacement_call(None, SubmitTransactionReplacementRequest { transaction }).await + } + async fn submit_transaction_replacement_call( + &self, + connection: Option<&DynRpcConnection>, + request: SubmitTransactionReplacementRequest, + ) -> RpcResult; + /// Requests information about a specific block. async fn get_block(&self, hash: RpcHash, include_transactions: bool) -> RpcResult { Ok(self.get_block_call(None, GetBlockRequest::new(hash, include_transactions)).await?.block) diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index ec15d4ed52..2631a67018 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -732,6 +732,69 @@ impl Deserializer for SubmitTransactionResponse { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] +pub struct SubmitTransactionReplacementRequest { + pub transaction: RpcTransaction, +} + +impl SubmitTransactionReplacementRequest { + pub fn new(transaction: RpcTransaction) -> Self { + Self { transaction } + } +} + +impl Serializer for SubmitTransactionReplacementRequest { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + serialize!(RpcTransaction, &self.transaction, writer)?; + + Ok(()) + } +} + +impl Deserializer for SubmitTransactionReplacementRequest { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + let transaction = deserialize!(RpcTransaction, reader)?; + + Ok(Self { transaction }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SubmitTransactionReplacementResponse { + pub transaction_id: RpcTransactionId, + pub replaced_transaction: RpcTransaction, +} + +impl SubmitTransactionReplacementResponse { + pub fn new(transaction_id: RpcTransactionId, replaced_transaction: RpcTransaction) -> Self { + Self { transaction_id, replaced_transaction } + } +} + +impl Serializer for SubmitTransactionReplacementResponse { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + store!(RpcTransactionId, &self.transaction_id, writer)?; + serialize!(RpcTransaction, &self.replaced_transaction, writer)?; + + Ok(()) + } +} + +impl Deserializer for SubmitTransactionReplacementResponse { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + let transaction_id = load!(RpcTransactionId, reader)?; + let replaced_transaction = deserialize!(RpcTransaction, reader)?; + + Ok(Self { transaction_id, replaced_transaction }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[serde(rename_all = "camelCase")] pub struct GetSubnetworkRequest { pub subnetwork_id: RpcSubnetworkId, } diff --git a/rpc/grpc/client/src/lib.rs b/rpc/grpc/client/src/lib.rs index 882f2a039b..144c74b989 100644 --- a/rpc/grpc/client/src/lib.rs +++ b/rpc/grpc/client/src/lib.rs @@ -255,6 +255,7 @@ impl RpcApi for GrpcClient { route!(get_connected_peer_info_call, GetConnectedPeerInfo); route!(add_peer_call, AddPeer); route!(submit_transaction_call, SubmitTransaction); + route!(submit_transaction_replacement_call, SubmitTransactionReplacement); route!(get_subnetwork_call, GetSubnetwork); route!(get_virtual_chain_from_block_call, GetVirtualChainFromBlock); route!(get_blocks_call, GetBlocks); diff --git a/rpc/grpc/core/proto/messages.proto b/rpc/grpc/core/proto/messages.proto index 1919c71ece..c3a8bf4c47 100644 --- a/rpc/grpc/core/proto/messages.proto +++ b/rpc/grpc/core/proto/messages.proto @@ -59,8 +59,10 @@ message KaspadRequest { GetServerInfoRequestMessage getServerInfoRequest = 1092; GetSyncStatusRequestMessage getSyncStatusRequest = 1094; GetDaaScoreTimestampEstimateRequestMessage GetDaaScoreTimestampEstimateRequest = 1096; - GetConnectionsRequestMessage getConnectionsRequest = 1098; - GetSystemInfoRequestMessage getSystemInfoRequest = 1100; + // GetFeeInfoRequestMessage getFeeInfoResponse = 1098; + SubmitTransactionReplacementRequestMessage submitTransactionReplacementRequest = 1100; + GetConnectionsRequestMessage getConnectionsRequest = 1102; + GetSystemInfoRequestMessage getSystemInfoRequest = 1104; } } @@ -120,8 +122,10 @@ message KaspadResponse { GetServerInfoResponseMessage getServerInfoResponse = 1093; GetSyncStatusResponseMessage getSyncStatusResponse = 1095; GetDaaScoreTimestampEstimateResponseMessage GetDaaScoreTimestampEstimateResponse = 1097; - GetConnectionsResponseMessage getConnectionsResponse= 1099; - GetSystemInfoResponseMessage getSystemInfoResponse= 1101; + // GetFeeInfoResponseMessage getFeeInfoResponse = 1099; + SubmitTransactionReplacementResponseMessage submitTransactionReplacementResponse = 1101; + GetConnectionsResponseMessage getConnectionsResponse= 1103; + GetSystemInfoResponseMessage getSystemInfoResponse= 1105; } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index 6ce249c448..49478262b3 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -307,6 +307,21 @@ message SubmitTransactionResponseMessage{ RPCError error = 1000; } +// SubmitTransactionReplacementRequestMessage submits a transaction to the mempool, applying a mandatory Replace by Fee policy +message SubmitTransactionReplacementRequestMessage{ + RpcTransaction transaction = 1; +} + +message SubmitTransactionReplacementResponseMessage{ + // The transaction ID of the submitted transaction + string transactionId = 1; + + // The previous transaction replaced in the mempool by the newly submitted one + RpcTransaction replacedTransaction = 2; + + RPCError error = 1000; +} + // NotifyVirtualChainChangedRequestMessage registers this connection for virtualChainChanged notifications. // // See: VirtualChainChangedNotificationMessage @@ -873,10 +888,10 @@ message GetSyncStatusResponseMessage{ } message GetDaaScoreTimestampEstimateRequestMessage { - repeated uint64 daa_scores = 1; + repeated uint64 daa_scores = 1; } message GetDaaScoreTimestampEstimateResponseMessage{ - repeated uint64 timestamps = 1; - RPCError error = 1000; + repeated uint64 timestamps = 1; + RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/kaspad.rs b/rpc/grpc/core/src/convert/kaspad.rs index 67b37d47f7..11f75a6084 100644 --- a/rpc/grpc/core/src/convert/kaspad.rs +++ b/rpc/grpc/core/src/convert/kaspad.rs @@ -36,6 +36,7 @@ pub mod kaspad_request_convert { impl_into_kaspad_request!(GetConnectedPeerInfo); impl_into_kaspad_request!(AddPeer); impl_into_kaspad_request!(SubmitTransaction); + impl_into_kaspad_request!(SubmitTransactionReplacement); impl_into_kaspad_request!(GetSubnetwork); impl_into_kaspad_request!(GetVirtualChainFromBlock); impl_into_kaspad_request!(GetBlocks); @@ -169,6 +170,7 @@ pub mod kaspad_response_convert { impl_into_kaspad_response!(GetConnectedPeerInfo); impl_into_kaspad_response!(AddPeer); impl_into_kaspad_response!(SubmitTransaction); + impl_into_kaspad_response!(SubmitTransactionReplacement); impl_into_kaspad_response!(GetSubnetwork); impl_into_kaspad_response!(GetVirtualChainFromBlock); impl_into_kaspad_response!(GetBlocks); diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index ea29778ae5..486b5284f3 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -249,6 +249,13 @@ from!(item: RpcResult<&kaspa_rpc_core::SubmitTransactionResponse>, protowire::Su Self { transaction_id: item.transaction_id.to_string(), error: None } }); +from!(item: &kaspa_rpc_core::SubmitTransactionReplacementRequest, protowire::SubmitTransactionReplacementRequestMessage, { + Self { transaction: Some((&item.transaction).into()) } +}); +from!(item: RpcResult<&kaspa_rpc_core::SubmitTransactionReplacementResponse>, protowire::SubmitTransactionReplacementResponseMessage, { + Self { transaction_id: item.transaction_id.to_string(), replaced_transaction: Some((&item.replaced_transaction).into()), error: None } +}); + from!(item: &kaspa_rpc_core::GetSubnetworkRequest, protowire::GetSubnetworkRequestMessage, { Self { subnetwork_id: item.subnetwork_id.to_string() } }); @@ -679,6 +686,26 @@ try_from!(item: &protowire::SubmitTransactionResponseMessage, RpcResult, { + Self { + transaction_id: RpcHash::from_str(&item.transaction_id)?, + replaced_transaction: item + .replaced_transaction + .as_ref() + .ok_or_else(|| RpcError::MissingRpcFieldError("SubmitTransactionReplacementRequestMessage".to_string(), "replaced_transaction".to_string()))? + .try_into()?, + } +}); + try_from!(item: &protowire::GetSubnetworkRequestMessage, kaspa_rpc_core::GetSubnetworkRequest, { Self { subnetwork_id: kaspa_rpc_core::RpcSubnetworkId::from_str(&item.subnetwork_id)? } }); diff --git a/rpc/grpc/core/src/ops.rs b/rpc/grpc/core/src/ops.rs index fc07988f33..fef9113e28 100644 --- a/rpc/grpc/core/src/ops.rs +++ b/rpc/grpc/core/src/ops.rs @@ -61,6 +61,7 @@ pub enum KaspadPayloadOps { GetConnectedPeerInfo, AddPeer, SubmitTransaction, + SubmitTransactionReplacement, GetSubnetwork, GetVirtualChainFromBlock, GetBlockCount, diff --git a/rpc/grpc/server/src/request_handler/factory.rs b/rpc/grpc/server/src/request_handler/factory.rs index 722ad19e97..f91ed0ab5e 100644 --- a/rpc/grpc/server/src/request_handler/factory.rs +++ b/rpc/grpc/server/src/request_handler/factory.rs @@ -55,6 +55,7 @@ impl Factory { GetConnectedPeerInfo, AddPeer, SubmitTransaction, + SubmitTransactionReplacement, GetSubnetwork, GetVirtualChainFromBlock, GetBlockCount, diff --git a/rpc/grpc/server/src/tests/rpc_core_mock.rs b/rpc/grpc/server/src/tests/rpc_core_mock.rs index 4f3c2a5653..935958087d 100644 --- a/rpc/grpc/server/src/tests/rpc_core_mock.rs +++ b/rpc/grpc/server/src/tests/rpc_core_mock.rs @@ -182,6 +182,14 @@ impl RpcApi for RpcCoreMock { Err(RpcError::NotImplemented) } + async fn submit_transaction_replacement_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: SubmitTransactionReplacementRequest, + ) -> RpcResult { + Err(RpcError::NotImplemented) + } + async fn add_peer_call(&self, _connection: Option<&DynRpcConnection>, _request: AddPeerRequest) -> RpcResult { Err(RpcError::NotImplemented) } diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index dbd03804b9..863b1b4996 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -536,6 +536,23 @@ NOTE: This error usually indicates an RPC conversion error between the node and Ok(SubmitTransactionResponse::new(transaction_id)) } + async fn submit_transaction_replacement_call( + &self, + _connection: Option<&DynRpcConnection>, + request: SubmitTransactionReplacementRequest, + ) -> RpcResult { + let transaction: Transaction = (&request.transaction).try_into()?; + let transaction_id = transaction.id(); + let session = self.consensus_manager.consensus().unguarded_session(); + let replaced_transaction = + self.flow_context.submit_rpc_transaction_replacement(&session, transaction).await.map_err(|err| { + let err = RpcError::RejectedTransaction(transaction_id, err.to_string()); + debug!("{err}"); + err + })?; + Ok(SubmitTransactionReplacementResponse::new(transaction_id, (&*replaced_transaction).into())) + } + async fn get_current_network_call( &self, _connection: Option<&DynRpcConnection>, diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index f15bccbf11..90e91b7592 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -638,6 +638,7 @@ impl RpcApi for KaspaRpcClient { Shutdown, SubmitBlock, SubmitTransaction, + SubmitTransactionReplacement, Unban, ] ); diff --git a/rpc/wrpc/server/src/router.rs b/rpc/wrpc/server/src/router.rs index 000b57a62c..e3a740c164 100644 --- a/rpc/wrpc/server/src/router.rs +++ b/rpc/wrpc/server/src/router.rs @@ -69,6 +69,7 @@ impl Router { Shutdown, SubmitBlock, SubmitTransaction, + SubmitTransactionReplacement, Unban, ] ); diff --git a/testing/integration/src/common/utils.rs b/testing/integration/src/common/utils.rs index f8c547766f..ab03411a8f 100644 --- a/testing/integration/src/common/utils.rs +++ b/testing/integration/src/common/utils.rs @@ -36,8 +36,8 @@ const fn estimated_mass(num_inputs: usize, num_outputs: u64) -> u64 { } pub const fn required_fee(num_inputs: usize, num_outputs: u64) -> u64 { - const FEE_PER_MASS: u64 = 10; - FEE_PER_MASS * estimated_mass(num_inputs, num_outputs) + const FEE_RATE: u64 = 10; + FEE_RATE * estimated_mass(num_inputs, num_outputs) } /// Builds a TX DAG based on the initial UTXO set and on constant params diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs index 3db80e25ef..3e7711586f 100644 --- a/testing/integration/src/rpc_tests.rs +++ b/testing/integration/src/rpc_tests.rs @@ -314,6 +314,17 @@ async fn sanity_test() { }) } + KaspadPayloadOps::SubmitTransactionReplacement => { + let rpc_client = client.clone(); + tst!(op, { + // Build an erroneous transaction... + let transaction = Transaction::new(0, vec![], vec![], 0, SubnetworkId::default(), 0, vec![]); + let result = rpc_client.submit_transaction_replacement((&transaction).into()).await; + // ...that gets rejected by the consensus + assert!(result.is_err()); + }) + } + KaspadPayloadOps::GetSubnetwork => { let rpc_client = client.clone(); tst!(op, { diff --git a/wallet/core/src/tests/rpc_core_mock.rs b/wallet/core/src/tests/rpc_core_mock.rs index 8f6d39b76f..4d82c945a8 100644 --- a/wallet/core/src/tests/rpc_core_mock.rs +++ b/wallet/core/src/tests/rpc_core_mock.rs @@ -199,6 +199,14 @@ impl RpcApi for RpcCoreMock { Err(RpcError::NotImplemented) } + async fn submit_transaction_replacement_call( + &self, + _connection: Option<&DynRpcConnection>, + _request: SubmitTransactionReplacementRequest, + ) -> RpcResult { + Err(RpcError::NotImplemented) + } + async fn add_peer_call(&self, _connection: Option<&DynRpcConnection>, _request: AddPeerRequest) -> RpcResult { Err(RpcError::NotImplemented) } From 766b3c1eb9ff1bdc904563e64fb9a8e9525c7a8a Mon Sep 17 00:00:00 2001 From: KaffinPX <73744616+KaffinPX@users.noreply.github.com> Date: Fri, 26 Jul 2024 00:56:21 +0300 Subject: [PATCH 108/158] createInputSignature() utility function (#79) * ``signTransactionInput`` and move sign_input to its proper location * Fix typedoc warnings left from old PR * createInputSignature --- consensus/core/src/hashing/wasm.rs | 1 - consensus/core/src/sign.rs | 15 ++++++++++++++- crypto/txscript/src/wasm/builder.rs | 2 -- wallet/core/src/tx/generator/pending.rs | 13 ++----------- wallet/core/src/wasm/signer.rs | 23 ++++++++++++++++++++++- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/consensus/core/src/hashing/wasm.rs b/consensus/core/src/hashing/wasm.rs index 1c3b51a7a3..4c9c94b223 100644 --- a/consensus/core/src/hashing/wasm.rs +++ b/consensus/core/src/hashing/wasm.rs @@ -2,7 +2,6 @@ use super::sighash_type::{self, SigHashType}; use wasm_bindgen::prelude::*; /// Kaspa Sighash types allowed by consensus -/// @see {@link signInput} /// @category Consensus #[wasm_bindgen] pub enum SighashType { diff --git a/consensus/core/src/sign.rs b/consensus/core/src/sign.rs index dee0d3844c..174f689710 100644 --- a/consensus/core/src/sign.rs +++ b/consensus/core/src/sign.rs @@ -1,7 +1,7 @@ use crate::{ hashing::{ sighash::{calc_schnorr_signature_hash, SigHashReusedValues}, - sighash_type::SIG_HASH_ALL, + sighash_type::{SigHashType, SIG_HASH_ALL}, }, tx::SignableTransaction, }; @@ -153,6 +153,19 @@ pub fn sign_with_multiple_v2(mut mutable_tx: SignableTransaction, privkeys: &[[u } } +/// Sign a transaction input with a sighash_type using schnorr +pub fn sign_input(tx: SignableTransaction, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Vec { + let mut reused_values = SigHashReusedValues::new(); + + let hash = calc_schnorr_signature_hash(&tx.as_verifiable(), input_index, hash_type, &mut reused_values); + let msg = secp256k1::Message::from_digest_slice(hash.as_bytes().as_slice()).unwrap(); + let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key).unwrap(); + let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref(); + + // This represents OP_DATA_65 (since signature length is 64 bytes and SIGHASH_TYPE is one byte) + std::iter::once(65u8).chain(sig).chain([hash_type.to_u8()]).collect() +} + pub fn verify(tx: &impl crate::tx::VerifiableTransaction) -> Result<(), Error> { let mut reused_values = SigHashReusedValues::new(); for (i, (input, entry)) in tx.populated_inputs().enumerate() { diff --git a/crypto/txscript/src/wasm/builder.rs b/crypto/txscript/src/wasm/builder.rs index 8a6688fd91..57c6b8b4f4 100644 --- a/crypto/txscript/src/wasm/builder.rs +++ b/crypto/txscript/src/wasm/builder.rs @@ -15,8 +15,6 @@ use workflow_wasm::prelude::*; /// data pushes which would exceed the maximum allowed script engine limits and /// are therefore guaranteed not to execute will not be pushed and will result in /// the Script function returning an error. -/// -/// @see {@link Opcode} /// @category Consensus #[derive(Clone)] #[wasm_bindgen(inspectable)] diff --git a/wallet/core/src/tx/generator/pending.rs b/wallet/core/src/tx/generator/pending.rs index 7d07af74e4..a671fd3437 100644 --- a/wallet/core/src/tx/generator/pending.rs +++ b/wallet/core/src/tx/generator/pending.rs @@ -8,9 +8,8 @@ use crate::result::Result; use crate::rpc::DynRpcApi; use crate::tx::{DataKind, Generator}; use crate::utxo::{UtxoContext, UtxoEntryId, UtxoEntryReference}; -use kaspa_consensus_core::hashing::sighash::{calc_schnorr_signature_hash, SigHashReusedValues}; use kaspa_consensus_core::hashing::sighash_type::SigHashType; -use kaspa_consensus_core::sign::{sign_with_multiple_v2, Signed}; +use kaspa_consensus_core::sign::{sign_input, sign_with_multiple_v2, Signed}; use kaspa_consensus_core::tx::{SignableTransaction, Transaction, TransactionId}; use kaspa_rpc_core::{RpcTransaction, RpcTransactionId}; @@ -226,17 +225,9 @@ impl PendingTransaction { } pub fn sign_input(&self, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Result> { - // TODO: Move to sign.rs let mutable_tx = self.inner.signable_tx.lock()?.clone(); - let mut reused_values = SigHashReusedValues::new(); - let hash = calc_schnorr_signature_hash(&mutable_tx.as_verifiable(), input_index, hash_type, &mut reused_values); - let msg = secp256k1::Message::from_digest_slice(hash.as_bytes().as_slice()).unwrap(); - let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key).unwrap(); - let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref(); - - // This represents OP_DATA_65 (since signature length is 64 bytes and SIGHASH_TYPE is one byte) - Ok(std::iter::once(65u8).chain(sig).chain([hash_type.to_u8()]).collect()) + Ok(sign_input(mutable_tx, input_index, private_key, hash_type)) } pub fn fill_input(&self, input_index: usize, signature_script: Vec) -> Result<()> { diff --git a/wallet/core/src/wasm/signer.rs b/wallet/core/src/wasm/signer.rs index e2ff8e6fb2..106988a3cf 100644 --- a/wallet/core/src/wasm/signer.rs +++ b/wallet/core/src/wasm/signer.rs @@ -2,10 +2,13 @@ use crate::imports::*; use crate::result::Result; use js_sys::Array; use kaspa_consensus_client::{sign_with_multiple_v3, Transaction}; -use kaspa_consensus_core::tx::PopulatedTransaction; +use kaspa_consensus_core::hashing::wasm::SighashType; +use kaspa_consensus_core::sign::sign_input; +use kaspa_consensus_core::tx::{PopulatedTransaction, SignableTransaction}; use kaspa_consensus_core::{hashing::sighash_type::SIG_HASH_ALL, sign::verify}; use kaspa_hashes::Hash; use kaspa_wallet_keys::privatekey::PrivateKey; +use kaspa_wasm_core::types::HexString; use serde_wasm_bindgen::from_value; #[wasm_bindgen] @@ -64,6 +67,24 @@ pub fn sign(tx: Transaction, privkeys: &[[u8; 32]]) -> Result { Ok(sign_with_multiple_v3(tx, privkeys)?.unwrap()) } +/// `createInputSignature()` is a helper function to sign a transaction input with a specific SigHash type using a private key. +/// @category Wallet SDK +#[wasm_bindgen(js_name = "createInputSignature")] +pub fn create_input_signature( + tx: Transaction, + input_index: u8, + private_key: &PrivateKey, + sighash_type: Option, +) -> Result { + let (cctx, _) = tx.tx_and_utxos(); + let mutable_tx = SignableTransaction::new(cctx); + + let signature = + sign_input(mutable_tx, input_index.into(), &private_key.secret_bytes(), sighash_type.unwrap_or(SighashType::All).into()); + + Ok(signature.to_hex().into()) +} + /// @category Wallet SDK #[wasm_bindgen(js_name=signScriptHash)] pub fn sign_script_hash(script_hash: JsValue, privkey: &PrivateKey) -> Result { From ae4774d44f1f3b01f00f76276f16dedba3fbd1fd Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 26 Jul 2024 02:14:42 +0300 Subject: [PATCH 109/158] Update docs for ConsensusParams (WASM mass calc) --- wallet/core/src/wasm/tx/consensus.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wallet/core/src/wasm/tx/consensus.rs b/wallet/core/src/wasm/tx/consensus.rs index f1400d0915..5299a00fc3 100644 --- a/wallet/core/src/wasm/tx/consensus.rs +++ b/wallet/core/src/wasm/tx/consensus.rs @@ -3,7 +3,14 @@ use kaspa_addresses::Address; use kaspa_consensus_core::{config::params::Params, network::NetworkType}; use wasm_bindgen::prelude::*; -/// @category Wallet SDK +/// +/// `ConsensusParams` can be obtained using `getConsensusParametersByNetwork` or `getConsensusParametersByAddress`. +/// +/// @see {@link getConsensusParametersByNetwork} +/// @see {@link getConsensusParametersByAddress} +/// +/// @category Wallet SDK +/// #[wasm_bindgen] pub struct ConsensusParams { params: Params, From dd2e5857734975fd3656649094cd6c2003c15f79 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 26 Jul 2024 02:15:02 +0300 Subject: [PATCH 110/158] fmt --- wallet/core/src/wasm/tx/consensus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/core/src/wasm/tx/consensus.rs b/wallet/core/src/wasm/tx/consensus.rs index 5299a00fc3..b1e722a61c 100644 --- a/wallet/core/src/wasm/tx/consensus.rs +++ b/wallet/core/src/wasm/tx/consensus.rs @@ -9,7 +9,7 @@ use wasm_bindgen::prelude::*; /// @see {@link getConsensusParametersByNetwork} /// @see {@link getConsensusParametersByAddress} /// -/// @category Wallet SDK +/// @category Wallet SDK /// #[wasm_bindgen] pub struct ConsensusParams { From c657a491a178513aa79e6257f704e091f5d9af39 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 26 Jul 2024 21:57:42 +0300 Subject: [PATCH 111/158] bump wRPC --- Cargo.lock | 84 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 24 ++++++++-------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index baaa434f04..1eefebeb2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6860,9 +6860,9 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d24f8060d11a8eeeb25f0af9c229a94e0998d14a7a1932eb25bf9662e0775ed" +checksum = "a13d04fd8ac3d6e6b085bcba441bf4b838014f30fc729493834cc6254d829563" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6875,9 +6875,9 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d889e6327e09b88827953e6bc6b13cb883b2b19b04185c75a6a05eede517fb1e" +checksum = "435deca3aa42b8d1d7291c715a7bce642b1a739b5185301583f03df0d1b07153" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6907,9 +6907,9 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fba00e4a92112229e904826d366b4f518068be9c6a7f7c2d3f9f847ec917cf6b" +checksum = "25c5038d09456bd035ba50141cf1561dd20e9bbb71a09d500aec19087197a163" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6924,9 +6924,9 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2981dac7b9c3148c70e98959f90d878de28ad3546c496825faa6821122ab76" +checksum = "51c1f9046b15bbe3fdf445f32b27c3b2407941759bff08f7858df46b06caa5a4" dependencies = [ "futures", "js-sys", @@ -6942,9 +6942,9 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb140b523579932d9233a73f7574d73a8e09f5e719cdb7121137116ef82820ac" +checksum = "e3611c28cecb0702186a1fe203a755a6230b996aa3ce31e2d47a624da40d5c7b" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6958,9 +6958,9 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b383ae3a35a179259b67b5e57ff80f545a86472c56c538ba7ee64934426906" +checksum = "325b15c8334711300005d9cd6210088e734648e7c9d5945d7d781123479636f1" dependencies = [ "cfg-if 1.0.0", "console", @@ -6974,9 +6974,9 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "059b3f3d8acfd2863ccebbd18096166ba45391f69f3825014968ea92bd96a9df" +checksum = "03365620b45f09dfbb17b2ee79d8f24adccf7b70661f8fa177fd94369785d6d3" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6987,9 +6987,9 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b310a6999a7e6d725316cb4869819fa0a0894c2cf05f7d93cc6e3a8ad081061" +checksum = "4d5efeeaa756b48703a642e0940d08152327ad80c053085c9276f22e28686ff3" dependencies = [ "borsh", "futures", @@ -7008,9 +7008,9 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916aeb34a69402bf2d9aac05da191272ad09882f5f1e952da1e6de5071446fdb" +checksum = "af34c929dd3c306671fabf19712ec03a8f3a3d7902bf583a75dfc223bc5ea1f8" dependencies = [ "ahash", "async-trait", @@ -7032,9 +7032,9 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273864a1c98165527a7291ca419c7637e3c42bbe24aa1821010d1bb12d1b28d4" +checksum = "332d8aded769e0f63cc0bead95105030e3d17ddb373eb6eca6de22672237bedb" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7057,9 +7057,9 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35df84239b442e2a4bc7f012aeb99bf56b2ce177bfc1fb511f39295284d8432" +checksum = "fcbfff11e7ffd0408c750070e18f3a9dc43a923ed17819721b6d469b181b875d" dependencies = [ "ahash", "async-std", @@ -7087,9 +7087,9 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe36cbf1e1a1a33122ed499dc91d367abc4358e86c9f9126db8829a7085b711" +checksum = "2de9f5fb79815a13e573421cfd477fcf7b34231b86454d8136abec0866ed8759" dependencies = [ "parse-variants", "proc-macro-error", @@ -7100,9 +7100,9 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a489dfd457e5128c7342efaee4d95479cb91bba3850b8138f6a2c1bc5ac8b3" +checksum = "05f373b7962b8c08133606d21a1c15b22944960d742fb187409485bf05a80a6d" dependencies = [ "ahash", "borsh", @@ -7111,9 +7111,9 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6a4827802926626f1b6359b180ff4540ae241b1844c0379aa8d567428f5f98" +checksum = "dc3472c3a1a310e1fc7e0dfd0bda9a0f1e5aead4b057f3041b2a685fb5f3f739" dependencies = [ "async-std", "base64 0.22.1", @@ -7139,9 +7139,9 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6549f54b7444b10514cc22193381ca4645f6f2d0c3513026c6fb79a05e4f097" +checksum = "20e4ef460dbebfec0bc0e0d41e63bddbf2c029d71f5670e81d942cb48beff8ba" dependencies = [ "futures", "thiserror", @@ -7151,9 +7151,9 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2bb83b731dae95ddb3478f57aa8afb9a6a30ce78b11ff319a84df9296db112" +checksum = "023d35ec92d9f39c529493ba042273b4986f32e5fb77a17b07bf8cce553a8f5a" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7167,9 +7167,9 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aee5b190b532d7761728be9b473425c3420798daf341be44c99102adbf13f9a" +checksum = "218a1a9b76fb0133523f7317e6cfc8dcedce6e288004fd9e4aae22077c4f87ae" dependencies = [ "async-std", "async-trait", @@ -7196,9 +7196,9 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "315bd4768689d133902dc2f8a1b4cb969e8e9875bcfc7703b2bf6ba42f294cc2" +checksum = "814642356470b14f552d41f753f67ffd7e77e9a9edaaa27b7d8a86b76d630935" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7212,9 +7212,9 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc78269a2f7384e99bdcde6bd9a32ef28c20e6610d6ba7d817e9d57e7494038" +checksum = "c38cf4ea411426847214b81b6d560218c1d8fcb4b08255385bdbc23c2b508117" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7233,9 +7233,9 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7af80a4b7508805932d80d7c8678374694a0f65cc60e0356597144128f6ee33e" +checksum = "ee34ee84d2bed7a7696d0fe4bb5575bcf5ff81f9b32db03dd6a6be9b2fc3d385" dependencies = [ "js-sys", "proc-macro-error", @@ -7247,9 +7247,9 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c781fb394b573bf46c574703e4d08600f2947185c39d58839d6fe7ecd0d375b" +checksum = "fec5010ee2e79cd1ed83afaead53c9f8a31895db0cbaf5d20c82e1bc86c63e9e" dependencies = [ "ahash", "async-channel 2.2.1", diff --git a/Cargo.toml b/Cargo.toml index 885cf9edd9..a3532234fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,18 +274,18 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.14.0" } -workflow-d3 = { version = "0.14.0" } -workflow-dom = { version = "0.14.0" } -workflow-http = { version = "0.14.0" } -workflow-log = { version = "0.14.0" } -workflow-node = { version = "0.14.0" } -workflow-nw = { version = "0.14.0" } -workflow-rpc = { version = "0.14.0" } -workflow-serializer = { version = "0.14.0" } -workflow-store = { version = "0.14.0" } -workflow-terminal = { version = "0.14.0" } -workflow-wasm = { version = "0.14.0" } +workflow-core = { version = "0.14.1" } +workflow-d3 = { version = "0.14.1" } +workflow-dom = { version = "0.14.1" } +workflow-http = { version = "0.14.1" } +workflow-log = { version = "0.14.1" } +workflow-node = { version = "0.14.1" } +workflow-nw = { version = "0.14.1" } +workflow-rpc = { version = "0.14.2" } +workflow-serializer = { version = "0.14.1" } +workflow-store = { version = "0.14.1" } +workflow-terminal = { version = "0.14.1" } +workflow-wasm = { version = "0.14.1" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs From f8d90f00ce464afd45d2b1a0f270019194615279 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 26 Jul 2024 22:42:56 +0300 Subject: [PATCH 112/158] wrs 0.15.0 --- Cargo.lock | 84 +++++++++++++++++++------------------- Cargo.toml | 24 +++++------ crypto/txscript/Cargo.toml | 1 + 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1eefebeb2b..2512149b31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6860,9 +6860,9 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13d04fd8ac3d6e6b085bcba441bf4b838014f30fc729493834cc6254d829563" +checksum = "2752ac67d1a571d034279a9af0519890dd49638789c273399c095433ee4198d5" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6875,9 +6875,9 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435deca3aa42b8d1d7291c715a7bce642b1a739b5185301583f03df0d1b07153" +checksum = "e3e4fbdb34a153dae3cb7ecb6f84960015b7cba37465ff02fb982ded2d1b79bf" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6907,9 +6907,9 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25c5038d09456bd035ba50141cf1561dd20e9bbb71a09d500aec19087197a163" +checksum = "fb3c5924a19620185bdbfdbd66211e1aa0e55e65497e6c2501b2e29f4ddb4d2b" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6924,9 +6924,9 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c1f9046b15bbe3fdf445f32b27c3b2407941759bff08f7858df46b06caa5a4" +checksum = "abfb07aa5b3da1f943cba196777afb1ee7c189372846b2d8de3c8510de8c8a0c" dependencies = [ "futures", "js-sys", @@ -6942,9 +6942,9 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3611c28cecb0702186a1fe203a755a6230b996aa3ce31e2d47a624da40d5c7b" +checksum = "924411a6fa2d3e2ddd591fe9056fcd6a753e62d6e4a6bc882158efb130c4bb1d" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6958,9 +6958,9 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325b15c8334711300005d9cd6210088e734648e7c9d5945d7d781123479636f1" +checksum = "e3ef6324e476d8fe35c97493b796cd5edb77ad1c9fc41b141ddd898b874c822f" dependencies = [ "cfg-if 1.0.0", "console", @@ -6974,9 +6974,9 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03365620b45f09dfbb17b2ee79d8f24adccf7b70661f8fa177fd94369785d6d3" +checksum = "41f2a006332e207a538701b09db662467d6cd612465aafad1057eb44847a652e" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6987,9 +6987,9 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5efeeaa756b48703a642e0940d08152327ad80c053085c9276f22e28686ff3" +checksum = "8035a06ca9213aae806e7839ab15050e818c7b616eef7905c4387bdc07fcad4c" dependencies = [ "borsh", "futures", @@ -7008,9 +7008,9 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af34c929dd3c306671fabf19712ec03a8f3a3d7902bf583a75dfc223bc5ea1f8" +checksum = "cb4fd7c2bee6f542cb01de7d4200a5999bd718578f28bd126bf853f93265b4b1" dependencies = [ "ahash", "async-trait", @@ -7032,9 +7032,9 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332d8aded769e0f63cc0bead95105030e3d17ddb373eb6eca6de22672237bedb" +checksum = "9324ed149bb13b633fda77b823ef98ff58c0072c9fc9ce70af1d74b83189b42c" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7057,9 +7057,9 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcbfff11e7ffd0408c750070e18f3a9dc43a923ed17819721b6d469b181b875d" +checksum = "b3a8d999bb77b939021f658447e83b3feb7205254eff6499f302a893d38111f0" dependencies = [ "ahash", "async-std", @@ -7087,9 +7087,9 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2de9f5fb79815a13e573421cfd477fcf7b34231b86454d8136abec0866ed8759" +checksum = "d940809c8856e40391337c212677bfa4b81af15ff5c5e5a023bf4a98e08d2898" dependencies = [ "parse-variants", "proc-macro-error", @@ -7100,9 +7100,9 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f373b7962b8c08133606d21a1c15b22944960d742fb187409485bf05a80a6d" +checksum = "a6d60f84ae0bbcb5df48aa8d755656729d5173cf8041f1da26ced4e6badd1607" dependencies = [ "ahash", "borsh", @@ -7111,9 +7111,9 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc3472c3a1a310e1fc7e0dfd0bda9a0f1e5aead4b057f3041b2a685fb5f3f739" +checksum = "74ce9b637375a9c329401177bdeed625ae89a46c3089167dde89432856834154" dependencies = [ "async-std", "base64 0.22.1", @@ -7139,9 +7139,9 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e4ef460dbebfec0bc0e0d41e63bddbf2c029d71f5670e81d942cb48beff8ba" +checksum = "c1b0efdb7d1c17f962e967d82815712149d5b50a3657bc3a19b7e4a1c8c4c05b" dependencies = [ "futures", "thiserror", @@ -7151,9 +7151,9 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023d35ec92d9f39c529493ba042273b4986f32e5fb77a17b07bf8cce553a8f5a" +checksum = "68b859c5a882b80b6b942f83afe020d1c2ebd0c610370e922ba35e7d5653f88f" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7167,9 +7167,9 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "218a1a9b76fb0133523f7317e6cfc8dcedce6e288004fd9e4aae22077c4f87ae" +checksum = "52f90f17fd25f45de3441f6cc8d34569f035787549d798819f4508b9bbb194bc" dependencies = [ "async-std", "async-trait", @@ -7196,9 +7196,9 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "814642356470b14f552d41f753f67ffd7e77e9a9edaaa27b7d8a86b76d630935" +checksum = "7978dc5e66d329ad9749e5965af0d83ee216347ed52d35f86d9b760f7e85b384" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7212,9 +7212,9 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38cf4ea411426847214b81b6d560218c1d8fcb4b08255385bdbc23c2b508117" +checksum = "99adc368acba2fd1c017bdeddca479fa0c170f0a8b2955652ceb07ac3e28bb57" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7233,9 +7233,9 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee34ee84d2bed7a7696d0fe4bb5575bcf5ff81f9b32db03dd6a6be9b2fc3d385" +checksum = "353dd87151d9e95940ab252c7d61f87c5c76f0a1e6ff0fce4114f5e645e8b6f7" dependencies = [ "js-sys", "proc-macro-error", @@ -7247,9 +7247,9 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec5010ee2e79cd1ed83afaead53c9f8a31895db0cbaf5d20c82e1bc86c63e9e" +checksum = "2343a4bbac5c0e51ee1c8dea0d581ca9387dba2d73fce3a05d82495d559ab797" dependencies = [ "ahash", "async-channel 2.2.1", diff --git a/Cargo.toml b/Cargo.toml index a3532234fe..56834b5281 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,18 +274,18 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.14.1" } -workflow-d3 = { version = "0.14.1" } -workflow-dom = { version = "0.14.1" } -workflow-http = { version = "0.14.1" } -workflow-log = { version = "0.14.1" } -workflow-node = { version = "0.14.1" } -workflow-nw = { version = "0.14.1" } -workflow-rpc = { version = "0.14.2" } -workflow-serializer = { version = "0.14.1" } -workflow-store = { version = "0.14.1" } -workflow-terminal = { version = "0.14.1" } -workflow-wasm = { version = "0.14.1" } +workflow-core = { version = "0.15.0" } +workflow-d3 = { version = "0.15.0" } +workflow-dom = { version = "0.15.0" } +workflow-http = { version = "0.15.0" } +workflow-log = { version = "0.15.0" } +workflow-node = { version = "0.15.0" } +workflow-nw = { version = "0.15.0" } +workflow-rpc = { version = "0.15.0" } +workflow-serializer = { version = "0.15.0" } +workflow-store = { version = "0.15.0" } +workflow-terminal = { version = "0.15.0" } +workflow-wasm = { version = "0.15.0" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs diff --git a/crypto/txscript/Cargo.toml b/crypto/txscript/Cargo.toml index dace708fd1..e2f492ad38 100644 --- a/crypto/txscript/Cargo.toml +++ b/crypto/txscript/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true repository.workspace = true [features] +wasm32-core = [] wasm32-sdk = [] [dependencies] From 762cb9fffef6ef5b441b9ce5933cd415701c9f99 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 26 Jul 2024 23:05:53 +0300 Subject: [PATCH 113/158] replace Uuid.as_ref() to as_bytes() --- protocol/p2p/src/echo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/p2p/src/echo.rs b/protocol/p2p/src/echo.rs index ed03db2044..07a26aac6b 100644 --- a/protocol/p2p/src/echo.rs +++ b/protocol/p2p/src/echo.rs @@ -96,7 +96,7 @@ fn build_dummy_version_message() -> VersionMessage { services: 0, timestamp: unix_now() as i64, address: None, - id: Vec::from(Uuid::new_v4().as_ref()), + id: Vec::from(Uuid::new_v4().as_bytes()), user_agent: String::new(), disable_relay_tx: false, subnetwork_id: None, From e65af3f87bb700e3099d90bc69ab64f4cd225591 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 26 Jul 2024 23:19:01 +0300 Subject: [PATCH 114/158] assign RpcApiOps variants numerical values --- rpc/core/src/api/ops.rs | 112 ++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index df3ab8a3a3..fd74272997 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -17,116 +17,116 @@ pub enum RpcApiOps { NoOp = 0, // connection control (provisional) - Connect, - Disconnect, + Connect = 1, + Disconnect = 2, // subscription management - Subscribe, - Unsubscribe, + Subscribe = 3, + Unsubscribe = 4, // ~~~ // Subscription commands for starting/stopping notifications NotifyBlockAdded = 10, - NotifyNewBlockTemplate, - NotifyUtxosChanged, - NotifyPruningPointUtxoSetOverride, - NotifyFinalityConflict, - NotifyFinalityConflictResolved, // for uniformity purpose only since subscribing to NotifyFinalityConflict means receiving both FinalityConflict and FinalityConflictResolved - NotifyVirtualDaaScoreChanged, - NotifyVirtualChainChanged, - NotifySinkBlueScoreChanged, + NotifyNewBlockTemplate = 11, + NotifyUtxosChanged = 12, + NotifyPruningPointUtxoSetOverride = 13, + NotifyFinalityConflict = 14, + NotifyFinalityConflictResolved = 15, // for uniformity purpose only since subscribing to NotifyFinalityConflict means receiving both FinalityConflict and FinalityConflictResolved + NotifyVirtualDaaScoreChanged = 16, + NotifyVirtualChainChanged = 17, + NotifySinkBlueScoreChanged = 18, // Notification ops required by wRPC // TODO: Remove these ops and use EventType as NotificationOps when workflow_rpc::server::interface::Interface // will be generic over a MethodOps and NotificationOps instead of a single Ops param. BlockAddedNotification = 60, - VirtualChainChangedNotification, - FinalityConflictNotification, - FinalityConflictResolvedNotification, - UtxosChangedNotification, - SinkBlueScoreChangedNotification, - VirtualDaaScoreChangedNotification, - PruningPointUtxoSetOverrideNotification, - NewBlockTemplateNotification, + VirtualChainChangedNotification = 61, + FinalityConflictNotification = 62, + FinalityConflictResolvedNotification = 63, + UtxosChangedNotification = 64, + SinkBlueScoreChangedNotification = 65, + VirtualDaaScoreChangedNotification = 66, + PruningPointUtxoSetOverrideNotification = 67, + NewBlockTemplateNotification = 68, // RPC methods /// Ping the node to check if connection is alive Ping = 110, /// Get metrics for consensus information and node performance - GetMetrics, + GetMetrics = 111, /// Get system information (RAM available, number of cores, available file descriptors) - GetSystemInfo, + GetSystemInfo = 112, /// Get current number of active TCP connections - GetConnections, + GetConnections = 113, /// Get state information on the node - GetServerInfo, + GetServerInfo = 114, /// Get the current sync status of the node - GetSyncStatus, + GetSyncStatus = 115, /// Returns the network this Kaspad is connected to (Mainnet, Testnet) - GetCurrentNetwork, + GetCurrentNetwork = 116, /// Extracts a block out of the request message and attempts to add it to the DAG Returns an empty response or an error message - SubmitBlock, + SubmitBlock = 117, /// Returns a "template" by which a miner can mine a new block - GetBlockTemplate, + GetBlockTemplate = 118, /// Returns a list of all the addresses (IP, port) this Kaspad knows and a list of all addresses that are currently banned by this Kaspad - GetPeerAddresses, + GetPeerAddresses = 119, /// Returns the hash of the current selected tip block of the DAG - GetSink, + GetSink = 120, /// Get information about an entry in the node's mempool - GetMempoolEntry, + GetMempoolEntry = 121, /// Get a snapshot of the node's mempool - GetMempoolEntries, + GetMempoolEntries = 122, /// Returns a list of the peers currently connected to this Kaspad, along with some statistics on them - GetConnectedPeerInfo, + GetConnectedPeerInfo = 123, /// Instructs Kaspad to connect to a given IP address. - AddPeer, + AddPeer = 124, /// Extracts a transaction out of the request message and attempts to add it to the mempool Returns an empty response or an error message - SubmitTransaction, + SubmitTransaction = 125, /// Requests info on a block corresponding to a given block hash Returns block info if the block is known. - GetBlock, + GetBlock = 126, // - GetSubnetwork, + GetSubnetwork = 127, // - GetVirtualChainFromBlock, + GetVirtualChainFromBlock = 128, // - GetBlocks, + GetBlocks = 129, /// Returns the amount of blocks in the DAG - GetBlockCount, + GetBlockCount = 130, /// Returns info on the current state of the DAG - GetBlockDagInfo, + GetBlockDagInfo = 131, // - ResolveFinalityConflict, + ResolveFinalityConflict = 132, /// Instructs this node to shut down Returns an empty response or an error message - Shutdown, + Shutdown = 133, // - GetHeaders, + GetHeaders = 134, /// Get a list of available UTXOs for a given address - GetUtxosByAddresses, + GetUtxosByAddresses = 135, /// Get a balance for a given address - GetBalanceByAddress, + GetBalanceByAddress = 136, /// Get a balance for a number of addresses - GetBalancesByAddresses, + GetBalancesByAddresses = 137, // ? - GetSinkBlueScore, + GetSinkBlueScore = 138, /// Ban a specific peer by it's IP address - Ban, + Ban = 139, /// Unban a specific peer by it's IP address - Unban, + Unban = 140, /// Get generic node information - GetInfo, + GetInfo = 141, // - EstimateNetworkHashesPerSecond, + EstimateNetworkHashesPerSecond = 142, /// Get a list of mempool entries that belong to a specific address - GetMempoolEntriesByAddresses, + GetMempoolEntriesByAddresses = 143, /// Get current issuance supply - GetCoinSupply, + GetCoinSupply = 144, /// Get DAA Score timestamp estimate - GetDaaScoreTimestampEstimate, + GetDaaScoreTimestampEstimate = 145, /// Extracts a transaction out of the request message and attempts to replace a matching transaction in the mempool with it, applying a mandatory Replace by Fee policy - SubmitTransactionReplacement, + SubmitTransactionReplacement = 146, } impl RpcApiOps { From eb9552c8123b2a5dcb8ebca30e7de5fa82cc2635 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 26 Jul 2024 23:19:37 +0300 Subject: [PATCH 115/158] cleanup --- rpc/core/src/api/ops.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index fd74272997..21703785cb 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -42,7 +42,7 @@ pub enum RpcApiOps { // TODO: Remove these ops and use EventType as NotificationOps when workflow_rpc::server::interface::Interface // will be generic over a MethodOps and NotificationOps instead of a single Ops param. BlockAddedNotification = 60, - VirtualChainChangedNotification = 61, + VirtualChainChangedNotification = 61, FinalityConflictNotification = 62, FinalityConflictResolvedNotification = 63, UtxosChangedNotification = 64, @@ -124,7 +124,6 @@ pub enum RpcApiOps { GetCoinSupply = 144, /// Get DAA Score timestamp estimate GetDaaScoreTimestampEstimate = 145, - /// Extracts a transaction out of the request message and attempts to replace a matching transaction in the mempool with it, applying a mandatory Replace by Fee policy SubmitTransactionReplacement = 146, } From fad7bd1a74cb362cc374c77bbda42567edf3de73 Mon Sep 17 00:00:00 2001 From: aspect Date: Fri, 26 Jul 2024 23:40:12 +0300 Subject: [PATCH 116/158] Remove WASM32 mass calculator + change createTransaction() signature (#81) * Kip9 updates to WASM/wallet framework mass calc (#66) * WIP * update kip9 processing in WASM mass calculator * XPrv.toPrivateKey support * replace lazy_static with OnceLock * remove NetworkParams Inner * make signatureScript optional on ITransactionInput (WASM32) * WIP mass calc (WASM32) * remove WASM32 mass calc, replace with dedicated functions * use OnceCell for NetworkParams (wallet-core) * Update changelog * fmt --------- Co-authored-by: Surinder Singh Matoo --- Cargo.lock | 1 - Cargo.toml | 1 - consensus/client/src/input.rs | 8 +- consensus/client/src/output.rs | 4 +- consensus/client/src/sign.rs | 2 +- consensus/client/src/transaction.rs | 52 +++++- consensus/client/src/utxo.rs | 6 + consensus/core/src/mass/mod.rs | 11 ++ consensus/src/processes/mass.rs | 12 +- wallet/core/Cargo.toml | 1 - wallet/core/src/imports.rs | 3 +- wallet/core/src/tx/generator/generator.rs | 24 +-- wallet/core/src/tx/generator/test.rs | 8 +- wallet/core/src/tx/mass.rs | 123 ++++++++----- wallet/core/src/utxo/context.rs | 6 +- wallet/core/src/utxo/processor.rs | 11 +- wallet/core/src/utxo/scan.rs | 4 +- wallet/core/src/utxo/settings.rs | 140 +++++++-------- wallet/core/src/wasm/signer.rs | 4 +- wallet/core/src/wasm/tx/consensus.rs | 43 ----- wallet/core/src/wasm/tx/mass.rs | 170 ++++-------------- wallet/core/src/wasm/tx/mod.rs | 2 - wallet/core/src/wasm/tx/utils.rs | 28 +-- wallet/keys/src/xprv.rs | 9 +- wasm/CHANGELOG.md | 6 + .../nodejs/javascript/general/derivation.js | 17 +- 26 files changed, 313 insertions(+), 383 deletions(-) delete mode 100644 wallet/core/src/wasm/tx/consensus.rs diff --git a/Cargo.lock b/Cargo.lock index 2512149b31..a41b22aebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3493,7 +3493,6 @@ dependencies = [ "kaspa-wasm-core", "kaspa-wrpc-client", "kaspa-wrpc-wasm", - "lazy_static", "md-5", "pad", "pbkdf2", diff --git a/Cargo.toml b/Cargo.toml index 56834b5281..463a49ab61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -192,7 +192,6 @@ ipnet = "2.9.0" itertools = "0.11.0" js-sys = "0.3.67" keccak = "0.1.4" -lazy_static = "1.4.0" local-ip-address = "0.5.6" log = "0.4.20" log4rs = "1.2.0" diff --git a/consensus/client/src/input.rs b/consensus/client/src/input.rs index d25e5f7d95..81b1d009a0 100644 --- a/consensus/client/src/input.rs +++ b/consensus/client/src/input.rs @@ -13,7 +13,7 @@ const TS_TRANSACTION: &'static str = r#" */ export interface ITransactionInput { previousOutpoint: ITransactionOutpoint; - signatureScript: HexString; + signatureScript?: HexString; sequence: bigint; sigOpCount: number; utxo?: UtxoEntryReference; @@ -95,6 +95,10 @@ impl TransactionInput { self.inner().sig_op_count } + pub fn signature_script_length(&self) -> usize { + self.inner().signature_script.len() + } + pub fn utxo(&self) -> Option { self.inner().utxo.clone() } @@ -187,7 +191,7 @@ impl TryCastFromJs for TransactionInput { Self::resolve_cast(&value, || { if let Some(object) = Object::try_from(value.as_ref()) { let previous_outpoint: TransactionOutpoint = object.get_value("previousOutpoint")?.as_ref().try_into()?; - let signature_script = object.get_vec_u8("signatureScript")?; + let signature_script = object.get_vec_u8("signatureScript").unwrap_or_default(); let sequence = object.get_u64("sequence")?; let sig_op_count = object.get_u8("sigOpCount")?; let utxo = object.try_get_cast::("utxo")?.map(Cast::into_owned); diff --git a/consensus/client/src/output.rs b/consensus/client/src/output.rs index 4dddb5067d..f4ee1ad1e6 100644 --- a/consensus/client/src/output.rs +++ b/consensus/client/src/output.rs @@ -65,7 +65,7 @@ impl TransactionOutput { self.inner.lock().unwrap() } - pub fn script_length(&self) -> usize { + pub fn script_public_key_length(&self) -> usize { self.inner().script_public_key.script().len() } } @@ -79,7 +79,7 @@ impl TransactionOutput { } #[wasm_bindgen(getter, js_name = value)] - pub fn get_value(&self) -> u64 { + pub fn value(&self) -> u64 { self.inner().value } diff --git a/consensus/client/src/sign.rs b/consensus/client/src/sign.rs index fdab66a602..b4afc11e68 100644 --- a/consensus/client/src/sign.rs +++ b/consensus/client/src/sign.rs @@ -44,7 +44,7 @@ pub fn sign_with_multiple_v3(tx: Transaction, privkeys: &[[u8; 32]]) -> crate::r let mut additional_signatures_required = false; { let input_len = tx.inner().inputs.len(); - let (cctx, utxos) = tx.tx_and_utxos(); + let (cctx, utxos) = tx.tx_and_utxos()?; let populated_transaction = PopulatedTransaction::new(&cctx, utxos); for i in 0..input_len { let script_pub_key = match tx.inner().inputs[i].script_public_key() { diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index d03e6a5128..0c1c8fa6a7 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -359,18 +359,18 @@ impl Transaction { }) } - pub fn tx_and_utxos(&self) -> (cctx::Transaction, Vec) { - let mut utxos = vec![]; + pub fn tx_and_utxos(&self) -> Result<(cctx::Transaction, Vec)> { + let mut inputs = vec![]; let inner = self.inner(); - let inputs: Vec = inner + let utxos: Vec = inner .inputs .clone() .into_iter() .map(|input| { - utxos.push((&input.get_utxo().unwrap().entry()).into()); - input.as_ref().into() + inputs.push(input.as_ref().into()); + Ok(input.get_utxo().ok_or(Error::MissingUtxoEntry)?.entry().as_ref().into()) }) - .collect::>(); + .collect::>>()?; let outputs: Vec = inner.outputs.clone().into_iter().map(|output| output.as_ref().into()).collect::>(); let tx = cctx::Transaction::new( @@ -383,7 +383,37 @@ impl Transaction { inner.payload.clone(), ); - (tx, utxos) + Ok((tx, utxos)) + } + + pub fn utxo_entry_references(&self) -> Result> { + let inner = self.inner(); + let utxo_entry_references = inner + .inputs + .clone() + .into_iter() + .map(|input| input.get_utxo().ok_or(Error::MissingUtxoEntry)) + .collect::>>()?; + Ok(utxo_entry_references) + } + + pub fn outputs(&self) -> Vec { + let inner = self.inner(); + let outputs = inner.outputs.iter().map(|output| output.into()).collect::>(); + outputs + } + + pub fn inputs(&self) -> Vec { + let inner = self.inner(); + let inputs = inner.inputs.iter().map(Into::into).collect::>(); + inputs + } + + pub fn inputs_outputs(&self) -> (Vec, Vec) { + let inner = self.inner(); + let inputs = inner.inputs.iter().map(Into::into).collect::>(); + let outputs = inner.outputs.iter().map(Into::into).collect::>(); + (inputs, outputs) } pub fn set_signature_script(&self, input_index: usize, signature_script: Vec) -> Result<()> { @@ -393,6 +423,14 @@ impl Transaction { self.inner().inputs[input_index].set_signature_script(signature_script); Ok(()) } + + pub fn payload(&self) -> Vec { + self.inner().payload.clone() + } + + pub fn payload_len(&self) -> usize { + self.inner().payload.len() + } } #[wasm_bindgen] diff --git a/consensus/client/src/utxo.rs b/consensus/client/src/utxo.rs index 8c248a6d35..afcf87c4b1 100644 --- a/consensus/client/src/utxo.rs +++ b/consensus/client/src/utxo.rs @@ -101,6 +101,12 @@ impl UtxoEntry { } } +impl AsRef for UtxoEntry { + fn as_ref(&self) -> &UtxoEntry { + self + } +} + impl From<&UtxoEntry> for cctx::UtxoEntry { fn from(utxo: &UtxoEntry) -> Self { cctx::UtxoEntry { diff --git a/consensus/core/src/mass/mod.rs b/consensus/core/src/mass/mod.rs index 6e348299c2..f58d104666 100644 --- a/consensus/core/src/mass/mod.rs +++ b/consensus/core/src/mass/mod.rs @@ -4,6 +4,17 @@ use crate::{ }; use kaspa_hashes::HASH_SIZE; +/// Temp enum for the transition phases of KIP9 +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Kip9Version { + /// Initial KIP9 mass calculation, w/o the relaxed formula and summing storage mass and compute mass + Alpha, + + /// Currently proposed KIP9 mass calculation, with the relaxed formula (for the cases `|O| = 1 OR |O| <= |I| <= 2`), + /// and using a maximum operator over storage and compute mass + Beta, +} + // transaction_estimated_serialized_size is the estimated size of a transaction in some // serialization. This has to be deterministic, but not necessarily accurate, since // it's only used as the size component in the transaction and block mass limit diff --git a/consensus/src/processes/mass.rs b/consensus/src/processes/mass.rs index 8bb5f3339f..e6198d3462 100644 --- a/consensus/src/processes/mass.rs +++ b/consensus/src/processes/mass.rs @@ -1,19 +1,9 @@ +pub use kaspa_consensus_core::mass::Kip9Version; use kaspa_consensus_core::{ mass::transaction_estimated_serialized_size, tx::{Transaction, VerifiableTransaction}, }; -/// Temp enum for the transition phases of KIP9 -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum Kip9Version { - /// Initial KIP9 mass calculation, w/o the relaxed formula and summing storage mass and compute mass - Alpha, - - /// Currently proposed KIP9 mass calculation, with the relaxed formula (for the cases `|O| = 1 OR |O| <= |I| <= 2`), - /// and using a maximum operator over storage and compute mass - Beta, -} - // TODO (aspect) - review and potentially merge this with the new MassCalculator currently located in the wallet core // (i.e. migrate mass calculator from wallet core here or to consensus core) #[derive(Clone)] diff --git a/wallet/core/Cargo.toml b/wallet/core/Cargo.toml index 1e528e3692..9a6c4cfa6a 100644 --- a/wallet/core/Cargo.toml +++ b/wallet/core/Cargo.toml @@ -74,7 +74,6 @@ kaspa-wallet-macros.workspace = true kaspa-wasm-core.workspace = true kaspa-wrpc-client.workspace = true kaspa-wrpc-wasm.workspace = true -lazy_static.workspace = true md-5.workspace = true pad.workspace = true pbkdf2.workspace = true diff --git a/wallet/core/src/imports.rs b/wallet/core/src/imports.rs index 2d2ce79fda..5ad48498c2 100644 --- a/wallet/core/src/imports.rs +++ b/wallet/core/src/imports.rs @@ -17,7 +17,6 @@ pub use crate::rpc::Rpc; pub use crate::rpc::{DynRpcApi, RpcCtl}; pub use crate::serializer::*; pub use crate::storage::*; -pub use crate::tx::MassCombinationStrategy; pub use crate::utxo::balance::Balance; pub use crate::utxo::scan::{Scan, ScanExtent}; pub use crate::utxo::{Maturity, NetworkParams, OutgoingTransaction, UtxoContext, UtxoEntryReference, UtxoProcessor}; @@ -49,7 +48,7 @@ pub use std::collections::{HashMap, HashSet}; pub use std::pin::Pin; pub use std::str::FromStr; pub use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; -pub use std::sync::{Arc, Mutex, MutexGuard, RwLock}; +pub use std::sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock}; pub use std::task::{Context, Poll}; pub use wasm_bindgen::prelude::*; pub use workflow_core::prelude::*; diff --git a/wallet/core/src/tx/generator/generator.rs b/wallet/core/src/tx/generator/generator.rs index 5ef2869f7b..359ee634a4 100644 --- a/wallet/core/src/tx/generator/generator.rs +++ b/wallet/core/src/tx/generator/generator.rs @@ -66,6 +66,7 @@ use crate::tx::{ use crate::utxo::{NetworkParams, UtxoContext, UtxoEntryReference}; use kaspa_consensus_client::UtxoEntry; use kaspa_consensus_core::constants::UNACCEPTED_DAA_SCORE; +use kaspa_consensus_core::mass::Kip9Version; use kaspa_consensus_core::subnets::SUBNETWORK_ID_NATIVE; use kaspa_consensus_core::tx::{Transaction, TransactionInput, TransactionOutpoint, TransactionOutput}; use kaspa_txscript::pay_to_address_script; @@ -215,7 +216,7 @@ struct Data { impl Data { fn new(calc: &MassCalculator) -> Self { - let aggregate_mass = calc.blank_transaction_mass(); + let aggregate_mass = calc.blank_transaction_compute_mass(); Data { inputs: vec![], @@ -266,7 +267,7 @@ struct Inner { // Current network id network_id: NetworkId, // Current network params - network_params: NetworkParams, + network_params: &'static NetworkParams, // Source Utxo Context (Used for source UtxoEntry aggregation) source_utxo_context: Option, @@ -357,7 +358,7 @@ impl Generator { let network_type = NetworkType::from(network_id); let network_params = NetworkParams::from(network_id); - let mass_calculator = MassCalculator::new(&network_id.into(), &network_params); + let mass_calculator = MassCalculator::new(&network_id.into(), network_params); let (final_transaction_outputs, final_transaction_amount) = match final_transaction_destination { PaymentDestination::Change => { @@ -402,11 +403,11 @@ impl Generator { } let standard_change_output_mass = - mass_calculator.calc_mass_for_output(&TransactionOutput::new(0, pay_to_address_script(&change_address))); - let signature_mass_per_input = mass_calculator.calc_signature_mass(minimum_signatures); - let final_transaction_outputs_compute_mass = mass_calculator.calc_mass_for_outputs(&final_transaction_outputs); + mass_calculator.calc_compute_mass_for_output(&TransactionOutput::new(0, pay_to_address_script(&change_address))); + let signature_mass_per_input = mass_calculator.calc_compute_mass_for_signature(minimum_signatures); + let final_transaction_outputs_compute_mass = mass_calculator.calc_compute_mass_for_outputs(&final_transaction_outputs); let final_transaction_payload = final_transaction_payload.unwrap_or_default(); - let final_transaction_payload_mass = mass_calculator.calc_mass_for_payload(final_transaction_payload.len()); + let final_transaction_payload_mass = mass_calculator.calc_compute_mass_for_payload(final_transaction_payload.len()); let final_transaction_outputs_harmonic = mass_calculator.calc_storage_mass_output_harmonic(&final_transaction_outputs).ok_or(Error::MassCalculationError)?; @@ -477,7 +478,7 @@ impl Generator { /// Returns current [`NetworkParams`] pub fn network_params(&self) -> &NetworkParams { - &self.inner.network_params + self.inner.network_params } /// The underlying [`UtxoContext`] (if available). @@ -662,7 +663,7 @@ impl Generator { let input = TransactionInput::new(utxo.outpoint.clone().into(), vec![], 0, self.inner.sig_op_count); let input_amount = utxo.amount(); - let input_compute_mass = calc.calc_mass_for_input(&input) + self.inner.signature_mass_per_input; + let input_compute_mass = calc.calc_compute_mass_for_input(&input) + self.inner.signature_mass_per_input; // NOTE: relay transactions have no storage mass // mass threshold reached, yield transaction @@ -865,8 +866,11 @@ impl Generator { calc.calc_storage_mass_output_harmonic_single(change_value) + self.inner.final_transaction_outputs_harmonic; let storage_mass_with_change = self.calc_storage_mass(data, output_harmonic_with_change); + // TODO - review and potentially simplify: + // this profiles the storage mass with change and without change + // and decides which one to use based on the fees if storage_mass_with_change == 0 - || (self.inner.network_params.mass_combination_strategy() == MassCombinationStrategy::Max + || (self.inner.network_params.kip9_version() == Kip9Version::Beta // max(compute vs storage) && storage_mass_with_change < compute_mass_with_change) { 0 diff --git a/wallet/core/src/tx/generator/test.rs b/wallet/core/src/tx/generator/test.rs index d7f5e7f839..045128a7b6 100644 --- a/wallet/core/src/tx/generator/test.rs +++ b/wallet/core/src/tx/generator/test.rs @@ -170,10 +170,10 @@ fn validate(pt: &PendingTransaction) { let calc = MassCalculator::new(&pt.network_type().into(), network_params); let additional_mass = if pt.is_final() { 0 } else { network_params.additional_compound_transaction_mass() }; - let compute_mass = calc.calc_mass_for_signed_transaction(&tx, 1); + let compute_mass = calc.calc_compute_mass_for_signed_transaction(&tx, 1); let utxo_entries = pt.utxo_entries().values().cloned().collect::>(); - let storage_mass = calc.calc_storage_mass_for_transaction(false, &utxo_entries, &tx.outputs).unwrap_or_default(); + let storage_mass = calc.calc_storage_mass_for_transaction_parts(&utxo_entries, &tx.outputs).unwrap_or_default(); let calculated_mass = calc.combine_mass(compute_mass, storage_mass) + additional_mass; @@ -201,10 +201,10 @@ where let calc = MassCalculator::new(&pt.network_type().into(), network_params); let additional_mass = if pt.is_final() { 0 } else { network_params.additional_compound_transaction_mass() }; - let compute_mass = calc.calc_mass_for_signed_transaction(&tx, 1); + let compute_mass = calc.calc_compute_mass_for_signed_transaction(&tx, 1); let utxo_entries = pt.utxo_entries().values().cloned().collect::>(); - let storage_mass = calc.calc_storage_mass_for_transaction(false, &utxo_entries, &tx.outputs).unwrap_or_default(); + let storage_mass = calc.calc_storage_mass_for_transaction_parts(&utxo_entries, &tx.outputs).unwrap_or_default(); if DISPLAY_LOGS && storage_mass != 0 { println!( "calculated storage mass: {} calculated_compute_mass: {} total: {}", diff --git a/wallet/core/src/tx/mass.rs b/wallet/core/src/tx/mass.rs index c76d109050..09545bf13d 100644 --- a/wallet/core/src/tx/mass.rs +++ b/wallet/core/src/tx/mass.rs @@ -2,20 +2,15 @@ //! Transaction mass calculator. //! +use crate::result::Result; use crate::utxo::NetworkParams; +use kaspa_consensus_client as kcc; use kaspa_consensus_client::UtxoEntryReference; +use kaspa_consensus_core::mass::Kip9Version; use kaspa_consensus_core::tx::{Transaction, TransactionInput, TransactionOutput, SCRIPT_VECTOR_SIZE}; use kaspa_consensus_core::{config::params::Params, constants::*, subnets::SUBNETWORK_ID_SIZE}; use kaspa_hashes::HASH_SIZE; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum MassCombinationStrategy { - /// `MassCombinator::Add` adds the storage and compute mass. - Add, - /// `MassCombinator::Max` returns the maximum of the storage and compute mass. - Max, -} - // pub const ECDSA_SIGNATURE_SIZE: u64 = 64; // pub const SCHNORR_SIGNATURE_SIZE: u64 = 64; pub const SIGNATURE_SIZE: u64 = 1 + 64 + 1; //1 byte for OP_DATA_65 + 64 (length of signature) + 1 byte for sig hash type @@ -222,7 +217,7 @@ pub struct MassCalculator { mass_per_script_pub_key_byte: u64, mass_per_sig_op: u64, storage_mass_parameter: u64, - mass_combination_strategy: MassCombinationStrategy, + kip9_version: Kip9Version, } impl MassCalculator { @@ -232,7 +227,7 @@ impl MassCalculator { mass_per_script_pub_key_byte: consensus_params.mass_per_script_pub_key_byte, mass_per_sig_op: consensus_params.mass_per_sig_op, storage_mass_parameter: consensus_params.storage_mass_parameter, - mass_combination_strategy: network_params.mass_combination_strategy(), + kip9_version: network_params.kip9_version(), } } @@ -243,44 +238,45 @@ impl MassCalculator { } } - pub fn calc_mass_for_transaction(&self, tx: &Transaction) -> u64 { - self.blank_transaction_mass() - + self.calc_mass_for_payload(tx.payload.len()) - + self.calc_mass_for_outputs(&tx.outputs) - + self.calc_mass_for_inputs(&tx.inputs) + pub fn calc_transaction_compute_mass(&self, tx: &Transaction) -> u64 { + let payload_len = tx.payload.len(); + self.blank_transaction_compute_mass() + + self.calc_compute_mass_for_payload(payload_len) + + self.calc_compute_mass_for_outputs(&tx.outputs) + + self.calc_compute_mass_for_inputs(&tx.inputs) } - pub fn blank_transaction_mass(&self) -> u64 { + pub(crate) fn blank_transaction_compute_mass(&self) -> u64 { blank_transaction_serialized_byte_size() * self.mass_per_tx_byte } - pub fn calc_mass_for_payload(&self, payload_byte_size: usize) -> u64 { + pub(crate) fn calc_compute_mass_for_payload(&self, payload_byte_size: usize) -> u64 { payload_byte_size as u64 * self.mass_per_tx_byte } - pub fn calc_mass_for_outputs(&self, outputs: &[TransactionOutput]) -> u64 { - outputs.iter().map(|output| self.calc_mass_for_output(output)).sum() + pub(crate) fn calc_compute_mass_for_outputs(&self, outputs: &[TransactionOutput]) -> u64 { + outputs.iter().map(|output| self.calc_compute_mass_for_output(output)).sum() } - pub fn calc_mass_for_inputs(&self, inputs: &[TransactionInput]) -> u64 { - inputs.iter().map(|input| self.calc_mass_for_input(input)).sum::() + pub(crate) fn calc_compute_mass_for_inputs(&self, inputs: &[TransactionInput]) -> u64 { + inputs.iter().map(|input| self.calc_compute_mass_for_input(input)).sum::() } - pub fn calc_mass_for_output(&self, output: &TransactionOutput) -> u64 { + pub(crate) fn calc_compute_mass_for_output(&self, output: &TransactionOutput) -> u64 { self.mass_per_script_pub_key_byte * (2 + output.script_public_key.script().len() as u64) + transaction_output_serialized_byte_size(output) * self.mass_per_tx_byte } - pub fn calc_mass_for_input(&self, input: &TransactionInput) -> u64 { + pub(crate) fn calc_compute_mass_for_input(&self, input: &TransactionInput) -> u64 { input.sig_op_count as u64 * self.mass_per_sig_op + transaction_input_serialized_byte_size(input) * self.mass_per_tx_byte } - pub fn calc_signature_mass(&self, minimum_signatures: u16) -> u64 { + pub(crate) fn calc_compute_mass_for_signature(&self, minimum_signatures: u16) -> u64 { let minimum_signatures = std::cmp::max(1, minimum_signatures); SIGNATURE_SIZE * self.mass_per_tx_byte * minimum_signatures as u64 } - pub fn calc_signature_mass_for_inputs(&self, number_of_inputs: usize, minimum_signatures: u16) -> u64 { + pub fn calc_signature_compute_mass_for_inputs(&self, number_of_inputs: usize, minimum_signatures: u16) -> u64 { let minimum_signatures = std::cmp::max(1, minimum_signatures); SIGNATURE_SIZE * self.mass_per_tx_byte * minimum_signatures as u64 * number_of_inputs as u64 } @@ -289,48 +285,66 @@ impl MassCalculator { calc_minimum_required_transaction_relay_fee(mass) } - pub fn calc_mass_for_signed_transaction(&self, tx: &Transaction, minimum_signatures: u16) -> u64 { - self.calc_mass_for_transaction(tx) + self.calc_signature_mass_for_inputs(tx.inputs.len(), minimum_signatures) + pub fn calc_compute_mass_for_signed_transaction(&self, tx: &Transaction, minimum_signatures: u16) -> u64 { + self.calc_transaction_compute_mass(tx) + self.calc_signature_compute_mass_for_inputs(tx.inputs.len(), minimum_signatures) } - pub fn calc_minium_transaction_relay_fee(&self, tx: &Transaction, minimum_signatures: u16) -> u64 { - let mass = self.calc_mass_for_transaction(tx) + self.calc_signature_mass_for_inputs(tx.inputs.len(), minimum_signatures); - calc_minimum_required_transaction_relay_fee(mass) + pub fn calc_transaction_storage_fee(&self, inputs: &[UtxoEntryReference], outputs: &[TransactionOutput]) -> u64 { + self.calc_fee_for_storage_mass(self.calc_storage_mass_for_transaction_parts(inputs, outputs).unwrap_or(u64::MAX)) } - pub fn calc_tx_storage_fee(&self, is_coinbase: bool, inputs: &[UtxoEntryReference], outputs: &[TransactionOutput]) -> u64 { - self.calc_fee_for_storage_mass(self.calc_storage_mass_for_transaction(is_coinbase, inputs, outputs).unwrap_or(u64::MAX)) + // provisional + pub fn calc_fee_for_storage_mass(&self, mass: u64) -> u64 { + mass } - pub fn calc_fee_for_storage_mass(&self, mass: u64) -> u64 { + // provisional + pub fn calc_fee_for_mass(&self, mass: u64) -> u64 { mass } pub fn combine_mass(&self, compute_mass: u64, storage_mass: u64) -> u64 { - match self.mass_combination_strategy { - MassCombinationStrategy::Add => compute_mass + storage_mass, - MassCombinationStrategy::Max => std::cmp::max(compute_mass, storage_mass), + match self.kip9_version { + Kip9Version::Alpha => compute_mass + storage_mass, + Kip9Version::Beta => std::cmp::max(compute_mass, storage_mass), } } - pub fn calc_storage_mass_for_transaction( + /// Calculates the overall mass of this transaction, combining both compute and storage masses. + pub fn calc_tx_overall_mass(&self, tx: &kcc::Transaction) -> Result> { + let cctx = Transaction::from(tx); + let mass = match self.kip9_version { + Kip9Version::Alpha => self + .calc_storage_mass_for_transaction(tx)? + .and_then(|mass| mass.checked_add(self.calc_transaction_compute_mass(&cctx))), + Kip9Version::Beta => { + self.calc_storage_mass_for_transaction(tx)?.map(|mass| mass.max(self.calc_transaction_compute_mass(&cctx))) + } + }; + + Ok(mass) + } + + pub fn calc_storage_mass_for_transaction(&self, tx: &kcc::Transaction) -> Result> { + let utxos = tx.utxo_entry_references()?; + let outputs = tx.outputs(); + Ok(self.calc_storage_mass_for_transaction_parts(&utxos, &outputs)) + } + + pub fn calc_storage_mass_for_transaction_parts( &self, - is_coinbase: bool, inputs: &[UtxoEntryReference], outputs: &[TransactionOutput], ) -> Option { - if is_coinbase { - return Some(0); - } /* The code below computes the following formula: - max( 0 , C·( |O|/H(O) - |I|/A(I) ) ) + max( 0 , C·( |O|/H(O) - |I|/A(I) ) ) where C is the mass storage parameter, O is the set of output values, I is the set of input values, H(S) := |S|/sum_{s in S} 1 / s is the harmonic mean over the set S and A(S) := sum_{s in S} / |S| is the arithmetic mean. - See the (to date unpublished) KIP-0009 for more details + See KIP-0009 for more details */ // Since we are doing integer division, we perform the multiplication with C over the inner @@ -338,15 +352,36 @@ impl MassCalculator { // // If sum of fractions overflowed (nearly impossible, requires 10^7 outputs for C = 10^12), // we return `None` indicating mass is incomputable + // + // Note: in theory this can be tighten by subtracting input mass in the process (possibly avoiding the overflow), + // however the overflow case is so unpractical with current mass limits so we avoid the hassle let harmonic_outs = outputs .iter() .map(|out| self.storage_mass_parameter / out.value) .try_fold(0u64, |total, current| total.checked_add(current))?; // C·|O|/H(O) + let outs_len = outputs.len() as u64; + let ins_len = inputs.len() as u64; + + /* + KIP-0009 relaxed formula for the cases |O| = 1 OR |O| <= |I| <= 2: + max( 0 , C·( |O|/H(O) - |I|/H(I) ) ) + + Note: in the case |I| = 1 both formulas are equal, yet the following code (harmonic_ins) is a bit more efficient. + Hence, we transform the condition to |O| = 1 OR |I| = 1 OR |O| = |I| = 2 which is equivalent (and faster). + */ + + if self.kip9_version == Kip9Version::Beta && (outs_len == 1 || ins_len == 1 || (outs_len == 2 && ins_len == 2)) { + let harmonic_ins = inputs + .iter() + .map(|entry| self.storage_mass_parameter / entry.amount()) + .fold(0u64, |total, current| total.saturating_add(current)); // C·|I|/H(I) + return Some(harmonic_outs.saturating_sub(harmonic_ins)); // max( 0 , C·( |O|/H(O) - |I|/H(I) ) ); + } + // Total supply is bounded, so a sum of existing UTXO entries cannot overflow (nor can it be zero) let sum_ins = inputs.iter().map(|entry| entry.amount()).sum::(); // |I|·A(I) - let ins_len = inputs.len() as u64; let mean_ins = sum_ins / ins_len; // Inner fraction must be with C and over the mean value, in order to maximize precision. diff --git a/wallet/core/src/utxo/context.rs b/wallet/core/src/utxo/context.rs index c65ea252b1..e8d1ff39be 100644 --- a/wallet/core/src/utxo/context.rs +++ b/wallet/core/src/utxo/context.rs @@ -299,7 +299,7 @@ impl UtxoContext { context.mature.sorted_insert_binary_asc_by_key(utxo_entry.clone(), |entry| entry.amount_as_ref()); } else { let params = NetworkParams::from(self.processor().network_id()?); - match utxo_entry.maturity(¶ms, current_daa_score) { + match utxo_entry.maturity(params, current_daa_score) { Maturity::Stasis => { context.stasis.insert(utxo_entry.id().clone(), utxo_entry.clone()); self.processor() @@ -428,7 +428,7 @@ impl UtxoContext { for utxo_entry in utxo_entries.into_iter() { if let std::collections::hash_map::Entry::Vacant(e) = context.map.entry(utxo_entry.id()) { e.insert(utxo_entry.clone()); - match utxo_entry.maturity(¶ms, current_daa_score) { + match utxo_entry.maturity(params, current_daa_score) { Maturity::Stasis => { context.stasis.insert(utxo_entry.id().clone(), utxo_entry.clone()); self.processor() @@ -531,7 +531,7 @@ impl UtxoContext { let force_maturity_if_outgoing = outgoing_transaction.is_some(); let is_coinbase_stasis = - utxos.first().map(|utxo| matches!(utxo.maturity(¶ms, current_daa_score), Maturity::Stasis)).unwrap_or_default(); + utxos.first().map(|utxo| matches!(utxo.maturity(params, current_daa_score), Maturity::Stasis)).unwrap_or_default(); let is_batch = outgoing_transaction.as_ref().map_or_else(|| false, |tx| tx.is_batch()); if !is_batch { for utxo in utxos.iter() { diff --git a/wallet/core/src/utxo/processor.rs b/wallet/core/src/utxo/processor.rs index 28405887ad..4195a00b5f 100644 --- a/wallet/core/src/utxo/processor.rs +++ b/wallet/core/src/utxo/processor.rs @@ -181,10 +181,11 @@ impl UtxoProcessor { (*self.inner.network_id.lock().unwrap()).ok_or(Error::MissingNetworkId) } - // pub fn network_params(&self) -> Result<&'static NetworkParams> { - pub fn network_params(&self) -> Result { + pub fn network_params(&self) -> Result<&'static NetworkParams> { + // pub fn network_params(&self) -> Result { let network_id = (*self.inner.network_id.lock().unwrap()).ok_or(Error::MissingNetworkId)?; - Ok(network_id.into()) + Ok(NetworkParams::from(network_id)) + // Ok(network_id.into()) } pub fn pending(&self) -> &DashMap { @@ -275,7 +276,7 @@ impl UtxoProcessor { // scan and remove any pending entries that gained maturity let mut mature_entries = vec![]; let pending_entries = &self.inner.pending; - pending_entries.retain(|_, pending_entry| match pending_entry.maturity(¶ms, current_daa_score) { + pending_entries.retain(|_, pending_entry| match pending_entry.maturity(params, current_daa_score) { Maturity::Confirmed => { mature_entries.push(pending_entry.clone()); false @@ -288,7 +289,7 @@ impl UtxoProcessor { let mut revived_entries = vec![]; let stasis_entries = &self.inner.stasis; stasis_entries.retain(|_, stasis_entry| { - match stasis_entry.maturity(¶ms, current_daa_score) { + match stasis_entry.maturity(params, current_daa_score) { Maturity::Confirmed => { mature_entries.push(stasis_entry.clone()); false diff --git a/wallet/core/src/utxo/scan.rs b/wallet/core/src/utxo/scan.rs index fda8ea67cf..f01257c965 100644 --- a/wallet/core/src/utxo/scan.rs +++ b/wallet/core/src/utxo/scan.rs @@ -107,7 +107,7 @@ impl Scan { } let balance: Balance = refs.iter().fold(Balance::default(), |mut balance, r| { - let entry_balance = r.balance(¶ms, self.current_daa_score); + let entry_balance = r.balance(params, self.current_daa_score); balance.mature += entry_balance.mature; balance.pending += entry_balance.pending; balance.mature_utxo_count += entry_balance.mature_utxo_count; @@ -151,7 +151,7 @@ impl Scan { let refs: Vec = resp.into_iter().map(UtxoEntryReference::from).collect(); let balance: Balance = refs.iter().fold(Balance::default(), |mut balance, r| { - let entry_balance = r.balance(¶ms, self.current_daa_score); + let entry_balance = r.balance(params, self.current_daa_score); balance.mature += entry_balance.mature; balance.pending += entry_balance.pending; balance.mature_utxo_count += entry_balance.mature_utxo_count; diff --git a/wallet/core/src/utxo/settings.rs b/wallet/core/src/utxo/settings.rs index caa8032eb4..69e79a05d2 100644 --- a/wallet/core/src/utxo/settings.rs +++ b/wallet/core/src/utxo/settings.rs @@ -4,128 +4,124 @@ //! use crate::imports::*; +use kaspa_consensus_core::mass::Kip9Version; #[derive(Debug)] -pub struct Inner { +pub struct NetworkParams { pub coinbase_transaction_maturity_period_daa: AtomicU64, pub coinbase_transaction_stasis_period_daa: u64, pub user_transaction_maturity_period_daa: AtomicU64, - pub mass_combination_strategy: MassCombinationStrategy, + pub kip9_version: Kip9Version, pub additional_compound_transaction_mass: u64, } -#[derive(Debug, Clone)] -pub struct NetworkParams { - inner: Arc, -} - impl NetworkParams { #[inline] pub fn coinbase_transaction_maturity_period_daa(&self) -> u64 { - self.inner.coinbase_transaction_maturity_period_daa.load(Ordering::Relaxed) + self.coinbase_transaction_maturity_period_daa.load(Ordering::Relaxed) } #[inline] pub fn coinbase_transaction_stasis_period_daa(&self) -> u64 { - self.inner.coinbase_transaction_stasis_period_daa + self.coinbase_transaction_stasis_period_daa } #[inline] pub fn user_transaction_maturity_period_daa(&self) -> u64 { - self.inner.user_transaction_maturity_period_daa.load(Ordering::Relaxed) + self.user_transaction_maturity_period_daa.load(Ordering::Relaxed) } #[inline] - pub fn mass_combination_strategy(&self) -> MassCombinationStrategy { - self.inner.mass_combination_strategy + pub fn kip9_version(&self) -> Kip9Version { + self.kip9_version } #[inline] pub fn additional_compound_transaction_mass(&self) -> u64 { - self.inner.additional_compound_transaction_mass + self.additional_compound_transaction_mass } pub fn set_coinbase_transaction_maturity_period_daa(&self, value: u64) { - self.inner.coinbase_transaction_maturity_period_daa.store(value, Ordering::Relaxed); + self.coinbase_transaction_maturity_period_daa.store(value, Ordering::Relaxed); } pub fn set_user_transaction_maturity_period_daa(&self, value: u64) { - self.inner.user_transaction_maturity_period_daa.store(value, Ordering::Relaxed); + self.user_transaction_maturity_period_daa.store(value, Ordering::Relaxed); } } -lazy_static::lazy_static! { - pub static ref MAINNET_NETWORK_PARAMS: NetworkParams = NetworkParams { - inner: Arc::new(Inner { - coinbase_transaction_maturity_period_daa: AtomicU64::new(100), - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: AtomicU64::new(10), - mass_combination_strategy: MassCombinationStrategy::Max, - additional_compound_transaction_mass: 0, - }), - }; +static MAINNET_NETWORK_PARAMS: OnceLock = OnceLock::new(); + +pub fn mainnet_network_params() -> &'static NetworkParams { + MAINNET_NETWORK_PARAMS.get_or_init(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + kip9_version: Kip9Version::Beta, + additional_compound_transaction_mass: 100, + }) } -lazy_static::lazy_static! { - pub static ref TESTNET10_NETWORK_PARAMS: NetworkParams = NetworkParams { - inner: Arc::new(Inner { - coinbase_transaction_maturity_period_daa: AtomicU64::new(100), - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: AtomicU64::new(10), - mass_combination_strategy: MassCombinationStrategy::Max, - additional_compound_transaction_mass: 0, - }), - }; +static TESTNET10_NETWORK_PARAMS: OnceLock = OnceLock::new(); + +pub fn testnet10_network_params() -> &'static NetworkParams { + TESTNET10_NETWORK_PARAMS.get_or_init(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + kip9_version: Kip9Version::Beta, + additional_compound_transaction_mass: 100, + }) } -lazy_static::lazy_static! { - pub static ref TESTNET11_NETWORK_PARAMS: NetworkParams = NetworkParams { - inner: Arc::new(Inner { - coinbase_transaction_maturity_period_daa: AtomicU64::new(1_000), - coinbase_transaction_stasis_period_daa: 500, - user_transaction_maturity_period_daa: AtomicU64::new(100), - mass_combination_strategy: MassCombinationStrategy::Max, - additional_compound_transaction_mass: 100, - }), - }; +static TESTNET11_NETWORK_PARAMS: OnceLock = OnceLock::new(); + +pub fn testnet11_network_params() -> &'static NetworkParams { + TESTNET11_NETWORK_PARAMS.get_or_init(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(1_000), + coinbase_transaction_stasis_period_daa: 500, + user_transaction_maturity_period_daa: AtomicU64::new(100), + kip9_version: Kip9Version::Alpha, + additional_compound_transaction_mass: 100, + }) } -lazy_static::lazy_static! { - pub static ref SIMNET_NETWORK_PARAMS: NetworkParams = NetworkParams { - inner: Arc::new(Inner { - coinbase_transaction_maturity_period_daa: AtomicU64::new(100), - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: AtomicU64::new(10), - mass_combination_strategy: MassCombinationStrategy::Max, - additional_compound_transaction_mass: 0, - }), - }; +static SIMNET_NETWORK_PARAMS: OnceLock = OnceLock::new(); + +pub fn simnet_network_params() -> &'static NetworkParams { + SIMNET_NETWORK_PARAMS.get_or_init(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + kip9_version: Kip9Version::Alpha, + additional_compound_transaction_mass: 0, + }) } -lazy_static::lazy_static! { - pub static ref DEVNET_NETWORK_PARAMS: NetworkParams = NetworkParams { - inner: Arc::new(Inner { - coinbase_transaction_maturity_period_daa: AtomicU64::new(100), - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: AtomicU64::new(10), - mass_combination_strategy: MassCombinationStrategy::Max, - additional_compound_transaction_mass: 0, - }), - }; +static DEVNET_NETWORK_PARAMS: OnceLock = OnceLock::new(); + +pub fn devnet_network_params() -> &'static NetworkParams { + DEVNET_NETWORK_PARAMS.get_or_init(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + kip9_version: Kip9Version::Beta, + additional_compound_transaction_mass: 0, + }) } -impl From for NetworkParams { - fn from(value: NetworkId) -> Self { +impl NetworkParams { + pub fn from(value: NetworkId) -> &'static NetworkParams { match value.network_type { - NetworkType::Mainnet => MAINNET_NETWORK_PARAMS.clone(), + NetworkType::Mainnet => mainnet_network_params(), NetworkType::Testnet => match value.suffix { - Some(10) => TESTNET10_NETWORK_PARAMS.clone(), - Some(11) => TESTNET11_NETWORK_PARAMS.clone(), + Some(10) => testnet10_network_params(), + Some(11) => testnet11_network_params(), Some(x) => panic!("Testnet suffix {} is not supported", x), None => panic!("Testnet suffix not provided"), }, - NetworkType::Devnet => DEVNET_NETWORK_PARAMS.clone(), - NetworkType::Simnet => SIMNET_NETWORK_PARAMS.clone(), + NetworkType::Devnet => devnet_network_params(), + NetworkType::Simnet => simnet_network_params(), } } } diff --git a/wallet/core/src/wasm/signer.rs b/wallet/core/src/wasm/signer.rs index 106988a3cf..9e0ce26b2e 100644 --- a/wallet/core/src/wasm/signer.rs +++ b/wallet/core/src/wasm/signer.rs @@ -53,7 +53,7 @@ pub fn js_sign_transaction(tx: Transaction, signer: PrivateKeyArrayT, verify_sig pub fn sign_transaction(tx: Transaction, private_keys: &[[u8; 32]], verify_sig: bool) -> Result { let tx = sign(tx, private_keys)?; if verify_sig { - let (cctx, utxos) = tx.tx_and_utxos(); + let (cctx, utxos) = tx.tx_and_utxos()?; let populated_transaction = PopulatedTransaction::new(&cctx, utxos); verify(&populated_transaction)?; } @@ -76,7 +76,7 @@ pub fn create_input_signature( private_key: &PrivateKey, sighash_type: Option, ) -> Result { - let (cctx, _) = tx.tx_and_utxos(); + let (cctx, _) = tx.tx_and_utxos()?; let mutable_tx = SignableTransaction::new(cctx); let signature = diff --git a/wallet/core/src/wasm/tx/consensus.rs b/wallet/core/src/wasm/tx/consensus.rs deleted file mode 100644 index b1e722a61c..0000000000 --- a/wallet/core/src/wasm/tx/consensus.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::tx::consensus as core; -use kaspa_addresses::Address; -use kaspa_consensus_core::{config::params::Params, network::NetworkType}; -use wasm_bindgen::prelude::*; - -/// -/// `ConsensusParams` can be obtained using `getConsensusParametersByNetwork` or `getConsensusParametersByAddress`. -/// -/// @see {@link getConsensusParametersByNetwork} -/// @see {@link getConsensusParametersByAddress} -/// -/// @category Wallet SDK -/// -#[wasm_bindgen] -pub struct ConsensusParams { - params: Params, -} - -impl From for ConsensusParams { - fn from(params: Params) -> Self { - Self { params } - } -} - -impl From for Params { - fn from(cp: ConsensusParams) -> Self { - cp.params - } -} - -/// find Consensus parameters for given Address -/// @category Wallet SDK -#[wasm_bindgen(js_name = getConsensusParametersByAddress)] -pub fn get_consensus_params_by_address(address: &Address) -> ConsensusParams { - core::get_consensus_params_by_address(address).into() -} - -/// find Consensus parameters for given NetworkType -/// @category Wallet SDK -#[wasm_bindgen(js_name = getConsensusParametersByNetwork)] -pub fn get_consensus_params_by_network(network: NetworkType) -> ConsensusParams { - core::get_consensus_params_by_network(&network).into() -} diff --git a/wallet/core/src/wasm/tx/mass.rs b/wallet/core/src/wasm/tx/mass.rs index cc522fd8e0..5dd09051f7 100644 --- a/wallet/core/src/wasm/tx/mass.rs +++ b/wallet/core/src/wasm/tx/mass.rs @@ -1,151 +1,43 @@ use crate::imports::NetworkParams; use crate::result::Result; use crate::tx::mass; -use crate::wasm::tx::*; use kaspa_consensus_client::*; use kaspa_consensus_core::config::params::Params; -use kaspa_consensus_core::tx as cctx; -use std::sync::Arc; +use kaspa_consensus_core::network::{NetworkId, NetworkIdT}; use wasm_bindgen::prelude::*; use workflow_wasm::convert::*; +/// `calculateTransactionMass()` returns the mass of the passed transaction. +/// If the transaction is invalid, the function throws an error. +/// If the mass is larger than the transaction mass allowed by the network, the function +/// returns `undefined` which can be treated as a mass overflow condition. +/// /// @category Wallet SDK -#[wasm_bindgen] -pub struct MassCalculator { - mc: Arc, +/// +#[wasm_bindgen(js_name = calculateTransactionMass)] +pub fn calculate_transaction_mass(network_id: NetworkIdT, tx: &TransactionT) -> Result> { + let tx = Transaction::try_cast_from(tx)?; + let network_id = NetworkId::try_owned_from(network_id)?; + let consensus_params = Params::from(network_id); + let network_params = NetworkParams::from(network_id); + let mc = mass::MassCalculator::new(&consensus_params, network_params); + mc.calc_tx_overall_mass(tx.as_ref()) } -#[wasm_bindgen] -impl MassCalculator { - #[wasm_bindgen(constructor)] - pub fn new(cp: ConsensusParams) -> Self { - let consensus_params = Params::from(cp); - let network_params = NetworkParams::from(consensus_params.net); - Self { mc: Arc::new(mass::MassCalculator::new(&consensus_params, &network_params)) } - } - - #[wasm_bindgen(js_name=isDust)] - pub fn is_dust(&self, amount: u64) -> bool { - self.mc.is_dust(amount) - } - - /// `isTransactionOutputDust()` returns whether or not the passed transaction output - /// amount is considered dust or not based on the configured minimum transaction - /// relay fee. - /// - /// Dust is defined in terms of the minimum transaction relay fee. In particular, - /// if the cost to the network to spend coins is more than 1/3 of the minimum - /// transaction relay fee, it is considered dust. - /// - /// It is exposed by `MiningManager` for use by transaction generators and wallets. - #[wasm_bindgen(js_name=isTransactionOutputDust)] - pub fn is_transaction_output_dust(transaction_output: &JsValue) -> Result { - let transaction_output = TransactionOutput::try_from(transaction_output)?; - let transaction_output = cctx::TransactionOutput::from(&transaction_output); - Ok(mass::is_transaction_output_dust(&transaction_output)) - } - - /// `minimumRelayTransactionFee()` specifies the minimum transaction fee for a transaction to be accepted to - /// the mempool and relayed. It is specified in sompi per 1kg (or 1000 grams) of transaction mass. - /// - /// `pub(crate) const MINIMUM_RELAY_TRANSACTION_FEE: u64 = 1000;` - #[wasm_bindgen(js_name=minimumRelayTransactionFee)] - pub fn minimum_relay_transaction_fee() -> u32 { - mass::MINIMUM_RELAY_TRANSACTION_FEE as u32 - } - - /// `maximumStandardTransactionMass()` is the maximum mass allowed for transactions that - /// are considered standard and will therefore be relayed and considered for mining. - /// - /// `pub const MAXIMUM_STANDARD_TRANSACTION_MASS: u64 = 100_000;` - #[wasm_bindgen(js_name=maximumStandardTransactionMass)] - pub fn maximum_standard_transaction_mass() -> u32 { - mass::MAXIMUM_STANDARD_TRANSACTION_MASS as u32 - } - - /// minimum_required_transaction_relay_fee returns the minimum transaction fee required - /// for a transaction with the passed mass to be accepted into the mempool and relayed. - #[wasm_bindgen(js_name=minimumRequiredTransactionRelayFee)] - pub fn calc_minimum_required_transaction_relay_fee(mass: u32) -> u32 { - mass::calc_minimum_required_transaction_relay_fee(mass as u64) as u32 - } - - #[wasm_bindgen(js_name=calcMassForTransaction)] - pub fn calc_mass_for_transaction(&self, tx: &JsValue) -> Result { - let tx = Transaction::try_cast_from(tx)?; - let tx = cctx::Transaction::from(tx.as_ref()); - Ok(self.mc.calc_mass_for_transaction(&tx) as u32) - } - - #[wasm_bindgen(js_name=blankTransactionSerializedByteSize)] - pub fn blank_transaction_serialized_byte_size() -> u32 { - mass::blank_transaction_serialized_byte_size() as u32 - } - - #[wasm_bindgen(js_name=blankTransactionMass)] - pub fn blank_transaction_mass(&self) -> u32 { - self.mc.blank_transaction_mass() as u32 - } - - #[wasm_bindgen(js_name=calcMassForPayload)] - pub fn calc_mass_for_payload(&self, payload_byte_size: usize) -> u32 { - self.mc.calc_mass_for_payload(payload_byte_size) as u32 - } - - #[wasm_bindgen(js_name=calcMassForOutputs)] - pub fn calc_mass_for_outputs(&self, outputs: JsValue) -> Result { - let outputs = outputs - .dyn_into::()? - .iter() - .map(TransactionOutput::try_from) - .collect::, kaspa_consensus_client::error::Error>>()?; - let outputs = outputs.iter().map(|output| self.calc_mass_for_output(output)).collect::>>()?; - Ok(outputs.iter().sum()) - } - - #[wasm_bindgen(js_name=calcMassForInputs)] - pub fn calc_mass_for_inputs(&self, inputs: JsValue) -> Result { - let inputs = inputs - .dyn_into::()? - .iter() - .map(TransactionInput::try_owned_from) - .collect::, kaspa_consensus_client::error::Error>>()?; - let inputs = inputs.iter().map(|input| self.calc_mass_for_input(input)).collect::>>()?; - Ok(inputs.iter().sum()) - } - - #[wasm_bindgen(js_name=calcMassForOutput)] - pub fn calc_mass_for_output(&self, output: &TransactionOutput) -> Result { - // let output = TransactionOutput::try_from(output)?; - let output = cctx::TransactionOutput::from(output); - Ok(self.mc.calc_mass_for_output(&output) as u32) - } - - #[wasm_bindgen(js_name=calcMassForInput)] - pub fn calc_mass_for_input(&self, input: &TransactionInput) -> Result { - // let input = TransactionInput::try_from(input)?; - let input = cctx::TransactionInput::from(input); - Ok(self.mc.calc_mass_for_input(&input) as u32) - } - - #[wasm_bindgen(js_name=calcSignatureMass)] - pub fn calc_signature_mass(&self, minimum_signatures: u16) -> u32 { - self.mc.calc_signature_mass(minimum_signatures) as u32 - } - - #[wasm_bindgen(js_name=calcSignatureMassForInputs)] - pub fn calc_signature_mass_for_inputs(&self, number_of_inputs: usize, minimum_signatures: u16) -> u32 { - self.mc.calc_signature_mass_for_inputs(number_of_inputs, minimum_signatures) as u32 - } - - #[wasm_bindgen(js_name=calcMinimumTransactionRelayFeeFromMass)] - pub fn calc_minimum_transaction_relay_fee_from_mass(&self, mass: u64) -> u32 { - self.mc.calc_minimum_transaction_fee_from_mass(mass) as u32 - } - - #[wasm_bindgen(js_name=calcMiniumTxRelayFee)] - pub fn calc_minimum_transaction_relay_fee(&self, transaction: &Transaction, minimum_signatures: u16) -> Result { - let tx = cctx::Transaction::from(transaction); - Ok(self.mc.calc_minium_transaction_relay_fee(&tx, minimum_signatures) as u32) - } +/// `calculateTransactionFee()` returns minimum fees needed for the transaction to be +/// accepted by the network. If the transaction is invalid, the function throws an error. +/// If the mass of the transaction is larger than the maximum allowed by the network, the +/// function returns `undefined` which can be treated as a mass overflow condition. +/// +/// @category Wallet SDK +/// +#[wasm_bindgen(js_name = calculateTransactionFee)] +pub fn calculate_transaction_fee(network_id: NetworkIdT, tx: &TransactionT) -> Result> { + let tx = Transaction::try_cast_from(tx)?; + let network_id = NetworkId::try_owned_from(network_id)?; + let consensus_params = Params::from(network_id); + let network_params = NetworkParams::from(network_id); + let mc = mass::MassCalculator::new(&consensus_params, network_params); + let fee = mc.calc_tx_overall_mass(tx.as_ref())?.map(|mass| mc.calc_fee_for_mass(mass)); + Ok(fee) } diff --git a/wallet/core/src/wasm/tx/mod.rs b/wallet/core/src/wasm/tx/mod.rs index df826a9971..742bb89cab 100644 --- a/wallet/core/src/wasm/tx/mod.rs +++ b/wallet/core/src/wasm/tx/mod.rs @@ -1,10 +1,8 @@ -pub mod consensus; pub mod fees; pub mod generator; pub mod mass; pub mod utils; -pub use self::consensus::*; pub use self::fees::*; pub use self::generator::*; pub use self::mass::*; diff --git a/wallet/core/src/wasm/tx/utils.rs b/wallet/core/src/wasm/tx/utils.rs index 0d911af76c..352966a431 100644 --- a/wallet/core/src/wasm/tx/utils.rs +++ b/wallet/core/src/wasm/tx/utils.rs @@ -1,14 +1,11 @@ use crate::imports::*; use crate::result::Result; use crate::tx::{IPaymentOutputArray, PaymentOutputs}; -use crate::wasm::tx::consensus::get_consensus_params_by_address; use crate::wasm::tx::generator::*; -use crate::wasm::tx::mass::MassCalculator; -use kaspa_addresses::{Address, AddressT}; use kaspa_consensus_client::*; use kaspa_consensus_core::subnets::SUBNETWORK_ID_NATIVE; -//use kaspa_consensus_wasm::*; use kaspa_wallet_macros::declare_typescript_wasm_interface as declare; +use kaspa_wasm_core::types::BinaryT; use workflow_core::runtime::is_web; /// Create a basic transaction without any mass limit checks. @@ -17,16 +14,10 @@ use workflow_core::runtime::is_web; pub fn create_transaction_js( utxo_entry_source: IUtxoEntryArray, outputs: IPaymentOutputArray, - change_address: AddressT, priority_fee: BigInt, - payload: JsValue, - sig_op_count: JsValue, - minimum_signatures: JsValue, + payload: BinaryT, + sig_op_count: Option, ) -> crate::result::Result { - let change_address = Address::try_cast_from(change_address)?; - let params = get_consensus_params_by_address(change_address.as_ref()); - let mc = MassCalculator::new(params); - let utxo_entries = if let Some(utxo_entries) = utxo_entry_source.dyn_ref::() { utxo_entries.to_vec().iter().map(UtxoEntryReference::try_cast_from).collect::, _>>()? } else { @@ -35,14 +26,7 @@ pub fn create_transaction_js( let priority_fee: u64 = priority_fee.try_into().map_err(|err| Error::custom(format!("invalid fee value: {err}")))?; let payload = payload.try_as_vec_u8().ok().unwrap_or_default(); let outputs = PaymentOutputs::try_owned_from(outputs)?; - let sig_op_count = - if !sig_op_count.is_undefined() { sig_op_count.as_f64().expect("sigOpCount should be a number") as u8 } else { 1 }; - - let minimum_signatures = if !minimum_signatures.is_undefined() { - minimum_signatures.as_f64().expect("minimumSignatures should be a number") as u16 - } else { - 1 - }; + let sig_op_count = sig_op_count.unwrap_or(1); // --- @@ -64,12 +48,8 @@ pub fn create_transaction_js( return Err(format!("priority fee({priority_fee}) > amount({total_input_amount})").into()); } - // TODO - Calculate mass and fees - let outputs: Vec = outputs.into(); let transaction = Transaction::new(None, 0, inputs, outputs, 0, SUBNETWORK_ID_NATIVE, 0, payload)?; - let _fee = mc.calc_minimum_transaction_relay_fee(&transaction, minimum_signatures); - //let mtx = SignableTransaction::new(transaction, entries.into()); Ok(transaction) } diff --git a/wallet/keys/src/xprv.rs b/wallet/keys/src/xprv.rs index c9a6bf18df..1f478140eb 100644 --- a/wallet/keys/src/xprv.rs +++ b/wallet/keys/src/xprv.rs @@ -1,4 +1,4 @@ -use kaspa_bip32::{ChainCode, KeyFingerprint, PrivateKey}; +use kaspa_bip32::{ChainCode, KeyFingerprint}; use crate::imports::*; @@ -73,6 +73,12 @@ impl XPrv { Ok(public_key.into()) } + #[wasm_bindgen(js_name = toPrivateKey)] + pub fn to_private_key(&self) -> Result { + let private_key = self.inner.private_key(); + Ok(private_key.into()) + } + // ~~~~ Getters ~~~~ #[wasm_bindgen(getter)] @@ -83,6 +89,7 @@ impl XPrv { #[wasm_bindgen(getter, js_name = "privateKey")] pub fn private_key_as_hex_string(&self) -> String { + use kaspa_bip32::PrivateKey; self.inner.private_key().to_bytes().to_vec().to_hex() } diff --git a/wasm/CHANGELOG.md b/wasm/CHANGELOG.md index 71b6375d20..d572dfd1d1 100644 --- a/wasm/CHANGELOG.md +++ b/wasm/CHANGELOG.md @@ -2,6 +2,12 @@ Latest online documentation available at: https://kaspa.aspectron.org/docs/ ### Latest Release +- Replace `MassCalculator` with `calculateTransactionMass` and `calculateTransactionFee` functions. +- Change `createTransaction` function signature (remove requirement for change address). +- Make `ITransactionInput.signatureScript` optional (if not supplied, the signatureScript is assigned an empty vector). + +### Release 2024-07-17 + - Fix issues with deserializing manually-created objects matching `IUtxoEntry` interface. - Allow arguments expecting ScriptPublicKey to receive `{ version, script }` object or a hex string. - Fix `Transaction::serializeToObject()` return type (now returning `ISerializeTransaction` interface). diff --git a/wasm/examples/nodejs/javascript/general/derivation.js b/wasm/examples/nodejs/javascript/general/derivation.js index f92508c889..942f665f98 100644 --- a/wasm/examples/nodejs/javascript/general/derivation.js +++ b/wasm/examples/nodejs/javascript/general/derivation.js @@ -21,6 +21,7 @@ kaspa.initConsolePanicHook(); let xPrv = new XPrv(seed); // derive full path upto second address of receive wallet let pubkey1 = xPrv.derivePath("m/44'/111111'/0'/0/1").toXPub().toPublicKey(); + console.log("publickey", pubkey1.toString()) console.log("address", pubkey1.toAddress(NetworkType.Mainnet)); // create receive wallet @@ -28,17 +29,25 @@ kaspa.initConsolePanicHook(); // derive receive wallet for second address let pubkey2 = receiveWalletXPub.deriveChild(1, false).toPublicKey(); console.log("address", pubkey2.toAddress(NetworkType.Mainnet)); + if (pubkey1.toString() != pubkey2.toString()){ + throw new Error("pubkey2 dont match") + } // create change wallet let changeWalletXPub = xPrv.derivePath("m/44'/111111'/0'/1").toXPub(); // derive change wallet for first address let pubkey3 = changeWalletXPub.deriveChild(0, false).toPublicKey(); - console.log("address", pubkey2.toAddress(NetworkType.Mainnet)); + console.log("change address", pubkey3.toAddress(NetworkType.Mainnet)); + // --- - if (pubkey1.toString() != pubkey2.toString()){ - throw new Error("pubkeyes dont match") + //drive address via private key + let privateKey = xPrv.derivePath("m/44'/111111'/0'/0/1").toPrivateKey(); + console.log("address via private key", privateKey.toAddress(NetworkType.Mainnet)) + console.log("privatekey", privateKey.toString()); + let pubkey4 = privateKey.toPublicKey(); + if (pubkey1.toString() != pubkey4.toString()){ + throw new Error("pubkey4 dont match") } - // --- // xprv with ktrv prefix const ktrv = xPrv.intoString("ktrv"); From 0532c0b5767f83410250eef32b22285e830b1c32 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 27 Jul 2024 00:08:54 +0300 Subject: [PATCH 117/158] change OnceCell to LazyLock in wallet-core utxo settings --- Cargo.toml | 2 +- wallet/core/src/imports.rs | 3 +- wallet/core/src/utxo/settings.rs | 108 +++++++++++++------------------ 3 files changed, 47 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 463a49ab61..841540a32a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ members = [ ] [workspace.package] -rust-version = "1.78.0" +rust-version = "1.80.0" version = "0.14.1" authors = ["Kaspa developers"] license = "ISC" diff --git a/wallet/core/src/imports.rs b/wallet/core/src/imports.rs index 5ad48498c2..94638bf78f 100644 --- a/wallet/core/src/imports.rs +++ b/wallet/core/src/imports.rs @@ -48,7 +48,8 @@ pub use std::collections::{HashMap, HashSet}; pub use std::pin::Pin; pub use std::str::FromStr; pub use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; -pub use std::sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock}; +pub use std::sync::LazyLock; +pub use std::sync::{Arc, Mutex, MutexGuard, RwLock}; pub use std::task::{Context, Poll}; pub use wasm_bindgen::prelude::*; pub use workflow_core::prelude::*; diff --git a/wallet/core/src/utxo/settings.rs b/wallet/core/src/utxo/settings.rs index 69e79a05d2..6828d73cfe 100644 --- a/wallet/core/src/utxo/settings.rs +++ b/wallet/core/src/utxo/settings.rs @@ -50,78 +50,58 @@ impl NetworkParams { } } -static MAINNET_NETWORK_PARAMS: OnceLock = OnceLock::new(); - -pub fn mainnet_network_params() -> &'static NetworkParams { - MAINNET_NETWORK_PARAMS.get_or_init(|| NetworkParams { - coinbase_transaction_maturity_period_daa: AtomicU64::new(100), - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: AtomicU64::new(10), - kip9_version: Kip9Version::Beta, - additional_compound_transaction_mass: 100, - }) -} - -static TESTNET10_NETWORK_PARAMS: OnceLock = OnceLock::new(); - -pub fn testnet10_network_params() -> &'static NetworkParams { - TESTNET10_NETWORK_PARAMS.get_or_init(|| NetworkParams { - coinbase_transaction_maturity_period_daa: AtomicU64::new(100), - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: AtomicU64::new(10), - kip9_version: Kip9Version::Beta, - additional_compound_transaction_mass: 100, - }) -} - -static TESTNET11_NETWORK_PARAMS: OnceLock = OnceLock::new(); - -pub fn testnet11_network_params() -> &'static NetworkParams { - TESTNET11_NETWORK_PARAMS.get_or_init(|| NetworkParams { - coinbase_transaction_maturity_period_daa: AtomicU64::new(1_000), - coinbase_transaction_stasis_period_daa: 500, - user_transaction_maturity_period_daa: AtomicU64::new(100), - kip9_version: Kip9Version::Alpha, - additional_compound_transaction_mass: 100, - }) -} - -static SIMNET_NETWORK_PARAMS: OnceLock = OnceLock::new(); - -pub fn simnet_network_params() -> &'static NetworkParams { - SIMNET_NETWORK_PARAMS.get_or_init(|| NetworkParams { - coinbase_transaction_maturity_period_daa: AtomicU64::new(100), - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: AtomicU64::new(10), - kip9_version: Kip9Version::Alpha, - additional_compound_transaction_mass: 0, - }) -} - -static DEVNET_NETWORK_PARAMS: OnceLock = OnceLock::new(); - -pub fn devnet_network_params() -> &'static NetworkParams { - DEVNET_NETWORK_PARAMS.get_or_init(|| NetworkParams { - coinbase_transaction_maturity_period_daa: AtomicU64::new(100), - coinbase_transaction_stasis_period_daa: 50, - user_transaction_maturity_period_daa: AtomicU64::new(10), - kip9_version: Kip9Version::Beta, - additional_compound_transaction_mass: 0, - }) -} +static MAINNET_NETWORK_PARAMS: LazyLock = LazyLock::new(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + kip9_version: Kip9Version::Beta, + additional_compound_transaction_mass: 100, +}); + +static TESTNET10_NETWORK_PARAMS: LazyLock = LazyLock::new(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + kip9_version: Kip9Version::Beta, + additional_compound_transaction_mass: 100, +}); + +static TESTNET11_NETWORK_PARAMS: LazyLock = LazyLock::new(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(1_000), + coinbase_transaction_stasis_period_daa: 500, + user_transaction_maturity_period_daa: AtomicU64::new(100), + kip9_version: Kip9Version::Alpha, + additional_compound_transaction_mass: 100, +}); + +static SIMNET_NETWORK_PARAMS: LazyLock = LazyLock::new(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + kip9_version: Kip9Version::Alpha, + additional_compound_transaction_mass: 0, +}); + +static DEVNET_NETWORK_PARAMS: LazyLock = LazyLock::new(|| NetworkParams { + coinbase_transaction_maturity_period_daa: AtomicU64::new(100), + coinbase_transaction_stasis_period_daa: 50, + user_transaction_maturity_period_daa: AtomicU64::new(10), + kip9_version: Kip9Version::Beta, + additional_compound_transaction_mass: 0, +}); impl NetworkParams { pub fn from(value: NetworkId) -> &'static NetworkParams { match value.network_type { - NetworkType::Mainnet => mainnet_network_params(), + NetworkType::Mainnet => &MAINNET_NETWORK_PARAMS, NetworkType::Testnet => match value.suffix { - Some(10) => testnet10_network_params(), - Some(11) => testnet11_network_params(), + Some(10) => &TESTNET10_NETWORK_PARAMS, + Some(11) => &TESTNET11_NETWORK_PARAMS, Some(x) => panic!("Testnet suffix {} is not supported", x), None => panic!("Testnet suffix not provided"), }, - NetworkType::Devnet => devnet_network_params(), - NetworkType::Simnet => simnet_network_params(), + NetworkType::Devnet => &DEVNET_NETWORK_PARAMS, + NetworkType::Simnet => &SIMNET_NETWORK_PARAMS, } } } From c02b7f826c1f5497108a0f21ad3c4830c7bab68d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 27 Jul 2024 03:17:02 +0300 Subject: [PATCH 118/158] WASM: update signTransaction() signature --- wallet/core/src/wasm/tx/utils.rs | 4 ++-- .../javascript/transactions/single-transaction-demo.js | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/wallet/core/src/wasm/tx/utils.rs b/wallet/core/src/wasm/tx/utils.rs index 352966a431..775419724f 100644 --- a/wallet/core/src/wasm/tx/utils.rs +++ b/wallet/core/src/wasm/tx/utils.rs @@ -15,7 +15,7 @@ pub fn create_transaction_js( utxo_entry_source: IUtxoEntryArray, outputs: IPaymentOutputArray, priority_fee: BigInt, - payload: BinaryT, + payload: Option, sig_op_count: Option, ) -> crate::result::Result { let utxo_entries = if let Some(utxo_entries) = utxo_entry_source.dyn_ref::() { @@ -24,7 +24,7 @@ pub fn create_transaction_js( return Err(Error::custom("utxo_entries must be an array")); }; let priority_fee: u64 = priority_fee.try_into().map_err(|err| Error::custom(format!("invalid fee value: {err}")))?; - let payload = payload.try_as_vec_u8().ok().unwrap_or_default(); + let payload = payload.and_then(|payload|payload.try_as_vec_u8().ok()).unwrap_or_default(); let outputs = PaymentOutputs::try_owned_from(outputs)?; let sig_op_count = sig_op_count.unwrap_or(1); diff --git a/wasm/examples/nodejs/javascript/transactions/single-transaction-demo.js b/wasm/examples/nodejs/javascript/transactions/single-transaction-demo.js index def206c158..0400c7d1c5 100644 --- a/wasm/examples/nodejs/javascript/transactions/single-transaction-demo.js +++ b/wasm/examples/nodejs/javascript/transactions/single-transaction-demo.js @@ -73,7 +73,13 @@ const { networkId, encoding } = require("../utils").parseArgs(); const changeAddress = address; console.log("changeAddress:", changeAddress) - const tx = createTransaction(utxos, outputs, changeAddress, 0n, 0, 1, 1); + + // utxo_entry_source: IUtxoEntry[], + // outputs: IPaymentOutput[], + // priority_fee: bigint, + // payload: HexString | Uint8Array, + // sig_op_count?: number + const tx = createTransaction(utxos, outputs, 0n, "", 1); console.info("Transaction before signing:", tx); From 98ba65271cd434f13b3cf98589331ddf5d703201 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 27 Jul 2024 03:23:09 +0300 Subject: [PATCH 119/158] fix TS types and method names --- .../nodejs/javascript/transactions/simple-transaction.js | 2 +- wasm/examples/nodejs/typescript/src/scriptBuilder.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/examples/nodejs/javascript/transactions/simple-transaction.js b/wasm/examples/nodejs/javascript/transactions/simple-transaction.js index b215fd597d..fc9aa3b0ab 100644 --- a/wasm/examples/nodejs/javascript/transactions/simple-transaction.js +++ b/wasm/examples/nodejs/javascript/transactions/simple-transaction.js @@ -55,7 +55,7 @@ initConsolePanicHook(); let { transactions, summary } = await createTransactions({ entries, - outputs: [{ address : destinationAddress, amount : kaspaToSompi(0.00012)}], + outputs: [{ address : destinationAddress, amount : kaspaToSompi("0.00012")}], priorityFee: 0n, changeAddress: sourceAddress, }); diff --git a/wasm/examples/nodejs/typescript/src/scriptBuilder.ts b/wasm/examples/nodejs/typescript/src/scriptBuilder.ts index 4e09c82990..13f02b12bf 100644 --- a/wasm/examples/nodejs/typescript/src/scriptBuilder.ts +++ b/wasm/examples/nodejs/typescript/src/scriptBuilder.ts @@ -10,4 +10,4 @@ const address = addressFromScriptPublicKey(P2SHScript, NetworkType.Mainnet) // Payable address console.log(address!.toString()) // Unlock signature script -console.log(myScript.createPayToScriptHashSignatureScript("")) \ No newline at end of file +console.log(myScript.encodePayToScriptHashSignatureScript("")) \ No newline at end of file From 2b7ef08e468022b1753b3ffef43e8e9d3d304ab4 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 27 Jul 2024 03:26:29 +0300 Subject: [PATCH 120/158] lints --- wallet/core/src/wasm/tx/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/core/src/wasm/tx/utils.rs b/wallet/core/src/wasm/tx/utils.rs index 775419724f..07ded545d4 100644 --- a/wallet/core/src/wasm/tx/utils.rs +++ b/wallet/core/src/wasm/tx/utils.rs @@ -24,7 +24,7 @@ pub fn create_transaction_js( return Err(Error::custom("utxo_entries must be an array")); }; let priority_fee: u64 = priority_fee.try_into().map_err(|err| Error::custom(format!("invalid fee value: {err}")))?; - let payload = payload.and_then(|payload|payload.try_as_vec_u8().ok()).unwrap_or_default(); + let payload = payload.and_then(|payload| payload.try_as_vec_u8().ok()).unwrap_or_default(); let outputs = PaymentOutputs::try_owned_from(outputs)?; let sig_op_count = sig_op_count.unwrap_or(1); From f21e3dac4a7b32508bdd2ff5e7a58ecb2f262af7 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 27 Jul 2024 11:09:18 +0300 Subject: [PATCH 121/158] split GetConnections counter into separate clients and peers variables --- rpc/core/src/api/rpc.rs | 4 ++-- rpc/core/src/model/message.rs | 11 +++++++---- rpc/core/src/model/tests.rs | 2 +- rpc/grpc/core/proto/rpc.proto | 3 ++- rpc/grpc/core/src/convert/message.rs | 6 ++++-- rpc/service/src/service.rs | 8 ++++---- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 0251aa7fc9..a94c85354e 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -40,8 +40,8 @@ pub trait RpcApi: Sync + Send + AnySync { // --- - async fn get_connections(&self) -> RpcResult { - Ok(self.get_connections_call(None, GetConnectionsRequest {}).await?.active_connections) + async fn get_connections(&self) -> RpcResult { + self.get_connections_call(None, GetConnectionsRequest {}).await } async fn get_connections_call( &self, diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 2631a67018..74457aa95a 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1829,20 +1829,23 @@ impl Deserializer for GetConnectionsRequest { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetConnectionsResponse { - pub active_connections: u32, + pub clients: u32, + pub peers: u32, } impl Serializer for GetConnectionsResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - store!(u32, &self.active_connections, writer)?; + store!(u32, &self.clients, writer)?; + store!(u32, &self.peers, writer)?; Ok(()) } } impl Deserializer for GetConnectionsResponse { fn deserialize(reader: &mut R) -> std::io::Result { - let active_connections = load!(u32, reader)?; - Ok(Self { active_connections }) + let clients = load!(u32, reader)?; + let peers = load!(u32, reader)?; + Ok(Self { clients, peers }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index ba28b0c1c9..25b278849a 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -916,7 +916,7 @@ mod mockery { impl Mock for GetConnectionsResponse { fn mock() -> Self { - GetConnectionsResponse { active_connections: mock() } + GetConnectionsResponse { clients: mock(), peers: mock() } } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index 49478262b3..98cc3f5165 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -829,7 +829,8 @@ message GetConnectionsRequestMessage{ } message GetConnectionsResponseMessage{ - uint32 activeConnections = 1; + uint32 clients = 1; + uint32 peers = 2; RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index 486b5284f3..33c14ad429 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -435,7 +435,8 @@ from!(_item: &kaspa_rpc_core::GetConnectionsRequest, protowire::GetConnectionsRe }); from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetConnectionsResponseMessage, { Self { - active_connections: item.active_connections, + clients: item.clients, + peers: item.peers, error: None, } }); @@ -881,7 +882,8 @@ try_from!(_item: &protowire::GetConnectionsRequestMessage, kaspa_rpc_core::GetCo }); try_from!(item: &protowire::GetConnectionsResponseMessage, RpcResult, { Self { - active_connections: item.active_connections, + clients: item.clients, + peers: item.peers, } }); diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index 863b1b4996..21d88c1740 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -893,11 +893,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and _connection: Option<&DynRpcConnection>, _req: GetConnectionsRequest, ) -> RpcResult { - let active_connections = (self.wrpc_borsh_counters.active_connections.load(Ordering::Relaxed) - + self.wrpc_json_counters.active_connections.load(Ordering::Relaxed) - + self.flow_context.hub().active_peers_len()) as u32; + let clients = (self.wrpc_borsh_counters.active_connections.load(Ordering::Relaxed) + + self.wrpc_json_counters.active_connections.load(Ordering::Relaxed)) as u32; + let peers = self.flow_context.hub().active_peers_len() as u32; - Ok(GetConnectionsResponse { active_connections }) + Ok(GetConnectionsResponse { clients, peers }) } async fn get_metrics_call(&self, _connection: Option<&DynRpcConnection>, req: GetMetricsRequest) -> RpcResult { From 2a1cff2e6f465a389e9216f80e8708f43a439dc3 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 27 Jul 2024 12:15:26 +0300 Subject: [PATCH 122/158] fix missing version in GetConnections method --- rpc/core/src/model/message.rs | 8 +++++--- rpc/grpc/core/src/convert/message.rs | 4 ++-- rpc/service/src/service.rs | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 74457aa95a..c780a11ec0 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1830,21 +1830,23 @@ impl Deserializer for GetConnectionsRequest { #[serde(rename_all = "camelCase")] pub struct GetConnectionsResponse { pub clients: u32, - pub peers: u32, + pub peers: u16, } impl Serializer for GetConnectionsResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; store!(u32, &self.clients, writer)?; - store!(u32, &self.peers, writer)?; + store!(u16, &self.peers, writer)?; Ok(()) } } impl Deserializer for GetConnectionsResponse { fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; let clients = load!(u32, reader)?; - let peers = load!(u32, reader)?; + let peers = load!(u16, reader)?; Ok(Self { clients, peers }) } } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index 33c14ad429..94a16d98a7 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -436,7 +436,7 @@ from!(_item: &kaspa_rpc_core::GetConnectionsRequest, protowire::GetConnectionsRe from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetConnectionsResponseMessage, { Self { clients: item.clients, - peers: item.peers, + peers: item.peers as u32, error: None, } }); @@ -883,7 +883,7 @@ try_from!(_item: &protowire::GetConnectionsRequestMessage, kaspa_rpc_core::GetCo try_from!(item: &protowire::GetConnectionsResponseMessage, RpcResult, { Self { clients: item.clients, - peers: item.peers, + peers: item.peers as u16, } }); diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index 21d88c1740..8aeff10861 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -895,7 +895,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and ) -> RpcResult { let clients = (self.wrpc_borsh_counters.active_connections.load(Ordering::Relaxed) + self.wrpc_json_counters.active_connections.load(Ordering::Relaxed)) as u32; - let peers = self.flow_context.hub().active_peers_len() as u32; + let peers = self.flow_context.hub().active_peers_len() as u16; Ok(GetConnectionsResponse { clients, peers }) } From b6608686ba98ad4852fc458861e0de58eef1180d Mon Sep 17 00:00:00 2001 From: 1bananagirl <168954040+1bananagirl@users.noreply.github.com> Date: Sat, 27 Jul 2024 14:51:17 +0200 Subject: [PATCH 123/158] Adding type conversion. (#76) --- cli/src/modules/pskb.rs | 71 ++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/cli/src/modules/pskb.rs b/cli/src/modules/pskb.rs index 8f750da782..91ea1adaa3 100644 --- a/cli/src/modules/pskb.rs +++ b/cli/src/modules/pskb.rs @@ -108,43 +108,40 @@ impl Pskb { } } "unlock" => { - todo!(); - - // if argv.len() != 1 { - // return self.display_help(ctx, argv).await; - // } - - // // Get locked UTXO set. - // let spend_utxos: Vec = - // ctx.wallet().rpc_api().get_utxos_by_addresses(vec![script_p2sh.clone()]).await?; - // let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.first())?.unwrap_or(0) as u64; - - // if spend_utxos.is_empty() { - // twarnln!(ctx, "No locked UTXO set found."); - // return Ok(()); - // } - - // let references: Vec<(UtxoEntry, TransactionOutpoint)> = - // spend_utxos.iter().map(|entry| (entry.utxo_entry.clone(), entry.outpoint)).collect(); - - // let total_locked_sompi: u64 = spend_utxos.iter().map(|entry| entry.utxo_entry.amount).sum(); - - // tprintln!( - // ctx, - // "{} locked UTXO{} found with total amount of {} KAS", - // spend_utxos.len(), - // if spend_utxos.len() == 1 { "" } else { "s" }, - // sompi_to_kaspa(total_locked_sompi) - // ); - - // // Sweep UTXO set. - // match unlock_utxos_as_pskb(references, &receive_address, script_sig, priority_fee_sompi as u64) { - // Ok(pskb) => { - // let pskb_hex = pskb.to_hex()?; - // tprintln!(ctx, "{pskb_hex}"); - // } - // Err(e) => tprintln!(ctx, "Error generating unlock PSKB: {}", e.to_string()), - // } + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } + + // Get locked UTXO set. + let spend_utxos: Vec = + ctx.wallet().rpc_api().get_utxos_by_addresses(vec![script_p2sh.clone()]).await?; + let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.first())?.unwrap_or(0) as u64; + + if spend_utxos.is_empty() { + twarnln!(ctx, "No locked UTXO set found."); + return Ok(()); + } + + let references: Vec<(UtxoEntry, TransactionOutpoint)> = spend_utxos.iter().map(|entry| (entry.utxo_entry.clone().into(), entry.outpoint.into())).collect(); + + let total_locked_sompi: u64 = spend_utxos.iter().map(|entry| entry.utxo_entry.amount).sum(); + + tprintln!( + ctx, + "{} locked UTXO{} found with total amount of {} KAS", + spend_utxos.len(), + if spend_utxos.len() == 1 { "" } else { "s" }, + sompi_to_kaspa(total_locked_sompi) + ); + + // Sweep UTXO set. + match unlock_utxos_as_pskb(references, &receive_address, script_sig, priority_fee_sompi as u64) { + Ok(pskb) => { + let pskb_hex = pskb.to_hex()?; + tprintln!(ctx, "{pskb_hex}"); + } + Err(e) => tprintln!(ctx, "Error generating unlock PSKB: {}", e.to_string()), + } } "sign" => { let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; From 921278b9b5bda809e80669b75d73119391c3eb5c Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 27 Jul 2024 15:51:41 +0300 Subject: [PATCH 124/158] fmt --- cli/src/modules/pskb.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/src/modules/pskb.rs b/cli/src/modules/pskb.rs index 91ea1adaa3..50ef5de116 100644 --- a/cli/src/modules/pskb.rs +++ b/cli/src/modules/pskb.rs @@ -122,7 +122,8 @@ impl Pskb { return Ok(()); } - let references: Vec<(UtxoEntry, TransactionOutpoint)> = spend_utxos.iter().map(|entry| (entry.utxo_entry.clone().into(), entry.outpoint.into())).collect(); + let references: Vec<(UtxoEntry, TransactionOutpoint)> = + spend_utxos.iter().map(|entry| (entry.utxo_entry.clone().into(), entry.outpoint.into())).collect(); let total_locked_sompi: u64 = spend_utxos.iter().map(|entry| entry.utxo_entry.amount).sum(); From c293af7a36aa184ab66e3cf80bf666aa8ef6671b Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 28 Jul 2024 21:46:12 +0300 Subject: [PATCH 125/158] refactor kaspa-metrics to expose some internal methods (needed for external processing). --- metrics/core/src/data.rs | 95 ++++++++++++++++--- metrics/core/src/error.rs | 3 + metrics/core/src/lib.rs | 191 ++++++++++++++++++++------------------ 3 files changed, 188 insertions(+), 101 deletions(-) diff --git a/metrics/core/src/data.rs b/metrics/core/src/data.rs index b46a3b09bb..d8030f1f3d 100644 --- a/metrics/core/src/data.rs +++ b/metrics/core/src/data.rs @@ -1,4 +1,7 @@ +use crate::error::Error; +use crate::result::Result; use borsh::{BorshDeserialize, BorshSerialize}; +use kaspa_rpc_core::GetMetricsResponse; use separator::{separated_float, separated_int, separated_uint_with_output, Separatable}; use serde::{Deserialize, Serialize}; use workflow_core::enums::Describe; @@ -514,17 +517,6 @@ pub struct MetricsData { pub node_grpc_user_bytes_rx: u64, pub node_total_bytes_tx: u64, pub node_total_bytes_rx: u64, - - pub node_borsh_bytes_tx_per_second: u64, - pub node_borsh_bytes_rx_per_second: u64, - pub node_json_bytes_tx_per_second: u64, - pub node_json_bytes_rx_per_second: u64, - pub node_p2p_bytes_tx_per_second: u64, - pub node_p2p_bytes_rx_per_second: u64, - pub node_grpc_user_bytes_tx_per_second: u64, - pub node_grpc_user_bytes_rx_per_second: u64, - pub node_total_bytes_tx_per_second: u64, - pub node_total_bytes_rx_per_second: u64, // --- pub node_blocks_submitted_count: u64, pub node_headers_processed_count: u64, @@ -551,6 +543,87 @@ impl MetricsData { } } +impl TryFrom for MetricsData { + type Error = Error; + fn try_from(response: GetMetricsResponse) -> Result { + let GetMetricsResponse { + server_time, + consensus_metrics, + connection_metrics, + bandwidth_metrics, + process_metrics, + storage_metrics, + custom_metrics: _, + } = response; //rpc.get_metrics(true, true, true, true, true, false).await?; + + let consensus_metrics = consensus_metrics.ok_or(Error::MissingData("Consensus Metrics"))?; + let connection_metrics = connection_metrics.ok_or(Error::MissingData("Connection Metrics"))?; + let bandwidth_metrics = bandwidth_metrics.ok_or(Error::MissingData("Bandwidth Metrics"))?; + let process_metrics = process_metrics.ok_or(Error::MissingData("Process Metrics"))?; + let storage_metrics = storage_metrics.ok_or(Error::MissingData("Storage Metrics"))?; + + Ok(MetricsData { + unixtime_millis: server_time as f64, + + node_blocks_submitted_count: consensus_metrics.node_blocks_submitted_count, + node_headers_processed_count: consensus_metrics.node_headers_processed_count, + node_dependencies_processed_count: consensus_metrics.node_dependencies_processed_count, + node_bodies_processed_count: consensus_metrics.node_bodies_processed_count, + node_transactions_processed_count: consensus_metrics.node_transactions_processed_count, + node_chain_blocks_processed_count: consensus_metrics.node_chain_blocks_processed_count, + node_mass_processed_count: consensus_metrics.node_mass_processed_count, + // -- + node_database_blocks_count: consensus_metrics.node_database_blocks_count, + node_database_headers_count: consensus_metrics.node_database_headers_count, + network_mempool_size: consensus_metrics.network_mempool_size, + network_tip_hashes_count: consensus_metrics.network_tip_hashes_count, + network_difficulty: consensus_metrics.network_difficulty, + network_past_median_time: consensus_metrics.network_past_median_time, + network_virtual_parent_hashes_count: consensus_metrics.network_virtual_parent_hashes_count, + network_virtual_daa_score: consensus_metrics.network_virtual_daa_score, + + node_borsh_live_connections: connection_metrics.borsh_live_connections, + node_borsh_connection_attempts: connection_metrics.borsh_connection_attempts, + node_borsh_handshake_failures: connection_metrics.borsh_handshake_failures, + node_json_live_connections: connection_metrics.json_live_connections, + node_json_connection_attempts: connection_metrics.json_connection_attempts, + node_json_handshake_failures: connection_metrics.json_handshake_failures, + node_active_peers: connection_metrics.active_peers, + + node_borsh_bytes_tx: bandwidth_metrics.borsh_bytes_tx, + node_borsh_bytes_rx: bandwidth_metrics.borsh_bytes_rx, + node_json_bytes_tx: bandwidth_metrics.json_bytes_tx, + node_json_bytes_rx: bandwidth_metrics.json_bytes_rx, + node_p2p_bytes_tx: bandwidth_metrics.p2p_bytes_tx, + node_p2p_bytes_rx: bandwidth_metrics.p2p_bytes_rx, + node_grpc_user_bytes_tx: bandwidth_metrics.grpc_bytes_tx, + node_grpc_user_bytes_rx: bandwidth_metrics.grpc_bytes_rx, + + node_total_bytes_tx: bandwidth_metrics.borsh_bytes_tx + + bandwidth_metrics.json_bytes_tx + + bandwidth_metrics.p2p_bytes_tx + + bandwidth_metrics.grpc_bytes_tx, + + node_total_bytes_rx: bandwidth_metrics.borsh_bytes_rx + + bandwidth_metrics.json_bytes_rx + + bandwidth_metrics.p2p_bytes_rx + + bandwidth_metrics.grpc_bytes_rx, + + node_resident_set_size_bytes: process_metrics.resident_set_size, + node_virtual_memory_size_bytes: process_metrics.virtual_memory_size, + node_cpu_cores: process_metrics.core_num, + node_cpu_usage: process_metrics.cpu_usage, + node_file_handles: process_metrics.fd_num, + node_disk_io_read_bytes: process_metrics.disk_io_read_bytes, + node_disk_io_write_bytes: process_metrics.disk_io_write_bytes, + node_disk_io_read_per_sec: process_metrics.disk_io_read_per_sec, + node_disk_io_write_per_sec: process_metrics.disk_io_write_per_sec, + + node_storage_size_bytes: storage_metrics.storage_size_bytes, + }) + } +} + #[derive(Default, Debug, Clone, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] pub struct MetricsSnapshot { pub data: MetricsData, diff --git a/metrics/core/src/error.rs b/metrics/core/src/error.rs index e31142a76b..4c8a441f8d 100644 --- a/metrics/core/src/error.rs +++ b/metrics/core/src/error.rs @@ -6,6 +6,9 @@ pub enum Error { #[error("{0}")] Custom(String), + #[error("Missing metrics data `{0}`")] + MissingData(&'static str), + #[error(transparent)] RpcError(#[from] RpcError), } diff --git a/metrics/core/src/lib.rs b/metrics/core/src/lib.rs index ca7eb98f32..b88dcf0946 100644 --- a/metrics/core/src/lib.rs +++ b/metrics/core/src/lib.rs @@ -6,7 +6,7 @@ pub use data::{Metric, MetricGroup, MetricsData, MetricsSnapshot}; use crate::result::Result; use futures::{pin_mut, select, FutureExt, StreamExt}; -use kaspa_rpc_core::{api::rpc::RpcApi, GetMetricsResponse}; +use kaspa_rpc_core::api::rpc::RpcApi; use std::{ future::Future, pin::Pin, @@ -81,23 +81,31 @@ impl Metrics { }, _ = interval.next().fuse() => { - let last_metrics_data = current_metrics_data; - current_metrics_data = MetricsData::new(unixtime_as_millis_f64()); + // current_metrics_data = MetricsData::new(unixtime_as_millis_f64()); if let Some(rpc) = this.rpc() { - if let Err(err) = this.sample_metrics(rpc.clone(), &mut current_metrics_data).await { - log_trace!("Metrics::sample_metrics() error: {}", err); + // if let Err(err) = this.sample_metrics(rpc.clone(), &mut current_metrics_data).await { + match this.sample_metrics(rpc.clone()).await { + Ok(incoming_data) => { + let last_metrics_data = current_metrics_data; + current_metrics_data = incoming_data; + this.data.lock().unwrap().replace(current_metrics_data.clone()); + + if let Some(sink) = this.sink() { + let snapshot = MetricsSnapshot::from((&last_metrics_data, ¤t_metrics_data)); + if let Some(future) = sink(snapshot) { + future.await.ok(); + } + } + + } + Err(err) => { + // current_metrics_data = last_metrics_data.clone(); + log_trace!("Metrics::sample_metrics() error: {}", err); + } } } - this.data.lock().unwrap().replace(current_metrics_data.clone()); - - if let Some(sink) = this.sink() { - let snapshot = MetricsSnapshot::from((&last_metrics_data, ¤t_metrics_data)); - if let Some(future) = sink(snapshot) { - future.await.ok(); - } - } } } } @@ -114,82 +122,85 @@ impl Metrics { // --- samplers - async fn sample_metrics(self: &Arc, rpc: Arc, data: &mut MetricsData) -> Result<()> { - let GetMetricsResponse { - server_time: _, - consensus_metrics, - connection_metrics, - bandwidth_metrics, - process_metrics, - storage_metrics, - custom_metrics: _, - } = rpc.get_metrics(true, true, true, true, true, false).await?; - - if let Some(consensus_metrics) = consensus_metrics { - data.node_blocks_submitted_count = consensus_metrics.node_blocks_submitted_count; - data.node_headers_processed_count = consensus_metrics.node_headers_processed_count; - data.node_dependencies_processed_count = consensus_metrics.node_dependencies_processed_count; - data.node_bodies_processed_count = consensus_metrics.node_bodies_processed_count; - data.node_transactions_processed_count = consensus_metrics.node_transactions_processed_count; - data.node_chain_blocks_processed_count = consensus_metrics.node_chain_blocks_processed_count; - data.node_mass_processed_count = consensus_metrics.node_mass_processed_count; - // -- - data.node_database_blocks_count = consensus_metrics.node_database_blocks_count; - data.node_database_headers_count = consensus_metrics.node_database_headers_count; - data.network_mempool_size = consensus_metrics.network_mempool_size; - data.network_tip_hashes_count = consensus_metrics.network_tip_hashes_count; - data.network_difficulty = consensus_metrics.network_difficulty; - data.network_past_median_time = consensus_metrics.network_past_median_time; - data.network_virtual_parent_hashes_count = consensus_metrics.network_virtual_parent_hashes_count; - data.network_virtual_daa_score = consensus_metrics.network_virtual_daa_score; - } - - if let Some(connection_metrics) = connection_metrics { - data.node_borsh_live_connections = connection_metrics.borsh_live_connections; - data.node_borsh_connection_attempts = connection_metrics.borsh_connection_attempts; - data.node_borsh_handshake_failures = connection_metrics.borsh_handshake_failures; - data.node_json_live_connections = connection_metrics.json_live_connections; - data.node_json_connection_attempts = connection_metrics.json_connection_attempts; - data.node_json_handshake_failures = connection_metrics.json_handshake_failures; - data.node_active_peers = connection_metrics.active_peers; - } - - if let Some(bandwidth_metrics) = bandwidth_metrics { - data.node_borsh_bytes_tx = bandwidth_metrics.borsh_bytes_tx; - data.node_borsh_bytes_rx = bandwidth_metrics.borsh_bytes_rx; - data.node_json_bytes_tx = bandwidth_metrics.json_bytes_tx; - data.node_json_bytes_rx = bandwidth_metrics.json_bytes_rx; - data.node_p2p_bytes_tx = bandwidth_metrics.p2p_bytes_tx; - data.node_p2p_bytes_rx = bandwidth_metrics.p2p_bytes_rx; - data.node_grpc_user_bytes_tx = bandwidth_metrics.grpc_bytes_tx; - data.node_grpc_user_bytes_rx = bandwidth_metrics.grpc_bytes_rx; - - data.node_total_bytes_tx = bandwidth_metrics.borsh_bytes_tx - + bandwidth_metrics.json_bytes_tx - + bandwidth_metrics.p2p_bytes_tx - + bandwidth_metrics.grpc_bytes_tx; - - data.node_total_bytes_rx = bandwidth_metrics.borsh_bytes_rx - + bandwidth_metrics.json_bytes_rx - + bandwidth_metrics.p2p_bytes_rx - + bandwidth_metrics.grpc_bytes_rx; - } - - if let Some(process_metrics) = process_metrics { - data.node_resident_set_size_bytes = process_metrics.resident_set_size; - data.node_virtual_memory_size_bytes = process_metrics.virtual_memory_size; - data.node_cpu_cores = process_metrics.core_num; - data.node_cpu_usage = process_metrics.cpu_usage; - data.node_file_handles = process_metrics.fd_num; - data.node_disk_io_read_bytes = process_metrics.disk_io_read_bytes; - data.node_disk_io_write_bytes = process_metrics.disk_io_write_bytes; - data.node_disk_io_read_per_sec = process_metrics.disk_io_read_per_sec; - data.node_disk_io_write_per_sec = process_metrics.disk_io_write_per_sec; - } - - if let Some(storage_metrics) = storage_metrics { - data.node_storage_size_bytes = storage_metrics.storage_size_bytes; - } - Ok(()) + async fn sample_metrics(self: &Arc, rpc: Arc) -> Result { + // let GetMetricsResponse { + // server_time: _, + // consensus_metrics, + // connection_metrics, + // bandwidth_metrics, + // process_metrics, + // storage_metrics, + // custom_metrics: _, + // } = + let response = rpc.get_metrics(true, true, true, true, true, false).await?; + + MetricsData::try_from(response) + + // if let Some(consensus_metrics) = consensus_metrics { + // data.node_blocks_submitted_count = consensus_metrics.node_blocks_submitted_count; + // data.node_headers_processed_count = consensus_metrics.node_headers_processed_count; + // data.node_dependencies_processed_count = consensus_metrics.node_dependencies_processed_count; + // data.node_bodies_processed_count = consensus_metrics.node_bodies_processed_count; + // data.node_transactions_processed_count = consensus_metrics.node_transactions_processed_count; + // data.node_chain_blocks_processed_count = consensus_metrics.node_chain_blocks_processed_count; + // data.node_mass_processed_count = consensus_metrics.node_mass_processed_count; + // // -- + // data.node_database_blocks_count = consensus_metrics.node_database_blocks_count; + // data.node_database_headers_count = consensus_metrics.node_database_headers_count; + // data.network_mempool_size = consensus_metrics.network_mempool_size; + // data.network_tip_hashes_count = consensus_metrics.network_tip_hashes_count; + // data.network_difficulty = consensus_metrics.network_difficulty; + // data.network_past_median_time = consensus_metrics.network_past_median_time; + // data.network_virtual_parent_hashes_count = consensus_metrics.network_virtual_parent_hashes_count; + // data.network_virtual_daa_score = consensus_metrics.network_virtual_daa_score; + // } + + // if let Some(connection_metrics) = connection_metrics { + // data.node_borsh_live_connections = connection_metrics.borsh_live_connections; + // data.node_borsh_connection_attempts = connection_metrics.borsh_connection_attempts; + // data.node_borsh_handshake_failures = connection_metrics.borsh_handshake_failures; + // data.node_json_live_connections = connection_metrics.json_live_connections; + // data.node_json_connection_attempts = connection_metrics.json_connection_attempts; + // data.node_json_handshake_failures = connection_metrics.json_handshake_failures; + // data.node_active_peers = connection_metrics.active_peers; + // } + + // if let Some(bandwidth_metrics) = bandwidth_metrics { + // data.node_borsh_bytes_tx = bandwidth_metrics.borsh_bytes_tx; + // data.node_borsh_bytes_rx = bandwidth_metrics.borsh_bytes_rx; + // data.node_json_bytes_tx = bandwidth_metrics.json_bytes_tx; + // data.node_json_bytes_rx = bandwidth_metrics.json_bytes_rx; + // data.node_p2p_bytes_tx = bandwidth_metrics.p2p_bytes_tx; + // data.node_p2p_bytes_rx = bandwidth_metrics.p2p_bytes_rx; + // data.node_grpc_user_bytes_tx = bandwidth_metrics.grpc_bytes_tx; + // data.node_grpc_user_bytes_rx = bandwidth_metrics.grpc_bytes_rx; + + // data.node_total_bytes_tx = bandwidth_metrics.borsh_bytes_tx + // + bandwidth_metrics.json_bytes_tx + // + bandwidth_metrics.p2p_bytes_tx + // + bandwidth_metrics.grpc_bytes_tx; + + // data.node_total_bytes_rx = bandwidth_metrics.borsh_bytes_rx + // + bandwidth_metrics.json_bytes_rx + // + bandwidth_metrics.p2p_bytes_rx + // + bandwidth_metrics.grpc_bytes_rx; + // } + + // if let Some(process_metrics) = process_metrics { + // data.node_resident_set_size_bytes = process_metrics.resident_set_size; + // data.node_virtual_memory_size_bytes = process_metrics.virtual_memory_size; + // data.node_cpu_cores = process_metrics.core_num; + // data.node_cpu_usage = process_metrics.cpu_usage; + // data.node_file_handles = process_metrics.fd_num; + // data.node_disk_io_read_bytes = process_metrics.disk_io_read_bytes; + // data.node_disk_io_write_bytes = process_metrics.disk_io_write_bytes; + // data.node_disk_io_read_per_sec = process_metrics.disk_io_read_per_sec; + // data.node_disk_io_write_per_sec = process_metrics.disk_io_write_per_sec; + // } + + // if let Some(storage_metrics) = storage_metrics { + // data.node_storage_size_bytes = storage_metrics.storage_size_bytes; + // } + // Ok(()) } } From d947f06c0fc48dfe6375c8e7a5c6d20b730254e7 Mon Sep 17 00:00:00 2001 From: surinder singh Date: Thu, 1 Aug 2024 00:32:37 +0530 Subject: [PATCH 126/158] Word count (#83) * Update phrase.rs * private context issue for importing legacy wallet * account filter updated for calculating account_index * gen1 decrypt_mnemonic updated for error unwraping --- wallet/core/src/compat/gen1.rs | 8 +++++--- wallet/core/src/wallet/mod.rs | 20 ++++++++++++++------ wallet/keys/src/derivation/gen0/hd.rs | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/wallet/core/src/compat/gen1.rs b/wallet/core/src/compat/gen1.rs index cd66b10166..5bf5554f55 100644 --- a/wallet/core/src/compat/gen1.rs +++ b/wallet/core/src/compat/gen1.rs @@ -14,7 +14,7 @@ pub fn decrypt_mnemonic>( let mut aead = chacha20poly1305::XChaCha20Poly1305::new(Key::from_slice(&key)); let (nonce, ciphertext) = cipher.as_ref().split_at(24); - let decrypted = aead.decrypt(nonce.into(), ciphertext).unwrap(); + let decrypted = aead.decrypt(nonce.into(), ciphertext)?; Ok(unsafe { String::from_utf8_unchecked(decrypted) }) } @@ -36,8 +36,10 @@ mod test { ecdsa: false, }; - let decrypted = decrypt_mnemonic(8, file.encrypted_mnemonic, b"").unwrap(); - assert_eq!("dizzy uncover funny time weapon chat volume squirrel comic motion until diamond response remind hurt spider door strategy entire oyster hawk marriage soon fabric", decrypted); + let decrypted = decrypt_mnemonic(8, file.encrypted_mnemonic, b""); + log_info!("decrypted: {decrypted:?}"); + assert!(decrypted.is_ok(), "decrypt error"); + assert_eq!("dizzy uncover funny time weapon chat volume squirrel comic motion until diamond response remind hurt spider door strategy entire oyster hawk marriage soon fabric", decrypted.unwrap()); } #[tokio::test] diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index c905d368a3..d5f4dfadd3 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -378,12 +378,8 @@ impl Wallet { if let Some(accounts) = &accounts { for account in accounts.iter() { if let Ok(legacy_account) = account.clone().as_legacy_account() { - // if let Ok(descriptor) = account.descriptor(){ - // legacy_account.create_private_context(wallet_secret, None, index) - // } - legacy_account.create_private_context(wallet_secret, None, None).await?; - log_info!("create_private_context: receive_address: {:?}", account.receive_address()); + log_info!("create_private_context, open_impl: receive_address: {:?}", account.receive_address()); self.legacy_accounts().insert(account.clone()); } } @@ -796,7 +792,13 @@ impl Wallet { let account_index = if let Some(account_index) = account_index { account_index } else { - account_store.clone().len(Some(prv_key_data_id)).await? as u64 + let accounts = account_store.clone().iter(Some(prv_key_data_id)).await?.collect::>().await; + + accounts + .into_iter() + .filter(|a| a.as_ref().ok().and_then(|(a, _)| (a.kind == BIP32_ACCOUNT_KIND).then_some(true)).unwrap_or(false)) + .collect::>() + .len() as u64 }; let xpub_key = prv_key_data.create_xpub(payment_secret, BIP32_ACCOUNT_KIND.into(), account_index).await?; @@ -862,6 +864,12 @@ impl Wallet { .ok_or_else(|| Error::PrivateKeyNotFound(prv_key_data_id))?; let account: Arc = Arc::new(legacy::Legacy::try_new(self, account_name, prv_key_data.id).await?); + if let Ok(legacy_account) = account.clone().as_legacy_account() { + legacy_account.create_private_context(wallet_secret, None, None).await?; + log_info!("create_private_context: create_account_legacy, receive_address: {:?}", account.receive_address()); + self.legacy_accounts().insert(account.clone()); + //legacy_account.clear_private_context().await?; + } if account_store.load_single(account.id()).await?.is_some() { return Err(Error::AccountAlreadyExists(*account.id())); diff --git a/wallet/keys/src/derivation/gen0/hd.rs b/wallet/keys/src/derivation/gen0/hd.rs index dd85f8fe5b..3b7da0e758 100644 --- a/wallet/keys/src/derivation/gen0/hd.rs +++ b/wallet/keys/src/derivation/gen0/hd.rs @@ -176,7 +176,7 @@ impl PubkeyDerivationManagerV0 { return Ok(*key); } - Err(crate::error::Error::Custom("PubkeyDerivationManagerV0 initialization is pending (Error: 102).".into())) + Err(crate::error::Error::Custom("PubkeyDerivationManagerV0 initialization is pending (Error: 105).".into())) } pub fn create_address(key: &secp256k1::PublicKey, prefix: AddressPrefix, _ecdsa: bool) -> Result
{ From 718919212e966517f2eacb14119ec5cb55cf89d6 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 1 Aug 2024 04:16:56 +0300 Subject: [PATCH 127/158] adding resolver tls option --- rpc/wrpc/client/src/resolver.rs | 38 ++++++++++++++++++++++----------- rpc/wrpc/wasm/src/resolver.rs | 26 ++++++++++++++++++---- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index 558db86c4e..0536ef0510 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -57,11 +57,18 @@ fn try_parse_resolvers(toml: &str) -> Result>> { #[derive(Debug)] struct Inner { pub urls: Vec>, + pub tls: bool, } impl Inner { - pub fn new(urls: Vec>) -> Self { - Self { urls } + pub fn new(urls: Option>>, tls: bool) -> Self { + if urls.as_ref().is_some_and(|urls| urls.is_empty()) { + panic!("Resolver: Empty URL list supplied to the constructor."); + } + + let urls = urls.unwrap_or_else(|| try_parse_resolvers(RESOLVER_CONFIG).expect("TOML: Unable to parse RPC Resolver list")); + + Self { urls, tls } } } @@ -75,24 +82,31 @@ pub struct Resolver { impl Default for Resolver { fn default() -> Self { - let urls = try_parse_resolvers(RESOLVER_CONFIG).expect("TOML: Unable to parse RPC Resolver list"); - Self { inner: Arc::new(Inner::new(urls)) } + Self { inner: Arc::new(Inner::new(None, false)) } } } impl Resolver { - pub fn new(urls: Vec>) -> Self { - if urls.is_empty() { - panic!("Resolver: Empty URL list supplied to the constructor."); - } - - Self { inner: Arc::new(Inner::new(urls)) } + pub fn new(urls: Option>>, tls: bool) -> Self { + Self { inner: Arc::new(Inner::new(urls, tls)) } } pub fn urls(&self) -> Vec> { self.inner.urls.clone() } + pub fn tls(&self) -> bool { + self.inner.tls + } + + pub fn tls_as_str(&self) -> &'static str { + if self.inner.tls { + "tls" + } else { + "any" + } + } + fn make_url(&self, url: &str, encoding: Encoding, network_id: NetworkId) -> String { static TLS: OnceLock<&'static str> = OnceLock::new(); @@ -107,10 +121,10 @@ impl Resolver { if tls { "tls" } else { - "any" + self.tls_as_str() } } else { - "tls" + self.tls_as_str() } }); diff --git a/rpc/wrpc/wasm/src/resolver.rs b/rpc/wrpc/wasm/src/resolver.rs index ee4b5d883e..e53f389fca 100644 --- a/rpc/wrpc/wasm/src/resolver.rs +++ b/rpc/wrpc/wasm/src/resolver.rs @@ -21,6 +21,20 @@ declare! { * Optional URLs for one or multiple resolvers. */ urls?: string[]; + /** + * Use strict TLS for RPC connections. + * If not set or `false` (default), the resolver will + * provide the best available connection regardless of + * whether this connection supports TLS or not. + * If set to `true`, the resolver will only provide + * TLS-enabled connections. + * + * This setting is ignored in the browser environment + * when the browser navigator location is `https`. + * In which case the resolver will always use TLS-enabled + * connections. + */ + tls?: boolean; } "#, } @@ -163,14 +177,18 @@ impl Resolver { impl TryFrom for NativeResolver { type Error = Error; fn try_from(config: IResolverConfig) -> Result { - let resolver = config + let tls = config.get_bool("tls").unwrap_or(false); + let urls = config .get_vec("urls") .map(|urls| urls.into_iter().map(|v| v.as_string()).collect::>>()) .or_else(|_| config.dyn_into::().map(|urls| urls.into_iter().map(|v| v.as_string()).collect::>>())) - .map_err(|_| Error::custom("Invalid or missing resolver URL"))? - .map(|urls| NativeResolver::new(urls.into_iter().map(Arc::new).collect())); + .map_err(|_| Error::custom("Invalid or missing resolver URL"))?; - Ok(resolver.unwrap_or_default()) + if let Some(urls) = urls { + Ok(NativeResolver::new(Some(urls.into_iter().map(Arc::new).collect()), tls)) + } else { + Ok(NativeResolver::new(None, tls)) + } } } From e922a4e36a105f1710424cc4ee46ee79024df30e Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 3 Aug 2024 03:38:48 +0300 Subject: [PATCH 128/158] cleanup --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 841540a32a..016daa1bad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,7 +128,6 @@ kaspa-wallet-macros = { version = "0.14.1", path = "wallet/macros" } kaspa-wasm = { version = "0.14.1", path = "wasm" } kaspa-wasm-core = { version = "0.14.1", path = "wasm/core" } kaspa-wrpc-client = { version = "0.14.1", path = "rpc/wrpc/client" } -kaspa-wrpc-core = { version = "0.14.1", path = "rpc/wrpc/core" } kaspa-wrpc-proxy = { version = "0.14.1", path = "rpc/wrpc/proxy" } kaspa-wrpc-server = { version = "0.14.1", path = "rpc/wrpc/server" } kaspa-wrpc-wasm = { version = "0.14.1", path = "rpc/wrpc/wasm" } From 02f25663c8cfef3acd691f68753795f8e24c56eb Mon Sep 17 00:00:00 2001 From: KaffinPX <73744616+KaffinPX@users.noreply.github.com> Date: Sat, 3 Aug 2024 22:47:34 +0300 Subject: [PATCH 129/158] Improve input signature capability (#85) * ``signTransactionInput`` and move sign_input to its proper location * Fix typedoc warnings left from old PR * createInputSignature * Fix createInputSignature and improve PendingTransaction Inputs DX * Format * A small Omega change applied to existing code * Pass reference of transaction in createInputSignature --- consensus/core/src/sign.rs | 8 +++---- wallet/core/src/tx/generator/pending.rs | 19 ++++++++++++++-- wallet/core/src/wasm/signer.rs | 16 +++++++++----- wallet/core/src/wasm/tx/generator/pending.rs | 23 ++++++++++++++++---- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/consensus/core/src/sign.rs b/consensus/core/src/sign.rs index 174f689710..a40b949e35 100644 --- a/consensus/core/src/sign.rs +++ b/consensus/core/src/sign.rs @@ -3,7 +3,7 @@ use crate::{ sighash::{calc_schnorr_signature_hash, SigHashReusedValues}, sighash_type::{SigHashType, SIG_HASH_ALL}, }, - tx::SignableTransaction, + tx::{SignableTransaction, VerifiableTransaction}, }; use itertools::Itertools; use std::collections::BTreeMap; @@ -154,10 +154,10 @@ pub fn sign_with_multiple_v2(mut mutable_tx: SignableTransaction, privkeys: &[[u } /// Sign a transaction input with a sighash_type using schnorr -pub fn sign_input(tx: SignableTransaction, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Vec { +pub fn sign_input(tx: &impl VerifiableTransaction, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Vec { let mut reused_values = SigHashReusedValues::new(); - let hash = calc_schnorr_signature_hash(&tx.as_verifiable(), input_index, hash_type, &mut reused_values); + let hash = calc_schnorr_signature_hash(tx, input_index, hash_type, &mut reused_values); let msg = secp256k1::Message::from_digest_slice(hash.as_bytes().as_slice()).unwrap(); let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key).unwrap(); let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref(); @@ -166,7 +166,7 @@ pub fn sign_input(tx: SignableTransaction, input_index: usize, private_key: &[u8 std::iter::once(65u8).chain(sig).chain([hash_type.to_u8()]).collect() } -pub fn verify(tx: &impl crate::tx::VerifiableTransaction) -> Result<(), Error> { +pub fn verify(tx: &impl VerifiableTransaction) -> Result<(), Error> { let mut reused_values = SigHashReusedValues::new(); for (i, (input, entry)) in tx.populated_inputs().enumerate() { if input.signature_script.is_empty() { diff --git a/wallet/core/src/tx/generator/pending.rs b/wallet/core/src/tx/generator/pending.rs index a671fd3437..ad7e2e40f2 100644 --- a/wallet/core/src/tx/generator/pending.rs +++ b/wallet/core/src/tx/generator/pending.rs @@ -224,10 +224,11 @@ impl PendingTransaction { Ok(()) } - pub fn sign_input(&self, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Result> { + pub fn create_input_signature(&self, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Result> { let mutable_tx = self.inner.signable_tx.lock()?.clone(); + let verifiable_tx = mutable_tx.as_verifiable(); - Ok(sign_input(mutable_tx, input_index, private_key, hash_type)) + Ok(sign_input(&verifiable_tx, input_index, private_key, hash_type)) } pub fn fill_input(&self, input_index: usize, signature_script: Vec) -> Result<()> { @@ -238,6 +239,20 @@ impl PendingTransaction { Ok(()) } + pub fn sign_input(&self, input_index: usize, private_key: &[u8; 32], hash_type: SigHashType) -> Result<()> { + let mut mutable_tx = self.inner.signable_tx.lock()?.clone(); + + let signature_script = { + let verifiable_tx = &mutable_tx.as_verifiable(); + sign_input(verifiable_tx, input_index, private_key, hash_type) + }; + + mutable_tx.tx.inputs[input_index].signature_script = signature_script; + *self.inner.signable_tx.lock().unwrap() = mutable_tx; + + Ok(()) + } + pub fn try_sign_with_keys(&self, privkeys: &[[u8; 32]], check_fully_signed: Option) -> Result<()> { let mutable_tx = self.inner.signable_tx.lock()?.clone(); let signed = sign_with_multiple_v2(mutable_tx, privkeys); diff --git a/wallet/core/src/wasm/signer.rs b/wallet/core/src/wasm/signer.rs index 9e0ce26b2e..748a95a553 100644 --- a/wallet/core/src/wasm/signer.rs +++ b/wallet/core/src/wasm/signer.rs @@ -4,7 +4,7 @@ use js_sys::Array; use kaspa_consensus_client::{sign_with_multiple_v3, Transaction}; use kaspa_consensus_core::hashing::wasm::SighashType; use kaspa_consensus_core::sign::sign_input; -use kaspa_consensus_core::tx::{PopulatedTransaction, SignableTransaction}; +use kaspa_consensus_core::tx::PopulatedTransaction; use kaspa_consensus_core::{hashing::sighash_type::SIG_HASH_ALL, sign::verify}; use kaspa_hashes::Hash; use kaspa_wallet_keys::privatekey::PrivateKey; @@ -71,16 +71,20 @@ pub fn sign(tx: Transaction, privkeys: &[[u8; 32]]) -> Result { /// @category Wallet SDK #[wasm_bindgen(js_name = "createInputSignature")] pub fn create_input_signature( - tx: Transaction, + tx: &Transaction, input_index: u8, private_key: &PrivateKey, sighash_type: Option, ) -> Result { - let (cctx, _) = tx.tx_and_utxos()?; - let mutable_tx = SignableTransaction::new(cctx); + let (cctx, utxos) = tx.tx_and_utxos()?; + let populated_transaction = PopulatedTransaction::new(&cctx, utxos); - let signature = - sign_input(mutable_tx, input_index.into(), &private_key.secret_bytes(), sighash_type.unwrap_or(SighashType::All).into()); + let signature = sign_input( + &populated_transaction, + input_index.into(), + &private_key.secret_bytes(), + sighash_type.unwrap_or(SighashType::All).into(), + ); Ok(signature.to_hex().into()) } diff --git a/wallet/core/src/wasm/tx/generator/pending.rs b/wallet/core/src/wasm/tx/generator/pending.rs index 2257440d9a..f3af3299e9 100644 --- a/wallet/core/src/wasm/tx/generator/pending.rs +++ b/wallet/core/src/wasm/tx/generator/pending.rs @@ -72,10 +72,18 @@ impl PendingTransaction { self.inner.utxo_entries().values().map(|utxo_entry| JsValue::from(utxo_entry.clone())).collect() } - #[wasm_bindgen(js_name = signInput)] - pub fn sign_input(&self, input_index: u8, private_key: &PrivateKey, sighash_type: Option) -> Result { - let signature = - self.inner.sign_input(input_index.into(), &private_key.secret_bytes(), sighash_type.unwrap_or(SighashType::All).into())?; + #[wasm_bindgen(js_name = createInputSignature)] + pub fn create_input_signature( + &self, + input_index: u8, + private_key: &PrivateKey, + sighash_type: Option, + ) -> Result { + let signature = self.inner.create_input_signature( + input_index.into(), + &private_key.secret_bytes(), + sighash_type.unwrap_or(SighashType::All).into(), + )?; Ok(signature.to_hex().into()) } @@ -85,6 +93,13 @@ impl PendingTransaction { self.inner.fill_input(input_index.into(), signature_script.try_as_vec_u8()?) } + #[wasm_bindgen(js_name = signInput)] + pub fn sign_input(&self, input_index: u8, private_key: &PrivateKey, sighash_type: Option) -> Result<()> { + self.inner.sign_input(input_index.into(), &private_key.secret_bytes(), sighash_type.unwrap_or(SighashType::All).into())?; + + Ok(()) + } + /// Sign transaction with supplied [`Array`] or [`PrivateKey`] or an array of /// raw private key bytes (encoded as `Uint8Array` or as hex strings) pub fn sign(&self, js_value: PrivateKeyArrayT, check_fully_signed: Option) -> Result<()> { From ba31ab9e8605af0e1b5b966dcf8eeffb523ef73e Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 4 Aug 2024 00:02:16 +0300 Subject: [PATCH 130/158] fix WASM32 PSKT function names --- consensus/client/src/utils.rs | 2 +- wallet/pskt/src/wasm/pskt.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/consensus/client/src/utils.rs b/consensus/client/src/utils.rs index 48e4cdfbd1..b1da47a22c 100644 --- a/consensus/client/src/utils.rs +++ b/consensus/client/src/utils.rs @@ -40,7 +40,7 @@ pub fn pay_to_script_hash_signature_script(redeem_script: BinaryT, signature: Bi /// Returns the address encoded in a script public key. /// @param script_public_key - The script public key ({@link ScriptPublicKey}). -/// @param prefix - The address prefix. +/// @param network - The network type. /// @category Wallet SDK #[wasm_bindgen(js_name = addressFromScriptPublicKey)] pub fn address_from_script_public_key(script_public_key: ScriptPublicKeyT, network: &NetworkTypeT) -> Result { diff --git a/wallet/pskt/src/wasm/pskt.rs b/wallet/pskt/src/wasm/pskt.rs index e0f6cef201..78fe02bf68 100644 --- a/wallet/pskt/src/wasm/pskt.rs +++ b/wallet/pskt/src/wasm/pskt.rs @@ -137,6 +137,7 @@ impl PSKT { } /// Change role to `CREATOR` + /// #[wasm_bindgen(js_name = toCreator)] pub fn creator(&self) -> Result { let state = match self.take() { State::NoOp(inner) => match inner { @@ -150,6 +151,7 @@ impl PSKT { } /// Change role to `CONSTRUCTOR` + #[wasm_bindgen(js_name = toConstructor)] pub fn constructor(&self) -> Result { let state = match self.take() { State::NoOp(inner) => State::Constructor(inner.ok_or(Error::NotInitialized)?.into()), @@ -161,6 +163,7 @@ impl PSKT { } /// Change role to `UPDATER` + #[wasm_bindgen(js_name = toUpdater)] pub fn updater(&self) -> Result { let state = match self.take() { State::NoOp(inner) => State::Updater(inner.ok_or(Error::NotInitialized)?.into()), @@ -172,6 +175,7 @@ impl PSKT { } /// Change role to `SIGNER` + #[wasm_bindgen(js_name = toSigner)] pub fn signer(&self) -> Result { let state = match self.take() { State::NoOp(inner) => State::Signer(inner.ok_or(Error::NotInitialized)?.into()), @@ -185,6 +189,7 @@ impl PSKT { } /// Change role to `COMBINER` + #[wasm_bindgen(js_name = toCombiner)] pub fn combiner(&self) -> Result { let state = match self.take() { State::NoOp(inner) => State::Combiner(inner.ok_or(Error::NotInitialized)?.into()), @@ -198,6 +203,7 @@ impl PSKT { } /// Change role to `FINALIZER` + #[wasm_bindgen(js_name = toFinalizer)] pub fn finalizer(&self) -> Result { let state = match self.take() { State::NoOp(inner) => State::Finalizer(inner.ok_or(Error::NotInitialized)?.into()), @@ -209,6 +215,7 @@ impl PSKT { } /// Change role to `EXTRACTOR` + #[wasm_bindgen(js_name = toExtractor)] pub fn extractor(&self) -> Result { let state = match self.take() { State::NoOp(inner) => State::Extractor(inner.ok_or(Error::NotInitialized)?.into()), From 78adc1944d314c7afedacaa62db038404dec6785 Mon Sep 17 00:00:00 2001 From: aspect Date: Sun, 4 Aug 2024 14:11:28 +0300 Subject: [PATCH 131/158] refactor PSKB as a type wrapper + update serialization (#86) --- Cargo.lock | 23 ------- Cargo.toml | 1 - cli/src/modules/pskb.rs | 8 +-- wallet/core/src/account/pskb.rs | 5 +- wallet/pskt/Cargo.toml | 1 - wallet/pskt/src/bundle.rs | 117 ++++++++++++-------------------- wallet/pskt/src/error.rs | 4 ++ wallet/pskt/src/global.rs | 1 + wallet/pskt/src/input.rs | 1 + wallet/pskt/src/output.rs | 1 + wallet/pskt/src/pskt.rs | 17 +++++ 11 files changed, 73 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc2ae93310..2c15749bbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3589,7 +3589,6 @@ dependencies = [ "kaspa-txscript", "kaspa-txscript-errors", "kaspa-utils", - "rmp-serde", "secp256k1", "serde", "serde-value", @@ -5170,28 +5169,6 @@ dependencies = [ "libc", ] -[[package]] -name = "rmp" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" -dependencies = [ - "byteorder", - "rmp", - "serde", -] - [[package]] name = "rocksdb" version = "0.21.0" diff --git a/Cargo.toml b/Cargo.toml index 65536e98a9..016daa1bad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -217,7 +217,6 @@ regex = "1.10.2" ripemd = { version = "0.1.3", default-features = false } rlimit = "0.10.1" rocksdb = "0.21.0" -rmp-serde = "1.3.0" secp256k1 = { version = "0.28.2", features = [ "global-context", "rand-std", diff --git a/cli/src/modules/pskb.rs b/cli/src/modules/pskb.rs index 50ef5de116..97ed5d9994 100644 --- a/cli/src/modules/pskb.rs +++ b/cli/src/modules/pskb.rs @@ -50,7 +50,7 @@ impl Pskb { ) .await?; - match signer.to_hex() { + match signer.serialize() { Ok(encoded) => tprintln!(ctx, "{encoded}"), Err(e) => return Err(e.into()), } @@ -102,7 +102,7 @@ impl Pskb { ) .await?; - match signer.to_hex() { + match signer.serialize() { Ok(encoded) => tprintln!(ctx, "{encoded}"), Err(e) => return Err(e.into()), } @@ -138,7 +138,7 @@ impl Pskb { // Sweep UTXO set. match unlock_utxos_as_pskb(references, &receive_address, script_sig, priority_fee_sompi as u64) { Ok(pskb) => { - let pskb_hex = pskb.to_hex()?; + let pskb_hex = pskb.serialize()?; tprintln!(ctx, "{pskb_hex}"); } Err(e) => tprintln!(ctx, "Error generating unlock PSKB: {}", e.to_string()), @@ -199,7 +199,7 @@ impl Pskb { // Debug bundle view. tprintln!(ctx, "{:?}", pskb); - match pskb.inner_list.first() { + match pskb.as_ref().first() { Some(bundle_inner) => { let pskt: PSKT = PSKT::::from(bundle_inner.to_owned()); let mut fin = pskt.finalizer(); diff --git a/wallet/core/src/account/pskb.rs b/wallet/core/src/account/pskb.rs index b03eab2bae..984b9c0599 100644 --- a/wallet/core/src/account/pskb.rs +++ b/wallet/core/src/account/pskb.rs @@ -162,7 +162,6 @@ pub async fn pskb_signer_for_address( let addresses: Vec
= match sign_for_address { Some(signer) => vec![signer.clone()], None => bundle - .inner_list .iter() .flat_map(|inner| { inner.inputs @@ -179,7 +178,7 @@ pub async fn pskb_signer_for_address( // Prepare the signer. signer.ingest(addresses.as_ref())?; - for pskt_inner in bundle.inner_list.clone() { + for pskt_inner in bundle.iter().cloned() { let pskt: PSKT = PSKT::from(pskt_inner); let mut sign = |signer_pskt: PSKT| { @@ -272,7 +271,7 @@ pub fn finalize_pskt_no_sig_and_redeem_script(pskt: PSKT) -> Result

impl Stream, Error>> + Send { - stream::iter(bundle.inner_list.clone()).map(move |pskt_inner| { + stream::iter(bundle.iter().cloned().collect::>()).map(move |pskt_inner| { let pskt: PSKT = PSKT::from(pskt_inner); let pskt_finalizer = pskt.constructor().updater().signer().finalizer(); finalize_pskt_one_or_more_sig_and_redeem_script(pskt_finalizer) diff --git a/wallet/pskt/Cargo.toml b/wallet/pskt/Cargo.toml index 3e45dbadbe..b3fff1bfaf 100644 --- a/wallet/pskt/Cargo.toml +++ b/wallet/pskt/Cargo.toml @@ -33,7 +33,6 @@ js-sys.workspace = true futures.workspace = true hex.workspace = true secp256k1.workspace = true -rmp-serde.workspace = true serde_repr.workspace = true serde-value.workspace = true serde.workspace = true diff --git a/wallet/pskt/src/bundle.rs b/wallet/pskt/src/bundle.rs index 22e3caa709..54c716fe3e 100644 --- a/wallet/pskt/src/bundle.rs +++ b/wallet/pskt/src/bundle.rs @@ -12,116 +12,85 @@ use serde::{Deserialize, Serialize}; use std::ops::Deref; #[derive(Debug, Serialize, Deserialize)] -pub struct Bundle { - pub inner_list: Vec, -} +#[serde(rename_all = "camelCase")] +pub struct Bundle(pub Vec); impl From> for Bundle { fn from(pskt: PSKT) -> Self { - Bundle { inner_list: vec![pskt.deref().clone()] } + Bundle(vec![pskt.deref().clone()]) } } impl From>> for Bundle { fn from(pskts: Vec>) -> Self { let inner_list = pskts.into_iter().map(|pskt| pskt.deref().clone()).collect(); - Bundle { inner_list } + Bundle(inner_list) } } impl Bundle { pub fn new() -> Self { - Self { inner_list: Vec::new() } + Self(Vec::new()) } /// Adds an Inner instance to the bundle pub fn add_inner(&mut self, inner: PSKTInner) { - self.inner_list.push(inner); + self.0.push(inner); } /// Adds a PSKT instance to the bundle pub fn add_pskt(&mut self, pskt: PSKT) { - self.inner_list.push(pskt.deref().clone()); + self.0.push(pskt.deref().clone()); } /// Merges another bundle into the current bundle pub fn merge(&mut self, other: Bundle) { - for inner in other.inner_list { - self.inner_list.push(inner); - } - } - - pub fn to_hex(&self) -> Result { - match TypeMarked::new(self, Marker::Pskb) { - Ok(type_marked) => match serde_json::to_string(&type_marked) { - Ok(result) => Ok(hex::encode(result)), - Err(e) => Err(Error::PskbSerializeToHexError(e.to_string())), - }, - Err(e) => Err(Error::PskbSerializeToHexError(e.to_string())), + for inner in other.0 { + self.0.push(inner); } } - pub fn from_hex(hex_data: &str) -> Result { - let bundle: TypeMarked = serde_json::from_slice(hex::decode(hex_data)?.as_slice())?; - Ok(bundle.data) + /// Iterator over the inner PSKT instances + pub fn iter(&self) -> std::slice::Iter { + self.0.iter() } -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -enum Marker { - Pskb, -} -impl Marker { - fn as_str(&self) -> &str { - match self { - Marker::Pskb => "pskb", - } + pub fn serialize(&self) -> Result { + Ok(format!("PSKB{}", hex::encode(serde_json::to_string(self)?))) } - fn from_str(marker: &str) -> Result { - match marker { - "pskb" => Ok(Marker::Pskb), - _ => Err("Invalid pskb type marker".into()), + pub fn deserialize(hex_data: &str) -> Result { + if let Some(hex_data) = hex_data.strip_prefix("PSKB") { + Ok(serde_json::from_slice(hex::decode(hex_data)?.as_slice())?) + } else { + Err(Error::PskbPrefixError) } } } -#[derive(Serialize, Deserialize, Debug)] -struct TypeMarked { - type_marker: String, - #[serde(flatten)] - data: T, -} - -impl TypeMarked { - fn new(data: T, marker: Marker) -> Result { - let type_marker = marker.as_str().to_string(); - if Marker::from_str(&type_marker)? == marker { - Ok(Self { type_marker, data }) - } else { - Err("Invalid pskb type marker".into()) - } +impl AsRef<[PSKTInner]> for Bundle { + fn as_ref(&self) -> &[PSKTInner] { + self.0.as_slice() } } impl TryFrom for Bundle { type Error = Error; fn try_from(value: String) -> Result { - Bundle::from_hex(&value) + Bundle::deserialize(&value) } } impl TryFrom<&str> for Bundle { type Error = Error; fn try_from(value: &str) -> Result { - Bundle::from_hex(value) + Bundle::deserialize(value) } } impl TryFrom for String { type Error = Error; fn try_from(value: Bundle) -> Result { - match Bundle::to_hex(&value) { + match Bundle::serialize(&value) { Ok(output) => Ok(output.to_owned()), Err(e) => Err(Error::PskbSerializeError(e.to_string())), } @@ -232,7 +201,6 @@ mod tests { use kaspa_consensus_core::tx::{TransactionId, TransactionOutpoint, UtxoEntry}; use kaspa_txscript::{multisig_redeem_script, pay_to_script_hash_script}; // use kaspa_txscript::{multisig_redeem_script, opcodes::codes::OpData65, pay_to_script_hash_script, script_builder::ScriptBuilder}; - use rmp_serde::{decode, encode}; use secp256k1::Secp256k1; use secp256k1::{rand::thread_rng, Keypair}; use std::str::FromStr; @@ -279,26 +247,27 @@ mod tests { } #[test] - fn test_serialization() { + fn test_pskb_serialization() { let constructor = mock_pskt_constructor(); let bundle = Bundle::from(constructor.clone()); - // Serialize to MessagePack - let mut buf = Vec::new(); - encode::write(&mut buf, &bundle).expect("Serialize PSKB"); - println!("Serialized: {:?}", buf); + println!("Bundle: {}", serde_json::to_string(&bundle).unwrap()); + + // Serialize Bundle + let serialized = bundle.serialize().map_err(|err| format!("Unable to serialize bundle: {err}")).unwrap(); + println!("Serialized: {}", serialized); - assert!(!bundle.inner_list.is_empty()); + assert!(!bundle.0.is_empty()); // todo: discuss why deserializing from MessagePack errors - match decode::from_slice::(&buf) { + match Bundle::deserialize(&serialized) { Ok(bundle_constructor_deser) => { println!("Deserialized: {:?}", bundle_constructor_deser); let pskt_constructor_deser: Option> = - bundle_constructor_deser.inner_list.first().map(|inner| PSKT::from(inner.clone())); + bundle_constructor_deser.0.first().map(|inner| PSKT::from(inner.clone())); match pskt_constructor_deser { Some(_) => println!("PSKT deserialized successfully"), - None => println!("No elements in inner_list to deserialize"), + None => println!("No elements in the inner list to deserialize"), } } Err(e) => { @@ -309,28 +278,28 @@ mod tests { } #[test] - fn test_bundle_creation() { + fn test_pskb_bundle_creation() { let bundle = Bundle::new(); - assert!(bundle.inner_list.is_empty()); + assert!(bundle.0.is_empty()); } #[test] - fn test_new_with_pskt() { + fn test_pskb_new_with_pskt() { let pskt = PSKT::::default(); let bundle = Bundle::from(pskt); - assert_eq!(bundle.inner_list.len(), 1); + assert_eq!(bundle.0.len(), 1); } #[test] - fn test_add_pskt() { + fn test_pskb_add_pskt() { let mut bundle = Bundle::new(); let pskt = PSKT::::default(); bundle.add_pskt(pskt); - assert_eq!(bundle.inner_list.len(), 1); + assert_eq!(bundle.0.len(), 1); } #[test] - fn test_merge_bundles() { + fn test_pskb_merge_bundles() { let mut bundle1 = Bundle::new(); let mut bundle2 = Bundle::new(); @@ -342,6 +311,6 @@ mod tests { bundle1.merge(bundle2); - assert_eq!(bundle1.inner_list.len(), 2); + assert_eq!(bundle1.0.len(), 2); } } diff --git a/wallet/pskt/src/error.rs b/wallet/pskt/src/error.rs index 4441bd231a..11303ae4a3 100644 --- a/wallet/pskt/src/error.rs +++ b/wallet/pskt/src/error.rs @@ -36,6 +36,10 @@ pub enum Error { P2SHExtractError(#[source] TxScriptError), #[error("PSKB hex serialization error: {0}")] PskbSerializeToHexError(String), + #[error("PSKB serialization requires 'PSKB' prefix")] + PskbPrefixError, + #[error("PSKT serialization requires 'PSKT' prefix")] + PsktPrefixError, } #[derive(thiserror::Error, Debug)] pub enum ConstructorError { diff --git a/wallet/pskt/src/global.rs b/wallet/pskt/src/global.rs index fe0b36fb91..b79798776d 100644 --- a/wallet/pskt/src/global.rs +++ b/wallet/pskt/src/global.rs @@ -11,6 +11,7 @@ use std::{ type Xpub = kaspa_bip32::ExtendedPublicKey; #[derive(Debug, Clone, Builder, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] #[builder(default)] pub struct Global { /// The version number of this PSKT. diff --git a/wallet/pskt/src/input.rs b/wallet/pskt/src/input.rs index 8c7102fb86..c99ae25426 100644 --- a/wallet/pskt/src/input.rs +++ b/wallet/pskt/src/input.rs @@ -10,6 +10,7 @@ use std::{collections::BTreeMap, marker::PhantomData, ops::Add}; // todo add unknown field? combine them by deduplicating, if there are different values - return error? #[derive(Builder, Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] #[builder(default)] #[builder(setter(skip))] pub struct Input { diff --git a/wallet/pskt/src/output.rs b/wallet/pskt/src/output.rs index ce1d2eec10..e873ce4a66 100644 --- a/wallet/pskt/src/output.rs +++ b/wallet/pskt/src/output.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, ops::Add}; #[derive(Builder, Default, Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] #[builder(default)] pub struct Output { /// The output's amount (serialized as sompi). diff --git a/wallet/pskt/src/pskt.rs b/wallet/pskt/src/pskt.rs index 024f033769..245609803d 100644 --- a/wallet/pskt/src/pskt.rs +++ b/wallet/pskt/src/pskt.rs @@ -17,6 +17,7 @@ use kaspa_consensus_core::{ use kaspa_txscript::{caches::Cache, TxScriptEngine}; #[derive(Debug, Default, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] pub struct Inner { /// The global map. pub global: Global, @@ -44,6 +45,7 @@ impl Display for Version { /// Full information on the used extended public key: fingerprint of the /// master extended public key and a derivation path from it. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct KeySource { #[serde(with = "kaspa_utils::serde_bytes_fixed")] pub key_fingerprint: KeyFingerprint, @@ -59,6 +61,7 @@ impl KeySource { pub type PartialSigs = BTreeMap; #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Copy, Clone)] +#[serde(rename_all = "camelCase")] pub enum Signature { ECDSA(secp256k1::ecdsa::Signature), Schnorr(secp256k1::schnorr::Signature), @@ -74,6 +77,7 @@ impl Signature { } #[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct PSKT { #[serde(flatten)] inner_pskt: Inner, @@ -137,6 +141,18 @@ impl PSKT { fn determine_lock_time(&self) -> u64 { self.inputs.iter().map(|input: &Input| input.min_time).max().unwrap_or(self.global.fallback_lock_time).unwrap_or(0) } + + pub fn to_hex(&self) -> Result { + Ok(format!("PSKT{}", hex::encode(serde_json::to_string(self)?))) + } + + pub fn from_hex(hex_data: &str) -> Result { + if let Some(hex_data) = hex_data.strip_prefix("PSKT") { + Ok(serde_json::from_slice(hex::decode(hex_data)?.as_slice())?) + } else { + Err(Error::PsktPrefixError) + } + } } impl Default for PSKT { @@ -278,6 +294,7 @@ impl PSKT { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct SignInputOk { pub signature: Signature, pub pub_key: secp256k1::PublicKey, From 346be90d0c4c679667a8400947ad1f0fe6e94bd0 Mon Sep 17 00:00:00 2001 From: 1bananagirl <168954040+1bananagirl@users.noreply.github.com> Date: Sun, 4 Aug 2024 21:31:33 +0200 Subject: [PATCH 132/158] Cleanup of unused JSON test file for PSKB and comments (#87) * Remove PSKB json test file. * Remove/change old PSKB comments and commented out inclusions. --- cli/src/modules/pskb_redeem.json | 99 -------------------------------- wallet/core/src/account/pskb.rs | 6 +- wallet/pskt/src/bundle.rs | 3 - 3 files changed, 3 insertions(+), 105 deletions(-) delete mode 100644 cli/src/modules/pskb_redeem.json diff --git a/cli/src/modules/pskb_redeem.json b/cli/src/modules/pskb_redeem.json deleted file mode 100644 index 9f3e00d654..0000000000 --- a/cli/src/modules/pskb_redeem.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "inner_list": [ - { - "global": { - "version": 0, - "tx_version": 0, - "fallback_lock_time": null, - "inputs_modifiable": false, - "outputs_modifiable": false, - "input_count": 1, - "output_count": 0, - "xpubs": {}, - "id": null, - "proprietaries": {} - }, - "inputs": [ - { - "utxo_entry": { - "amount": 3300000000, - "scriptPublicKey": "000020f2b9984ea0bf9464b028534a51c84dca8d8e8037499ce9a01f43d717dc4b157cac", - "blockDaaScore": 160987746, - "isCoinbase": false - }, - "previous_outpoint": { - "transactionId": "6323bee26238b82f926176c08b8919c42cdb204a14c17fd35c0e2664326133ff", - "index": 0 - }, - "sequence": null, - "min_time": null, - "partial_sigs": {}, - "sighash_type": 1, - "redeem_script": null, - "sig_op_count": 1, - "bip32_derivations": {}, - "final_script_sig": null, - "proprietaries": {} - }, - { - "utxo_entry": { - "amount": 33210791745569, - "scriptPublicKey": "0000200e6227c375a68b02da57c58f8eaf837a003c59e957890baf1cf18aaf28aca76dac", - "blockDaaScore": 160987746, - "isCoinbase": false - }, - "previous_outpoint": { - "transactionId": "6323bee26238b82f926176c08b8919c42cdb204a14c17fd35c0e2664326133ff", - "index": 1 - }, - "sequence": null, - "min_time": null, - "partial_sigs": {}, - "sighash_type": 1, - "redeem_script": null, - "sig_op_count": 1, - "bip32_derivations": {}, - "final_script_sig": null, - "proprietaries": {} - }, - { - "utxo_entry": { - "amount": 3300000000, - "scriptPublicKey": "000020f2b9984ea0bf9464b028534a51c84dca8d8e8037499ce9a01f43d717dc4b157cac", - "blockDaaScore": 160987746, - "isCoinbase": false - }, - "previous_outpoint": { - "transactionId": "w", - "index": 0 - }, - "sequence": null, - "min_time": null, - "partial_sigs": {}, - "sighash_type": 1, - "redeem_script": "ac", - "sig_op_count": 1, - "bip32_derivations": {}, - "final_script_sig": null, - "proprietaries": {} - } - ], - "outputs": [ - { - "amount": 6400000000, - "script_public_key": "000020f2b9984ea0bf9464b028534a51c84dca8d8e8037499ce9a01f43d717dc4b157cac", - "redeem_script": null, - "bip32_derivations": {}, - "proprietaries": {} - }, - { - "amount": 33207691742259, - "script_public_key": "0000200e6227c375a68b02da57c58f8eaf837a003c59e957890baf1cf18aaf28aca76dac", - "redeem_script": null, - "bip32_derivations": {}, - "proprietaries": {} - } - ] - } - ] -} \ No newline at end of file diff --git a/wallet/core/src/account/pskb.rs b/wallet/core/src/account/pskb.rs index 984b9c0599..5cf1eeea9a 100644 --- a/wallet/core/src/account/pskb.rs +++ b/wallet/core/src/account/pskb.rs @@ -107,7 +107,7 @@ impl Stream for PSKTStream { type Item = Result, Error>; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = self.as_ref(); // Access the mutable reference to self + let this = self.as_ref(); let _prefix = this.prefix; let _signer = this.signer.clone(); @@ -192,8 +192,8 @@ pub async fn pskb_signer_for_address( let hash = calc_schnorr_signature_hash(&tx.as_verifiable(), idx, sighash[idx], &mut reused_values); let msg = secp256k1::Message::from_digest_slice(hash.as_bytes().as_slice()).unwrap(); - // When address represents a lock utxo, no private key is available. - // Instead, use the account receive address private key. + // When address represents a locked UTXO, no private key is available. + // Instead, use the account receive address' private key. let address: &Address = match sign_for_address { Some(address) => address, None => addresses.get(idx).expect("Input indexed address"), diff --git a/wallet/pskt/src/bundle.rs b/wallet/pskt/src/bundle.rs index 54c716fe3e..c508684106 100644 --- a/wallet/pskt/src/bundle.rs +++ b/wallet/pskt/src/bundle.rs @@ -197,10 +197,8 @@ mod tests { use crate::prelude::*; use crate::role::Creator; use crate::role::*; - // hashing::sighash::{calc_schnorr_signature_hash, SigHashReusedValues}, use kaspa_consensus_core::tx::{TransactionId, TransactionOutpoint, UtxoEntry}; use kaspa_txscript::{multisig_redeem_script, pay_to_script_hash_script}; - // use kaspa_txscript::{multisig_redeem_script, opcodes::codes::OpData65, pay_to_script_hash_script, script_builder::ScriptBuilder}; use secp256k1::Secp256k1; use secp256k1::{rand::thread_rng, Keypair}; use std::str::FromStr; @@ -259,7 +257,6 @@ mod tests { assert!(!bundle.0.is_empty()); - // todo: discuss why deserializing from MessagePack errors match Bundle::deserialize(&serialized) { Ok(bundle_constructor_deser) => { println!("Deserialized: {:?}", bundle_constructor_deser); From 7702b9ff5e765d2d58074a8a2d657ec070a0d556 Mon Sep 17 00:00:00 2001 From: aspect Date: Sun, 4 Aug 2024 22:32:09 +0300 Subject: [PATCH 133/158] PSKB+PSKT merge with omega branch (#82) * TransactionInput signature_script as Option, add associated TS types * restructure PSKT + WASM scaffolding (WIP) * Base implementation for PSKB and usage in core account with generator wrapper (#64) * Base implementation for PSKB and usage in core account with generator wrapper stream handling. * prune test file * prune test file * Converters for transanction and populated transaction. * Optional signature import in PSKT conversion. * PSKB wallet cli commands. * More PSKB wallet cli commands for custom script locks. * Serialization test case * Reviews patches, cli script debug command added. * Doc about fee per transaction for script unlocking UTXOS * Parameter changed to priority_fee_sompi_per_transaction, revert function renaming. * Error handling * Adding type conversion. (#76) * fmt * fix WASM32 PSKT function names * refactor PSKB as a type wrapper + update serialization (#86) * Cleanup of unused JSON test file for PSKB and comments (#87) * Remove PSKB json test file. * Remove/change old PSKB comments and commented out inclusions. --------- Co-authored-by: 1bananagirl <168954040+1bananagirl@users.noreply.github.com> --- Cargo.lock | 11 + cli/Cargo.toml | 2 + cli/src/error.rs | 6 + cli/src/modules/mod.rs | 3 +- cli/src/modules/pskb.rs | 259 ++++++++++ cli/src/modules/send.rs | 2 +- consensus/client/src/input.rs | 21 +- consensus/client/src/lib.rs | 13 +- consensus/client/src/outpoint.rs | 9 + consensus/client/src/output.rs | 30 +- consensus/client/src/serializable/mod.rs | 2 +- consensus/client/src/serializable/numeric.rs | 19 +- consensus/client/src/serializable/string.rs | 6 +- consensus/client/src/transaction.rs | 12 +- consensus/client/src/utils.rs | 2 +- rpc/core/src/wasm/convert.rs | 2 +- wallet/core/Cargo.toml | 1 + wallet/core/src/account/mod.rs | 67 +++ wallet/core/src/account/pskb.rs | 360 ++++++++++++++ wallet/core/src/error.rs | 5 + wallet/core/src/wasm/tx/utils.rs | 2 +- wallet/pskt/Cargo.toml | 11 +- wallet/pskt/examples/multisig.rs | 4 +- wallet/pskt/src/bundle.rs | 313 ++++++++++++ wallet/pskt/src/convert.rs | 109 +++++ wallet/pskt/src/error.rs | 55 ++- wallet/pskt/src/global.rs | 5 +- wallet/pskt/src/input.rs | 9 +- wallet/pskt/src/lib.rs | 484 ++----------------- wallet/pskt/src/output.rs | 3 +- wallet/pskt/src/pskt.rs | 472 ++++++++++++++++++ wallet/pskt/src/wasm/bundle.rs | 1 + wallet/pskt/src/wasm/error.rs | 64 +++ wallet/pskt/src/wasm/input.rs | 1 + wallet/pskt/src/wasm/mod.rs | 6 + wallet/pskt/src/wasm/output.rs | 1 + wallet/pskt/src/wasm/pskt.rs | 317 ++++++++++++ wallet/pskt/src/wasm/result.rs | 1 + 38 files changed, 2171 insertions(+), 519 deletions(-) create mode 100644 cli/src/modules/pskb.rs create mode 100644 wallet/core/src/account/pskb.rs create mode 100644 wallet/pskt/src/bundle.rs create mode 100644 wallet/pskt/src/convert.rs create mode 100644 wallet/pskt/src/pskt.rs create mode 100644 wallet/pskt/src/wasm/bundle.rs create mode 100644 wallet/pskt/src/wasm/error.rs create mode 100644 wallet/pskt/src/wasm/input.rs create mode 100644 wallet/pskt/src/wasm/mod.rs create mode 100644 wallet/pskt/src/wasm/output.rs create mode 100644 wallet/pskt/src/wasm/pskt.rs create mode 100644 wallet/pskt/src/wasm/result.rs diff --git a/Cargo.lock b/Cargo.lock index a41b22aebb..2c15749bbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2504,6 +2504,7 @@ dependencies = [ "downcast", "faster-hex 0.6.1", "futures", + "hex", "js-sys", "kaspa-addresses", "kaspa-bip32", @@ -2515,6 +2516,7 @@ dependencies = [ "kaspa-utils", "kaspa-wallet-core", "kaspa-wallet-keys", + "kaspa-wallet-pskt", "kaspa-wrpc-client", "nw-sys", "pad", @@ -3490,6 +3492,7 @@ dependencies = [ "kaspa-utils", "kaspa-wallet-keys", "kaspa-wallet-macros", + "kaspa-wallet-pskt", "kaspa-wasm-core", "kaspa-wrpc-client", "kaspa-wrpc-wasm", @@ -3574,7 +3577,12 @@ dependencies = [ name = "kaspa-wallet-pskt" version = "0.14.1" dependencies = [ + "bincode", "derive_builder", + "futures", + "hex", + "js-sys", + "kaspa-addresses", "kaspa-bip32", "kaspa-consensus-client", "kaspa-consensus-core", @@ -3584,9 +3592,12 @@ dependencies = [ "secp256k1", "serde", "serde-value", + "serde-wasm-bindgen", "serde_json", "serde_repr", "thiserror", + "wasm-bindgen", + "workflow-wasm", ] [[package]] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f35f7fd57f..60a43002a0 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -33,6 +33,7 @@ downcast.workspace = true faster-hex.workspace = true futures.workspace = true js-sys.workspace = true +hex.workspace = true kaspa-addresses.workspace = true kaspa-bip32.workspace = true kaspa-consensus-core.workspace = true @@ -43,6 +44,7 @@ kaspa-rpc-core.workspace = true kaspa-utils.workspace = true kaspa-wallet-core.workspace = true kaspa-wallet-keys.workspace = true +kaspa-wallet-pskt.workspace = true kaspa-wrpc-client.workspace = true nw-sys.workspace = true pad.workspace = true diff --git a/cli/src/error.rs b/cli/src/error.rs index 10f43118fa..23bb261243 100644 --- a/cli/src/error.rs +++ b/cli/src/error.rs @@ -128,6 +128,12 @@ pub enum Error { #[error(transparent)] KaspaWalletKeys(#[from] kaspa_wallet_keys::error::Error), + + #[error(transparent)] + PskbLockScriptSigError(#[from] kaspa_wallet_pskt::error::Error), + + #[error("To hex serialization error")] + PskbSerializeToHexError, } impl Error { diff --git a/cli/src/modules/mod.rs b/cli/src/modules/mod.rs index a6371814f2..7990a9d6e1 100644 --- a/cli/src/modules/mod.rs +++ b/cli/src/modules/mod.rs @@ -26,6 +26,7 @@ pub mod network; pub mod node; pub mod open; pub mod ping; +pub mod pskb; pub mod reload; pub mod rpc; pub mod select; @@ -57,7 +58,7 @@ pub fn register_handlers(cli: &Arc) -> Result<()> { cli.handlers(), [ account, address, close, connect, details, disconnect, estimate, exit, export, guide, help, history, rpc, list, miner, - message, monitor, mute, network, node, open, ping, reload, select, send, server, settings, sweep, track, transfer, + message, monitor, mute, network, node, open, ping, pskb, reload, select, send, server, settings, sweep, track, transfer, wallet, // halt, // theme, start, stop diff --git a/cli/src/modules/pskb.rs b/cli/src/modules/pskb.rs new file mode 100644 index 0000000000..97ed5d9994 --- /dev/null +++ b/cli/src/modules/pskb.rs @@ -0,0 +1,259 @@ +#![allow(unused_imports)] + +use crate::imports::*; +use kaspa_consensus_core::tx::{TransactionOutpoint, UtxoEntry}; +use kaspa_wallet_core::account::pskb::finalize_pskt_one_or_more_sig_and_redeem_script; +use kaspa_wallet_pskt::prelude::{lock_script_sig_templating, script_sig_to_address, unlock_utxos_as_pskb, Bundle, Signer, PSKT}; + +#[derive(Default, Handler)] +#[help("Send a Kaspa transaction to a public address")] +pub struct Pskb; + +impl Pskb { + async fn main(self: Arc, ctx: &Arc, mut argv: Vec, _cmd: &str) -> Result<()> { + let ctx = ctx.clone().downcast_arc::()?; + + if !ctx.wallet().is_open() { + return Err(Error::WalletIsNotOpen); + } + + let account = ctx.wallet().account()?; + + if argv.is_empty() { + return self.display_help(ctx, argv).await; + } + + let action = argv.remove(0); + + match action.as_str() { + "create" => { + if argv.len() < 2 || argv.len() > 3 { + return self.display_help(ctx, argv).await; + } else { + let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; + let _ = ctx.notifier().show(Notification::Processing).await; + + let address = Address::try_from(argv.first().unwrap().as_str())?; + let amount_sompi = try_parse_required_nonzero_kaspa_as_sompi_u64(argv.get(1))?; + let outputs = PaymentOutputs::from((address, amount_sompi)); + let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.get(2))?.unwrap_or(0); + let abortable = Abortable::default(); + + let signer = account + .pskb_from_send_generator( + outputs.into(), + priority_fee_sompi.into(), + None, + wallet_secret.clone(), + payment_secret.clone(), + &abortable, + ) + .await?; + + match signer.serialize() { + Ok(encoded) => tprintln!(ctx, "{encoded}"), + Err(e) => return Err(e.into()), + } + } + } + "script" => { + if argv.len() < 2 || argv.len() > 4 { + return self.display_help(ctx, argv).await; + } + + let subcommand = argv.remove(0); + let payload = argv.remove(0); + + let receive_address = account.receive_address()?; + let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; + let _ = ctx.notifier().show(Notification::Processing).await; + + let script_sig = match lock_script_sig_templating(payload.clone(), Some(&receive_address.payload)) { + Ok(value) => value, + Err(e) => { + terrorln!(ctx, "{}", e.to_string()); + return Err(e.into()); + } + }; + + let script_p2sh = match script_sig_to_address(&script_sig, ctx.wallet().address_prefix()?) { + Ok(p2sh) => p2sh, + Err(e) => { + terrorln!(ctx, "Error generating script address: {}", e.to_string()); + return Err(e.into()); + } + }; + + match subcommand.as_str() { + "lock" => { + let amount_sompi = try_parse_required_nonzero_kaspa_as_sompi_u64(argv.first())?; + let outputs = PaymentOutputs::from((script_p2sh, amount_sompi)); + let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.get(1))?.unwrap_or(0); + let abortable = Abortable::default(); + + let signer = account + .pskb_from_send_generator( + outputs.into(), + priority_fee_sompi.into(), + None, + wallet_secret.clone(), + payment_secret.clone(), + &abortable, + ) + .await?; + + match signer.serialize() { + Ok(encoded) => tprintln!(ctx, "{encoded}"), + Err(e) => return Err(e.into()), + } + } + "unlock" => { + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } + + // Get locked UTXO set. + let spend_utxos: Vec = + ctx.wallet().rpc_api().get_utxos_by_addresses(vec![script_p2sh.clone()]).await?; + let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.first())?.unwrap_or(0) as u64; + + if spend_utxos.is_empty() { + twarnln!(ctx, "No locked UTXO set found."); + return Ok(()); + } + + let references: Vec<(UtxoEntry, TransactionOutpoint)> = + spend_utxos.iter().map(|entry| (entry.utxo_entry.clone().into(), entry.outpoint.into())).collect(); + + let total_locked_sompi: u64 = spend_utxos.iter().map(|entry| entry.utxo_entry.amount).sum(); + + tprintln!( + ctx, + "{} locked UTXO{} found with total amount of {} KAS", + spend_utxos.len(), + if spend_utxos.len() == 1 { "" } else { "s" }, + sompi_to_kaspa(total_locked_sompi) + ); + + // Sweep UTXO set. + match unlock_utxos_as_pskb(references, &receive_address, script_sig, priority_fee_sompi as u64) { + Ok(pskb) => { + let pskb_hex = pskb.serialize()?; + tprintln!(ctx, "{pskb_hex}"); + } + Err(e) => tprintln!(ctx, "Error generating unlock PSKB: {}", e.to_string()), + } + } + "sign" => { + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + + // Sign PSKB using the account's receiver address. + match account.pskb_sign(&pskb, wallet_secret.clone(), payment_secret.clone(), Some(&receive_address)).await { + Ok(signed_pskb) => { + let pskb_pack = String::try_from(signed_pskb)?; + tprintln!(ctx, "{pskb_pack}"); + } + Err(e) => terrorln!(ctx, "{}", e.to_string()), + } + } + "address" => { + tprintln!(ctx, "\r\nP2SH address: {}", script_p2sh); + } + v => { + terrorln!(ctx, "unknown command: '{v}'\r\n"); + return self.display_help(ctx, argv).await; + } + } + } + "sign" => { + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } else { + let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + + match account.pskb_sign(&pskb, wallet_secret.clone(), payment_secret.clone(), None).await { + Ok(signed_pskb) => { + let pskb_pack = String::try_from(signed_pskb)?; + tprintln!(ctx, "{pskb_pack}"); + } + Err(e) => terrorln!(ctx, "{}", e.to_string()), + } + } + } + "send" => { + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } else { + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + + match account.pskb_broadcast(&pskb).await { + Ok(sent) => tprintln!(ctx, "Sent transactions {:?}", sent), + Err(e) => terrorln!(ctx, "Send error {:?}", e), + } + } + } + "debug" => { + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + + // Debug bundle view. + tprintln!(ctx, "{:?}", pskb); + + match pskb.as_ref().first() { + Some(bundle_inner) => { + let pskt: PSKT = PSKT::::from(bundle_inner.to_owned()); + let mut fin = pskt.finalizer(); + + fin = finalize_pskt_one_or_more_sig_and_redeem_script(fin).expect("Finalized PSKT"); + + // Verify if extraction is possible. + match fin.extractor() { + Ok(ex) => match ex.extract_tx() { + Ok(_) => tprintln!( + ctx, + "Transaction extracted successfuly meaning it is finalized with a valid script signature." + ), + Err(e) => terrorln!(ctx, "Transaction extraction error: {}", e.to_string()), + }, + Err(_) => twarnln!(ctx, "PSKB not finalized"), + } + } + None => { + twarnln!(ctx, "Debugging an empty PSKB"); + } + } + } + v => { + tprintln!(ctx, "unknown command: '{v}'\r\n"); + return self.display_help(ctx, argv).await; + } + } + Ok(()) + } + + fn parse_input_pskb(input: &str) -> Result { + match Bundle::try_from(input) { + Ok(bundle) => Ok(bundle), + Err(e) => Err(Error::custom(format!("Error while parsing input PSKB {}", e))), + } + } + + async fn display_help(self: Arc, ctx: Arc, _argv: Vec) -> Result<()> { + ctx.term().help( + &[ + ("pskb create

", "Create a PSKB from single send transaction"), + ("pskb sign ", "Sign given PSKB"), + ("pskb send ", "Broadcast bundled transactions"), + ("pskb debug ", "Print PSKB debug view"), + ("pskb script lock [priority fee]", "Generate a PSKB with one send transaction to given P2SH payload. Optional public key placeholder in payload: {{pubkey}}"), + ("pskb script unlock ", "Generate a PSKB to unlock UTXOS one by one from given P2SH payload. Fee amount will be applied to every spent UTXO, meaning every transaction. Optional public key placeholder in payload: {{pubkey}}"), + ("pskb script sign ", "Sign all PSKB's P2SH locked inputs"), + ("pskb script sign ", "Sign all PSKB's P2SH locked inputs"), + ("pskb script address ", "Prints P2SH address"), + ], + None, + )?; + + Ok(()) + } +} diff --git a/cli/src/modules/send.rs b/cli/src/modules/send.rs index d9f35d994d..773861dd4a 100644 --- a/cli/src/modules/send.rs +++ b/cli/src/modules/send.rs @@ -39,7 +39,7 @@ impl Send { .await?; tprintln!(ctx, "Send - {summary}"); - // tprintln!(ctx, "\nSending {} KAS to {address}, tx ids:", sompi_to_kaspa_string(amount_sompi)); + tprintln!(ctx, "\nSending {} KAS to {address}, tx ids:", sompi_to_kaspa_string(amount_sompi)); // tprintln!(ctx, "{}\n", ids.into_iter().map(|a| a.to_string()).collect::>().join("\n")); Ok(()) diff --git a/consensus/client/src/input.rs b/consensus/client/src/input.rs index 81b1d009a0..8633e2deab 100644 --- a/consensus/client/src/input.rs +++ b/consensus/client/src/input.rs @@ -45,7 +45,7 @@ extern "C" { #[serde(rename_all = "camelCase")] pub struct TransactionInputInner { pub previous_outpoint: TransactionOutpoint, - pub signature_script: Vec, + pub signature_script: Option>, pub sequence: u64, pub sig_op_count: u8, pub utxo: Option, @@ -54,7 +54,7 @@ pub struct TransactionInputInner { impl TransactionInputInner { pub fn new( previous_outpoint: TransactionOutpoint, - signature_script: Vec, + signature_script: Option>, sequence: u64, sig_op_count: u8, utxo: Option, @@ -74,7 +74,7 @@ pub struct TransactionInput { impl TransactionInput { pub fn new( previous_outpoint: TransactionOutpoint, - signature_script: Vec, + signature_script: Option>, sequence: u64, sig_op_count: u8, utxo: Option, @@ -96,7 +96,7 @@ impl TransactionInput { } pub fn signature_script_length(&self) -> usize { - self.inner().signature_script.len() + self.inner().signature_script.as_ref().map(|signature_script| signature_script.len()).unwrap_or_default() } pub fn utxo(&self) -> Option { @@ -128,8 +128,8 @@ impl TransactionInput { } #[wasm_bindgen(getter = signatureScript)] - pub fn get_signature_script_as_hex(&self) -> String { - self.inner().signature_script.to_hex() + pub fn get_signature_script_as_hex(&self) -> Option { + self.inner().signature_script.as_ref().map(|script| script.to_hex()) } #[wasm_bindgen(setter = signatureScript)] @@ -171,7 +171,7 @@ impl TransactionInput { impl TransactionInput { pub fn set_signature_script(&self, signature_script: Vec) { - self.inner().signature_script = signature_script; + self.inner().signature_script.replace(signature_script); } pub fn script_public_key(&self) -> Option { @@ -191,7 +191,7 @@ impl TryCastFromJs for TransactionInput { Self::resolve_cast(&value, || { if let Some(object) = Object::try_from(value.as_ref()) { let previous_outpoint: TransactionOutpoint = object.get_value("previousOutpoint")?.as_ref().try_into()?; - let signature_script = object.get_vec_u8("signatureScript").unwrap_or_default(); + let signature_script = object.get_vec_u8("signatureScript").ok(); let sequence = object.get_u64("sequence")?; let sig_op_count = object.get_u8("sigOpCount")?; let utxo = object.try_get_cast::("utxo")?.map(Cast::into_owned); @@ -207,7 +207,7 @@ impl From for TransactionInput { fn from(tx_input: cctx::TransactionInput) -> Self { TransactionInput::new( tx_input.previous_outpoint.into(), - tx_input.signature_script, + Some(tx_input.signature_script), tx_input.sequence, tx_input.sig_op_count, None, @@ -220,7 +220,8 @@ impl From<&TransactionInput> for cctx::TransactionInput { let inner = tx_input.inner(); cctx::TransactionInput::new( inner.previous_outpoint.clone().into(), - inner.signature_script.clone(), + // TODO - discuss: should this unwrap_or_default or return an error? + inner.signature_script.clone().unwrap_or_default(), inner.sequence, inner.sig_op_count, ) diff --git a/consensus/client/src/lib.rs b/consensus/client/src/lib.rs index 1c46d49191..eb482eab16 100644 --- a/consensus/client/src/lib.rs +++ b/consensus/client/src/lib.rs @@ -1,30 +1,29 @@ pub mod error; mod imports; +mod input; mod outpoint; mod output; pub mod result; +mod serializable; +mod transaction; mod utxo; +pub use input::*; pub use outpoint::*; pub use output::*; +pub use serializable::*; +pub use transaction::*; pub use utxo::*; cfg_if::cfg_if! { if #[cfg(feature = "wasm32-sdk")] { mod header; - mod input; - mod transaction; mod utils; mod hash; mod sign; - mod serializable; pub use header::*; - pub use input::*; - pub use transaction::*; - pub use serializable::*; pub use utils::*; pub use hash::*; - // pub use signing::*; pub use sign::sign_with_multiple_v3; } } diff --git a/consensus/client/src/outpoint.rs b/consensus/client/src/outpoint.rs index 77e17d542e..06be53f6aa 100644 --- a/consensus/client/src/outpoint.rs +++ b/consensus/client/src/outpoint.rs @@ -165,6 +165,15 @@ impl From for cctx::TransactionOutpoint { } } +impl From<&TransactionOutpoint> for cctx::TransactionOutpoint { + fn from(outpoint: &TransactionOutpoint) -> Self { + let inner = outpoint.inner(); + let transaction_id = inner.transaction_id; + let index = inner.index; + cctx::TransactionOutpoint::new(transaction_id, index) + } +} + impl TransactionOutpoint { pub fn simulated() -> Self { Self::new(TransactionId::from_slice(&rand::random::<[u8; kaspa_hashes::HASH_SIZE]>()), 0) diff --git a/consensus/client/src/output.rs b/consensus/client/src/output.rs index f4ee1ad1e6..37694a0705 100644 --- a/consensus/client/src/output.rs +++ b/consensus/client/src/output.rs @@ -124,25 +124,17 @@ impl From<&TransactionOutput> for cctx::TransactionOutput { } } -impl TryFrom<&JsValue> for TransactionOutput { +impl TryCastFromJs for TransactionOutput { type Error = Error; - fn try_from(js_value: &JsValue) -> Result { - // workflow_log::log_trace!("js_value->TransactionOutput: {js_value:?}"); - if let Some(object) = Object::try_from(js_value) { - let has_address = Object::has_own(object, &JsValue::from("address")); - workflow_log::log_trace!("js_value->TransactionOutput: has_address:{has_address:?}"); - let value = object.get_u64("value")?; - let script_public_key = ScriptPublicKey::try_cast_from(object.get_value("scriptPublicKey")?)?; - Ok(TransactionOutput::new(value, script_public_key.into_owned())) - } else { - Err("TransactionInput must be an object".into()) - } - } -} - -impl TryFrom for TransactionOutput { - type Error = Error; - fn try_from(js_value: JsValue) -> Result { - Self::try_from(&js_value) + fn try_cast_from(value: impl AsRef) -> std::result::Result, Self::Error> { + Self::resolve_cast(&value, || { + if let Some(object) = Object::try_from(value.as_ref()) { + let value = object.get_u64("value")?; + let script_public_key = ScriptPublicKey::try_cast_from(object.get_value("scriptPublicKey")?)?; + Ok(TransactionOutput::new(value, script_public_key.into_owned()).into()) + } else { + Err("TransactionInput must be an object".into()) + } + }) } } diff --git a/consensus/client/src/serializable/mod.rs b/consensus/client/src/serializable/mod.rs index 6099924e7c..a590ab2862 100644 --- a/consensus/client/src/serializable/mod.rs +++ b/consensus/client/src/serializable/mod.rs @@ -30,7 +30,7 @@ export interface ISerializableTransactionInput { index: number; sequence: bigint; sigOpCount: number; - signatureScript: HexString; + signatureScript?: HexString; utxo: ISerializableUtxoEntry; } diff --git a/consensus/client/src/serializable/numeric.rs b/consensus/client/src/serializable/numeric.rs index 0c413fdcfe..d4c145a1bc 100644 --- a/consensus/client/src/serializable/numeric.rs +++ b/consensus/client/src/serializable/numeric.rs @@ -80,6 +80,7 @@ pub struct SerializableTransactionInput { pub sequence: u64, pub sig_op_count: u8, #[serde(with = "hex::serde")] + // TODO - convert to Option> and use hex serialization over Option pub signature_script: Vec, pub utxo: SerializableUtxoEntry, } @@ -91,6 +92,8 @@ impl SerializableTransactionInput { Self { transaction_id: input.previous_outpoint.transaction_id, index: input.previous_outpoint.index, + // TODO - convert signature_script to Option> + // signature_script: (!input.signature_script.is_empty()).then_some(input.signature_script.clone()), signature_script: input.signature_script.clone(), sequence: input.sequence, sig_op_count: input.sig_op_count, @@ -134,15 +137,16 @@ impl TryFrom for cctx::TransactionInput { impl TryFrom<&SerializableTransactionInput> for TransactionInput { type Error = Error; - fn try_from(signable_input: &SerializableTransactionInput) -> Result { - let utxo = UtxoEntryReference::try_from(signable_input)?; + fn try_from(serializable_input: &SerializableTransactionInput) -> Result { + let utxo = UtxoEntryReference::try_from(serializable_input)?; - let previous_outpoint = TransactionOutpoint::new(signable_input.transaction_id, signable_input.index); + let previous_outpoint = TransactionOutpoint::new(serializable_input.transaction_id, serializable_input.index); let inner = TransactionInputInner { previous_outpoint, - signature_script: signable_input.signature_script.clone(), - sequence: signable_input.sequence, - sig_op_count: signable_input.sig_op_count, + // TODO - convert to Option> and use hex serialization over Option + signature_script: (!serializable_input.signature_script.is_empty()).then_some(serializable_input.signature_script.clone()), + sequence: serializable_input.sequence, + sig_op_count: serializable_input.sig_op_count, utxo: Some(utxo), }; @@ -159,7 +163,8 @@ impl TryFrom<&TransactionInput> for SerializableTransactionInput { Ok(Self { transaction_id: inner.previous_outpoint.transaction_id(), index: inner.previous_outpoint.index(), - signature_script: inner.signature_script.clone(), + // TODO - convert to Option> and use hex serialization over Option + signature_script: inner.signature_script.clone().unwrap_or_default(), sequence: inner.sequence, sig_op_count: inner.sig_op_count, utxo, diff --git a/consensus/client/src/serializable/string.rs b/consensus/client/src/serializable/string.rs index be3981b0ea..33c054fac3 100644 --- a/consensus/client/src/serializable/string.rs +++ b/consensus/client/src/serializable/string.rs @@ -139,7 +139,8 @@ impl TryFrom<&SerializableTransactionInput> for TransactionInput { let previous_outpoint = TransactionOutpoint::new(serializable_input.transaction_id, serializable_input.index); let inner = TransactionInputInner { previous_outpoint, - signature_script: serializable_input.signature_script.clone(), + // TODO - convert to Option> and use hex serialization over Option + signature_script: (!serializable_input.signature_script.is_empty()).then_some(serializable_input.signature_script.clone()), sequence: serializable_input.sequence.parse()?, sig_op_count: serializable_input.sig_op_count, utxo: Some(utxo), @@ -158,7 +159,8 @@ impl TryFrom<&TransactionInput> for SerializableTransactionInput { Ok(Self { transaction_id: inner.previous_outpoint.transaction_id(), index: inner.previous_outpoint.index(), - signature_script: inner.signature_script.clone(), + // TODO - convert to Option> and use hex serialization over Option + signature_script: inner.signature_script.clone().unwrap_or_default(), sequence: inner.sequence.to_string(), sig_op_count: inner.sig_op_count, utxo, diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index 0c1c8fa6a7..ccad2e2c46 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -199,7 +199,7 @@ impl Transaction { pub fn set_outputs_from_js_array(&mut self, js_value: &TransactionOutputArrayAsArgT) { let outputs = Array::from(js_value) .iter() - .map(|js_value| TransactionOutput::try_from(&js_value).unwrap_or_else(|err| panic!("invalid transaction output: {err}"))) + .map(|js_value| TryCastFromJs::try_owned_from(&js_value).unwrap_or_else(|err| panic!("invalid transaction output: {err}"))) .collect::>(); self.inner().outputs = outputs; } @@ -285,7 +285,7 @@ impl TryCastFromJs for Transaction { let outputs: Vec = object .get_vec("outputs")? .iter() - .map(|jsv| jsv.try_into()) + .map(TryCastFromJs::try_owned_from) .collect::, Error>>()?; Transaction::new(id, version, inputs, outputs, lock_time, subnetwork_id, gas, payload).map(Into::into) } @@ -342,7 +342,13 @@ impl Transaction { .map(|input| { let previous_outpoint: TransactionOutpoint = input.previous_outpoint.into(); let utxo = utxos.get(previous_outpoint.id()).cloned(); - TransactionInput::new(previous_outpoint, input.signature_script.clone(), input.sequence, input.sig_op_count, utxo) + TransactionInput::new( + previous_outpoint, + Some(input.signature_script.clone()), + input.sequence, + input.sig_op_count, + utxo, + ) }) .collect::>(); let outputs: Vec = tx.outputs.iter().map(|output| output.into()).collect::>(); diff --git a/consensus/client/src/utils.rs b/consensus/client/src/utils.rs index 48e4cdfbd1..b1da47a22c 100644 --- a/consensus/client/src/utils.rs +++ b/consensus/client/src/utils.rs @@ -40,7 +40,7 @@ pub fn pay_to_script_hash_signature_script(redeem_script: BinaryT, signature: Bi /// Returns the address encoded in a script public key. /// @param script_public_key - The script public key ({@link ScriptPublicKey}). -/// @param prefix - The address prefix. +/// @param network - The network type. /// @category Wallet SDK #[wasm_bindgen(js_name = addressFromScriptPublicKey)] pub fn address_from_script_public_key(script_public_key: ScriptPublicKeyT, network: &NetworkTypeT) -> Result { diff --git a/rpc/core/src/wasm/convert.rs b/rpc/core/src/wasm/convert.rs index ce38e32c4a..ddbb88dd72 100644 --- a/rpc/core/src/wasm/convert.rs +++ b/rpc/core/src/wasm/convert.rs @@ -30,7 +30,7 @@ cfg_if::cfg_if! { let inner = tx_input.inner(); RpcTransactionInput { previous_outpoint: inner.previous_outpoint.clone().into(), - signature_script: inner.signature_script.clone(), + signature_script: inner.signature_script.clone().unwrap_or_default(), sequence: inner.sequence, sig_op_count: inner.sig_op_count, verbose_data: None, diff --git a/wallet/core/Cargo.toml b/wallet/core/Cargo.toml index 9a6c4cfa6a..3e057b6a77 100644 --- a/wallet/core/Cargo.toml +++ b/wallet/core/Cargo.toml @@ -71,6 +71,7 @@ kaspa-txscript.workspace = true kaspa-utils.workspace = true kaspa-wallet-keys.workspace = true kaspa-wallet-macros.workspace = true +kaspa-wallet-pskt.workspace = true kaspa-wasm-core.workspace = true kaspa-wrpc-client.workspace = true kaspa-wrpc-wasm.workspace = true diff --git a/wallet/core/src/account/mod.rs b/wallet/core/src/account/mod.rs index a377d33398..31c7fea9d5 100644 --- a/wallet/core/src/account/mod.rs +++ b/wallet/core/src/account/mod.rs @@ -5,8 +5,15 @@ pub mod descriptor; pub mod kind; +pub mod pskb; pub mod variants; +use kaspa_hashes::Hash; +use kaspa_wallet_pskt::bundle::Bundle; pub use kind::*; +use pskb::{ + bundle_from_pskt_generator, bundle_to_finalizer_stream, pskb_signer_for_address, pskt_to_pending_transaction, PSKBSigner, + PSKTGenerator, +}; pub use variants::*; use crate::derivation::build_derivate_paths; @@ -356,6 +363,66 @@ pub trait Account: AnySync + Send + Sync + 'static { Ok((generator.summary(), ids)) } + async fn pskb_from_send_generator( + self: Arc, + destination: PaymentDestination, + priority_fee_sompi: Fees, + payload: Option>, + wallet_secret: Secret, + payment_secret: Option, + abortable: &Abortable, + ) -> Result { + let settings = GeneratorSettings::try_new_with_account(self.clone().as_dyn_arc(), destination, priority_fee_sompi, payload)?; + let keydata = self.prv_key_data(wallet_secret).await?; + let signer = Arc::new(PSKBSigner::new(self.clone().as_dyn_arc(), keydata, payment_secret)); + let generator = Generator::try_new(settings, None, Some(abortable))?; + let pskt_generator = PSKTGenerator::new(generator, signer, self.wallet().address_prefix()?); + bundle_from_pskt_generator(pskt_generator).await + } + + async fn pskb_sign( + self: Arc, + bundle: &Bundle, + wallet_secret: Secret, + payment_secret: Option, + sign_for_address: Option<&Address>, + ) -> Result { + let keydata = self.prv_key_data(wallet_secret).await?; + let signer = Arc::new(PSKBSigner::new(self.clone().as_dyn_arc(), keydata.clone(), payment_secret.clone())); + + let network_id = self.wallet().clone().network_id()?; + let derivation = self.as_derivation_capable()?; + + let (derivation_path, _) = + build_derivate_paths(&derivation.account_kind(), derivation.account_index(), derivation.cosigner_index())?; + + let key_fingerprint = keydata.get_xprv(payment_secret.clone().as_ref())?.public_key().fingerprint(); + + match pskb_signer_for_address(bundle, signer, network_id, sign_for_address, derivation_path, key_fingerprint).await { + Ok(signer) => Ok(signer), + Err(e) => Err(Error::from(e.to_string())), + } + } + + async fn pskb_broadcast(self: Arc, bundle: &Bundle) -> Result, Error> { + let mut ids = Vec::new(); + let mut stream = bundle_to_finalizer_stream(bundle); + + while let Some(result) = stream.next().await { + match result { + Ok(pskt) => { + let change = self.wallet().account()?.change_address()?; + let transaction = pskt_to_pending_transaction(pskt, self.wallet().network_id()?, change)?; + ids.push(transaction.try_submit(&self.wallet().rpc_api()).await?); + } + Err(e) => { + eprintln!("Error processing a PSKT from bundle: {:?}", e); + } + } + } + Ok(ids) + } + /// Execute a transfer to another wallet account. async fn transfer( self: Arc, diff --git a/wallet/core/src/account/pskb.rs b/wallet/core/src/account/pskb.rs new file mode 100644 index 0000000000..5cf1eeea9a --- /dev/null +++ b/wallet/core/src/account/pskb.rs @@ -0,0 +1,360 @@ +pub use crate::error::Error; +use crate::imports::*; +use crate::tx::PaymentOutputs; +use futures::stream; +use kaspa_bip32::{DerivationPath, KeyFingerprint, PrivateKey}; +use kaspa_consensus_client::UtxoEntry as ClientUTXO; +use kaspa_consensus_core::hashing::sighash::{calc_schnorr_signature_hash, SigHashReusedValues}; +use kaspa_consensus_core::tx::VerifiableTransaction; +use kaspa_consensus_core::tx::{TransactionInput, UtxoEntry}; +use kaspa_txscript::extract_script_pub_key_address; +use kaspa_txscript::opcodes::codes::OpData65; +use kaspa_txscript::script_builder::ScriptBuilder; +use kaspa_wallet_core::tx::{Generator, GeneratorSettings, PaymentDestination, PendingTransaction}; +pub use kaspa_wallet_pskt::bundle::Bundle; +use kaspa_wallet_pskt::prelude::KeySource; +use kaspa_wallet_pskt::prelude::{Finalizer, Inner, SignInputOk, Signature, Signer}; +pub use kaspa_wallet_pskt::pskt::{Creator, PSKT}; +use secp256k1::schnorr; +use secp256k1::{Message, PublicKey}; +use std::iter; + +struct PSKBSignerInner { + keydata: PrvKeyData, + account: Arc, + payment_secret: Option, + keys: Mutex>, +} + +pub struct PSKBSigner { + inner: Arc, +} + +impl PSKBSigner { + pub fn new(account: Arc, keydata: PrvKeyData, payment_secret: Option) -> Self { + Self { inner: Arc::new(PSKBSignerInner { keydata, account, payment_secret, keys: Mutex::new(AHashMap::new()) }) } + } + + pub fn ingest(&self, addresses: &[Address]) -> Result<()> { + let mut keys = self.inner.keys.lock()?; + + // Skip addresses that are already present in the key map. + let addresses = addresses.iter().filter(|a| !keys.contains_key(a)).collect::>(); + if !addresses.is_empty() { + let account = self.inner.account.clone().as_derivation_capable().expect("expecting derivation capable account"); + let (receive, change) = account.derivation().addresses_indexes(&addresses)?; + let private_keys = account.create_private_keys(&self.inner.keydata, &self.inner.payment_secret, &receive, &change)?; + for (address, private_key) in private_keys { + keys.insert(address.clone(), private_key.to_bytes()); + } + } + Ok(()) + } + + fn public_key(&self, for_address: &Address) -> Result { + let keys = self.inner.keys.lock()?; + match keys.get(for_address) { + Some(private_key) => { + let kp = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key)?; + Ok(kp.public_key()) + } + None => Err(Error::from("PSKBSigner address coverage error")), + } + } + + fn sign_schnorr(&self, for_address: &Address, message: Message) -> Result { + let keys = self.inner.keys.lock()?; + match keys.get(for_address) { + Some(private_key) => { + let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, private_key)?; + Ok(schnorr_key.sign_schnorr(message)) + } + None => Err(Error::from("PSKBSigner address coverage error")), + } + } +} + +pub struct PSKTGenerator { + generator: Generator, + signer: Arc, + prefix: Prefix, +} + +impl PSKTGenerator { + pub fn new(generator: Generator, signer: Arc, prefix: Prefix) -> Self { + Self { generator, signer, prefix } + } + + pub fn stream(&self) -> impl Stream, Error>> { + PSKTStream::new(self.generator.clone(), self.signer.clone(), self.prefix) + } +} + +struct PSKTStream { + generator_stream: Pin> + Send>>, + signer: Arc, + prefix: Prefix, +} + +impl PSKTStream { + fn new(generator: Generator, signer: Arc, prefix: Prefix) -> Self { + let generator_stream = generator.stream().map_err(Error::from); + Self { generator_stream: Box::pin(generator_stream), signer, prefix } + } +} + +impl Stream for PSKTStream { + type Item = Result, Error>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.as_ref(); + + let _prefix = this.prefix; + let _signer = this.signer.clone(); + + match self.get_mut().generator_stream.as_mut().poll_next(cx) { + Poll::Ready(Some(Ok(pending_tx))) => { + let pskt = convert_pending_tx_to_pskt(pending_tx); + Poll::Ready(Some(pskt)) + } + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +fn convert_pending_tx_to_pskt(pending_tx: PendingTransaction) -> Result, Error> { + let signable_tx = pending_tx.signable_transaction(); + let verifiable_tx = signable_tx.as_verifiable(); + let populated_inputs: Vec<(&TransactionInput, &UtxoEntry)> = verifiable_tx.populated_inputs().collect(); + let pskt_inner = Inner::try_from((pending_tx.transaction(), populated_inputs.to_owned()))?; + Ok(PSKT::::from(pskt_inner)) +} + +pub async fn bundle_from_pskt_generator(generator: PSKTGenerator) -> Result { + let mut bundle: Bundle = Bundle::new(); + let mut stream = generator.stream(); + + while let Some(pskt_result) = stream.next().await { + match pskt_result { + Ok(pskt) => bundle.add_pskt(pskt), + Err(e) => return Err(e), + } + } + + Ok(bundle) +} + +pub async fn pskb_signer_for_address( + bundle: &Bundle, + signer: Arc, + network_id: NetworkId, + sign_for_address: Option<&Address>, + derivation_path: DerivationPath, + key_fingerprint: KeyFingerprint, +) -> Result { + let mut signed_bundle = Bundle::new(); + let mut reused_values = SigHashReusedValues::new(); + + // If set, sign-for address is used for signing. + // Else, all addresses from inputs are. + let addresses: Vec
= match sign_for_address { + Some(signer) => vec![signer.clone()], + None => bundle + .iter() + .flat_map(|inner| { + inner.inputs + .iter() + .filter_map(|input| input.utxo_entry.as_ref()) // Filter out None and get a reference to UtxoEntry if it exists + .filter_map(|utxo_entry| { + extract_script_pub_key_address(&utxo_entry.script_public_key.clone(), network_id.into()).ok() + }) + .collect::>() + }) + .collect(), + }; + + // Prepare the signer. + signer.ingest(addresses.as_ref())?; + + for pskt_inner in bundle.iter().cloned() { + let pskt: PSKT = PSKT::from(pskt_inner); + + let mut sign = |signer_pskt: PSKT| { + signer_pskt + .pass_signature_sync(|tx, sighash| -> Result, String> { + tx.tx + .inputs + .iter() + .enumerate() + .map(|(idx, _input)| { + let hash = calc_schnorr_signature_hash(&tx.as_verifiable(), idx, sighash[idx], &mut reused_values); + let msg = secp256k1::Message::from_digest_slice(hash.as_bytes().as_slice()).unwrap(); + + // When address represents a locked UTXO, no private key is available. + // Instead, use the account receive address' private key. + let address: &Address = match sign_for_address { + Some(address) => address, + None => addresses.get(idx).expect("Input indexed address"), + }; + + let public_key = signer.public_key(address).expect("Public key for input indexed address"); + + Ok(SignInputOk { + signature: Signature::Schnorr(signer.sign_schnorr(address, msg).unwrap()), + pub_key: public_key, + key_source: Some(KeySource { key_fingerprint, derivation_path: derivation_path.clone() }), + }) + }) + .collect() + }) + .unwrap() + }; + signed_bundle.add_pskt(sign(pskt.clone())); + } + Ok(signed_bundle) +} + +pub fn finalize_pskt_one_or_more_sig_and_redeem_script(pskt: PSKT) -> Result, Error> { + let result = pskt.finalize_sync(|inner: &Inner| -> Result>, String> { + Ok(inner + .inputs + .iter() + .map(|input| -> Vec { + let signatures: Vec<_> = input + .partial_sigs + .clone() + .into_iter() + .flat_map(|(_, signature)| iter::once(OpData65).chain(signature.into_bytes()).chain([input.sighash_type.to_u8()])) + .collect(); + + signatures + .into_iter() + .chain( + input + .redeem_script + .as_ref() + .map(|redeem_script| ScriptBuilder::new().add_data(redeem_script.as_slice()).unwrap().drain().to_vec()) + .unwrap_or_default(), + ) + .collect() + }) + .collect()) + }); + + match result { + Ok(finalized_pskt) => Ok(finalized_pskt), + Err(e) => Err(Error::from(e.to_string())), + } +} + +pub fn finalize_pskt_no_sig_and_redeem_script(pskt: PSKT) -> Result, Error> { + let result = pskt.finalize_sync(|inner: &Inner| -> Result>, String> { + Ok(inner + .inputs + .iter() + .map(|input| -> Vec { + input + .redeem_script + .as_ref() + .map(|redeem_script| ScriptBuilder::new().add_data(redeem_script.as_slice()).unwrap().drain().to_vec()) + .unwrap_or_default() + }) + .collect()) + }); + + match result { + Ok(finalized_pskt) => Ok(finalized_pskt), + Err(e) => Err(Error::from(e.to_string())), + } +} + +pub fn bundle_to_finalizer_stream(bundle: &Bundle) -> impl Stream, Error>> + Send { + stream::iter(bundle.iter().cloned().collect::>()).map(move |pskt_inner| { + let pskt: PSKT = PSKT::from(pskt_inner); + let pskt_finalizer = pskt.constructor().updater().signer().finalizer(); + finalize_pskt_one_or_more_sig_and_redeem_script(pskt_finalizer) + }) +} + +pub fn pskt_to_pending_transaction( + finalized_pskt: PSKT, + network_id: NetworkId, + change_address: Address, +) -> Result { + let mass = 10; + let (signed_tx, _) = match finalized_pskt.clone().extractor() { + Ok(extractor) => match extractor.extract_tx() { + Ok(once_mass) => once_mass(mass), + Err(e) => return Err(Error::PendingTransactionFromPSKTError(e.to_string())), + }, + Err(e) => return Err(Error::PendingTransactionFromPSKTError(e.to_string())), + }; + + let inner_pskt = finalized_pskt.deref().clone(); + + let utxo_entries_ref: Vec = inner_pskt + .inputs + .iter() + .filter_map(|input| { + if let Some(ue) = input.clone().utxo_entry { + return Some(UtxoEntryReference { + utxo: Arc::new(ClientUTXO { + address: Some(extract_script_pub_key_address(&ue.script_public_key, network_id.into()).unwrap()), + amount: ue.amount, + outpoint: input.previous_outpoint.into(), + script_public_key: ue.script_public_key, + block_daa_score: ue.block_daa_score, + is_coinbase: ue.is_coinbase, + }), + }); + } + None + }) + .collect(); + + let output: Vec = signed_tx.outputs.clone(); + let recipient = extract_script_pub_key_address(&output[0].script_public_key, network_id.into())?; + let fee_u: u64 = 0; + + let utxo_iterator: Box + Send + Sync + 'static> = + Box::new(utxo_entries_ref.clone().into_iter()); + + let final_transaction_destination = PaymentDestination::PaymentOutputs(PaymentOutputs::from((recipient.clone(), output[0].value))); + + let settings = GeneratorSettings { + network_id, + multiplexer: None, + sig_op_count: 1, + minimum_signatures: 1, + change_address, + utxo_iterator, + priority_utxo_entries: None, + source_utxo_context: None, + destination_utxo_context: None, + final_transaction_priority_fee: fee_u.into(), + final_transaction_destination, + final_transaction_payload: None, + }; + + // Create the Generator + let generator = Generator::try_new(settings, None, None)?; + + // Create PendingTransaction + let pending_tx = PendingTransaction::try_new( + &generator, + signed_tx.clone(), + utxo_entries_ref.clone(), + vec![], + None, + 0, + 0, + 0, + 0, + 0, + kaspa_wallet_core::tx::DataKind::Final, + )?; + + Ok(pending_tx) +} diff --git a/wallet/core/src/error.rs b/wallet/core/src/error.rs index b8e06aeedb..8992a8a924 100644 --- a/wallet/core/src/error.rs +++ b/wallet/core/src/error.rs @@ -336,6 +336,11 @@ pub enum Error { #[error("Connected node is not synced")] NotSynced, + #[error(transparent)] + Pskt(#[from] kaspa_wallet_pskt::error::Error), + + #[error("Error generating pending transaction from PSKT: {0}")] + PendingTransactionFromPSKTError(String), } impl From for Error { diff --git a/wallet/core/src/wasm/tx/utils.rs b/wallet/core/src/wasm/tx/utils.rs index 07ded545d4..3041071324 100644 --- a/wallet/core/src/wasm/tx/utils.rs +++ b/wallet/core/src/wasm/tx/utils.rs @@ -40,7 +40,7 @@ pub fn create_transaction_js( let UtxoEntryReference { utxo } = reference.as_ref(); total_input_amount += utxo.amount(); entries.push(reference.as_ref().clone()); - TransactionInput::new(utxo.outpoint.clone(), vec![], sequence as u64, sig_op_count, Some(reference.into_owned())) + TransactionInput::new(utxo.outpoint.clone(), None, sequence as u64, sig_op_count, Some(reference.into_owned())) }) .collect::>(); diff --git a/wallet/pskt/Cargo.toml b/wallet/pskt/Cargo.toml index f2d82cf07c..b3fff1bfaf 100644 --- a/wallet/pskt/Cargo.toml +++ b/wallet/pskt/Cargo.toml @@ -19,6 +19,7 @@ wasm32-sdk = ["kaspa-consensus-client/wasm32-sdk"] wasm32-types = ["kaspa-consensus-client/wasm32-types"] [dependencies] +kaspa-addresses.workspace = true kaspa-bip32.workspace = true kaspa-consensus-client.workspace = true kaspa-consensus-core.workspace = true @@ -26,12 +27,20 @@ kaspa-txscript-errors.workspace = true kaspa-txscript.workspace = true kaspa-utils.workspace = true +bincode.workspace = true derive_builder.workspace = true +js-sys.workspace = true +futures.workspace = true +hex.workspace = true secp256k1.workspace = true +serde_repr.workspace = true serde-value.workspace = true serde.workspace = true -serde_repr.workspace = true thiserror.workspace = true +wasm-bindgen.workspace = true +serde_json.workspace = true +serde-wasm-bindgen.workspace = true +workflow-wasm.workspace = true [dev-dependencies] serde_json.workspace = true diff --git a/wallet/pskt/examples/multisig.rs b/wallet/pskt/examples/multisig.rs index a34bef9b55..fb011402fb 100644 --- a/wallet/pskt/examples/multisig.rs +++ b/wallet/pskt/examples/multisig.rs @@ -3,7 +3,9 @@ use kaspa_consensus_core::{ tx::{TransactionId, TransactionOutpoint, UtxoEntry}, }; use kaspa_txscript::{multisig_redeem_script, opcodes::codes::OpData65, pay_to_script_hash_script, script_builder::ScriptBuilder}; -use kaspa_wallet_pskt::{Combiner, Creator, Extractor, Finalizer, Inner, InputBuilder, SignInputOk, Signature, Signer, Updater, PSKT}; +use kaspa_wallet_pskt::prelude::{ + Combiner, Creator, Extractor, Finalizer, Inner, InputBuilder, SignInputOk, Signature, Signer, Updater, PSKT, +}; use secp256k1::{rand::thread_rng, Keypair}; use std::{iter, str::FromStr}; diff --git a/wallet/pskt/src/bundle.rs b/wallet/pskt/src/bundle.rs new file mode 100644 index 0000000000..c508684106 --- /dev/null +++ b/wallet/pskt/src/bundle.rs @@ -0,0 +1,313 @@ +use crate::error::Error; +use crate::prelude::*; +use crate::pskt::{Inner as PSKTInner, PSKT}; +// use crate::wasm::result; + +use kaspa_addresses::Address; +use kaspa_consensus_core::tx::{ScriptPublicKey, TransactionOutpoint, UtxoEntry}; + +use hex; +use kaspa_txscript::{extract_script_pub_key_address, pay_to_address_script, pay_to_script_hash_script}; +use serde::{Deserialize, Serialize}; +use std::ops::Deref; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Bundle(pub Vec); + +impl From> for Bundle { + fn from(pskt: PSKT) -> Self { + Bundle(vec![pskt.deref().clone()]) + } +} + +impl From>> for Bundle { + fn from(pskts: Vec>) -> Self { + let inner_list = pskts.into_iter().map(|pskt| pskt.deref().clone()).collect(); + Bundle(inner_list) + } +} + +impl Bundle { + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Adds an Inner instance to the bundle + pub fn add_inner(&mut self, inner: PSKTInner) { + self.0.push(inner); + } + + /// Adds a PSKT instance to the bundle + pub fn add_pskt(&mut self, pskt: PSKT) { + self.0.push(pskt.deref().clone()); + } + + /// Merges another bundle into the current bundle + pub fn merge(&mut self, other: Bundle) { + for inner in other.0 { + self.0.push(inner); + } + } + + /// Iterator over the inner PSKT instances + pub fn iter(&self) -> std::slice::Iter { + self.0.iter() + } + + pub fn serialize(&self) -> Result { + Ok(format!("PSKB{}", hex::encode(serde_json::to_string(self)?))) + } + + pub fn deserialize(hex_data: &str) -> Result { + if let Some(hex_data) = hex_data.strip_prefix("PSKB") { + Ok(serde_json::from_slice(hex::decode(hex_data)?.as_slice())?) + } else { + Err(Error::PskbPrefixError) + } + } +} + +impl AsRef<[PSKTInner]> for Bundle { + fn as_ref(&self) -> &[PSKTInner] { + self.0.as_slice() + } +} + +impl TryFrom for Bundle { + type Error = Error; + fn try_from(value: String) -> Result { + Bundle::deserialize(&value) + } +} + +impl TryFrom<&str> for Bundle { + type Error = Error; + fn try_from(value: &str) -> Result { + Bundle::deserialize(value) + } +} +impl TryFrom for String { + type Error = Error; + fn try_from(value: Bundle) -> Result { + match Bundle::serialize(&value) { + Ok(output) => Ok(output.to_owned()), + Err(e) => Err(Error::PskbSerializeError(e.to_string())), + } + } +} + +impl Default for Bundle { + fn default() -> Self { + Self::new() + } +} + +pub fn lock_script_sig_templating(payload: String, pubkey_bytes: Option<&[u8]>) -> Result, Error> { + let mut payload_bytes: Vec = hex::decode(payload)?; + + if let Some(pubkey) = pubkey_bytes { + let placeholder = b"{{pubkey}}"; + + // Search for the placeholder in payload bytes to be replaced by public key. + if let Some(pos) = payload_bytes.windows(placeholder.len()).position(|window| window == placeholder) { + payload_bytes.splice(pos..pos + placeholder.len(), pubkey.iter().cloned()); + } + } + Ok(payload_bytes) +} + +pub fn script_sig_to_address(script_sig: &[u8], prefix: kaspa_addresses::Prefix) -> Result { + extract_script_pub_key_address(&pay_to_script_hash_script(script_sig), prefix).map_err(Error::P2SHExtractError) +} + +pub fn unlock_utxos_as_pskb( + utxo_references: Vec<(UtxoEntry, TransactionOutpoint)>, + recipient: &Address, + script_sig: Vec, + priority_fee_sompi_per_transaction: u64, +) -> Result { + // Fee per transaction. + // Check if each UTXO's amounts can cover priority fee. + utxo_references + .iter() + .map(|(entry, _)| { + if entry.amount <= priority_fee_sompi_per_transaction { + return Err(Error::ExcessUnlockFeeError); + } + Ok(()) + }) + .collect::, _>>()?; + + let recipient_spk = pay_to_address_script(recipient); + let (successes, errors): (Vec<_>, Vec<_>) = utxo_references + .into_iter() + .map(|(utxo_entry, outpoint)| { + unlock_utxo(&utxo_entry, &outpoint, &recipient_spk, &script_sig, priority_fee_sompi_per_transaction) + }) + .partition(Result::is_ok); + + let successful_bundles: Vec<_> = successes.into_iter().filter_map(Result::ok).collect(); + let error_list: Vec<_> = errors.into_iter().filter_map(Result::err).collect(); + + if !error_list.is_empty() { + return Err(Error::MultipleUnlockUtxoError(error_list)); + } + + let merged_bundle = successful_bundles.into_iter().fold(None, |acc: Option, bundle| match acc { + Some(mut merged_bundle) => { + merged_bundle.merge(bundle); + Some(merged_bundle) + } + None => Some(bundle), + }); + + match merged_bundle { + None => Err("Generating an empty PSKB".into()), + Some(bundle) => Ok(bundle), + } +} + +pub fn unlock_utxo( + utxo_entry: &UtxoEntry, + outpoint: &TransactionOutpoint, + script_public_key: &ScriptPublicKey, + script_sig: &[u8], + priority_fee_sompi: u64, +) -> Result { + let input = InputBuilder::default() + .utxo_entry(utxo_entry.to_owned()) + .previous_outpoint(outpoint.to_owned()) + .sig_op_count(1) + .redeem_script(script_sig.to_vec()) + .build()?; + + let output = OutputBuilder::default() + .amount(utxo_entry.amount - priority_fee_sompi) + .script_public_key(script_public_key.clone()) + .build()?; + + let pskt: PSKT = PSKT::::default().constructor().input(input).output(output); + Ok(pskt.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::prelude::*; + use crate::role::Creator; + use crate::role::*; + use kaspa_consensus_core::tx::{TransactionId, TransactionOutpoint, UtxoEntry}; + use kaspa_txscript::{multisig_redeem_script, pay_to_script_hash_script}; + use secp256k1::Secp256k1; + use secp256k1::{rand::thread_rng, Keypair}; + use std::str::FromStr; + use std::sync::Once; + + static INIT: Once = Once::new(); + static mut CONTEXT: Option)>> = None; + + fn mock_context() -> &'static ([Keypair; 2], Vec) { + unsafe { + INIT.call_once(|| { + let kps = [Keypair::new(&Secp256k1::new(), &mut thread_rng()), Keypair::new(&Secp256k1::new(), &mut thread_rng())]; + let redeem_script: Vec = multisig_redeem_script(kps.iter().map(|pk| pk.x_only_public_key().0.serialize()), 2) + .expect("Test multisig redeem script"); + + CONTEXT = Some(Box::new((kps, redeem_script))); + }); + + CONTEXT.as_ref().unwrap() + } + } + + // Mock multisig PSKT from example + fn mock_pskt_constructor() -> PSKT { + let (_, redeem_script) = mock_context(); + let pskt = PSKT::::default().inputs_modifiable().outputs_modifiable(); + let input_0 = InputBuilder::default() + .utxo_entry(UtxoEntry { + amount: 12793000000000, + script_public_key: pay_to_script_hash_script(redeem_script), + block_daa_score: 36151168, + is_coinbase: false, + }) + .previous_outpoint(TransactionOutpoint { + transaction_id: TransactionId::from_str("63020db736215f8b1105a9281f7bcbb6473d965ecc45bb2fb5da59bd35e6ff84").unwrap(), + index: 0, + }) + .sig_op_count(2) + .redeem_script(redeem_script.to_owned()) + .build() + .expect("Mock PSKT constructor"); + + pskt.constructor().input(input_0) + } + + #[test] + fn test_pskb_serialization() { + let constructor = mock_pskt_constructor(); + let bundle = Bundle::from(constructor.clone()); + + println!("Bundle: {}", serde_json::to_string(&bundle).unwrap()); + + // Serialize Bundle + let serialized = bundle.serialize().map_err(|err| format!("Unable to serialize bundle: {err}")).unwrap(); + println!("Serialized: {}", serialized); + + assert!(!bundle.0.is_empty()); + + match Bundle::deserialize(&serialized) { + Ok(bundle_constructor_deser) => { + println!("Deserialized: {:?}", bundle_constructor_deser); + let pskt_constructor_deser: Option> = + bundle_constructor_deser.0.first().map(|inner| PSKT::from(inner.clone())); + match pskt_constructor_deser { + Some(_) => println!("PSKT deserialized successfully"), + None => println!("No elements in the inner list to deserialize"), + } + } + Err(e) => { + eprintln!("Failed to deserialize: {}", e); + panic!() + } + } + } + + #[test] + fn test_pskb_bundle_creation() { + let bundle = Bundle::new(); + assert!(bundle.0.is_empty()); + } + + #[test] + fn test_pskb_new_with_pskt() { + let pskt = PSKT::::default(); + let bundle = Bundle::from(pskt); + assert_eq!(bundle.0.len(), 1); + } + + #[test] + fn test_pskb_add_pskt() { + let mut bundle = Bundle::new(); + let pskt = PSKT::::default(); + bundle.add_pskt(pskt); + assert_eq!(bundle.0.len(), 1); + } + + #[test] + fn test_pskb_merge_bundles() { + let mut bundle1 = Bundle::new(); + let mut bundle2 = Bundle::new(); + + let inner1 = PSKTInner::default(); + let inner2 = PSKTInner::default(); + + bundle1.add_inner(inner1.clone()); + bundle2.add_inner(inner2.clone()); + + bundle1.merge(bundle2); + + assert_eq!(bundle1.0.len(), 2); + } +} diff --git a/wallet/pskt/src/convert.rs b/wallet/pskt/src/convert.rs new file mode 100644 index 0000000000..18acf94ed9 --- /dev/null +++ b/wallet/pskt/src/convert.rs @@ -0,0 +1,109 @@ +use crate::error::Error; +use crate::input::{Input, InputBuilder}; +use crate::output::{Output, OutputBuilder}; +use crate::pskt::{Global, Inner}; +use kaspa_consensus_client::{Transaction, TransactionInput, TransactionInputInner, TransactionOutput, TransactionOutputInner}; +use kaspa_consensus_core::tx as cctx; + +impl TryFrom for Inner { + type Error = Error; + fn try_from(_transaction: Transaction) -> Result { + Inner::try_from(cctx::Transaction::from(&_transaction)) + } +} + +impl TryFrom for Input { + type Error = Error; + fn try_from(input: TransactionInput) -> std::result::Result { + let TransactionInputInner { previous_outpoint, signature_script: _, sequence: _, sig_op_count, utxo } = &*input.inner(); + + let input = InputBuilder::default() + .utxo_entry(utxo.as_ref().ok_or(Error::MissingUtxoEntry)?.into()) + .previous_outpoint(previous_outpoint.into()) + // .sequence(*sequence) + // min_time + // partial_sigs + // sighash_type + // redeem_script + .sig_op_count(*sig_op_count) + // bip32_derivations + // final_script_sig + .build()?; + + Ok(input) + } +} + +impl TryFrom for Output { + type Error = Error; + fn try_from(output: TransactionOutput) -> std::result::Result { + // Self::Transaction(transaction) + + let TransactionOutputInner { value, script_public_key } = &*output.inner(); + + let output = OutputBuilder::default() + .amount(*value) + .script_public_key(script_public_key.clone()) + // .redeem_script + // .bip32_derivations + // .proprietaries + // .unknowns + .build()?; + + Ok(output) + } +} + +impl TryFrom<(cctx::Transaction, Vec<(&cctx::TransactionInput, &cctx::UtxoEntry)>)> for Inner { + type Error = Error; // Define your error type + + fn try_from( + (transaction, populated_inputs): (cctx::Transaction, Vec<(&cctx::TransactionInput, &cctx::UtxoEntry)>), + ) -> Result { + let inputs: Result, Self::Error> = populated_inputs + .into_iter() + .map(|(input, utxo)| { + InputBuilder::default() + .utxo_entry(utxo.to_owned().clone()) + .previous_outpoint(input.previous_outpoint) + .sig_op_count(input.sig_op_count) + .build() + .map_err(Error::TxToInnerConversionInputBuildingError) + // Handle the error + }) + .collect::>(); + + let outputs: Result, Self::Error> = transaction + .outputs + .iter() + .map(|output| { + Output::try_from(TransactionOutput::from(output.to_owned())).map_err(|e| Error::TxToInnerConversionError(Box::new(e))) + }) + .collect::>(); + + Ok(Inner { global: Global::default(), inputs: inputs?, outputs: outputs? }) + } +} + +impl TryFrom for Inner { + type Error = Error; + fn try_from(transaction: cctx::Transaction) -> Result { + let inputs = transaction + .inputs + .iter() + .map(|input| { + Input::try_from(TransactionInput::from(input.to_owned())).map_err(|e| Error::TxToInnerConversionError(Box::new(e))) + }) + .collect::>()?; + + let outputs = transaction + .outputs + .iter() + .map(|output| { + Output::try_from(TransactionOutput::from(output.to_owned())).map_err(|e| Error::TxToInnerConversionError(Box::new(e))) + }) + .collect::>()?; + + Ok(Inner { global: Global::default(), inputs, outputs }) + } +} diff --git a/wallet/pskt/src/error.rs b/wallet/pskt/src/error.rs index 5041190862..11303ae4a3 100644 --- a/wallet/pskt/src/error.rs +++ b/wallet/pskt/src/error.rs @@ -1,11 +1,46 @@ +use kaspa_txscript_errors::TxScriptError; + +use crate::input::InputBuilderError; + #[derive(thiserror::Error, Debug)] pub enum Error { + #[error("{0}")] + Custom(String), #[error(transparent)] ConstructorError(#[from] ConstructorError), #[error("OutputNotModifiable")] OutOfBounds, + #[error("Missing UTXO entry")] + MissingUtxoEntry, + #[error("Missing redeem script")] + MissingRedeemScript, + #[error(transparent)] + InputBuilder(#[from] crate::input::InputBuilderError), + #[error(transparent)] + OutputBuilder(#[from] crate::output::OutputBuilderError), + #[error("Serialization error: {0}")] + HexDecodeError(#[from] hex::FromHexError), + #[error("Json deserialize error: {0}")] + JsonDeserializeError(#[from] serde_json::Error), + #[error("Serialize error")] + PskbSerializeError(String), + #[error("Unlock utxo error")] + MultipleUnlockUtxoError(Vec), + #[error("Unlock fees exceed available amount")] + ExcessUnlockFeeError, + #[error("Transaction output to output conversion error")] + TxToInnerConversionError(#[source] Box), + #[error("Transaction input building error in conversion")] + TxToInnerConversionInputBuildingError(#[source] InputBuilderError), + #[error("P2SH extraction error")] + P2SHExtractError(#[source] TxScriptError), + #[error("PSKB hex serialization error: {0}")] + PskbSerializeToHexError(String), + #[error("PSKB serialization requires 'PSKB' prefix")] + PskbPrefixError, + #[error("PSKT serialization requires 'PSKT' prefix")] + PsktPrefixError, } - #[derive(thiserror::Error, Debug)] pub enum ConstructorError { #[error("InputNotModifiable")] @@ -13,3 +48,21 @@ pub enum ConstructorError { #[error("OutputNotModifiable")] OutputNotModifiable, } + +impl From for Error { + fn from(err: String) -> Self { + Self::Custom(err) + } +} + +impl From<&str> for Error { + fn from(err: &str) -> Self { + Self::Custom(err.to_string()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ConversionError { + #[error("Invalid output conversion")] + InvalidOutput, +} diff --git a/wallet/pskt/src/global.rs b/wallet/pskt/src/global.rs index 8e16b832bc..b79798776d 100644 --- a/wallet/pskt/src/global.rs +++ b/wallet/pskt/src/global.rs @@ -1,4 +1,5 @@ -use crate::{utils::combine_if_no_conflicts, KeySource, Version}; +use crate::pskt::{KeySource, Version}; +use crate::utils::combine_if_no_conflicts; use derive_builder::Builder; use kaspa_consensus_core::tx::TransactionId; use serde::{Deserialize, Serialize}; @@ -10,6 +11,7 @@ use std::{ type Xpub = kaspa_bip32::ExtendedPublicKey; #[derive(Debug, Clone, Builder, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] #[builder(default)] pub struct Global { /// The version number of this PSKT. @@ -33,6 +35,7 @@ pub struct Global { /// Proprietary key-value pairs for this output. pub proprietaries: BTreeMap, /// Unknown key-value pairs for this output. + #[serde(flatten)] pub unknowns: BTreeMap, } diff --git a/wallet/pskt/src/input.rs b/wallet/pskt/src/input.rs index 4c25600a1f..c99ae25426 100644 --- a/wallet/pskt/src/input.rs +++ b/wallet/pskt/src/input.rs @@ -1,7 +1,5 @@ -use crate::{ - utils::{combine_if_no_conflicts, Error as CombineMapErr}, - KeySource, PartialSigs, -}; +use crate::pskt::{KeySource, PartialSigs}; +use crate::utils::{combine_if_no_conflicts, Error as CombineMapErr}; use derive_builder::Builder; use kaspa_consensus_core::{ hashing::sighash_type::{SigHashType, SIG_HASH_ALL}, @@ -12,6 +10,7 @@ use std::{collections::BTreeMap, marker::PhantomData, ops::Add}; // todo add unknown field? combine them by deduplicating, if there are different values - return error? #[derive(Builder, Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] #[builder(default)] #[builder(setter(skip))] pub struct Input { @@ -47,7 +46,7 @@ pub struct Input { /// scripts necessary for this input to pass validation. pub final_script_sig: Option>, #[serde(skip_serializing, default)] - hidden: PhantomData<()>, // prevents manual filling of fields + pub(crate) hidden: PhantomData<()>, // prevents manual filling of fields #[builder(setter)] /// Proprietary key-value pairs for this output. pub proprietaries: BTreeMap, diff --git a/wallet/pskt/src/lib.rs b/wallet/pskt/src/lib.rs index e26d5c9ea3..ed57e9b1ed 100644 --- a/wallet/pskt/src/lib.rs +++ b/wallet/pskt/src/lib.rs @@ -1,458 +1,32 @@ -use kaspa_bip32::{secp256k1, DerivationPath, KeyFingerprint}; -use serde::{Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::{collections::BTreeMap, fmt::Display, fmt::Formatter, future::Future, marker::PhantomData, ops::Deref}; - -mod error; -mod global; -mod input; - -mod output; - -mod role; +//! +//! PSKT is a crate for working with Partially Signed Kaspa Transactions (PSKTs). +//! This crate provides following primitives: `PSKT`, `PSKTBuilder` and `Bundle`. +//! The `Bundle` struct is used for PSKT exchange payload serialization and carries +//! multiple `PSKT` instances allowing for exchange of Kaspa sweep transactions. +//! + +pub mod bundle; +pub mod error; +pub mod global; +pub mod input; +pub mod output; +pub mod pskt; +pub mod role; +pub mod wasm; + +mod convert; mod utils; -pub use error::Error; -pub use global::{Global, GlobalBuilder}; -pub use input::{Input, InputBuilder}; -use kaspa_consensus_core::tx::UtxoEntry; -use kaspa_consensus_core::{ - hashing::{sighash::SigHashReusedValues, sighash_type::SigHashType}, - subnets::SUBNETWORK_ID_NATIVE, - tx::{MutableTransaction, SignableTransaction, Transaction, TransactionId, TransactionInput, TransactionOutput}, -}; -use kaspa_txscript::{caches::Cache, TxScriptEngine}; -pub use output::{Output, OutputBuilder}; -pub use role::{Combiner, Constructor, Creator, Extractor, Finalizer, Signer, Updater}; - -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct Inner { - /// The global map. - pub global: Global, - /// The corresponding key-value map for each input in the unsigned transaction. - pub inputs: Vec, - /// The corresponding key-value map for each output in the unsigned transaction. - pub outputs: Vec, -} - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] -#[repr(u8)] -pub enum Version { - #[default] - Zero = 0, -} - -impl Display for Version { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Version::Zero => write!(f, "{}", Version::Zero as u8), - } - } -} - -/// Full information on the used extended public key: fingerprint of the -/// master extended public key and a derivation path from it. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct KeySource { - #[serde(with = "kaspa_utils::serde_bytes_fixed")] - pub key_fingerprint: KeyFingerprint, - pub derivation_path: DerivationPath, -} - -impl KeySource { - pub fn new(key_fingerprint: KeyFingerprint, derivation_path: DerivationPath) -> Self { - Self { key_fingerprint, derivation_path } - } -} - -pub type PartialSigs = BTreeMap; - -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Copy, Clone)] -pub enum Signature { - ECDSA(secp256k1::ecdsa::Signature), - Schnorr(secp256k1::schnorr::Signature), -} - -impl Signature { - pub fn into_bytes(self) -> [u8; 64] { - match self { - Signature::ECDSA(s) => s.serialize_compact(), - Signature::Schnorr(s) => s.serialize(), - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct PSKT { - #[serde(flatten)] - inner_pskt: Inner, - #[serde(skip_serializing, default)] - role: PhantomData, -} - -impl Clone for PSKT { - fn clone(&self) -> Self { - PSKT { inner_pskt: self.inner_pskt.clone(), role: Default::default() } - } -} - -impl Deref for PSKT { - type Target = Inner; - - fn deref(&self) -> &Self::Target { - &self.inner_pskt - } -} - -impl PSKT { - fn unsigned_tx(&self) -> SignableTransaction { - let tx = Transaction::new( - self.global.tx_version, - self.inputs - .iter() - .map(|Input { previous_outpoint, sequence, sig_op_count, .. }| TransactionInput { - previous_outpoint: *previous_outpoint, - signature_script: vec![], - sequence: sequence.unwrap_or(u64::MAX), - sig_op_count: sig_op_count.unwrap_or(0), - }) - .collect(), - self.outputs - .iter() - .map(|Output { amount, script_public_key, .. }: &Output| TransactionOutput { - value: *amount, - script_public_key: script_public_key.clone(), - }) - .collect(), - self.determine_lock_time(), - SUBNETWORK_ID_NATIVE, - 0, - vec![], - ); - let entries = self.inputs.iter().filter_map(|Input { utxo_entry, .. }| utxo_entry.clone()).collect(); - SignableTransaction::with_entries(tx, entries) - } - - fn calculate_id_internal(&self) -> TransactionId { - self.unsigned_tx().tx.id() - } - - fn determine_lock_time(&self) -> u64 { - self.inputs.iter().map(|input: &Input| input.min_time).max().unwrap_or(self.global.fallback_lock_time).unwrap_or(0) - } -} - -impl Default for PSKT { - fn default() -> Self { - PSKT { inner_pskt: Default::default(), role: Default::default() } - } -} - -impl PSKT { - /// Sets the fallback lock time. - pub fn fallback_lock_time(mut self, fallback: u64) -> Self { - self.inner_pskt.global.fallback_lock_time = Some(fallback); - self - } - - // todo generic const - /// Sets the inputs modifiable bit in the transaction modifiable flags. - pub fn inputs_modifiable(mut self) -> Self { - self.inner_pskt.global.inputs_modifiable = true; - self - } - // todo generic const - /// Sets the outputs modifiable bit in the transaction modifiable flags. - pub fn outputs_modifiable(mut self) -> Self { - self.inner_pskt.global.outputs_modifiable = true; - self - } - - pub fn constructor(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -impl PSKT { - // todo generic const - /// Marks that the `PSKT` can not have any more inputs added to it. - pub fn no_more_inputs(mut self) -> Self { - self.inner_pskt.global.inputs_modifiable = false; - self - } - // todo generic const - /// Marks that the `PSKT` can not have any more outputs added to it. - pub fn no_more_outputs(mut self) -> Self { - self.inner_pskt.global.outputs_modifiable = false; - self - } - - /// Adds an input to the PSKT. - pub fn input(mut self, input: Input) -> Self { - self.inner_pskt.inputs.push(input); - self.inner_pskt.global.input_count += 1; - self - } - - /// Adds an output to the PSKT. - pub fn output(mut self, output: Output) -> Self { - self.inner_pskt.outputs.push(output); - self.inner_pskt.global.output_count += 1; - self - } - - /// Returns a PSKT [`Updater`] once construction is completed. - pub fn updater(self) -> PSKT { - let pskt = self.no_more_inputs().no_more_outputs(); - PSKT { inner_pskt: pskt.inner_pskt, role: Default::default() } - } - - pub fn signer(self) -> PSKT { - self.updater().signer() - } - - pub fn combiner(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -impl PSKT { - pub fn set_sequence(mut self, n: u64, input_index: usize) -> Result { - self.inner_pskt.inputs.get_mut(input_index).ok_or(Error::OutOfBounds)?.sequence = Some(n); - Ok(self) - } - - pub fn signer(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } - - pub fn combiner(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -impl PSKT { - // todo use iterator instead of vector - pub fn pass_signature_sync(mut self, sign_fn: SignFn) -> Result - where - E: Display, - SignFn: FnOnce(SignableTransaction, Vec) -> Result, E>, - { - let unsigned_tx = self.unsigned_tx(); - let sighashes = self.inputs.iter().map(|input| input.sighash_type).collect(); - self.inner_pskt.inputs.iter_mut().zip(sign_fn(unsigned_tx, sighashes)?).for_each( - |(input, SignInputOk { signature, pub_key, key_source })| { - input.bip32_derivations.insert(pub_key, key_source); - input.partial_sigs.insert(pub_key, signature); - }, - ); - - Ok(self) - } - // todo use iterator instead of vector - pub async fn pass_signature(mut self, sign_fn: SignFn) -> Result - where - E: Display, - Fut: Future, E>>, - SignFn: FnOnce(SignableTransaction, Vec) -> Fut, - { - let unsigned_tx = self.unsigned_tx(); - let sighashes = self.inputs.iter().map(|input| input.sighash_type).collect(); - self.inner_pskt.inputs.iter_mut().zip(sign_fn(unsigned_tx, sighashes).await?).for_each( - |(input, SignInputOk { signature, pub_key, key_source })| { - input.bip32_derivations.insert(pub_key, key_source); - input.partial_sigs.insert(pub_key, signature); - }, - ); - Ok(self) - } - - pub fn calculate_id(&self) -> TransactionId { - self.calculate_id_internal() - } - - pub fn finalizer(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } - - pub fn combiner(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SignInputOk { - pub signature: Signature, - pub pub_key: secp256k1::PublicKey, - pub key_source: Option, -} - -impl std::ops::Add> for PSKT { - type Output = Result; - - fn add(mut self, mut rhs: PSKT) -> Self::Output { - self.inner_pskt.global = (self.inner_pskt.global + rhs.inner_pskt.global)?; - macro_rules! combine { - ($left:expr, $right:expr, $err: ty) => { - if $left.len() > $right.len() { - $left.iter_mut().zip($right.iter_mut()).try_for_each(|(left, right)| -> Result<(), $err> { - *left = (std::mem::take(left) + std::mem::take(right))?; - Ok(()) - })?; - $left - } else { - $right.iter_mut().zip($left.iter_mut()).try_for_each(|(left, right)| -> Result<(), $err> { - *left = (std::mem::take(left) + std::mem::take(right))?; - Ok(()) - })?; - $right - } - }; - } - // todo add sort to build deterministic combination - self.inner_pskt.inputs = combine!(self.inner_pskt.inputs, rhs.inner_pskt.inputs, input::CombineError); - self.inner_pskt.outputs = combine!(self.inner_pskt.outputs, rhs.inner_pskt.outputs, output::CombineError); - Ok(self) - } -} - -impl PSKT { - pub fn signer(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } - pub fn finalizer(self) -> PSKT { - PSKT { inner_pskt: self.inner_pskt, role: Default::default() } - } -} - -impl PSKT { - pub fn finalize_sync( - self, - final_sig_fn: impl FnOnce(&Inner) -> Result>, E>, - ) -> Result> { - let sigs = final_sig_fn(&self); - self.finalize_internal(sigs) - } - - pub async fn finalize(self, final_sig_fn: F) -> Result> - where - E: Display, - F: FnOnce(&Inner) -> Fut, - Fut: Future>, E>>, - { - let sigs = final_sig_fn(&self).await; - self.finalize_internal(sigs) - } - - pub fn id(&self) -> Option { - self.global.id - } - - pub fn extractor(self) -> Result, TxNotFinalized> { - if self.global.id.is_none() { - Err(TxNotFinalized {}) - } else { - Ok(PSKT { inner_pskt: self.inner_pskt, role: Default::default() }) - } - } - - fn finalize_internal(mut self, sigs: Result>, E>) -> Result> { - let sigs = sigs?; - if sigs.len() != self.inputs.len() { - return Err(FinalizeError::WrongFinalizedSigsCount { expected: self.inputs.len(), actual: sigs.len() }); - } - self.inner_pskt.inputs.iter_mut().enumerate().zip(sigs).try_for_each(|((idx, input), sig)| { - if sig.is_empty() { - return Err(FinalizeError::EmptySignature(idx)); - } - input.sequence = Some(input.sequence.unwrap_or(u64::MAX)); // todo discussable - input.final_script_sig = Some(sig); - Ok(()) - })?; - self.inner_pskt.global.id = Some(self.calculate_id_internal()); - Ok(self) - } -} - -impl PSKT { - pub fn extract_tx_unchecked(self) -> Result (Transaction, Vec>), TxNotFinalized> { - let tx = self.unsigned_tx(); - let entries = tx.entries; - let mut tx = tx.tx; - tx.inputs.iter_mut().zip(self.inner_pskt.inputs).try_for_each(|(dest, src)| { - dest.signature_script = src.final_script_sig.ok_or(TxNotFinalized {})?; - Ok(()) - })?; - Ok(move |mass| { - tx.set_mass(mass); - (tx, entries) - }) - } - - pub fn extract_tx(self) -> Result (Transaction, Vec>), ExtractError> { - let (tx, entries) = self.extract_tx_unchecked()?(0); - - let tx = MutableTransaction::with_entries(tx, entries.into_iter().flatten().collect()); - use kaspa_consensus_core::tx::VerifiableTransaction; - { - let tx = tx.as_verifiable(); - let cache = Cache::new(10_000); - let mut reused_values = SigHashReusedValues::new(); - - tx.populated_inputs().enumerate().try_for_each(|(idx, (input, entry))| { - TxScriptEngine::from_transaction_input(&tx, input, idx, entry, &mut reused_values, &cache)?.execute()?; - >::Ok(()) - })?; - } - let entries = tx.entries; - let tx = tx.tx; - let closure = move |mass| { - tx.set_mass(mass); - (tx, entries) - }; - Ok(closure) - } -} - -/// Error combining pskt. -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -pub enum CombineError { - #[error(transparent)] - Global(#[from] global::CombineError), - #[error(transparent)] - Inputs(#[from] input::CombineError), - #[error(transparent)] - Outputs(#[from] output::CombineError), -} - -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -pub enum FinalizeError { - #[error("Signatures count mismatch")] - WrongFinalizedSigsCount { expected: usize, actual: usize }, - #[error("Signatures at index: {0} is empty")] - EmptySignature(usize), - #[error(transparent)] - FinalaziCb(#[from] E), -} - -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -pub enum ExtractError { - #[error(transparent)] - TxScriptError(#[from] kaspa_txscript_errors::TxScriptError), - #[error(transparent)] - TxNotFinalized(#[from] TxNotFinalized), -} - -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -#[error("Transaction is not finalized")] -pub struct TxNotFinalized {} - -#[cfg(test)] -mod tests { - - // #[test] - // fn it_works() { - // let result = add(2, 2); - // assert_eq!(result, 4); - // } +pub mod prelude { + pub use crate::bundle::Bundle; + pub use crate::bundle::*; + pub use crate::global::Global; + pub use crate::input::Input; + pub use crate::output::Output; + pub use crate::pskt::*; + + // not quite sure why it warns of unused imports, + // perhaps due to the fact that enums have no variants? + #[allow(unused_imports)] + pub use crate::role::*; } diff --git a/wallet/pskt/src/output.rs b/wallet/pskt/src/output.rs index 952b63d3fb..e873ce4a66 100644 --- a/wallet/pskt/src/output.rs +++ b/wallet/pskt/src/output.rs @@ -1,11 +1,12 @@ +use crate::pskt::KeySource; use crate::utils::combine_if_no_conflicts; -use crate::KeySource; use derive_builder::Builder; use kaspa_consensus_core::tx::ScriptPublicKey; use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, ops::Add}; #[derive(Builder, Default, Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] #[builder(default)] pub struct Output { /// The output's amount (serialized as sompi). diff --git a/wallet/pskt/src/pskt.rs b/wallet/pskt/src/pskt.rs new file mode 100644 index 0000000000..245609803d --- /dev/null +++ b/wallet/pskt/src/pskt.rs @@ -0,0 +1,472 @@ +use kaspa_bip32::{secp256k1, DerivationPath, KeyFingerprint}; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; +use std::{collections::BTreeMap, fmt::Display, fmt::Formatter, future::Future, marker::PhantomData, ops::Deref}; + +pub use crate::error::Error; +pub use crate::global::{Global, GlobalBuilder}; +pub use crate::input::{Input, InputBuilder}; +pub use crate::output::{Output, OutputBuilder}; +pub use crate::role::{Combiner, Constructor, Creator, Extractor, Finalizer, Signer, Updater}; +use kaspa_consensus_core::tx::UtxoEntry; +use kaspa_consensus_core::{ + hashing::{sighash::SigHashReusedValues, sighash_type::SigHashType}, + subnets::SUBNETWORK_ID_NATIVE, + tx::{MutableTransaction, SignableTransaction, Transaction, TransactionId, TransactionInput, TransactionOutput}, +}; +use kaspa_txscript::{caches::Cache, TxScriptEngine}; + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Inner { + /// The global map. + pub global: Global, + /// The corresponding key-value map for each input in the unsigned transaction. + pub inputs: Vec, + /// The corresponding key-value map for each output in the unsigned transaction. + pub outputs: Vec, +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] +#[repr(u8)] +pub enum Version { + #[default] + Zero = 0, +} + +impl Display for Version { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Version::Zero => write!(f, "{}", Version::Zero as u8), + } + } +} + +/// Full information on the used extended public key: fingerprint of the +/// master extended public key and a derivation path from it. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct KeySource { + #[serde(with = "kaspa_utils::serde_bytes_fixed")] + pub key_fingerprint: KeyFingerprint, + pub derivation_path: DerivationPath, +} + +impl KeySource { + pub fn new(key_fingerprint: KeyFingerprint, derivation_path: DerivationPath) -> Self { + Self { key_fingerprint, derivation_path } + } +} + +pub type PartialSigs = BTreeMap; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Copy, Clone)] +#[serde(rename_all = "camelCase")] +pub enum Signature { + ECDSA(secp256k1::ecdsa::Signature), + Schnorr(secp256k1::schnorr::Signature), +} + +impl Signature { + pub fn into_bytes(self) -> [u8; 64] { + match self { + Signature::ECDSA(s) => s.serialize_compact(), + Signature::Schnorr(s) => s.serialize(), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PSKT { + #[serde(flatten)] + inner_pskt: Inner, + #[serde(skip_serializing, default)] + role: PhantomData, +} + +impl From for PSKT { + fn from(inner_pskt: Inner) -> Self { + PSKT { inner_pskt, role: Default::default() } + } +} + +impl Clone for PSKT { + fn clone(&self) -> Self { + PSKT { inner_pskt: self.inner_pskt.clone(), role: Default::default() } + } +} + +impl Deref for PSKT { + type Target = Inner; + + fn deref(&self) -> &Self::Target { + &self.inner_pskt + } +} + +impl PSKT { + fn unsigned_tx(&self) -> SignableTransaction { + let tx = Transaction::new( + self.global.tx_version, + self.inputs + .iter() + .map(|Input { previous_outpoint, sequence, sig_op_count, .. }| TransactionInput { + previous_outpoint: *previous_outpoint, + signature_script: vec![], + sequence: sequence.unwrap_or(u64::MAX), + sig_op_count: sig_op_count.unwrap_or(0), + }) + .collect(), + self.outputs + .iter() + .map(|Output { amount, script_public_key, .. }: &Output| TransactionOutput { + value: *amount, + script_public_key: script_public_key.clone(), + }) + .collect(), + self.determine_lock_time(), + SUBNETWORK_ID_NATIVE, + 0, + vec![], + ); + let entries = self.inputs.iter().filter_map(|Input { utxo_entry, .. }| utxo_entry.clone()).collect(); + SignableTransaction::with_entries(tx, entries) + } + + fn calculate_id_internal(&self) -> TransactionId { + self.unsigned_tx().tx.id() + } + + fn determine_lock_time(&self) -> u64 { + self.inputs.iter().map(|input: &Input| input.min_time).max().unwrap_or(self.global.fallback_lock_time).unwrap_or(0) + } + + pub fn to_hex(&self) -> Result { + Ok(format!("PSKT{}", hex::encode(serde_json::to_string(self)?))) + } + + pub fn from_hex(hex_data: &str) -> Result { + if let Some(hex_data) = hex_data.strip_prefix("PSKT") { + Ok(serde_json::from_slice(hex::decode(hex_data)?.as_slice())?) + } else { + Err(Error::PsktPrefixError) + } + } +} + +impl Default for PSKT { + fn default() -> Self { + PSKT { inner_pskt: Default::default(), role: Default::default() } + } +} + +impl PSKT { + /// Sets the fallback lock time. + pub fn fallback_lock_time(mut self, fallback: u64) -> Self { + self.inner_pskt.global.fallback_lock_time = Some(fallback); + self + } + + // todo generic const + /// Sets the inputs modifiable bit in the transaction modifiable flags. + pub fn inputs_modifiable(mut self) -> Self { + self.inner_pskt.global.inputs_modifiable = true; + self + } + // todo generic const + /// Sets the outputs modifiable bit in the transaction modifiable flags. + pub fn outputs_modifiable(mut self) -> Self { + self.inner_pskt.global.outputs_modifiable = true; + self + } + + pub fn constructor(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +impl PSKT { + // todo generic const + /// Marks that the `PSKT` can not have any more inputs added to it. + pub fn no_more_inputs(mut self) -> Self { + self.inner_pskt.global.inputs_modifiable = false; + self + } + // todo generic const + /// Marks that the `PSKT` can not have any more outputs added to it. + pub fn no_more_outputs(mut self) -> Self { + self.inner_pskt.global.outputs_modifiable = false; + self + } + + /// Adds an input to the PSKT. + pub fn input(mut self, input: Input) -> Self { + self.inner_pskt.inputs.push(input); + self.inner_pskt.global.input_count += 1; + self + } + + /// Adds an output to the PSKT. + pub fn output(mut self, output: Output) -> Self { + self.inner_pskt.outputs.push(output); + self.inner_pskt.global.output_count += 1; + self + } + + /// Returns a PSKT [`Updater`] once construction is completed. + pub fn updater(self) -> PSKT { + let pskt = self.no_more_inputs().no_more_outputs(); + PSKT { inner_pskt: pskt.inner_pskt, role: Default::default() } + } + + pub fn signer(self) -> PSKT { + self.updater().signer() + } + + pub fn combiner(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +impl PSKT { + pub fn set_sequence(mut self, n: u64, input_index: usize) -> Result { + self.inner_pskt.inputs.get_mut(input_index).ok_or(Error::OutOfBounds)?.sequence = Some(n); + Ok(self) + } + + pub fn signer(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } + + pub fn combiner(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +impl PSKT { + // todo use iterator instead of vector + pub fn pass_signature_sync(mut self, sign_fn: SignFn) -> Result + where + E: Display, + SignFn: FnOnce(SignableTransaction, Vec) -> Result, E>, + { + let unsigned_tx = self.unsigned_tx(); + let sighashes = self.inputs.iter().map(|input| input.sighash_type).collect(); + self.inner_pskt.inputs.iter_mut().zip(sign_fn(unsigned_tx, sighashes)?).for_each( + |(input, SignInputOk { signature, pub_key, key_source })| { + input.bip32_derivations.insert(pub_key, key_source); + input.partial_sigs.insert(pub_key, signature); + }, + ); + + Ok(self) + } + // todo use iterator instead of vector + pub async fn pass_signature(mut self, sign_fn: SignFn) -> Result + where + E: Display, + Fut: Future, E>>, + SignFn: FnOnce(SignableTransaction, Vec) -> Fut, + { + let unsigned_tx = self.unsigned_tx(); + let sighashes = self.inputs.iter().map(|input| input.sighash_type).collect(); + self.inner_pskt.inputs.iter_mut().zip(sign_fn(unsigned_tx, sighashes).await?).for_each( + |(input, SignInputOk { signature, pub_key, key_source })| { + input.bip32_derivations.insert(pub_key, key_source); + input.partial_sigs.insert(pub_key, signature); + }, + ); + Ok(self) + } + + pub fn calculate_id(&self) -> TransactionId { + self.calculate_id_internal() + } + + pub fn finalizer(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } + + pub fn combiner(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SignInputOk { + pub signature: Signature, + pub pub_key: secp256k1::PublicKey, + pub key_source: Option, +} + +impl std::ops::Add> for PSKT { + type Output = Result; + + fn add(mut self, mut rhs: PSKT) -> Self::Output { + self.inner_pskt.global = (self.inner_pskt.global + rhs.inner_pskt.global)?; + macro_rules! combine { + ($left:expr, $right:expr, $err: ty) => { + if $left.len() > $right.len() { + $left.iter_mut().zip($right.iter_mut()).try_for_each(|(left, right)| -> Result<(), $err> { + *left = (std::mem::take(left) + std::mem::take(right))?; + Ok(()) + })?; + $left + } else { + $right.iter_mut().zip($left.iter_mut()).try_for_each(|(left, right)| -> Result<(), $err> { + *left = (std::mem::take(left) + std::mem::take(right))?; + Ok(()) + })?; + $right + } + }; + } + // todo add sort to build deterministic combination + self.inner_pskt.inputs = combine!(self.inner_pskt.inputs, rhs.inner_pskt.inputs, crate::input::CombineError); + self.inner_pskt.outputs = combine!(self.inner_pskt.outputs, rhs.inner_pskt.outputs, crate::output::CombineError); + Ok(self) + } +} + +impl PSKT { + pub fn signer(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } + pub fn finalizer(self) -> PSKT { + PSKT { inner_pskt: self.inner_pskt, role: Default::default() } + } +} + +impl PSKT { + pub fn finalize_sync( + self, + final_sig_fn: impl FnOnce(&Inner) -> Result>, E>, + ) -> Result> { + let sigs = final_sig_fn(&self); + self.finalize_internal(sigs) + } + + pub async fn finalize(self, final_sig_fn: F) -> Result> + where + E: Display, + F: FnOnce(&Inner) -> Fut, + Fut: Future>, E>>, + { + let sigs = final_sig_fn(&self).await; + self.finalize_internal(sigs) + } + + pub fn id(&self) -> Option { + self.global.id + } + + pub fn extractor(self) -> Result, TxNotFinalized> { + if self.global.id.is_none() { + Err(TxNotFinalized {}) + } else { + Ok(PSKT { inner_pskt: self.inner_pskt, role: Default::default() }) + } + } + + fn finalize_internal(mut self, sigs: Result>, E>) -> Result> { + let sigs = sigs?; + if sigs.len() != self.inputs.len() { + return Err(FinalizeError::WrongFinalizedSigsCount { expected: self.inputs.len(), actual: sigs.len() }); + } + self.inner_pskt.inputs.iter_mut().enumerate().zip(sigs).try_for_each(|((idx, input), sig)| { + if sig.is_empty() { + return Err(FinalizeError::EmptySignature(idx)); + } + input.sequence = Some(input.sequence.unwrap_or(u64::MAX)); // todo discussable + input.final_script_sig = Some(sig); + Ok(()) + })?; + self.inner_pskt.global.id = Some(self.calculate_id_internal()); + Ok(self) + } +} + +impl PSKT { + pub fn extract_tx_unchecked(self) -> Result (Transaction, Vec>), TxNotFinalized> { + let tx = self.unsigned_tx(); + let entries = tx.entries; + let mut tx = tx.tx; + tx.inputs.iter_mut().zip(self.inner_pskt.inputs).try_for_each(|(dest, src)| { + dest.signature_script = src.final_script_sig.ok_or(TxNotFinalized {})?; + Ok(()) + })?; + Ok(move |mass| { + tx.set_mass(mass); + (tx, entries) + }) + } + + pub fn extract_tx(self) -> Result (Transaction, Vec>), ExtractError> { + let (tx, entries) = self.extract_tx_unchecked()?(0); + + let tx = MutableTransaction::with_entries(tx, entries.into_iter().flatten().collect()); + use kaspa_consensus_core::tx::VerifiableTransaction; + { + let tx = tx.as_verifiable(); + let cache = Cache::new(10_000); + let mut reused_values = SigHashReusedValues::new(); + + tx.populated_inputs().enumerate().try_for_each(|(idx, (input, entry))| { + TxScriptEngine::from_transaction_input(&tx, input, idx, entry, &mut reused_values, &cache)?.execute()?; + >::Ok(()) + })?; + } + let entries = tx.entries; + let tx = tx.tx; + let closure = move |mass| { + tx.set_mass(mass); + (tx, entries) + }; + Ok(closure) + } +} + +/// Error combining pskt. +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +pub enum CombineError { + #[error(transparent)] + Global(#[from] crate::global::CombineError), + #[error(transparent)] + Inputs(#[from] crate::input::CombineError), + #[error(transparent)] + Outputs(#[from] crate::output::CombineError), +} + +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +pub enum FinalizeError { + #[error("Signatures count mismatch")] + WrongFinalizedSigsCount { expected: usize, actual: usize }, + #[error("Signatures at index: {0} is empty")] + EmptySignature(usize), + #[error(transparent)] + FinalaziCb(#[from] E), +} + +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +pub enum ExtractError { + #[error(transparent)] + TxScriptError(#[from] kaspa_txscript_errors::TxScriptError), + #[error(transparent)] + TxNotFinalized(#[from] TxNotFinalized), +} + +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +#[error("Transaction is not finalized")] +pub struct TxNotFinalized {} + +#[cfg(test)] +mod tests { + + // #[test] + // fn it_works() { + // let result = add(2, 2); + // assert_eq!(result, 4); + // } +} diff --git a/wallet/pskt/src/wasm/bundle.rs b/wallet/pskt/src/wasm/bundle.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/wallet/pskt/src/wasm/bundle.rs @@ -0,0 +1 @@ + diff --git a/wallet/pskt/src/wasm/error.rs b/wallet/pskt/src/wasm/error.rs new file mode 100644 index 0000000000..77fb0d8b16 --- /dev/null +++ b/wallet/pskt/src/wasm/error.rs @@ -0,0 +1,64 @@ +use super::pskt::State; +use thiserror::Error; +use wasm_bindgen::prelude::*; + +#[derive(Error, Debug)] +pub enum Error { + #[error("{0}")] + Custom(String), + + #[error("Unexpected state: {0}")] + State(String), + + #[error("Constructor argument must be a valid payload, another PSKT instance, Transaction or undefined")] + Ctor(String), + + #[error("Invalid payload")] + InvalidPayload, + + #[error("Transaction not finalized")] + TxNotFinalized(#[from] crate::pskt::TxNotFinalized), + + #[error(transparent)] + Wasm(#[from] workflow_wasm::error::Error), + + #[error("Create state is not allowed for PSKT initialized from transaction or a payload")] + CreateNotAllowed, + + #[error("PSKT must be initialized with a payload or CREATE role")] + NotInitialized, + + #[error(transparent)] + ConsensusClient(#[from] kaspa_consensus_client::error::Error), + + #[error(transparent)] + Pskt(#[from] crate::error::Error), +} + +impl Error { + pub fn custom(msg: T) -> Self { + Error::Custom(msg.to_string()) + } + + pub fn state(state: impl AsRef) -> Self { + Error::State(state.as_ref().display().to_string()) + } +} + +impl From<&str> for Error { + fn from(msg: &str) -> Self { + Error::Custom(msg.to_string()) + } +} + +impl From for Error { + fn from(msg: String) -> Self { + Error::Custom(msg) + } +} + +impl From for JsValue { + fn from(err: Error) -> Self { + JsValue::from_str(&err.to_string()) + } +} diff --git a/wallet/pskt/src/wasm/input.rs b/wallet/pskt/src/wasm/input.rs new file mode 100644 index 0000000000..b6a827daf1 --- /dev/null +++ b/wallet/pskt/src/wasm/input.rs @@ -0,0 +1 @@ +// TODO - InputBuilder & Input diff --git a/wallet/pskt/src/wasm/mod.rs b/wallet/pskt/src/wasm/mod.rs new file mode 100644 index 0000000000..f5e9bea3fb --- /dev/null +++ b/wallet/pskt/src/wasm/mod.rs @@ -0,0 +1,6 @@ +pub mod bundle; +pub mod error; +pub mod input; +pub mod output; +pub mod pskt; +pub mod result; diff --git a/wallet/pskt/src/wasm/output.rs b/wallet/pskt/src/wasm/output.rs new file mode 100644 index 0000000000..eb91824d1a --- /dev/null +++ b/wallet/pskt/src/wasm/output.rs @@ -0,0 +1 @@ +// TODO - OutputBuilder & Output diff --git a/wallet/pskt/src/wasm/pskt.rs b/wallet/pskt/src/wasm/pskt.rs new file mode 100644 index 0000000000..78fe02bf68 --- /dev/null +++ b/wallet/pskt/src/wasm/pskt.rs @@ -0,0 +1,317 @@ +use crate::pskt::PSKT as Native; +use crate::role::*; +use kaspa_consensus_core::tx::TransactionId; +use wasm_bindgen::prelude::*; +// use js_sys::Object; +use crate::pskt::Inner; +use kaspa_consensus_client::{Transaction, TransactionInput, TransactionInputT, TransactionOutput, TransactionOutputT}; +use serde::{Deserialize, Serialize}; +use std::sync::MutexGuard; +use std::sync::{Arc, Mutex}; +use workflow_wasm::{ + convert::{Cast, CastFromJs, TryCastFromJs}, + // extensions::object::*, + // error::Error as CastError, +}; + +use super::error::*; +use super::result::*; + +#[derive(Clone, Serialize, Deserialize)] +#[serde(tag = "state", content = "payload")] +pub enum State { + NoOp(Option), + Creator(Native), + Constructor(Native), + Updater(Native), + Signer(Native), + Combiner(Native), + Finalizer(Native), + Extractor(Native), +} + +impl AsRef for State { + fn as_ref(&self) -> &State { + self + } +} + +impl State { + // this is not a Display trait intentionally + pub fn display(&self) -> &'static str { + match self { + State::NoOp(_) => "Init", + State::Creator(_) => "Creator", + State::Constructor(_) => "Constructor", + State::Updater(_) => "Updater", + State::Signer(_) => "Signer", + State::Combiner(_) => "Combiner", + State::Finalizer(_) => "Finalizer", + State::Extractor(_) => "Extractor", + } + } +} + +impl From for PSKT { + fn from(state: State) -> Self { + PSKT { state: Arc::new(Mutex::new(Some(state))) } + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "PSKT | Transaction | string | undefined")] + pub type CtorT; +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct Payload { + data: String, +} + +impl TryFrom for Native { + type Error = Error; + + fn try_from(value: Payload) -> Result { + let Payload { data } = value; + if data.starts_with("PSKT") { + unimplemented!("PSKT binary serialization") + } else { + Ok(serde_json::from_str(&data).map_err(|err| format!("Invalid JSON: {err}"))?) + } + } +} + +#[wasm_bindgen(inspectable)] +#[derive(Clone, CastFromJs)] +pub struct PSKT { + state: Arc>>, +} + +impl TryCastFromJs for PSKT { + type Error = Error; + fn try_cast_from(value: impl AsRef) -> std::result::Result, Self::Error> { + Self::resolve(&value, || { + if let Some(data) = value.as_ref().as_string() { + let pskt_inner: Inner = serde_json::from_str(&data).map_err(|_| Error::InvalidPayload)?; + Ok(PSKT::from(State::NoOp(Some(pskt_inner)))) + } else if let Ok(transaction) = Transaction::try_owned_from(&value) { + let pskt_inner: Inner = transaction.try_into()?; + Ok(PSKT::from(State::NoOp(Some(pskt_inner)))) + } else { + Err(Error::InvalidPayload) + } + }) + } +} + +#[wasm_bindgen] +impl PSKT { + #[wasm_bindgen(constructor)] + pub fn new(payload: CtorT) -> Result { + PSKT::try_owned_from(payload.unchecked_into::().as_ref()).map_err(|err| Error::Ctor(err.to_string())) + } + + #[wasm_bindgen(getter, js_name = "role")] + pub fn role_getter(&self) -> String { + self.state().as_ref().unwrap().display().to_string() + } + + #[wasm_bindgen(getter, js_name = "payload")] + pub fn payload_getter(&self) -> JsValue { + let state = self.state(); + serde_wasm_bindgen::to_value(state.as_ref().unwrap()).unwrap() + } + + fn state(&self) -> MutexGuard> { + self.state.lock().unwrap() + } + + fn take(&self) -> State { + self.state.lock().unwrap().take().unwrap() + } + + fn replace(&self, state: State) -> Result { + self.state.lock().unwrap().replace(state); + Ok(self.clone()) + } + + /// Change role to `CREATOR` + /// #[wasm_bindgen(js_name = toCreator)] + pub fn creator(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => match inner { + None => State::Creator(Native::default()), + Some(_) => Err(Error::CreateNotAllowed)?, + }, + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `CONSTRUCTOR` + #[wasm_bindgen(js_name = toConstructor)] + pub fn constructor(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Constructor(inner.ok_or(Error::NotInitialized)?.into()), + State::Creator(pskt) => State::Constructor(pskt.constructor()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `UPDATER` + #[wasm_bindgen(js_name = toUpdater)] + pub fn updater(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Updater(inner.ok_or(Error::NotInitialized)?.into()), + State::Constructor(constructor) => State::Updater(constructor.updater()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `SIGNER` + #[wasm_bindgen(js_name = toSigner)] + pub fn signer(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Signer(inner.ok_or(Error::NotInitialized)?.into()), + State::Constructor(pskt) => State::Signer(pskt.signer()), + State::Updater(pskt) => State::Signer(pskt.signer()), + State::Combiner(pskt) => State::Signer(pskt.signer()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `COMBINER` + #[wasm_bindgen(js_name = toCombiner)] + pub fn combiner(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Combiner(inner.ok_or(Error::NotInitialized)?.into()), + State::Constructor(pskt) => State::Combiner(pskt.combiner()), + State::Updater(pskt) => State::Combiner(pskt.combiner()), + State::Signer(pskt) => State::Combiner(pskt.combiner()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `FINALIZER` + #[wasm_bindgen(js_name = toFinalizer)] + pub fn finalizer(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Finalizer(inner.ok_or(Error::NotInitialized)?.into()), + State::Combiner(pskt) => State::Finalizer(pskt.finalizer()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + /// Change role to `EXTRACTOR` + #[wasm_bindgen(js_name = toExtractor)] + pub fn extractor(&self) -> Result { + let state = match self.take() { + State::NoOp(inner) => State::Extractor(inner.ok_or(Error::NotInitialized)?.into()), + State::Finalizer(pskt) => State::Extractor(pskt.extractor()?), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = fallbackLockTime)] + pub fn fallback_lock_time(&self, lock_time: u64) -> Result { + let state = match self.take() { + State::Creator(pskt) => State::Creator(pskt.fallback_lock_time(lock_time)), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = inputsModifiable)] + pub fn inputs_modifiable(&self) -> Result { + let state = match self.take() { + State::Creator(pskt) => State::Creator(pskt.inputs_modifiable()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = outputsModifiable)] + pub fn outputs_modifiable(&self) -> Result { + let state = match self.take() { + State::Creator(pskt) => State::Creator(pskt.outputs_modifiable()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = noMoreInputs)] + pub fn no_more_inputs(&self) -> Result { + let state = match self.take() { + State::Constructor(pskt) => State::Constructor(pskt.no_more_inputs()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = noMoreOutputs)] + pub fn no_more_outputs(&self) -> Result { + let state = match self.take() { + State::Constructor(pskt) => State::Constructor(pskt.no_more_outputs()), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + pub fn input(&self, input: &TransactionInputT) -> Result { + let input = TransactionInput::try_owned_from(input)?; + let state = match self.take() { + State::Constructor(pskt) => State::Constructor(pskt.input(input.try_into()?)), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + pub fn output(&self, output: &TransactionOutputT) -> Result { + let output = TransactionOutput::try_owned_from(output)?; + let state = match self.take() { + State::Constructor(pskt) => State::Constructor(pskt.output(output.try_into()?)), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = setSequence)] + pub fn set_sequence(&self, n: u64, input_index: usize) -> Result { + let state = match self.take() { + State::Updater(pskt) => State::Updater(pskt.set_sequence(n, input_index)?), + state => Err(Error::state(state))?, + }; + + self.replace(state) + } + + #[wasm_bindgen(js_name = calculateId)] + pub fn calculate_id(&self) -> Result { + let state = self.state(); + match state.as_ref().unwrap() { + State::Signer(pskt) => Ok(pskt.calculate_id()), + state => Err(Error::state(state))?, + } + } +} diff --git a/wallet/pskt/src/wasm/result.rs b/wallet/pskt/src/wasm/result.rs new file mode 100644 index 0000000000..32f663388a --- /dev/null +++ b/wallet/pskt/src/wasm/result.rs @@ -0,0 +1 @@ +pub type Result = std::result::Result; From 37f11d0bd654bd793a2bd1859f20e2f83e393a8f Mon Sep 17 00:00:00 2001 From: surinder singh Date: Thu, 8 Aug 2024 04:30:27 +0530 Subject: [PATCH 134/158] extra new line char removed (#89) --- wallet/core/src/account/descriptor.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wallet/core/src/account/descriptor.rs b/wallet/core/src/account/descriptor.rs index fd9c37c613..c549b739d2 100644 --- a/wallet/core/src/account/descriptor.rs +++ b/wallet/core/src/account/descriptor.rs @@ -122,11 +122,12 @@ impl std::fmt::Display for AccountDescriptorValue { AccountDescriptorValue::Bool(value) => write!(f, "{}", value), AccountDescriptorValue::AddressDerivationMeta(value) => write!(f, "{}", value), AccountDescriptorValue::XPubKeys(value) => { - let mut s = String::new(); + let mut s = vec![]; for xpub in value.iter() { - s.push_str(&format!("{}\n", xpub)); + //s.push(xpub.to_string(None)); + s.push(format!("{}", xpub)); } - write!(f, "{}", s) + write!(f, "{}", s.join("\n")) } AccountDescriptorValue::Json(value) => write!(f, "{}", value), } From d35efb7359c623d5d7c17f7ab0e39e2675e9d2c9 Mon Sep 17 00:00:00 2001 From: surinder singh Date: Sat, 10 Aug 2024 09:21:54 +0530 Subject: [PATCH 135/158] lock issue on wallet creation (#91) --- cli/src/modules/wallet.rs | 2 +- cli/src/wizards/wallet.rs | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/cli/src/modules/wallet.rs b/cli/src/modules/wallet.rs index 0c5f44211d..70180e78d1 100644 --- a/cli/src/modules/wallet.rs +++ b/cli/src/modules/wallet.rs @@ -51,7 +51,7 @@ impl Wallet { let wallet_name = wallet_name.as_deref(); let import_with_mnemonic = op.as_str() == "import"; - wizards::wallet::create(&ctx, wallet_name, import_with_mnemonic).await?; + wizards::wallet::create(&ctx, guard.into(), wallet_name, import_with_mnemonic).await?; } "open" => { let name = if let Some(name) = argv.first().cloned() { diff --git a/cli/src/wizards/wallet.rs b/cli/src/wizards/wallet.rs index 95d7b353d0..0fae267a36 100644 --- a/cli/src/wizards/wallet.rs +++ b/cli/src/wizards/wallet.rs @@ -2,15 +2,25 @@ use crate::cli::KaspaCli; use crate::imports::*; use crate::result::Result; use kaspa_bip32::{Language, Mnemonic, WordCount}; -use kaspa_wallet_core::storage::{make_filename, Hint}; - -pub(crate) async fn create(ctx: &Arc, name: Option<&str>, import_with_mnemonic: bool) -> Result<()> { +use kaspa_wallet_core::{ + storage::{make_filename, Hint}, + wallet::WalletGuard, +}; + +pub(crate) async fn create( + ctx: &Arc, + wallet_guard: Option>, + name: Option<&str>, + import_with_mnemonic: bool, +) -> Result<()> { let term = ctx.term(); let wallet = ctx.wallet(); + let local_guard = ctx.wallet().guard(); - let guard = ctx.wallet().guard(); - let guard = guard.lock().await; - + let guard = match wallet_guard { + Some(locked_guard) => locked_guard, + None => local_guard.lock().await, + }; // TODO @aspect let word_count = WordCount::Words12; From 7a333d983ddb0cad20c30b7b2aaf37e5bba46172 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 18 Aug 2024 04:39:24 +0300 Subject: [PATCH 136/158] wasm cast refs --- Cargo.lock | 43 +------- Cargo.toml | 48 ++++---- consensus/client/src/header.rs | 7 +- consensus/client/src/input.rs | 9 +- consensus/client/src/output.rs | 11 +- consensus/client/src/sign.rs | 12 +- consensus/client/src/transaction.rs | 13 ++- consensus/client/src/utils.rs | 4 +- consensus/client/src/utxo.rs | 18 ++- consensus/core/src/network.rs | 7 +- consensus/core/src/tx/script_public_key.rs | 7 +- consensus/pow/src/wasm.rs | 2 +- crypto/addresses/src/lib.rs | 7 +- crypto/hashes/src/lib.rs | 7 +- rpc/core/src/wasm/message.rs | 2 +- wallet/core/src/derivation.rs | 4 +- wallet/core/src/tx/generator/pending.rs | 18 +++ wallet/core/src/tx/payment.rs | 16 ++- wallet/core/src/wasm/api/message.rs | 4 +- wallet/core/src/wasm/cryptobox.rs | 16 ++- wallet/core/src/wasm/message.rs | 8 +- wallet/core/src/wasm/signer.rs | 12 +- wallet/core/src/wasm/tx/fees.rs | 2 +- .../core/src/wasm/tx/generator/generator.rs | 6 +- wallet/core/src/wasm/tx/generator/pending.rs | 27 ++++- wallet/core/src/wasm/tx/utils.rs | 8 +- wallet/core/src/wasm/utxo/context.rs | 13 ++- wallet/core/src/wasm/utxo/processor.rs | 9 +- wallet/core/src/wasm/wallet/account.rs | 104 +++++++++--------- wallet/core/src/wasm/wallet/mod.rs | 2 +- wallet/keys/src/derivation_path.rs | 7 +- wallet/keys/src/keypair.rs | 5 +- wallet/keys/src/privatekey.rs | 7 +- wallet/keys/src/pubkeygen.rs | 2 +- wallet/keys/src/publickey.rs | 17 +-- wallet/keys/src/xprv.rs | 7 +- wallet/keys/src/xpub.rs | 7 +- wallet/pskt/src/wasm/pskt.rs | 9 +- 38 files changed, 286 insertions(+), 221 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c15749bbc..a48868e2f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6871,8 +6871,6 @@ dependencies = [ [[package]] name = "workflow-chrome" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2752ac67d1a571d034279a9af0519890dd49638789c273399c095433ee4198d5" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6886,8 +6884,6 @@ dependencies = [ [[package]] name = "workflow-core" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e4fbdb34a153dae3cb7ecb6f84960015b7cba37465ff02fb982ded2d1b79bf" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6902,6 +6898,7 @@ dependencies = [ "instant", "js-sys", "rand 0.8.5", + "rlimit", "serde", "serde-wasm-bindgen", "thiserror", @@ -6918,8 +6915,6 @@ dependencies = [ [[package]] name = "workflow-core-macros" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3c5924a19620185bdbfdbd66211e1aa0e55e65497e6c2501b2e29f4ddb4d2b" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6935,8 +6930,6 @@ dependencies = [ [[package]] name = "workflow-dom" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfb07aa5b3da1f943cba196777afb1ee7c189372846b2d8de3c8510de8c8a0c" dependencies = [ "futures", "js-sys", @@ -6953,8 +6946,6 @@ dependencies = [ [[package]] name = "workflow-http" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924411a6fa2d3e2ddd591fe9056fcd6a753e62d6e4a6bc882158efb130c4bb1d" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6969,8 +6960,6 @@ dependencies = [ [[package]] name = "workflow-log" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ef6324e476d8fe35c97493b796cd5edb77ad1c9fc41b141ddd898b874c822f" dependencies = [ "cfg-if 1.0.0", "console", @@ -6985,8 +6974,6 @@ dependencies = [ [[package]] name = "workflow-macro-tools" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2a006332e207a538701b09db662467d6cd612465aafad1057eb44847a652e" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6998,8 +6985,6 @@ dependencies = [ [[package]] name = "workflow-node" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8035a06ca9213aae806e7839ab15050e818c7b616eef7905c4387bdc07fcad4c" dependencies = [ "borsh", "futures", @@ -7019,8 +7004,6 @@ dependencies = [ [[package]] name = "workflow-nw" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4fd7c2bee6f542cb01de7d4200a5999bd718578f28bd126bf853f93265b4b1" dependencies = [ "ahash", "async-trait", @@ -7043,8 +7026,6 @@ dependencies = [ [[package]] name = "workflow-panic-hook" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9324ed149bb13b633fda77b823ef98ff58c0072c9fc9ce70af1d74b83189b42c" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7068,8 +7049,6 @@ dependencies = [ [[package]] name = "workflow-rpc" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a8d999bb77b939021f658447e83b3feb7205254eff6499f302a893d38111f0" dependencies = [ "ahash", "async-std", @@ -7098,8 +7077,6 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d940809c8856e40391337c212677bfa4b81af15ff5c5e5a023bf4a98e08d2898" dependencies = [ "parse-variants", "proc-macro-error", @@ -7111,8 +7088,6 @@ dependencies = [ [[package]] name = "workflow-serializer" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d60f84ae0bbcb5df48aa8d755656729d5173cf8041f1da26ced4e6badd1607" dependencies = [ "ahash", "borsh", @@ -7122,8 +7097,6 @@ dependencies = [ [[package]] name = "workflow-store" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ce9b637375a9c329401177bdeed625ae89a46c3089167dde89432856834154" dependencies = [ "async-std", "base64 0.22.1", @@ -7150,8 +7123,6 @@ dependencies = [ [[package]] name = "workflow-task" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b0efdb7d1c17f962e967d82815712149d5b50a3657bc3a19b7e4a1c8c4c05b" dependencies = [ "futures", "thiserror", @@ -7162,8 +7133,6 @@ dependencies = [ [[package]] name = "workflow-task-macros" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b859c5a882b80b6b942f83afe020d1c2ebd0c610370e922ba35e7d5653f88f" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7178,8 +7147,6 @@ dependencies = [ [[package]] name = "workflow-terminal" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f90f17fd25f45de3441f6cc8d34569f035787549d798819f4508b9bbb194bc" dependencies = [ "async-std", "async-trait", @@ -7207,8 +7174,6 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7978dc5e66d329ad9749e5965af0d83ee216347ed52d35f86d9b760f7e85b384" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7223,8 +7188,6 @@ dependencies = [ [[package]] name = "workflow-wasm" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99adc368acba2fd1c017bdeddca479fa0c170f0a8b2955652ceb07ac3e28bb57" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7244,8 +7207,6 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353dd87151d9e95940ab252c7d61f87c5c76f0a1e6ff0fce4114f5e645e8b6f7" dependencies = [ "js-sys", "proc-macro-error", @@ -7258,8 +7219,6 @@ dependencies = [ [[package]] name = "workflow-websocket" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2343a4bbac5c0e51ee1c8dea0d581ca9387dba2d73fce3a05d82495d559ab797" dependencies = [ "ahash", "async-channel 2.2.1", diff --git a/Cargo.toml b/Cargo.toml index 016daa1bad..430d055809 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -272,34 +272,34 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -workflow-core = { version = "0.15.0" } -workflow-d3 = { version = "0.15.0" } -workflow-dom = { version = "0.15.0" } -workflow-http = { version = "0.15.0" } -workflow-log = { version = "0.15.0" } -workflow-node = { version = "0.15.0" } -workflow-nw = { version = "0.15.0" } -workflow-rpc = { version = "0.15.0" } -workflow-serializer = { version = "0.15.0" } -workflow-store = { version = "0.15.0" } -workflow-terminal = { version = "0.15.0" } -workflow-wasm = { version = "0.15.0" } +# workflow-core = { version = "0.15.0" } +# workflow-d3 = { version = "0.15.0" } +# workflow-dom = { version = "0.15.0" } +# workflow-http = { version = "0.15.0" } +# workflow-log = { version = "0.15.0" } +# workflow-node = { version = "0.15.0" } +# workflow-nw = { version = "0.15.0" } +# workflow-rpc = { version = "0.15.0" } +# workflow-serializer = { version = "0.15.0" } +# workflow-store = { version = "0.15.0" } +# workflow-terminal = { version = "0.15.0" } +# workflow-wasm = { version = "0.15.0" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs # into a sibling folder from https://github.com/workflow-rs/workflow-rs -# workflow-core = { path = "../workflow-rs/core" } -# workflow-d3 = { path = "../workflow-rs/d3" } -# workflow-dom = { path = "../workflow-rs/dom" } -# workflow-http = { path = "../workflow-rs/http" } -# workflow-log = { path = "../workflow-rs/log" } -# workflow-node = { path = "../workflow-rs/node" } -# workflow-nw = { path = "../workflow-rs/nw" } -# workflow-rpc = { path = "../workflow-rs/rpc" } -# workflow-serializer = { path = "../workflow-rs/serializer" } -# workflow-store = { path = "../workflow-rs/store" } -# workflow-terminal = { path = "../workflow-rs/terminal" } -# workflow-wasm = { path = "../workflow-rs/wasm" } +workflow-core = { path = "../workflow-rs/core" } +workflow-d3 = { path = "../workflow-rs/d3" } +workflow-dom = { path = "../workflow-rs/dom" } +workflow-http = { path = "../workflow-rs/http" } +workflow-log = { path = "../workflow-rs/log" } +workflow-node = { path = "../workflow-rs/node" } +workflow-nw = { path = "../workflow-rs/nw" } +workflow-rpc = { path = "../workflow-rs/rpc" } +workflow-serializer = { path = "../workflow-rs/serializer" } +workflow-store = { path = "../workflow-rs/store" } +workflow-terminal = { path = "../workflow-rs/terminal" } +workflow-wasm = { path = "../workflow-rs/wasm" } # --- # workflow-core = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } diff --git a/consensus/client/src/header.rs b/consensus/client/src/header.rs index 69990b7df2..927e172fa6 100644 --- a/consensus/client/src/header.rs +++ b/consensus/client/src/header.rs @@ -232,8 +232,11 @@ impl Header { impl TryCastFromJs for Header { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(object) = Object::try_from(value.as_ref()) { let parents_by_level = object .get_vec("parentsByLevel")? diff --git a/consensus/client/src/input.rs b/consensus/client/src/input.rs index 8633e2deab..736696bfae 100644 --- a/consensus/client/src/input.rs +++ b/consensus/client/src/input.rs @@ -187,14 +187,17 @@ impl AsRef for TransactionInput { impl TryCastFromJs for TransactionInput { type Error = Error; - fn try_cast_from(value: impl AsRef) -> std::result::Result, Self::Error> { - Self::resolve_cast(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> std::result::Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve_cast(value, || { if let Some(object) = Object::try_from(value.as_ref()) { let previous_outpoint: TransactionOutpoint = object.get_value("previousOutpoint")?.as_ref().try_into()?; let signature_script = object.get_vec_u8("signatureScript").ok(); let sequence = object.get_u64("sequence")?; let sig_op_count = object.get_u8("sigOpCount")?; - let utxo = object.try_get_cast::("utxo")?.map(Cast::into_owned); + let utxo = object.try_cast_into::("utxo")?; Ok(TransactionInput::new(previous_outpoint, signature_script, sequence, sig_op_count, utxo).into()) } else { Err("TransactionInput must be an object".into()) diff --git a/consensus/client/src/output.rs b/consensus/client/src/output.rs index 37694a0705..1e1e23e078 100644 --- a/consensus/client/src/output.rs +++ b/consensus/client/src/output.rs @@ -126,12 +126,15 @@ impl From<&TransactionOutput> for cctx::TransactionOutput { impl TryCastFromJs for TransactionOutput { type Error = Error; - fn try_cast_from(value: impl AsRef) -> std::result::Result, Self::Error> { - Self::resolve_cast(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> std::result::Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve_cast(value, || { if let Some(object) = Object::try_from(value.as_ref()) { let value = object.get_u64("value")?; - let script_public_key = ScriptPublicKey::try_cast_from(object.get_value("scriptPublicKey")?)?; - Ok(TransactionOutput::new(value, script_public_key.into_owned()).into()) + let script_public_key = ScriptPublicKey::try_owned_from(object.get_value("scriptPublicKey")?)?; + Ok(TransactionOutput::new(value, script_public_key).into()) } else { Err("TransactionInput must be an object".into()) } diff --git a/consensus/client/src/sign.rs b/consensus/client/src/sign.rs index b4afc11e68..c254aee076 100644 --- a/consensus/client/src/sign.rs +++ b/consensus/client/src/sign.rs @@ -13,14 +13,14 @@ use std::collections::BTreeMap; /// A wrapper enum that represents the transaction signed state. A transaction /// contained by this enum can be either fully signed or partially signed. -pub enum Signed { - Fully(Transaction), - Partially(Transaction), +pub enum Signed<'a> { + Fully(&'a Transaction), + Partially(&'a Transaction), } -impl Signed { +impl<'a> Signed<'a> { /// Returns the transaction regardless of whether it is fully or partially signed - pub fn unwrap(self) -> Transaction { + pub fn unwrap(self) -> &'a Transaction { match self { Signed::Fully(tx) => tx, Signed::Partially(tx) => tx, @@ -31,7 +31,7 @@ impl Signed { /// TODO (aspect) - merge this with `v1` fn above or refactor wallet core to use the script engine. /// Sign a transaction using schnorr #[allow(clippy::result_large_err)] -pub fn sign_with_multiple_v3(tx: Transaction, privkeys: &[[u8; 32]]) -> crate::result::Result { +pub fn sign_with_multiple_v3<'a>(tx: &'a Transaction, privkeys: &[[u8; 32]]) -> crate::result::Result> { let mut map = BTreeMap::new(); for privkey in privkeys { let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, privkey).unwrap(); diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index ccad2e2c46..7562c4bd3b 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -258,13 +258,18 @@ impl Transaction { impl TryCastFromJs for Transaction { type Error = Error; - fn try_cast_from(value: impl AsRef) -> std::result::Result, Self::Error> { - Self::resolve_cast(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> std::result::Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve_cast(value, || { if let Some(object) = Object::try_from(value.as_ref()) { if let Some(tx) = object.try_get_value("tx")? { - Transaction::try_cast_from(&tx) + // TODO - optimize to use ref anchor + Transaction::try_captured_cast_from(tx) + // Ok(Cast::value(Transaction::try_owned_from(tx)?)) } else { - let id = object.try_get_cast::("id")?.map(|id| id.into_owned()); + let id = object.try_cast_into::("id")?; let version = object.get_u16("version")?; let lock_time = object.get_u64("lockTime")?; let gas = object.get_u64("gas")?; diff --git a/consensus/client/src/utils.rs b/consensus/client/src/utils.rs index b1da47a22c..4f543d45bc 100644 --- a/consensus/client/src/utils.rs +++ b/consensus/client/src/utils.rs @@ -12,7 +12,7 @@ use kaspa_wasm_core::types::{BinaryT, HexString}; /// Creates a new script to pay a transaction output to the specified address. /// @category Wallet SDK #[wasm_bindgen(js_name = payToAddressScript)] -pub fn pay_to_address_script(address: AddressT) -> Result { +pub fn pay_to_address_script(address: &AddressT) -> Result { let address = Address::try_cast_from(address)?; Ok(standard::pay_to_address_script(address.as_ref())) } @@ -43,7 +43,7 @@ pub fn pay_to_script_hash_signature_script(redeem_script: BinaryT, signature: Bi /// @param network - The network type. /// @category Wallet SDK #[wasm_bindgen(js_name = addressFromScriptPublicKey)] -pub fn address_from_script_public_key(script_public_key: ScriptPublicKeyT, network: &NetworkTypeT) -> Result { +pub fn address_from_script_public_key(script_public_key: &ScriptPublicKeyT, network: &NetworkTypeT) -> Result { let script_public_key = ScriptPublicKey::try_cast_from(script_public_key)?; let network_type = NetworkType::try_from(network)?; diff --git a/consensus/client/src/utxo.rs b/consensus/client/src/utxo.rs index afcf87c4b1..a2c346cdea 100644 --- a/consensus/client/src/utxo.rs +++ b/consensus/client/src/utxo.rs @@ -258,7 +258,10 @@ impl TryIntoUtxoEntryReferences for JsValue { impl TryCastFromJs for UtxoEntry { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { Ok(Self::try_ref_from_js_value_as_cast(value)?) } } @@ -378,12 +381,15 @@ impl TryFrom for UtxoEntries { impl TryCastFromJs for UtxoEntryReference { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Ok(utxo_entry) = UtxoEntry::try_ref_from_js_value(&value) { Ok(Self::from(utxo_entry.clone())) } else if let Some(object) = Object::try_from(value.as_ref()) { - let address = object.get_cast::
("address")?.into_owned(); + let address = object.try_cast_into::
("address")?; let outpoint = TransactionOutpoint::try_from(object.get_value("outpoint")?.as_ref())?; let utxo_entry = Object::from(object.get_value("utxoEntry")?); @@ -400,7 +406,7 @@ impl TryCastFromJs for UtxoEntryReference { })?; let is_coinbase = utxo_entry.get_bool("isCoinbase")?; - UtxoEntry { address: Some(address), outpoint, amount, script_public_key, block_daa_score, is_coinbase } + UtxoEntry { address, outpoint, amount, script_public_key, block_daa_score, is_coinbase } } else { let amount = object.get_u64("amount").map_err(|_| { Error::custom("Supplied object does not contain `amount` property (or it is not a numerical value)") @@ -412,7 +418,7 @@ impl TryCastFromJs for UtxoEntryReference { })?; let is_coinbase = object.try_get_bool("isCoinbase")?.unwrap_or(false); - UtxoEntry { address: Some(address), outpoint, amount, script_public_key, block_daa_score, is_coinbase } + UtxoEntry { address, outpoint, amount, script_public_key, block_daa_score, is_coinbase } }; Ok(UtxoEntryReference::from(utxo_entry)) diff --git a/consensus/core/src/network.rs b/consensus/core/src/network.rs index ad59adfc3f..d5e9abd244 100644 --- a/consensus/core/src/network.rs +++ b/consensus/core/src/network.rs @@ -400,8 +400,11 @@ impl TryFrom for NetworkId { impl TryCastFromJs for NetworkId { type Error = NetworkIdError; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(network_id) = value.as_ref().as_string() { Ok(NetworkId::from_str(&network_id)?) } else { diff --git a/consensus/core/src/tx/script_public_key.rs b/consensus/core/src/tx/script_public_key.rs index f1955dfd51..dfed2ab5ce 100644 --- a/consensus/core/src/tx/script_public_key.rs +++ b/consensus/core/src/tx/script_public_key.rs @@ -374,8 +374,11 @@ impl BorshDeserialize for ScriptPublicKey { type CastError = workflow_wasm::error::Error; impl TryCastFromJs for ScriptPublicKey { type Error = workflow_wasm::error::Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(hex_str) = value.as_ref().as_string() { Ok(Self::from_str(&hex_str).map_err(CastError::custom)?) } else if let Some(object) = Object::try_from(value.as_ref()) { diff --git a/consensus/pow/src/wasm.rs b/consensus/pow/src/wasm.rs index 037aaaa819..92017d6c8a 100644 --- a/consensus/pow/src/wasm.rs +++ b/consensus/pow/src/wasm.rs @@ -31,7 +31,7 @@ pub struct PoW { #[wasm_bindgen] impl PoW { #[wasm_bindgen(constructor)] - pub fn new(header: HeaderT, timestamp: Option) -> Result { + pub fn new(header: &HeaderT, timestamp: Option) -> Result { // this function replicates crate::State::new() but caches // the pre_pow_hash value internally, making it available // via the `pre_pow_hash` property getter. diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index 112bc641ce..8aca863866 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -496,8 +496,11 @@ impl<'de> Deserialize<'de> for Address { impl TryCastFromJs for Address { type Error = AddressError; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(string) = value.as_ref().as_string() { Address::try_from(string) } else if let Some(object) = js_sys::Object::try_from(value.as_ref()) { diff --git a/crypto/hashes/src/lib.rs b/crypto/hashes/src/lib.rs index 6384a96c5d..d9ff47997c 100644 --- a/crypto/hashes/src/lib.rs +++ b/crypto/hashes/src/lib.rs @@ -187,8 +187,11 @@ impl Hash { type TryFromError = workflow_wasm::error::Error; impl TryCastFromJs for Hash { type Error = TryFromError; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { let bytes = value.as_ref().try_as_vec_u8()?; Ok(Hash( <[u8; HASH_SIZE]>::try_from(bytes) diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index d8e2707453..6fb2f9fe3a 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -820,7 +820,7 @@ declare! { } try_from! ( args: IGetBlockTemplateRequest, GetBlockTemplateRequest, { - let pay_address = args.get_cast::
("payAddress")?.into_owned(); + let pay_address = args.cast_into::
("payAddress")?; let extra_data = if let Some(extra_data) = args.try_get_value("extraData")? { if let Some(text) = extra_data.as_string() { text.into_bytes() diff --git a/wallet/core/src/derivation.rs b/wallet/core/src/derivation.rs index 8aec7364fe..2e598334e8 100644 --- a/wallet/core/src/derivation.rs +++ b/wallet/core/src/derivation.rs @@ -458,7 +458,7 @@ pub fn create_multisig_address( /// @category Wallet SDK #[wasm_bindgen(js_name=createAddress)] pub fn create_address_js( - key: PublicKeyT, + key: &PublicKeyT, network: &NetworkTypeT, ecdsa: Option, account_kind: Option, @@ -477,7 +477,7 @@ pub fn create_address_js( #[wasm_bindgen(js_name=createMultisigAddress)] pub fn create_multisig_address_js( minimum_signatures: usize, - keys: PublicKeyArrayT, + keys: &PublicKeyArrayT, network_type: NetworkType, ecdsa: Option, account_kind: Option, diff --git a/wallet/core/src/tx/generator/pending.rs b/wallet/core/src/tx/generator/pending.rs index ad7e2e40f2..5bd2ce065d 100644 --- a/wallet/core/src/tx/generator/pending.rs +++ b/wallet/core/src/tx/generator/pending.rs @@ -177,39 +177,57 @@ impl PendingTransaction { /// Submit the transaction on the supplied rpc pub async fn try_submit(&self, rpc: &Arc) -> Result { + log_info!("try_submit called on inner pending..."); + + log_info!("-- sleeping"); + + workflow_core::task::sleep(Duration::from_secs(5)).await; + log_info!("-- resuming..."); +log_info!("A"); // sanity check to prevent multiple invocations (for API use) self.inner.is_submitted.load(Ordering::SeqCst).then(|| { panic!("PendingTransaction::try_submit() called multiple times"); }); +log_info!("B"); self.inner.is_submitted.store(true, Ordering::SeqCst); +log_info!("C"); let rpc_transaction: RpcTransaction = self.rpc_transaction(); +log_info!("D"); // if we are running under UtxoProcessor if let Some(utxo_context) = self.inner.generator.source_utxo_context() { +log_info!("E"); // lock UtxoProcessor notification ingest let _lock = utxo_context.processor().notification_lock().await; +log_info!("F"); // register pending UTXOs with UtxoProcessor utxo_context.register_outgoing_transaction(self).await?; +log_info!("G"); // try to submit transaction match rpc.submit_transaction(rpc_transaction, false).await { Ok(id) => { +log_info!("H"); // on successful submit, create a notification utxo_context.notify_outgoing_transaction(self).await?; Ok(id) } Err(error) => { + log_info!("I"); // in case of failure, remove transaction UTXOs from the consumed list utxo_context.cancel_outgoing_transaction(self).await?; Err(error.into()) } } } else { +log_info!("X"); // No UtxoProcessor present (API etc) Ok(rpc.submit_transaction(rpc_transaction, false).await?) } + + } pub async fn log(&self) -> Result<()> { diff --git a/wallet/core/src/tx/payment.rs b/wallet/core/src/tx/payment.rs index 0cce1f30f3..e28c75a22f 100644 --- a/wallet/core/src/tx/payment.rs +++ b/wallet/core/src/tx/payment.rs @@ -62,8 +62,11 @@ pub struct PaymentOutput { impl TryCastFromJs for PaymentOutput { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(array) = value.as_ref().dyn_ref::() { let length = array.length(); if length != 2 { @@ -74,7 +77,7 @@ impl TryCastFromJs for PaymentOutput { Ok(Self { address, amount }) } } else if let Some(object) = Object::try_from(value.as_ref()) { - let address = object.get_cast::
("address")?.into_owned(); + let address = object.cast_into::
("address")?; let amount = object.get_u64("amount")?; Ok(Self { address, amount }) } else { @@ -145,8 +148,11 @@ impl PaymentOutputs { impl TryCastFromJs for PaymentOutputs { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { let outputs = if let Some(output_array) = value.as_ref().dyn_ref::() { let vec = output_array.to_vec(); vec.into_iter().map(PaymentOutput::try_owned_from).collect::, _>>()? diff --git a/wallet/core/src/wasm/api/message.rs b/wallet/core/src/wasm/api/message.rs index 801032eeb8..8a023267b8 100644 --- a/wallet/core/src/wasm/api/message.rs +++ b/wallet/core/src/wasm/api/message.rs @@ -982,7 +982,7 @@ try_from! (args: IAccountsDiscoveryRequest, AccountsDiscoveryRequest, { let discovery_kind = if let Some(discovery_kind) = discovery_kind.as_string() { discovery_kind.parse()? } else { - AccountsDiscoveryKind::try_cast_from(&discovery_kind)? + AccountsDiscoveryKind::try_enum_from(&discovery_kind)? }; let account_scan_extent = args.get_u32("accountScanExtent")?; let address_scan_extent = args.get_u32("addressScanExtent")?; @@ -1323,7 +1323,7 @@ try_from!(args: IAccountsCreateNewAddressRequest, AccountsCreateNewAddressReques let value = args.get_value("addressKind")?; let kind: NewAddressKind = if let Some(string) = value.as_string() { string.parse()? - } else if let Ok(kind) = NewAddressKind::try_cast_from(&value) { + } else if let Ok(kind) = NewAddressKind::try_enum_from(&value) { kind } else { NewAddressKind::Receive diff --git a/wallet/core/src/wasm/cryptobox.rs b/wallet/core/src/wasm/cryptobox.rs index 1892b08d22..118020fe4d 100644 --- a/wallet/core/src/wasm/cryptobox.rs +++ b/wallet/core/src/wasm/cryptobox.rs @@ -35,8 +35,11 @@ impl CryptoBoxPrivateKey { impl TryCastFromJs for CryptoBoxPrivateKey { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result> + where + R: AsRef + 'a, + { + Self::resolve(value, || { let secret_key = value.as_ref().try_as_vec_u8()?; if secret_key.len() != KEY_SIZE { return Err(Error::InvalidPrivateKeyLength); @@ -63,8 +66,11 @@ pub struct CryptoBoxPublicKey { impl TryCastFromJs for CryptoBoxPublicKey { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result> + where + R: AsRef + 'a, + { + Self::resolve(value, || { let public_key = value.as_ref().try_as_vec_u8()?; if public_key.len() != KEY_SIZE { Err(Error::InvalidPublicKeyLength) @@ -114,7 +120,7 @@ pub struct CryptoBox { impl CryptoBox { #[wasm_bindgen(constructor)] #[allow(non_snake_case)] - pub fn ctor(secretKey: CryptoBoxPrivateKeyT, peerPublicKey: CryptoBoxPublicKeyT) -> Result { + pub fn ctor(secretKey: &CryptoBoxPrivateKeyT, peerPublicKey: &CryptoBoxPublicKeyT) -> Result { let secret_key = CryptoBoxPrivateKey::try_cast_from(secretKey)?; let peer_public_key = CryptoBoxPublicKey::try_cast_from(peerPublicKey)?; Ok(Self { inner: Arc::new(NativeCryptoBox::new(&secret_key, &peer_public_key)) }) diff --git a/wallet/core/src/wasm/message.rs b/wallet/core/src/wasm/message.rs index 7df6f16720..25c7f399ad 100644 --- a/wallet/core/src/wasm/message.rs +++ b/wallet/core/src/wasm/message.rs @@ -28,10 +28,10 @@ extern "C" { #[wasm_bindgen(js_name = signMessage)] pub fn js_sign_message(value: ISignMessage) -> Result { if let Some(object) = Object::try_from(&value) { - let private_key = object.get_cast::("privateKey")?; + let private_key = object.cast_into::("privateKey")?; let raw_msg = object.get_string("message")?; let mut privkey_bytes = [0u8; 32]; - privkey_bytes.copy_from_slice(&private_key.as_ref().secret_bytes()); + privkey_bytes.copy_from_slice(&private_key.secret_bytes()); let pm = PersonalMessage(&raw_msg); let sig_vec = sign_message(&pm, &privkey_bytes)?; privkey_bytes.zeroize(); @@ -66,7 +66,7 @@ extern "C" { #[wasm_bindgen(js_name = verifyMessage, skip_jsdoc)] pub fn js_verify_message(value: IVerifyMessage) -> Result { if let Some(object) = Object::try_from(&value) { - let public_key = object.get_cast::("publicKey")?; + let public_key = object.cast_into::("publicKey")?; let raw_msg = object.get_string("message")?; let signature = object.get_string("signature")?; @@ -74,7 +74,7 @@ pub fn js_verify_message(value: IVerifyMessage) -> Result { let mut signature_bytes = [0u8; 64]; faster_hex::hex_decode(signature.as_bytes(), &mut signature_bytes)?; - Ok(verify_message(&pm, &signature_bytes.to_vec(), &public_key.as_ref().xonly_public_key).is_ok()) + Ok(verify_message(&pm, &signature_bytes.to_vec(), &public_key.xonly_public_key).is_ok()) } else { Err(Error::custom("Failed to parse input")) } diff --git a/wallet/core/src/wasm/signer.rs b/wallet/core/src/wasm/signer.rs index 748a95a553..e00729ef50 100644 --- a/wallet/core/src/wasm/signer.rs +++ b/wallet/core/src/wasm/signer.rs @@ -34,23 +34,23 @@ impl TryFrom for Vec { /// `signTransaction()` is a helper function to sign a transaction using a private key array or a signer array. /// @category Wallet SDK #[wasm_bindgen(js_name = "signTransaction")] -pub fn js_sign_transaction(tx: Transaction, signer: PrivateKeyArrayT, verify_sig: bool) -> Result { +pub fn js_sign_transaction(tx: &Transaction, signer: &PrivateKeyArrayT, verify_sig: bool) -> Result { if signer.is_array() { let mut private_keys: Vec<[u8; 32]> = vec![]; - for key in Array::from(&signer).iter() { - let key = PrivateKey::try_cast_from(key).map_err(|_| Error::Custom("Unable to cast PrivateKey".to_string()))?; + for key in Array::from(signer).iter() { + let key = PrivateKey::try_cast_from(&key).map_err(|_| Error::Custom("Unable to cast PrivateKey".to_string()))?; private_keys.push(key.as_ref().secret_bytes()); } let tx = sign_transaction(tx, &private_keys, verify_sig).map_err(|err| Error::Custom(format!("Unable to sign: {err:?}")))?; private_keys.zeroize(); - Ok(tx) + Ok(tx.clone()) } else { Err(Error::custom("signTransaction() requires an array of signatures")) } } -pub fn sign_transaction(tx: Transaction, private_keys: &[[u8; 32]], verify_sig: bool) -> Result { +pub fn sign_transaction<'a>(tx: &'a Transaction, private_keys: &[[u8; 32]], verify_sig: bool) -> Result<&'a Transaction> { let tx = sign(tx, private_keys)?; if verify_sig { let (cctx, utxos) = tx.tx_and_utxos()?; @@ -63,7 +63,7 @@ pub fn sign_transaction(tx: Transaction, private_keys: &[[u8; 32]], verify_sig: /// Sign a transaction using schnorr, returns a new transaction with the signatures added. /// The resulting transaction may be partially signed if the supplied keys are not sufficient /// to sign all of its inputs. -pub fn sign(tx: Transaction, privkeys: &[[u8; 32]]) -> Result { +pub fn sign<'a>(tx: &'a Transaction, privkeys: &[[u8; 32]]) -> Result<&'a Transaction> { Ok(sign_with_multiple_v3(tx, privkeys)?.unwrap()) } diff --git a/wallet/core/src/wasm/tx/fees.rs b/wallet/core/src/wasm/tx/fees.rs index ea38c6ebf6..200634cd56 100644 --- a/wallet/core/src/wasm/tx/fees.rs +++ b/wallet/core/src/wasm/tx/fees.rs @@ -39,7 +39,7 @@ impl TryFrom for Fees { } else if let Ok(object) = args.dyn_into::() { let amount = object.get_u64("amount")?; if let Some(source) = object.try_get_value("source")? { - let source = FeeSource::try_cast_from(&source)?; + let source = FeeSource::try_enum_from(&source)?; match source { FeeSource::SenderPays => Ok(Fees::SenderPays(amount)), FeeSource::ReceiverPays => Ok(Fees::ReceiverPays(amount)), diff --git a/wallet/core/src/wasm/tx/generator/generator.rs b/wallet/core/src/wasm/tx/generator/generator.rs index a110174bd6..7abe7b8981 100644 --- a/wallet/core/src/wasm/tx/generator/generator.rs +++ b/wallet/core/src/wasm/tx/generator/generator.rs @@ -276,12 +276,12 @@ impl TryFrom for GeneratorSettings { let final_transaction_destination: PaymentDestination = if outputs.is_undefined() { PaymentDestination::Change } else { PaymentOutputs::try_owned_from(outputs)?.into() }; - let change_address = args.try_get_cast::
("changeAddress")?.map(Cast::into_owned); + let change_address = args.try_cast_into::
("changeAddress")?; let final_priority_fee = args.get::("priorityFee")?.try_into()?; - let generator_source = if let Ok(Some(context)) = args.try_get_cast::("entries") { - GeneratorSource::UtxoContext(context.into_owned()) + let generator_source = if let Ok(Some(context)) = args.try_cast_into::("entries") { + GeneratorSource::UtxoContext(context) } else if let Some(utxo_entries) = args.try_get_value("entries")? { GeneratorSource::UtxoEntries(utxo_entries.try_into_utxo_entry_references()?) } else { diff --git a/wallet/core/src/wasm/tx/generator/pending.rs b/wallet/core/src/wasm/tx/generator/pending.rs index f3af3299e9..5b327912f0 100644 --- a/wallet/core/src/wasm/tx/generator/pending.rs +++ b/wallet/core/src/wasm/tx/generator/pending.rs @@ -8,6 +8,7 @@ use kaspa_consensus_core::hashing::wasm::SighashType; use kaspa_wallet_keys::privatekey::PrivateKey; use kaspa_wasm_core::types::{BinaryT, HexString}; use kaspa_wrpc_wasm::RpcClient; +use wasm_bindgen::convert::LongRefFromWasmAbi; /// @category Wallet SDK #[wasm_bindgen(inspectable)] @@ -106,9 +107,9 @@ impl PendingTransaction { if let Ok(keys) = js_value.dyn_into::() { let keys = keys .iter() - .map(PrivateKey::try_cast_from) + .map(PrivateKey::try_owned_from) .collect::, kaspa_wallet_keys::error::Error>>()?; - let mut keys = keys.iter().map(|key| key.as_ref().secret_bytes()).collect::>(); + let mut keys = keys.iter().map(|key| key.secret_bytes()).collect::>(); self.inner.try_sign_with_keys(&keys, check_fully_signed)?; keys.zeroize(); Ok(()) @@ -123,10 +124,26 @@ impl PendingTransaction { /// and will return UTXOs back to {@link UtxoContext} in case of /// a failed submission. /// @see {@link RpcClient.submitTransaction} + // pub async fn submit(&self, js_value: &JsValue) -> Result { + + // let wasm_rpc_client = unsafe { RpcClient::long_ref_from_abi(wasm_rpc_client)? }; pub async fn submit(&self, wasm_rpc_client: &RpcClient) -> Result { - let rpc: Arc = wasm_rpc_client.client().clone(); - let txid = self.inner.try_submit(&rpc).await?; - Ok(txid.to_string()) + + // let wasm_rpc_client = RpcClient::try_ref_from_js_value(js_value)?; + // let rpc: Arc = wasm_rpc_client.client().clone(); + // let txid = self.inner.try_submit(&rpc).await?; + + log_info!("### sleeping"); + workflow_core::task::sleep(Duration::from_secs(10)).await; + log_info!("### returning..."); + Ok("Hello World".to_string()) + // log_info!("Submitting transaction: {}", self.id()); + // log_info!("cloning rpc client"); + // let rpc: Arc = wasm_rpc_client.client().clone(); + // log_info!("rpc client cloned"); + // let txid = self.inner.try_submit(&rpc).await?; + // log_info!("Transaction submitted: {}", txid); + // Ok(txid.to_string()) } /// Returns encapsulated network [`Transaction`] diff --git a/wallet/core/src/wasm/tx/utils.rs b/wallet/core/src/wasm/tx/utils.rs index 3041071324..cb32c2689f 100644 --- a/wallet/core/src/wasm/tx/utils.rs +++ b/wallet/core/src/wasm/tx/utils.rs @@ -19,7 +19,7 @@ pub fn create_transaction_js( sig_op_count: Option, ) -> crate::result::Result { let utxo_entries = if let Some(utxo_entries) = utxo_entry_source.dyn_ref::() { - utxo_entries.to_vec().iter().map(UtxoEntryReference::try_cast_from).collect::, _>>()? + utxo_entries.to_vec().iter().map(UtxoEntryReference::try_owned_from).collect::, _>>()? } else { return Err(Error::custom("utxo_entries must be an array")); }; @@ -37,10 +37,10 @@ pub fn create_transaction_js( .into_iter() .enumerate() .map(|(sequence, reference)| { - let UtxoEntryReference { utxo } = reference.as_ref(); + let UtxoEntryReference { utxo } = &reference; total_input_amount += utxo.amount(); - entries.push(reference.as_ref().clone()); - TransactionInput::new(utxo.outpoint.clone(), None, sequence as u64, sig_op_count, Some(reference.into_owned())) + entries.push(reference.clone()); + TransactionInput::new(utxo.outpoint.clone(), None, sequence as u64, sig_op_count, Some(reference)) }) .collect::>(); diff --git a/wallet/core/src/wasm/utxo/context.rs b/wallet/core/src/wasm/utxo/context.rs index c66a3f647e..3298a4829e 100644 --- a/wallet/core/src/wasm/utxo/context.rs +++ b/wallet/core/src/wasm/utxo/context.rs @@ -252,7 +252,10 @@ impl From for native::UtxoContext { impl TryCastFromJs for UtxoContext { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { Ok(Self::try_ref_from_js_value_as_cast(value)?) } } @@ -266,15 +269,15 @@ impl TryFrom for UtxoContextCreateArgs { type Error = Error; fn try_from(value: IUtxoContextArgs) -> std::result::Result { if let Some(object) = Object::try_from(&value) { - let processor = object.get_cast::("processor")?; + let processor = object.cast_into::("processor")?; - let binding = if let Some(id) = object.try_get_cast::("id")? { - UtxoContextBinding::Id(UtxoContextId::new(id.into_owned())) + let binding = if let Some(id) = object.try_cast_into::("id")? { + UtxoContextBinding::Id(UtxoContextId::new(id)) } else { UtxoContextBinding::default() }; - Ok(UtxoContextCreateArgs { binding, processor: processor.into_owned() }) + Ok(UtxoContextCreateArgs { binding, processor }) } else { Err(Error::custom("UtxoProcessor: supplied value must be an object")) } diff --git a/wallet/core/src/wasm/utxo/processor.rs b/wallet/core/src/wasm/utxo/processor.rs index 09296a2a6f..be922d64c0 100644 --- a/wallet/core/src/wasm/utxo/processor.rs +++ b/wallet/core/src/wasm/utxo/processor.rs @@ -171,7 +171,7 @@ impl UtxoProcessor { /// @category Wallet SDK /// #[wasm_bindgen(js_name = "setCoinbaseTransactionMaturityDAA")] - pub fn set_coinbase_transaction_maturity_period_daa_js(network_id: NetworkIdT, value: u64) -> Result<()> { + pub fn set_coinbase_transaction_maturity_period_daa_js(network_id: &NetworkIdT, value: u64) -> Result<()> { let network_id = NetworkId::try_cast_from(network_id)?.into_owned(); crate::utxo::set_coinbase_transaction_maturity_period_daa(&network_id, value); Ok(()) @@ -188,7 +188,7 @@ impl UtxoProcessor { /// @category Wallet SDK /// #[wasm_bindgen(js_name = "setUserTransactionMaturityDAA")] - pub fn set_user_transaction_maturity_period_daa_js(network_id: NetworkIdT, value: u64) -> Result<()> { + pub fn set_user_transaction_maturity_period_daa_js(network_id: &NetworkIdT, value: u64) -> Result<()> { let network_id = NetworkId::try_cast_from(network_id)?.into_owned(); crate::utxo::set_user_transaction_maturity_period_daa(&network_id, value); Ok(()) @@ -197,7 +197,10 @@ impl UtxoProcessor { impl TryCastFromJs for UtxoProcessor { type Error = workflow_wasm::error::Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { Self::try_ref_from_js_value_as_cast(value) } } diff --git a/wallet/core/src/wasm/wallet/account.rs b/wallet/core/src/wasm/wallet/account.rs index 976e1df622..4e0938e64d 100644 --- a/wallet/core/src/wasm/wallet/account.rs +++ b/wallet/core/src/wasm/wallet/account.rs @@ -85,11 +85,11 @@ impl Account { self.inner.clone().scan(None, None).await } - pub async fn send(&self, js_value: JsValue) -> Result { - let _args = AccountSendArgs::try_from(js_value)?; + // pub async fn send(&self, js_value: JsValue) -> Result { + // let _args = AccountSendArgs::try_from(js_value)?; - todo!() - } + // todo!() + // } } impl From for Arc { @@ -105,51 +105,51 @@ impl TryFrom<&JsValue> for Account { } } -pub struct AccountSendArgs { - pub outputs: PaymentOutputs, - pub priority_fee_sompi: Option, - pub include_fees_in_amount: bool, - - pub wallet_secret: Secret, - pub payment_secret: Option, - pub abortable: Abortable, -} - -impl TryFrom for AccountSendArgs { - type Error = Error; - fn try_from(js_value: JsValue) -> std::result::Result { - if let Some(object) = Object::try_from(&js_value) { - let outputs = object.get_cast::("outputs")?.into_owned(); - - let priority_fee_sompi = object.get_u64("priorityFee").ok(); - let include_fees_in_amount = object.get_bool("includeFeesInAmount").unwrap_or(false); - let abortable = object.get("abortable").ok().and_then(|v| Abortable::try_from(&v).ok()).unwrap_or_default(); - - let wallet_secret = object.get_string("walletSecret")?.into(); - let payment_secret = object.get_value("paymentSecret")?.as_string().map(|s| s.into()); - - let send_args = - AccountSendArgs { outputs, priority_fee_sompi, include_fees_in_amount, wallet_secret, payment_secret, abortable }; - - Ok(send_args) - } else { - Err("Argument to Account::send() must be an object".into()) - } - } -} - -pub struct AccountCreateArgs {} - -impl TryFrom for AccountCreateArgs { - type Error = Error; - fn try_from(value: JsValue) -> std::result::Result { - if let Some(object) = Object::try_from(&value) { - let _keypair = object.try_get_cast::("keypair")?; - let _public_key = object.try_get_cast::("keypair")?; - - Ok(AccountCreateArgs {}) - } else { - Err(Error::custom("Account: supplied value must be an object")) - } - } -} +// pub struct AccountSendArgs { +// pub outputs: PaymentOutputs, +// pub priority_fee_sompi: Option, +// pub include_fees_in_amount: bool, + +// pub wallet_secret: Secret, +// pub payment_secret: Option, +// pub abortable: Abortable, +// } + +// impl TryFrom for AccountSendArgs { +// type Error = Error; +// fn try_from<'a,R>(js_value: JsValue) -> std::result::Result { +// if let Some(object) = Object::try_from(&js_value) { +// let outputs = object.cast_into::("outputs")?.into_owned(); + +// let priority_fee_sompi = object.get_u64("priorityFee").ok(); +// let include_fees_in_amount = object.get_bool("includeFeesInAmount").unwrap_or(false); +// let abortable = object.get("abortable").ok().and_then(|v| Abortable::try_from(&v).ok()).unwrap_or_default(); + +// let wallet_secret = object.get_string("walletSecret")?.into(); +// let payment_secret = object.get_value("paymentSecret")?.as_string().map(|s| s.into()); + +// let send_args = +// AccountSendArgs { outputs, priority_fee_sompi, include_fees_in_amount, wallet_secret, payment_secret, abortable }; + +// Ok(send_args) +// } else { +// Err("Argument to Account::send() must be an object".into()) +// } +// } +// } + +// pub struct AccountCreateArgs {} + +// impl TryFrom for AccountCreateArgs { +// type Error = Error; +// fn try_from(value: JsValue) -> std::result::Result { +// if let Some(object) = Object::try_from(&value) { +// let _keypair = object.try_cast_into::("keypair")?; +// let _public_key = object.try_cast_into::("keypair")?; + +// Ok(AccountCreateArgs {}) +// } else { +// Err(Error::custom("Account: supplied value must be an object")) +// } +// } +// } diff --git a/wallet/core/src/wasm/wallet/mod.rs b/wallet/core/src/wasm/wallet/mod.rs index cacd73628d..41de19f5a5 100644 --- a/wallet/core/src/wasm/wallet/mod.rs +++ b/wallet/core/src/wasm/wallet/mod.rs @@ -1,4 +1,4 @@ -pub mod account; +// pub mod account; pub mod keydata; #[allow(clippy::module_inception)] pub mod wallet; diff --git a/wallet/keys/src/derivation_path.rs b/wallet/keys/src/derivation_path.rs index a4c3efe691..df220ee445 100644 --- a/wallet/keys/src/derivation_path.rs +++ b/wallet/keys/src/derivation_path.rs @@ -51,8 +51,11 @@ impl DerivationPath { impl TryCastFromJs for DerivationPath { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { let value = value.as_ref(); if let Some(path) = value.as_string() { Ok(DerivationPath::new(&path)?) diff --git a/wallet/keys/src/keypair.rs b/wallet/keys/src/keypair.rs index 3bed0152b7..f4b39f3d39 100644 --- a/wallet/keys/src/keypair.rs +++ b/wallet/keys/src/keypair.rs @@ -100,7 +100,10 @@ impl Keypair { impl TryCastFromJs for Keypair { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { Ok(Self::try_ref_from_js_value_as_cast(value)?) } } diff --git a/wallet/keys/src/privatekey.rs b/wallet/keys/src/privatekey.rs index 825bf395fa..84e2d2e3ba 100644 --- a/wallet/keys/src/privatekey.rs +++ b/wallet/keys/src/privatekey.rs @@ -93,8 +93,11 @@ impl PrivateKey { impl TryCastFromJs for PrivateKey { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(hex_str) = value.as_ref().as_string() { Self::try_new(hex_str.as_str()) } else if Array::is_array(value.as_ref()) { diff --git a/wallet/keys/src/pubkeygen.rs b/wallet/keys/src/pubkeygen.rs index c5bf0f7972..c05ae844fa 100644 --- a/wallet/keys/src/pubkeygen.rs +++ b/wallet/keys/src/pubkeygen.rs @@ -21,7 +21,7 @@ pub struct PublicKeyGenerator { #[wasm_bindgen] impl PublicKeyGenerator { #[wasm_bindgen(js_name=fromXPub)] - pub fn from_xpub(kpub: XPubT, cosigner_index: Option) -> Result { + pub fn from_xpub(kpub: &XPubT, cosigner_index: Option) -> Result { let kpub = XPub::try_cast_from(kpub)?; let xpub = kpub.as_ref().inner(); let hd_wallet = WalletDerivationManager::from_extended_public_key(xpub.clone(), cosigner_index)?; diff --git a/wallet/keys/src/publickey.rs b/wallet/keys/src/publickey.rs index f262a12f4a..513f292c43 100644 --- a/wallet/keys/src/publickey.rs +++ b/wallet/keys/src/publickey.rs @@ -138,8 +138,11 @@ extern "C" { impl TryCastFromJs for PublicKey { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { let value = value.as_ref(); if let Some(hex_str) = value.as_string() { Ok(PublicKey::try_new(hex_str.as_str())?) @@ -150,13 +153,13 @@ impl TryCastFromJs for PublicKey { } } -impl TryFrom for Vec { +impl TryFrom<&PublicKeyArrayT> for Vec { type Error = Error; - fn try_from(value: PublicKeyArrayT) -> Result { + fn try_from(value: &PublicKeyArrayT) -> Result { if value.is_array() { - let array = Array::from(&value); - let pubkeys = array.iter().map(PublicKey::try_cast_from).collect::>>()?; - Ok(pubkeys.iter().map(|pk| pk.as_ref().try_into()).collect::>>()?) + let array = Array::from(value); + let pubkeys = array.iter().map(PublicKey::try_owned_from).collect::>>()?; + Ok(pubkeys.iter().map(|pk| pk.try_into()).collect::>>()?) } else { Err(Error::InvalidPublicKeyArray) } diff --git a/wallet/keys/src/xprv.rs b/wallet/keys/src/xprv.rs index 1f478140eb..3e120841b7 100644 --- a/wallet/keys/src/xprv.rs +++ b/wallet/keys/src/xprv.rs @@ -142,8 +142,11 @@ extern "C" { impl TryCastFromJs for XPrv { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(xprv) = value.as_ref().as_string() { Ok(XPrv::from_xprv_str(xprv)?) } else { diff --git a/wallet/keys/src/xpub.rs b/wallet/keys/src/xpub.rs index 623136c988..551881d8ee 100644 --- a/wallet/keys/src/xpub.rs +++ b/wallet/keys/src/xpub.rs @@ -112,8 +112,11 @@ extern "C" { impl TryCastFromJs for XPub { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(xpub) = value.as_ref().as_string() { Ok(XPub::try_new(xpub.as_str())?) } else { diff --git a/wallet/pskt/src/wasm/pskt.rs b/wallet/pskt/src/wasm/pskt.rs index 78fe02bf68..8ee370a4b9 100644 --- a/wallet/pskt/src/wasm/pskt.rs +++ b/wallet/pskt/src/wasm/pskt.rs @@ -90,12 +90,15 @@ pub struct PSKT { impl TryCastFromJs for PSKT { type Error = Error; - fn try_cast_from(value: impl AsRef) -> std::result::Result, Self::Error> { - Self::resolve(&value, || { + fn try_cast_from<'a, R>(value: &'a R) -> std::result::Result, Self::Error> + where + R: AsRef + 'a, + { + Self::resolve(value, || { if let Some(data) = value.as_ref().as_string() { let pskt_inner: Inner = serde_json::from_str(&data).map_err(|_| Error::InvalidPayload)?; Ok(PSKT::from(State::NoOp(Some(pskt_inner)))) - } else if let Ok(transaction) = Transaction::try_owned_from(&value) { + } else if let Ok(transaction) = Transaction::try_owned_from(value) { let pskt_inner: Inner = transaction.try_into()?; Ok(PSKT::from(State::NoOp(Some(pskt_inner)))) } else { From 3021689c0bcb5c07ddab172ccdf551015dffab6c Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 18 Aug 2024 05:31:36 +0300 Subject: [PATCH 137/158] resolver updates --- rpc/wrpc/client/src/resolver.rs | 17 +++++++++++++---- rpc/wrpc/wasm/src/resolver.rs | 14 ++++++++++++-- wasm/build-node-dev | 3 ++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index 0536ef0510..8dcb194476 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -58,6 +58,7 @@ fn try_parse_resolvers(toml: &str) -> Result>> { struct Inner { pub urls: Vec>, pub tls: bool, + public: bool, } impl Inner { @@ -66,9 +67,13 @@ impl Inner { panic!("Resolver: Empty URL list supplied to the constructor."); } - let urls = urls.unwrap_or_else(|| try_parse_resolvers(RESOLVER_CONFIG).expect("TOML: Unable to parse RPC Resolver list")); + let mut public = false; + let urls = urls.unwrap_or_else(|| { + public = true; + try_parse_resolvers(RESOLVER_CONFIG).expect("TOML: Unable to parse RPC Resolver list") + }); - Self { urls, tls } + Self { urls, tls, public } } } @@ -91,8 +96,12 @@ impl Resolver { Self { inner: Arc::new(Inner::new(urls, tls)) } } - pub fn urls(&self) -> Vec> { - self.inner.urls.clone() + pub fn urls(&self) -> Option>> { + if self.inner.public { + None + } else { + Some(self.inner.urls.clone()) + } } pub fn tls(&self) -> bool { diff --git a/rpc/wrpc/wasm/src/resolver.rs b/rpc/wrpc/wasm/src/resolver.rs index e53f389fca..d78079ca28 100644 --- a/rpc/wrpc/wasm/src/resolver.rs +++ b/rpc/wrpc/wasm/src/resolver.rs @@ -144,8 +144,8 @@ impl Resolver { impl Resolver { /// List of public Kaspa Resolver URLs. #[wasm_bindgen(getter)] - pub fn urls(&self) -> ResolverArrayT { - Array::from_iter(self.resolver.urls().iter().map(|v| JsValue::from(v.as_str()))).unchecked_into() + pub fn urls(&self) -> Option { + self.resolver.urls().map(|urls| Array::from_iter(urls.iter().map(|v| JsValue::from(v.as_str()))).unchecked_into()) } /// Fetches a public Kaspa wRPC endpoint for the given encoding and network identifier. @@ -199,6 +199,16 @@ impl TryCastFromJs for Resolver { } } +// impl TryCastFromJs for Resolver { +// type Error = Error; +// fn try_cast_from<'a, R>(value: &'a R) -> Result> +// where +// R: AsRef + 'a, +// { +// Ok(Self::try_ref_from_js_value_as_cast(value)?) +// } +// } + impl TryFrom<&JsValue> for Resolver { type Error = Error; fn try_from(js_value: &JsValue) -> Result { diff --git a/wasm/build-node-dev b/wasm/build-node-dev index b8de2b6acf..6dc5446fd1 100755 --- a/wasm/build-node-dev +++ b/wasm/build-node-dev @@ -5,4 +5,5 @@ RED='\033[0;31m' NC='\033[0m' # No Color echo -e "${RED}WARNING: do not use resulting WASM binaries in production!${NC}" -wasm-pack build --weak-refs --dev --target nodejs --out-name kaspa --out-dir nodejs/kaspa --features wasm32-sdk $@ +# wasm-pack build --weak-refs --dev --target nodejs --out-name kaspa --out-dir nodejs/kaspa-dev --features wasm32-sdk $@ +wasm-pack build --weak-refs --dev --target nodejs --out-name kaspa --out-dir nodejs/kaspa-dev --features wasm32-sdk $@ From 111d218a0d159eea11d43b11a63716c21116a38f Mon Sep 17 00:00:00 2001 From: 1bananagirl <168954040+1bananagirl@users.noreply.github.com> Date: Sun, 18 Aug 2024 04:32:41 +0200 Subject: [PATCH 138/158] CLI review: import cli watch-only changed to watch, PSKB parse view added (#92) * CLI - Import commands for watch-only accounts changed to: account watch bip32 and account watch multisig. * CLI - PSKB parse view added next to debug view showing input/output addresses and amounts. PSKT finalized check moved from debug view to parse view. Selected account requirement in commands only if needed. --- cli/src/modules/account.rs | 60 ++++++++++-------- cli/src/modules/pskb.rs | 127 +++++++++++++++++++------------------ wallet/pskt/src/bundle.rs | 42 +++++++++++- 3 files changed, 142 insertions(+), 87 deletions(-) diff --git a/cli/src/modules/account.rs b/cli/src/modules/account.rs index 45b8cd1b76..5848d43fba 100644 --- a/cli/src/modules/account.rs +++ b/cli/src/modules/account.rs @@ -84,8 +84,6 @@ impl Account { "account import mnemonic multisig [additional keys]", "Import mnemonic and additional keys for a multisig account", ), - ("account import bip32-watch", "Import a extended public key for a watch-only bip32 account"), - ("account import multisig-watch", "Import extended public keys for a watch-only multisig account"), ], None, )?; @@ -177,36 +175,46 @@ impl Account { return Ok(()); } - "bip32-watch" => { - let account_name = if argv.is_empty() { - None - } else { - let name = argv.remove(0); - let name = name.trim().to_string(); - Some(name) - }; - - let account_name = account_name.as_deref(); - wizards::account::bip32_watch(&ctx, account_name).await?; + _ => { + tprintln!(ctx, "unknown account import type: '{import_kind}'"); + tprintln!(ctx, "supported import types are: 'mnemonic', 'legacy-data' or 'multisig-watch'\r\n"); + return Ok(()); } - "multisig-watch" => { - let account_name = if argv.is_empty() { - None - } else { - let name = argv.remove(0); - let name = name.trim().to_string(); + } + } + "watch" => { + if argv.is_empty() { + tprintln!(ctx, "usage: 'account watch [account name]'"); + tprintln!(ctx, ""); + tprintln!(ctx, "examples:"); + tprintln!(ctx, ""); + ctx.term().help( + &[ + ("account watch bip32", "Import a extended public key for a watch-only bip32 account"), + ("account watch multisig", "Import extended public keys for a watch-only multisig account"), + ], + None, + )?; + + return Ok(()); + } - Some(name) - }; + let watch_kind = argv.remove(0); - let account_name = account_name.as_deref(); - wizards::account::multisig_watch(&ctx, account_name).await?; + let account_name = argv.first().map(|name| name.trim()).filter(|name| !name.is_empty()).map(|name| name.to_string()); - return Ok(()); + let account_name = account_name.as_deref(); + + match watch_kind.as_ref() { + "bip32" => { + wizards::account::bip32_watch(&ctx, account_name).await?; + } + "multisig" => { + wizards::account::multisig_watch(&ctx, account_name).await?; } _ => { - tprintln!(ctx, "unknown account import type: '{import_kind}'"); - tprintln!(ctx, "supported import types are: 'mnemonic', 'legacy-data' or 'multisig-watch'\r\n"); + tprintln!(ctx, "unknown account watch type: '{watch_kind}'"); + tprintln!(ctx, "supported watch types are: 'bip32' or 'multisig'\r\n"); return Ok(()); } } diff --git a/cli/src/modules/pskb.rs b/cli/src/modules/pskb.rs index 97ed5d9994..fd33087c22 100644 --- a/cli/src/modules/pskb.rs +++ b/cli/src/modules/pskb.rs @@ -1,9 +1,13 @@ #![allow(unused_imports)] use crate::imports::*; +use kaspa_addresses::Prefix; use kaspa_consensus_core::tx::{TransactionOutpoint, UtxoEntry}; use kaspa_wallet_core::account::pskb::finalize_pskt_one_or_more_sig_and_redeem_script; -use kaspa_wallet_pskt::prelude::{lock_script_sig_templating, script_sig_to_address, unlock_utxos_as_pskb, Bundle, Signer, PSKT}; +use kaspa_wallet_pskt::{ + prelude::{lock_script_sig_templating, script_sig_to_address, unlock_utxos_as_pskb, Bundle, Signer, PSKT}, + pskt::Inner, +}; #[derive(Default, Handler)] #[help("Send a Kaspa transaction to a public address")] @@ -17,8 +21,6 @@ impl Pskb { return Err(Error::WalletIsNotOpen); } - let account = ctx.wallet().account()?; - if argv.is_empty() { return self.display_help(ctx, argv).await; } @@ -29,41 +31,40 @@ impl Pskb { "create" => { if argv.len() < 2 || argv.len() > 3 { return self.display_help(ctx, argv).await; - } else { - let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; - let _ = ctx.notifier().show(Notification::Processing).await; - - let address = Address::try_from(argv.first().unwrap().as_str())?; - let amount_sompi = try_parse_required_nonzero_kaspa_as_sompi_u64(argv.get(1))?; - let outputs = PaymentOutputs::from((address, amount_sompi)); - let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.get(2))?.unwrap_or(0); - let abortable = Abortable::default(); - - let signer = account - .pskb_from_send_generator( - outputs.into(), - priority_fee_sompi.into(), - None, - wallet_secret.clone(), - payment_secret.clone(), - &abortable, - ) - .await?; + } + let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; + let _ = ctx.notifier().show(Notification::Processing).await; - match signer.serialize() { - Ok(encoded) => tprintln!(ctx, "{encoded}"), - Err(e) => return Err(e.into()), - } + let address = Address::try_from(argv.first().unwrap().as_str())?; + let amount_sompi = try_parse_required_nonzero_kaspa_as_sompi_u64(argv.get(1))?; + let outputs = PaymentOutputs::from((address, amount_sompi)); + let priority_fee_sompi = try_parse_optional_kaspa_as_sompi_i64(argv.get(2))?.unwrap_or(0); + let abortable = Abortable::default(); + + let account: Arc = ctx.wallet().account()?; + let signer = account + .pskb_from_send_generator( + outputs.into(), + priority_fee_sompi.into(), + None, + wallet_secret.clone(), + payment_secret.clone(), + &abortable, + ) + .await?; + + match signer.serialize() { + Ok(encoded) => tprintln!(ctx, "{encoded}"), + Err(e) => return Err(e.into()), } } "script" => { if argv.len() < 2 || argv.len() > 4 { return self.display_help(ctx, argv).await; } - let subcommand = argv.remove(0); let payload = argv.remove(0); - + let account = ctx.wallet().account()?; let receive_address = account.receive_address()?; let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; let _ = ctx.notifier().show(Notification::Processing).await; @@ -168,58 +169,63 @@ impl Pskb { "sign" => { if argv.len() != 1 { return self.display_help(ctx, argv).await; - } else { - let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; - let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; - - match account.pskb_sign(&pskb, wallet_secret.clone(), payment_secret.clone(), None).await { - Ok(signed_pskb) => { - let pskb_pack = String::try_from(signed_pskb)?; - tprintln!(ctx, "{pskb_pack}"); - } - Err(e) => terrorln!(ctx, "{}", e.to_string()), + } + let (wallet_secret, payment_secret) = ctx.ask_wallet_secret(None).await?; + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + let account = ctx.wallet().account()?; + match account.pskb_sign(&pskb, wallet_secret.clone(), payment_secret.clone(), None).await { + Ok(signed_pskb) => { + let pskb_pack = String::try_from(signed_pskb)?; + tprintln!(ctx, "{pskb_pack}"); } + Err(e) => terrorln!(ctx, "{}", e.to_string()), } } "send" => { if argv.len() != 1 { return self.display_help(ctx, argv).await; - } else { - let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; - - match account.pskb_broadcast(&pskb).await { - Ok(sent) => tprintln!(ctx, "Sent transactions {:?}", sent), - Err(e) => terrorln!(ctx, "Send error {:?}", e), - } + } + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + let account = ctx.wallet().account()?; + match account.pskb_broadcast(&pskb).await { + Ok(sent) => tprintln!(ctx, "Sent transactions {:?}", sent), + Err(e) => terrorln!(ctx, "Send error {:?}", e), } } "debug" => { + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; - - // Debug bundle view. tprintln!(ctx, "{:?}", pskb); + } + "parse" => { + if argv.len() != 1 { + return self.display_help(ctx, argv).await; + } + let pskb = Self::parse_input_pskb(argv.first().unwrap().as_str())?; + tprintln!(ctx, "{}", pskb.display_format(ctx.wallet().network_id()?, sompi_to_kaspa_string_with_suffix)); - match pskb.as_ref().first() { - Some(bundle_inner) => { - let pskt: PSKT = PSKT::::from(bundle_inner.to_owned()); - let mut fin = pskt.finalizer(); + for (pskt_index, bundle_inner) in pskb.0.iter().enumerate() { + tprintln!(ctx, "PSKT #{:03} finalized check:", pskt_index + 1); + let pskt: PSKT = PSKT::::from(bundle_inner.to_owned()); - fin = finalize_pskt_one_or_more_sig_and_redeem_script(fin).expect("Finalized PSKT"); + let finalizer = pskt.finalizer(); + if let Ok(pskt_finalizer) = finalize_pskt_one_or_more_sig_and_redeem_script(finalizer) { // Verify if extraction is possible. - match fin.extractor() { + match pskt_finalizer.extractor() { Ok(ex) => match ex.extract_tx() { Ok(_) => tprintln!( ctx, - "Transaction extracted successfuly meaning it is finalized with a valid script signature." + " Transaction extracted successfully: PSKT is finalized with a valid script signature." ), - Err(e) => terrorln!(ctx, "Transaction extraction error: {}", e.to_string()), + Err(e) => terrorln!(ctx, " PSKT transaction extraction error: {}", e.to_string()), }, - Err(_) => twarnln!(ctx, "PSKB not finalized"), + Err(_) => twarnln!(ctx, " PSKT not finalized"), } - } - None => { - twarnln!(ctx, "Debugging an empty PSKB"); + } else { + twarnln!(ctx, " PSKT not signed"); } } } @@ -245,6 +251,7 @@ impl Pskb { ("pskb sign ", "Sign given PSKB"), ("pskb send ", "Broadcast bundled transactions"), ("pskb debug ", "Print PSKB debug view"), + ("pskb parse ", "Print PSKB formatted view"), ("pskb script lock [priority fee]", "Generate a PSKB with one send transaction to given P2SH payload. Optional public key placeholder in payload: {{pubkey}}"), ("pskb script unlock ", "Generate a PSKB to unlock UTXOS one by one from given P2SH payload. Fee amount will be applied to every spent UTXO, meaning every transaction. Optional public key placeholder in payload: {{pubkey}}"), ("pskb script sign ", "Sign all PSKB's P2SH locked inputs"), diff --git a/wallet/pskt/src/bundle.rs b/wallet/pskt/src/bundle.rs index c508684106..6e8dc83506 100644 --- a/wallet/pskt/src/bundle.rs +++ b/wallet/pskt/src/bundle.rs @@ -3,7 +3,9 @@ use crate::prelude::*; use crate::pskt::{Inner as PSKTInner, PSKT}; // use crate::wasm::result; -use kaspa_addresses::Address; +use kaspa_addresses::{Address, Prefix}; +// use kaspa_bip32::Prefix; +use kaspa_consensus_core::network::{NetworkId, NetworkType}; use kaspa_consensus_core::tx::{ScriptPublicKey, TransactionOutpoint, UtxoEntry}; use hex; @@ -66,6 +68,44 @@ impl Bundle { Err(Error::PskbPrefixError) } } + + pub fn display_format(&self, network_id: NetworkId, sompi_formatter: F) -> String + where + F: Fn(u64, &NetworkType) -> String, + { + let mut result = "".to_string(); + + for (pskt_index, bundle_inner) in self.0.iter().enumerate() { + let pskt: PSKT = PSKT::::from(bundle_inner.to_owned()); + + result.push_str(&format!("\r\nPSKT #{:02}\r\n", pskt_index + 1)); + + for (key_inner, input) in pskt.clone().inputs.iter().enumerate() { + result.push_str(&format!("Input #{:02}\r\n", key_inner + 1)); + + if let Some(utxo_entry) = &input.utxo_entry { + result.push_str(&format!(" amount: {}\r\n", sompi_formatter(utxo_entry.amount, &NetworkType::from(network_id)))); + result.push_str(&format!( + " address: {}\r\n", + extract_script_pub_key_address(&utxo_entry.script_public_key, Prefix::from(network_id)) + .expect("Input address") + )); + } + } + + result.push_str("---\r\n"); + + for (key_inner, output) in pskt.clone().outputs.iter().enumerate() { + result.push_str(&format!("Output #{:02}\r\n", key_inner + 1)); + result.push_str(&format!(" amount: {}\r\n", sompi_formatter(output.amount, &NetworkType::from(network_id)))); + result.push_str(&format!( + " address: {}\r\n", + extract_script_pub_key_address(&output.script_public_key, Prefix::from(network_id)).expect("Input address") + )); + } + } + result + } } impl AsRef<[PSKTInner]> for Bundle { From 23c4fbe755add877b9a7caa457dc330ac8ed54a1 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 18 Aug 2024 06:54:00 +0300 Subject: [PATCH 139/158] WASM RBF (RPC) --- rpc/core/src/wasm/message.rs | 61 ++++++++++++++++++++++++++++++++++++ rpc/wrpc/wasm/src/client.rs | 5 ++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index d8e2707453..0b8ae58027 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -7,6 +7,7 @@ use kaspa_addresses::Address; use kaspa_addresses::AddressOrStringArrayT; use kaspa_consensus_client::Transaction; use kaspa_consensus_client::UtxoEntryReference; +use kaspa_consensus_core::tx as cctx; use kaspa_rpc_macros::declare_typescript_wasm_interface as declare; pub use serde_wasm_bindgen::from_value; use wasm_bindgen::prelude::*; @@ -1324,6 +1325,66 @@ try_from! ( args: SubmitBlockResponse, ISubmitBlockResponse, { // --- +declare! { + ISubmitTransactionReplacementRequest, + // "ISubmitTransactionRequest | Transaction", + r#" + /** + * Submit transaction replacement to the node. + * + * @category Node RPC + */ + export interface ISubmitTransactionReplacementRequest { + transaction : Transaction, + } + "#, +} + +try_from! ( args: ISubmitTransactionReplacementRequest, SubmitTransactionReplacementRequest, { + let transaction = if let Some(transaction) = args.try_get_value("transaction")? { + transaction + } else { + args.into() + }; + + let request = if let Ok(transaction) = Transaction::try_owned_from(&transaction) { + SubmitTransactionReplacementRequest { + transaction : transaction.into(), + } + } else { + from_value(transaction)? + }; + Ok(request) +}); + +declare! { + ISubmitTransactionReplacementResponse, + r#" + /** + * + * + * @category Node RPC + */ + export interface ISubmitTransactionReplacementResponse { + transactionId : HexString; + replacedTransaction: Transaction; + } + "#, +} + +try_from! ( args: SubmitTransactionReplacementResponse, ISubmitTransactionReplacementResponse, { + let transaction_id = args.transaction_id; + let replaced_transaction = cctx::Transaction::try_from(&args.replaced_transaction)?; + let replaced_transaction = Transaction::from(replaced_transaction); + + let response = ISubmitTransactionReplacementResponse::default(); + response.set("transactionId", &transaction_id.into())?; + response.set("replacedTransaction", &replaced_transaction.into())?; + Ok(response) +}); + +// --- + declare! { ISubmitTransactionRequest, // "ISubmitTransactionRequest | Transaction", diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index 4d2cecc8f2..9d83f8ee35 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -1032,8 +1032,11 @@ build_wrpc_wasm_bindgen_interface!( /// Returned information: None. SubmitBlock, /// Submits a transaction to the Kaspa network. - /// Returned information: None. + /// Returned information: Submitted Transaction Id. SubmitTransaction, + /// Submits an RBF transaction to the Kaspa network. + /// Returned information: Submitted Transaction Id, Transaction that was replaced. + SubmitTransactionReplacement, /// Unbans a previously banned peer, allowing it to connect /// to the Kaspa node again. /// Returned information: None. From 89d962473b0ff9fffb594447f1baba26a3566931 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 18 Aug 2024 08:03:27 +0300 Subject: [PATCH 140/158] WASM FeeEstimate (RPC) --- rpc/core/src/wasm/message.rs | 208 ++++++++++++++++++++++++++++++++++- rpc/wrpc/wasm/src/client.rs | 4 + 2 files changed, 211 insertions(+), 1 deletion(-) diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index 0b8ae58027..b9fbc4ba1e 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -1,5 +1,4 @@ #![allow(non_snake_case)] - use crate::error::RpcError as Error; use crate::error::RpcResult as Result; use crate::model::*; @@ -1476,3 +1475,210 @@ declare! { try_from! ( args: UnbanResponse, IUnbanResponse, { Ok(to_value(&args)?.into()) }); + +// --- + +declare! { + IFeerateBucket, + r#" + /** + * + * + * @category Node RPC + */ + export interface IFeerateBucket { + /** + * The fee/mass ratio estimated to be required for inclusion time <= estimated_seconds + */ + feerate : number; + /** + * The estimated inclusion time for a transaction with fee/mass = feerate + */ + estimatedSeconds : number; + } + "#, +} + +declare! { + IFeeEstimate, + r#" + /** + * + * + * @category Node RPC + */ + export interface IFeeEstimate { + /** + * *Top-priority* feerate bucket. Provides an estimation of the feerate required for sub-second DAG inclusion. + * + * Note: for all buckets, feerate values represent fee/mass of a transaction in `sompi/gram` units. + * Given a feerate value recommendation, calculate the required fee by + * taking the transaction mass and multiplying it by feerate: `fee = feerate * mass(tx)` + */ + + priorityBucket : IFeerateBucket; + /** + * A vector of *normal* priority feerate values. The first value of this vector is guaranteed to exist and + * provide an estimation for sub-*minute* DAG inclusion. All other values will have shorter estimation + * times than all `low_bucket` values. Therefor by chaining `[priority] | normal | low` and interpolating + * between them, one can compose a complete feerate function on the client side. The API makes an effort + * to sample enough "interesting" points on the feerate-to-time curve, so that the interpolation is meaningful. + */ + + normalBucket : IFeerateBucket[]; + /** + * An array of *low* priority feerate values. The first value of this vector is guaranteed to + * exist and provide an estimation for sub-*hour* DAG inclusion. + */ + lowBucket : IFeerateBucket[]; + } + "#, +} + +try_from!( estimate: RpcFeeEstimate, IFeeEstimate, { + + let priority_bucket = IFeerateBucket::default(); + priority_bucket.set("feerate", &estimate.priority_bucket.feerate.into())?; + priority_bucket.set("estimatedSeconds", &estimate.priority_bucket.estimated_seconds.into())?; + + let normal_buckets = estimate.normal_buckets.into_iter().map(|normal_bucket| { + let bucket = IFeerateBucket::default(); + bucket.set("feerate", &normal_bucket.feerate.into())?; + bucket.set("estimatedSeconds", &normal_bucket.estimated_seconds.into())?; + Ok(bucket) + }).collect::>>()?; + + let low_buckets = estimate.low_buckets.into_iter().map(|low_bucket| { + let bucket = IFeerateBucket::default(); + bucket.set("feerate", &low_bucket.feerate.into())?; + bucket.set("estimatedSeconds", &low_bucket.estimated_seconds.into())?; + Ok(bucket) + }).collect::>>()?; + + let estimate = IFeeEstimate::default(); + estimate.set("priorityBucket", &priority_bucket)?; + estimate.set("normalBuckets", &js_sys::Array::from_iter(normal_buckets))?; + estimate.set("lowBuckets", &js_sys::Array::from_iter(low_buckets))?; + + Ok(estimate) +}); + +// --- + +declare! { + IGetFeeEstimateRequest, + r#" + /** + * Get fee estimate from the node. + * + * @category Node RPC + */ + export interface IGetFeeEstimateRequest { } + "#, +} + +try_from! ( args: IGetFeeEstimateRequest, GetFeeEstimateRequest, { + Ok(from_value(args.into())?) +}); + +declare! { + IGetFeeEstimateResponse, + r#" + /** + * + * + * @category Node RPC + */ + export interface IGetFeeEstimateResponse { + estimate : IFeeEstimate; + } + "#, +} + +try_from!( args: GetFeeEstimateResponse, IGetFeeEstimateResponse, { + let estimate = IFeeEstimate::try_from(args.estimate)?; + let response = IGetFeeEstimateResponse::default(); + response.set("estimate", &estimate)?; + Ok(response) +}); + +// --- + +declare! { + IFeeEstimateVerboseExperimentalData, + r#" + /** + * + * + * @category Node RPC + */ + export interface IFeeEstimateVerboseExperimentalData { + mempoolReadyTransactionsCount : bigint; + mempoolReadyTransactionsTotalMass : bigint; + networkMassPerSecond : bigint; + nextBlockTemplateFeerateMin : number; + nextBlockTemplateFeerateMedian : number; + nextBlockTemplateFeerateMax : number; + } + "#, +} + +try_from!( data: RpcFeeEstimateVerboseExperimentalData, IFeeEstimateVerboseExperimentalData, { + + let target = IFeeEstimateVerboseExperimentalData::default(); + target.set("mempoolReadyTransactionsCount", &js_sys::BigInt::from(data.mempool_ready_transactions_count).into())?; + target.set("mempoolReadyTransactionsTotalMass", &js_sys::BigInt::from(data.mempool_ready_transactions_total_mass).into())?; + target.set("networkMassPerSecond", &js_sys::BigInt::from(data.network_mass_per_second).into())?; + target.set("nextBlockTemplateFeerateMin", &data.next_block_template_feerate_min.into())?; + target.set("nextBlockTemplateFeerateMedian", &data.next_block_template_feerate_median.into())?; + target.set("nextBlockTemplateFeerateMax", &data.next_block_template_feerate_max.into())?; + + Ok(target) +}); + +declare! { + IGetFeeEstimateExperimentalRequest, + // "ISubmitTransactionRequest | Transaction", + r#" + /** + * Get fee estimate from the node. + * + * @category Node RPC + */ + export interface IGetFeeEstimateExperimentalRequest { } + "#, +} + +try_from! ( args: IGetFeeEstimateExperimentalRequest, GetFeeEstimateExperimentalRequest, { + Ok(from_value(args.into())?) +}); + +declare! { + IGetFeeEstimateExperimentalResponse, + r#" + /** + * + * + * @category Node RPC + */ + export interface IGetFeeEstimateExperimentalResponse { + estimate : IFeeEstimate; + verbose? : IFeeEstimateVerboseExperimentalData + } + "#, +} + +try_from!( args: GetFeeEstimateExperimentalResponse, IGetFeeEstimateExperimentalResponse, { + let estimate = IFeeEstimate::try_from(args.estimate)?; + let response = IGetFeeEstimateExperimentalResponse::default(); + response.set("estimate", &estimate)?; + + if let Some(verbose) = args.verbose { + let verbose = IFeeEstimateVerboseExperimentalData::try_from(verbose)?; + response.set("verbose", &verbose)?; + } + + Ok(response) +}); + +// --- diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index 9d83f8ee35..631af0454f 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -1000,6 +1000,10 @@ build_wrpc_wasm_bindgen_interface!( /// score timestamp estimate. /// Returned information: DAA score timestamp estimate. GetDaaScoreTimestampEstimate, + /// Feerate estimates + GetFeeEstimate, + /// Feerate estimates (experimental) + GetFeeEstimateExperimental, /// Retrieves the current network configuration. /// Returned information: Current network configuration. GetCurrentNetwork, From 3448ff8161e373883dbd498d5bf94a16e0dc9434 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 18 Aug 2024 08:14:43 +0300 Subject: [PATCH 141/158] hex encoding for kaspa_utils::SystemInfo --- utils/src/sysinfo.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/utils/src/sysinfo.rs b/utils/src/sysinfo.rs index 3127dc0da5..4e009d4495 100644 --- a/utils/src/sysinfo.rs +++ b/utils/src/sysinfo.rs @@ -1,11 +1,14 @@ use crate::fd_budget; use crate::git; +use crate::hex::ToHex; use sha2::{Digest, Sha256}; use std::fs::File; use std::io::Read; use std::sync::OnceLock; -#[derive(Debug, Clone)] +static SYSTEM_INFO: OnceLock = OnceLock::new(); + +#[derive(Clone)] pub struct SystemInfo { pub system_id: Option>, pub git_hash: Option>, @@ -16,7 +19,20 @@ pub struct SystemInfo { pub fd_limit: u32, } -static SYSTEM_INFO: OnceLock = OnceLock::new(); +// provide hex encoding for system_id, git_hash, and git_short_hash +impl std::fmt::Debug for SystemInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SystemInfo") + .field("system_id", &self.system_id.as_ref().map(|id| id.to_hex())) + .field("git_hash", &self.git_hash.as_ref().map(|hash| hash.to_hex())) + .field("git_short_hash", &self.git_short_hash.as_ref().map(|hash| hash.to_hex())) + .field("version", &self.version) + .field("cpu_physical_cores", &self.cpu_physical_cores) + .field("total_memory", &self.total_memory) + .field("fd_limit", &self.fd_limit) + .finish() + } +} impl Default for SystemInfo { fn default() -> Self { From 8680b005d550f812e7aa40616f8fb8c3bd3cc3af Mon Sep 17 00:00:00 2001 From: KaffinPX <73744616+KaffinPX@users.noreply.github.com> Date: Mon, 19 Aug 2024 02:48:44 +0300 Subject: [PATCH 142/158] Some symmetry and type fixes (#93) * UtxoEntry typing fix and isometry w UtxoEntryReference * Fix type of IUtxoProcessorEvent * Fix interface typings of UtxoProcessor * Remove UtxoProcessorEventData and improve UtxoProcessor event * Remove unneeded overwrite * Clippy and a small mistake fix * Note: Clippy can cause fmt issues :nerd: --- consensus/client/src/utxo.rs | 17 +++++++----- rpc/core/src/wasm/message.rs | 2 +- wallet/core/src/wasm/notify.rs | 36 +++++++------------------- wallet/core/src/wasm/utxo/processor.rs | 8 +++--- 4 files changed, 25 insertions(+), 38 deletions(-) diff --git a/consensus/client/src/utxo.rs b/consensus/client/src/utxo.rs index afcf87c4b1..4faf0211aa 100644 --- a/consensus/client/src/utxo.rs +++ b/consensus/client/src/utxo.rs @@ -142,14 +142,14 @@ impl UtxoEntryReference { self.as_ref().clone() } - #[wasm_bindgen(js_name = "getTransactionId")] - pub fn transaction_id_as_string(&self) -> String { - self.utxo.outpoint.get_transaction_id_as_string() + #[wasm_bindgen(getter)] + pub fn outpoint(&self) -> TransactionOutpoint { + self.utxo.outpoint.clone() } - #[wasm_bindgen(js_name = "getId")] - pub fn id_string(&self) -> String { - self.utxo.outpoint.id_string() + #[wasm_bindgen(getter)] + pub fn address(&self) -> Option
{ + self.utxo.address.clone() } #[wasm_bindgen(getter)] @@ -166,6 +166,11 @@ impl UtxoEntryReference { pub fn block_daa_score(&self) -> u64 { self.utxo.block_daa_score } + + #[wasm_bindgen(getter, js_name = "scriptPublicKey")] + pub fn script_public_key(&self) -> ScriptPublicKey { + self.utxo.script_public_key.clone() + } } impl UtxoEntryReference { diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index b9fbc4ba1e..9517561a0e 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -1161,7 +1161,7 @@ declare! { * @category Node RPC */ export interface IGetUtxosByAddressesResponse { - entries : IUtxoEntry[]; + entries : UtxoEntryReference[]; } "#, } diff --git a/wallet/core/src/wasm/notify.rs b/wallet/core/src/wasm/notify.rs index 61d1d16876..5172d33f85 100644 --- a/wallet/core/src/wasm/notify.rs +++ b/wallet/core/src/wasm/notify.rs @@ -32,33 +32,12 @@ cfg_if! { } /** - * {@link UtxoProcessor} notification event data. - * @category Wallet SDK - */ - export type UtxoProcessorEventData = IConnectEvent - | IDisconnectEvent - | IUtxoIndexNotEnabledEvent - | ISyncStateEvent - | IServerStatusEvent - | IUtxoProcErrorEvent - | IDaaScoreChangeEvent - | IPendingEvent - | IReorgEvent - | IStasisEvent - | IMaturityEvent - | IDiscoveryEvent - | IBalanceEvent - | IErrorEvent - | undefined - ; - - /** - * UtxoProcessor notification event data map. + * {@link UtxoProcessor} notification event data map. * * @category Wallet API */ export type UtxoProcessorEventMap = { - "connect":IConnectEvent, + "connect": IConnectEvent, "disconnect": IDisconnectEvent, "utxo-index-not-enabled": IUtxoIndexNotEnabledEvent, "sync-state": ISyncStateEvent, @@ -80,9 +59,12 @@ cfg_if! { * * @category Wallet API */ - export type IUtxoProcessorEvent = { - [K in keyof UtxoProcessorEventMap]: { event: K, data: UtxoProcessorEventMap[K] } - }[keyof UtxoProcessorEventMap]; + export type UtxoProcessorEvent = { + [K in T]: { + type: K, + data: UtxoProcessorEventMap[K] + } + }[T]; /** @@ -95,7 +77,7 @@ cfg_if! { * * @category Wallet SDK */ - export type UtxoProcessorNotificationCallback = (event: IUtxoProcessorEvent) => void; + export type UtxoProcessorNotificationCallback = (event: UtxoProcessorEvent) => void; "#; #[wasm_bindgen] diff --git a/wallet/core/src/wasm/utxo/processor.rs b/wallet/core/src/wasm/utxo/processor.rs index 09296a2a6f..f54f551f9d 100644 --- a/wallet/core/src/wasm/utxo/processor.rs +++ b/wallet/core/src/wasm/utxo/processor.rs @@ -63,14 +63,14 @@ cfg_if! { /** * @param {UtxoProcessorNotificationCallback} callback */ - addEventListener(callback:UtxoProcessorNotificationCallback): void; + addEventListener(callback: UtxoProcessorNotificationCallback): void; /** * @param {UtxoProcessorEventType} event * @param {UtxoProcessorNotificationCallback} [callback] */ - addEventListener( - event: M, - callback: (eventData: UtxoProcessorEventMap[M]) => void + addEventListener( + event: E, + callback: UtxoProcessorNotificationCallback ) }"#; } From 823e46e60ef1d4db7ac9b7bac49bb4a0039071a9 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 19 Aug 2024 02:49:38 +0300 Subject: [PATCH 143/158] update WASM GeneratorSettings::priorityEntries? to accept UtxoEntryReference[] --- wallet/core/src/wasm/tx/generator/generator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/core/src/wasm/tx/generator/generator.rs b/wallet/core/src/wasm/tx/generator/generator.rs index a110174bd6..26ba74ee41 100644 --- a/wallet/core/src/wasm/tx/generator/generator.rs +++ b/wallet/core/src/wasm/tx/generator/generator.rs @@ -72,7 +72,7 @@ interface IGeneratorSettingsObject { * overflows, these inputs will be consumed into a batch/sweep transaction * where the destination if the `changeAddress`. */ - priorityEntries?: IUtxoEntry[], + priorityEntries?: IUtxoEntry[] | UtxoEntryReference[], /** * Optional number of signature operations in the transaction. */ From 1c45172703dd6c8eaa14700e6e76fbc7e207e4f8 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 19 Aug 2024 12:54:43 +0300 Subject: [PATCH 144/158] WASM32 - update resolver casting --- Cargo.lock | 42 ++++++++++++------------- rpc/wrpc/wasm/src/resolver.rs | 15 +++------ wallet/core/src/tx/generator/pending.rs | 18 ----------- 3 files changed, 25 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86d09f54aa..06c35b62a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6878,7 +6878,7 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.15.0" +version = "0.16.0" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6891,7 +6891,7 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.15.0" +version = "0.16.0" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6922,7 +6922,7 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.15.0" +version = "0.16.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6937,7 +6937,7 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.15.0" +version = "0.16.0" dependencies = [ "futures", "js-sys", @@ -6953,7 +6953,7 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.15.0" +version = "0.16.0" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6967,7 +6967,7 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.15.0" +version = "0.16.0" dependencies = [ "cfg-if 1.0.0", "console", @@ -6981,7 +6981,7 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.15.0" +version = "0.16.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6992,7 +6992,7 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.15.0" +version = "0.16.0" dependencies = [ "borsh", "futures", @@ -7011,7 +7011,7 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.15.0" +version = "0.16.0" dependencies = [ "ahash", "async-trait", @@ -7033,7 +7033,7 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.15.0" +version = "0.16.0" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7056,7 +7056,7 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.15.0" +version = "0.16.0" dependencies = [ "ahash", "async-std", @@ -7084,7 +7084,7 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.15.0" +version = "0.16.0" dependencies = [ "parse-variants", "proc-macro-error", @@ -7095,7 +7095,7 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.15.0" +version = "0.16.0" dependencies = [ "ahash", "borsh", @@ -7104,7 +7104,7 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.15.0" +version = "0.16.0" dependencies = [ "async-std", "base64 0.22.1", @@ -7130,7 +7130,7 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.15.0" +version = "0.16.0" dependencies = [ "futures", "thiserror", @@ -7140,7 +7140,7 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.15.0" +version = "0.16.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7154,7 +7154,7 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.15.0" +version = "0.16.0" dependencies = [ "async-std", "async-trait", @@ -7181,7 +7181,7 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.15.0" +version = "0.16.0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7195,7 +7195,7 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.15.0" +version = "0.16.0" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7214,7 +7214,7 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.15.0" +version = "0.16.0" dependencies = [ "js-sys", "proc-macro-error", @@ -7226,7 +7226,7 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.15.0" +version = "0.16.0" dependencies = [ "ahash", "async-channel 2.2.1", diff --git a/rpc/wrpc/wasm/src/resolver.rs b/rpc/wrpc/wasm/src/resolver.rs index d78079ca28..2ffc7ea56a 100644 --- a/rpc/wrpc/wasm/src/resolver.rs +++ b/rpc/wrpc/wasm/src/resolver.rs @@ -194,21 +194,14 @@ impl TryFrom for NativeResolver { impl TryCastFromJs for Resolver { type Error = Error; - fn try_cast_from(value: impl AsRef) -> Result> { + fn try_cast_from<'a, R>(value: &'a R) -> Result> + where + R: AsRef + 'a, + { Ok(Self::try_ref_from_js_value_as_cast(value)?) } } -// impl TryCastFromJs for Resolver { -// type Error = Error; -// fn try_cast_from<'a, R>(value: &'a R) -> Result> -// where -// R: AsRef + 'a, -// { -// Ok(Self::try_ref_from_js_value_as_cast(value)?) -// } -// } - impl TryFrom<&JsValue> for Resolver { type Error = Error; fn try_from(js_value: &JsValue) -> Result { diff --git a/wallet/core/src/tx/generator/pending.rs b/wallet/core/src/tx/generator/pending.rs index 5bd2ce065d..ad7e2e40f2 100644 --- a/wallet/core/src/tx/generator/pending.rs +++ b/wallet/core/src/tx/generator/pending.rs @@ -177,57 +177,39 @@ impl PendingTransaction { /// Submit the transaction on the supplied rpc pub async fn try_submit(&self, rpc: &Arc) -> Result { - log_info!("try_submit called on inner pending..."); - - log_info!("-- sleeping"); - - workflow_core::task::sleep(Duration::from_secs(5)).await; - log_info!("-- resuming..."); -log_info!("A"); // sanity check to prevent multiple invocations (for API use) self.inner.is_submitted.load(Ordering::SeqCst).then(|| { panic!("PendingTransaction::try_submit() called multiple times"); }); -log_info!("B"); self.inner.is_submitted.store(true, Ordering::SeqCst); -log_info!("C"); let rpc_transaction: RpcTransaction = self.rpc_transaction(); -log_info!("D"); // if we are running under UtxoProcessor if let Some(utxo_context) = self.inner.generator.source_utxo_context() { -log_info!("E"); // lock UtxoProcessor notification ingest let _lock = utxo_context.processor().notification_lock().await; -log_info!("F"); // register pending UTXOs with UtxoProcessor utxo_context.register_outgoing_transaction(self).await?; -log_info!("G"); // try to submit transaction match rpc.submit_transaction(rpc_transaction, false).await { Ok(id) => { -log_info!("H"); // on successful submit, create a notification utxo_context.notify_outgoing_transaction(self).await?; Ok(id) } Err(error) => { - log_info!("I"); // in case of failure, remove transaction UTXOs from the consumed list utxo_context.cancel_outgoing_transaction(self).await?; Err(error.into()) } } } else { -log_info!("X"); // No UtxoProcessor present (API etc) Ok(rpc.submit_transaction(rpc_transaction, false).await?) } - - } pub async fn log(&self) -> Result<()> { From b4beadb99585e5054223e7a307e08acf7edd0bd4 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 19 Aug 2024 12:55:32 +0300 Subject: [PATCH 145/158] cleanup --- wallet/core/src/wasm/tx/generator/pending.rs | 21 +++----------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/wallet/core/src/wasm/tx/generator/pending.rs b/wallet/core/src/wasm/tx/generator/pending.rs index 5b327912f0..28b6806c49 100644 --- a/wallet/core/src/wasm/tx/generator/pending.rs +++ b/wallet/core/src/wasm/tx/generator/pending.rs @@ -8,7 +8,6 @@ use kaspa_consensus_core::hashing::wasm::SighashType; use kaspa_wallet_keys::privatekey::PrivateKey; use kaspa_wasm_core::types::{BinaryT, HexString}; use kaspa_wrpc_wasm::RpcClient; -use wasm_bindgen::convert::LongRefFromWasmAbi; /// @category Wallet SDK #[wasm_bindgen(inspectable)] @@ -126,24 +125,10 @@ impl PendingTransaction { /// @see {@link RpcClient.submitTransaction} // pub async fn submit(&self, js_value: &JsValue) -> Result { - // let wasm_rpc_client = unsafe { RpcClient::long_ref_from_abi(wasm_rpc_client)? }; pub async fn submit(&self, wasm_rpc_client: &RpcClient) -> Result { - - // let wasm_rpc_client = RpcClient::try_ref_from_js_value(js_value)?; - // let rpc: Arc = wasm_rpc_client.client().clone(); - // let txid = self.inner.try_submit(&rpc).await?; - - log_info!("### sleeping"); - workflow_core::task::sleep(Duration::from_secs(10)).await; - log_info!("### returning..."); - Ok("Hello World".to_string()) - // log_info!("Submitting transaction: {}", self.id()); - // log_info!("cloning rpc client"); - // let rpc: Arc = wasm_rpc_client.client().clone(); - // log_info!("rpc client cloned"); - // let txid = self.inner.try_submit(&rpc).await?; - // log_info!("Transaction submitted: {}", txid); - // Ok(txid.to_string()) + let rpc: Arc = wasm_rpc_client.client().clone(); + let txid = self.inner.try_submit(&rpc).await?; + Ok(txid.to_string()) } /// Returns encapsulated network [`Transaction`] From dce55a1a33a6b227f3c4ca2f5eedaa1531d85aa6 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 19 Aug 2024 13:03:43 +0300 Subject: [PATCH 146/158] WASM32: remove no longer used WAPI account module --- wallet/core/src/wasm/wallet/account.rs | 155 ------------------------- wallet/core/src/wasm/wallet/mod.rs | 1 - 2 files changed, 156 deletions(-) delete mode 100644 wallet/core/src/wasm/wallet/account.rs diff --git a/wallet/core/src/wasm/wallet/account.rs b/wallet/core/src/wasm/wallet/account.rs deleted file mode 100644 index 4e0938e64d..0000000000 --- a/wallet/core/src/wasm/wallet/account.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::account as native; -use crate::imports::*; -use crate::tx::PaymentOutputs; -use crate::wasm::utxo::UtxoContext; -use kaspa_consensus_core::network::NetworkTypeT; -use kaspa_wallet_keys::keypair::Keypair; -use workflow_core::abortable::Abortable; - -/// -/// The `Account` class is a wallet account that can be used to send and receive payments. -/// -/// -/// @category Wallet API -#[wasm_bindgen(inspectable)] -#[derive(Clone, CastFromJs)] -pub struct Account { - inner: Arc, - #[wasm_bindgen(getter_with_clone)] - pub context: UtxoContext, -} - -impl Account { - pub async fn try_new(inner: Arc) -> Result { - let context = inner.utxo_context().clone(); - Ok(Self { inner, context: context.into() }) - } -} - -#[wasm_bindgen] -impl Account { - pub fn ctor(js_value: JsValue) -> Result { - let AccountCreateArgs {} = js_value.try_into()?; - - todo!(); - - // Ok(account) - } - - #[wasm_bindgen(getter)] - pub fn balance(&self) -> JsValue { - match self.inner.balance() { - Some(balance) => crate::wasm::Balance::from(balance).into(), - None => JsValue::UNDEFINED, - } - } - - #[wasm_bindgen(getter, js_name = "type")] - pub fn account_kind(&self) -> String { - self.inner.account_kind().to_string() - } - - #[wasm_bindgen(js_name = balanceStrings)] - pub fn balance_strings(&self, network_type: &NetworkTypeT) -> Result { - match self.inner.balance() { - Some(balance) => Ok(crate::wasm::Balance::from(balance).to_balance_strings(network_type)?.into()), - None => Ok(JsValue::UNDEFINED), - } - } - - #[wasm_bindgen(getter, js_name = "receiveAddress")] - pub fn receive_address(&self) -> Result { - Ok(self.inner.receive_address()?.to_string()) - } - - #[wasm_bindgen(getter, js_name = "changeAddress")] - pub fn change_address(&self) -> Result { - Ok(self.inner.change_address()?.to_string()) - } - - #[wasm_bindgen(js_name = "deriveReceiveAddress")] - pub async fn derive_receive_address(&self) -> Result
{ - let account = self.inner.clone().as_derivation_capable()?; - let receive_address = account.new_receive_address().await?; - Ok(receive_address) - } - - #[wasm_bindgen(js_name = "deriveChangeAddress")] - pub async fn derive_change_address(&self) -> Result
{ - let account = self.inner.clone().as_derivation_capable()?; - let change_address = account.new_change_address().await?; - Ok(change_address) - } - - pub async fn scan(&self) -> Result<()> { - self.inner.clone().scan(None, None).await - } - - // pub async fn send(&self, js_value: JsValue) -> Result { - // let _args = AccountSendArgs::try_from(js_value)?; - - // todo!() - // } -} - -impl From for Arc { - fn from(account: Account) -> Self { - account.inner - } -} - -impl TryFrom<&JsValue> for Account { - type Error = Error; - fn try_from(js_value: &JsValue) -> std::result::Result { - Ok(Account::try_ref_from_js_value(js_value)?.clone()) - } -} - -// pub struct AccountSendArgs { -// pub outputs: PaymentOutputs, -// pub priority_fee_sompi: Option, -// pub include_fees_in_amount: bool, - -// pub wallet_secret: Secret, -// pub payment_secret: Option, -// pub abortable: Abortable, -// } - -// impl TryFrom for AccountSendArgs { -// type Error = Error; -// fn try_from<'a,R>(js_value: JsValue) -> std::result::Result { -// if let Some(object) = Object::try_from(&js_value) { -// let outputs = object.cast_into::("outputs")?.into_owned(); - -// let priority_fee_sompi = object.get_u64("priorityFee").ok(); -// let include_fees_in_amount = object.get_bool("includeFeesInAmount").unwrap_or(false); -// let abortable = object.get("abortable").ok().and_then(|v| Abortable::try_from(&v).ok()).unwrap_or_default(); - -// let wallet_secret = object.get_string("walletSecret")?.into(); -// let payment_secret = object.get_value("paymentSecret")?.as_string().map(|s| s.into()); - -// let send_args = -// AccountSendArgs { outputs, priority_fee_sompi, include_fees_in_amount, wallet_secret, payment_secret, abortable }; - -// Ok(send_args) -// } else { -// Err("Argument to Account::send() must be an object".into()) -// } -// } -// } - -// pub struct AccountCreateArgs {} - -// impl TryFrom for AccountCreateArgs { -// type Error = Error; -// fn try_from(value: JsValue) -> std::result::Result { -// if let Some(object) = Object::try_from(&value) { -// let _keypair = object.try_cast_into::("keypair")?; -// let _public_key = object.try_cast_into::("keypair")?; - -// Ok(AccountCreateArgs {}) -// } else { -// Err(Error::custom("Account: supplied value must be an object")) -// } -// } -// } diff --git a/wallet/core/src/wasm/wallet/mod.rs b/wallet/core/src/wasm/wallet/mod.rs index 41de19f5a5..2ae871c1e3 100644 --- a/wallet/core/src/wasm/wallet/mod.rs +++ b/wallet/core/src/wasm/wallet/mod.rs @@ -1,4 +1,3 @@ -// pub mod account; pub mod keydata; #[allow(clippy::module_inception)] pub mod wallet; From afef7da75072b601639afb45ae6631451c163c0d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 19 Aug 2024 13:06:08 +0300 Subject: [PATCH 147/158] cleanup --- wallet/core/src/wasm/tx/generator/pending.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/wallet/core/src/wasm/tx/generator/pending.rs b/wallet/core/src/wasm/tx/generator/pending.rs index 28b6806c49..8ec04e6b3e 100644 --- a/wallet/core/src/wasm/tx/generator/pending.rs +++ b/wallet/core/src/wasm/tx/generator/pending.rs @@ -123,8 +123,6 @@ impl PendingTransaction { /// and will return UTXOs back to {@link UtxoContext} in case of /// a failed submission. /// @see {@link RpcClient.submitTransaction} - // pub async fn submit(&self, js_value: &JsValue) -> Result { - pub async fn submit(&self, wasm_rpc_client: &RpcClient) -> Result { let rpc: Arc = wasm_rpc_client.client().clone(); let txid = self.inner.try_submit(&rpc).await?; From e6233725ba48524b13c9292c72b3b85666eecd06 Mon Sep 17 00:00:00 2001 From: Michael Sutton Date: Tue, 20 Aug 2024 10:07:51 +0300 Subject: [PATCH 148/158] introduce new rpc header/block types for BBT and SB (#95) * introduce new rpc header/block types for BBT and SB * remove unneeded clone --- rpc/core/src/api/rpc.rs | 2 +- rpc/core/src/convert/block.rs | 43 ++++- rpc/core/src/convert/tx.rs | 20 +-- rpc/core/src/model/block.rs | 30 ++++ rpc/core/src/model/header.rs | 156 +++++++++++++++++- rpc/core/src/model/message.rs | 14 +- rpc/core/src/model/tests.rs | 25 +++ rpc/core/src/wasm/message.rs | 2 +- rpc/grpc/core/src/convert/block.rs | 19 +++ rpc/grpc/core/src/convert/header.rs | 79 ++++++++- rpc/service/src/service.rs | 8 +- testing/integration/src/common/utils.rs | 4 +- .../src/daemon_integration_tests.rs | 7 +- testing/integration/src/rpc_tests.rs | 10 +- testing/integration/src/tasks/block/miner.rs | 10 +- .../integration/src/tasks/block/submitter.rs | 10 +- 16 files changed, 387 insertions(+), 52 deletions(-) diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 978d46b56b..9825a97380 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -115,7 +115,7 @@ pub trait RpcApi: Sync + Send + AnySync { /// Submit a block into the DAG. /// /// Blocks are generally expected to have been generated using the get_block_template call. - async fn submit_block(&self, block: RpcBlock, allow_non_daa_blocks: bool) -> RpcResult { + async fn submit_block(&self, block: RpcRawBlock, allow_non_daa_blocks: bool) -> RpcResult { self.submit_block_call(None, SubmitBlockRequest::new(block, allow_non_daa_blocks)).await } async fn submit_block_call( diff --git a/rpc/core/src/convert/block.rs b/rpc/core/src/convert/block.rs index 6d1d165c18..8888fe2bb8 100644 --- a/rpc/core/src/convert/block.rs +++ b/rpc/core/src/convert/block.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::{RpcBlock, RpcError, RpcResult, RpcTransaction}; +use crate::{RpcBlock, RpcError, RpcRawBlock, RpcResult, RpcTransaction}; use kaspa_consensus_core::block::{Block, MutableBlock}; // ---------------------------------------------------------------------------- @@ -18,6 +18,12 @@ impl From<&Block> for RpcBlock { } } +impl From<&Block> for RpcRawBlock { + fn from(item: &Block) -> Self { + Self { header: item.header.as_ref().into(), transactions: item.transactions.iter().map(RpcTransaction::from).collect() } + } +} + impl From<&MutableBlock> for RpcBlock { fn from(item: &MutableBlock) -> Self { Self { @@ -28,18 +34,45 @@ impl From<&MutableBlock> for RpcBlock { } } +impl From<&MutableBlock> for RpcRawBlock { + fn from(item: &MutableBlock) -> Self { + Self { header: item.header.as_ref().into(), transactions: item.transactions.iter().map(RpcTransaction::from).collect() } + } +} + +impl From for RpcRawBlock { + fn from(item: MutableBlock) -> Self { + Self { header: item.header.into(), transactions: item.transactions.iter().map(RpcTransaction::from).collect() } + } +} + // ---------------------------------------------------------------------------- // rpc_core to consensus_core // ---------------------------------------------------------------------------- -impl TryFrom<&RpcBlock> for Block { +impl TryFrom for Block { + type Error = RpcError; + fn try_from(item: RpcBlock) -> RpcResult { + Ok(Self { + header: Arc::new(item.header.into()), + transactions: Arc::new( + item.transactions + .into_iter() + .map(kaspa_consensus_core::tx::Transaction::try_from) + .collect::>>()?, + ), + }) + } +} + +impl TryFrom for Block { type Error = RpcError; - fn try_from(item: &RpcBlock) -> RpcResult { + fn try_from(item: RpcRawBlock) -> RpcResult { Ok(Self { - header: Arc::new(item.header.as_ref().into()), + header: Arc::new(item.header.into()), transactions: Arc::new( item.transactions - .iter() + .into_iter() .map(kaspa_consensus_core::tx::Transaction::try_from) .collect::>>()?, ), diff --git a/rpc/core/src/convert/tx.rs b/rpc/core/src/convert/tx.rs index 78fe6c77ca..20d41d6741 100644 --- a/rpc/core/src/convert/tx.rs +++ b/rpc/core/src/convert/tx.rs @@ -50,17 +50,17 @@ impl From<&TransactionInput> for RpcTransactionInput { // rpc_core to consensus_core // ---------------------------------------------------------------------------- -impl TryFrom<&RpcTransaction> for Transaction { +impl TryFrom for Transaction { type Error = RpcError; - fn try_from(item: &RpcTransaction) -> RpcResult { + fn try_from(item: RpcTransaction) -> RpcResult { let transaction = Transaction::new( item.version, item.inputs - .iter() + .into_iter() .map(kaspa_consensus_core::tx::TransactionInput::try_from) .collect::>>()?, item.outputs - .iter() + .into_iter() .map(kaspa_consensus_core::tx::TransactionOutput::try_from) .collect::>>()?, item.lock_time, @@ -73,16 +73,16 @@ impl TryFrom<&RpcTransaction> for Transaction { } } -impl TryFrom<&RpcTransactionOutput> for TransactionOutput { +impl TryFrom for TransactionOutput { type Error = RpcError; - fn try_from(item: &RpcTransactionOutput) -> RpcResult { - Ok(Self::new(item.value, item.script_public_key.clone())) + fn try_from(item: RpcTransactionOutput) -> RpcResult { + Ok(Self::new(item.value, item.script_public_key)) } } -impl TryFrom<&RpcTransactionInput> for TransactionInput { +impl TryFrom for TransactionInput { type Error = RpcError; - fn try_from(item: &RpcTransactionInput) -> RpcResult { - Ok(Self::new(item.previous_outpoint.into(), item.signature_script.clone(), item.sequence, item.sig_op_count)) + fn try_from(item: RpcTransactionInput) -> RpcResult { + Ok(Self::new(item.previous_outpoint.into(), item.signature_script, item.sequence, item.sig_op_count)) } } diff --git a/rpc/core/src/model/block.rs b/rpc/core/src/model/block.rs index bfa9a68453..96dbcd335e 100644 --- a/rpc/core/src/model/block.rs +++ b/rpc/core/src/model/block.rs @@ -1,7 +1,17 @@ +use super::RpcRawHeader; use crate::prelude::{RpcHash, RpcHeader, RpcTransaction}; use serde::{Deserialize, Serialize}; use workflow_serializer::prelude::*; +/// Raw Rpc block type - without a cached header hash and without verbose data. +/// Used for mining APIs (get_block_template & submit_block) +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcRawBlock { + pub header: RpcRawHeader, + pub transactions: Vec, +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcBlock { @@ -32,6 +42,26 @@ impl Deserializer for RpcBlock { } } +impl Serializer for RpcRawBlock { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + serialize!(RpcRawHeader, &self.header, writer)?; + serialize!(Vec, &self.transactions, writer)?; + + Ok(()) + } +} + +impl Deserializer for RpcRawBlock { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + let header = deserialize!(RpcRawHeader, reader)?; + let transactions = deserialize!(Vec, reader)?; + + Ok(Self { header, transactions }) + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcBlockVerboseData { diff --git a/rpc/core/src/model/header.rs b/rpc/core/src/model/header.rs index 59648ef458..dddf767b7f 100644 --- a/rpc/core/src/model/header.rs +++ b/rpc/core/src/model/header.rs @@ -1,11 +1,30 @@ -// pub type RpcHeader = kaspa_consensus_core::header::Header; use borsh::{BorshDeserialize, BorshSerialize}; -use kaspa_consensus_core::header::Header; -use kaspa_consensus_core::BlueWorkType; +use kaspa_consensus_core::{header::Header, BlueWorkType}; use kaspa_hashes::Hash; use serde::{Deserialize, Serialize}; use workflow_serializer::prelude::*; +/// Raw Rpc header type - without a cached header hash. +/// Used for mining APIs (get_block_template & submit_block) +#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[serde(rename_all = "camelCase")] + +pub struct RpcRawHeader { + pub version: u16, + pub parents_by_level: Vec>, + pub hash_merkle_root: Hash, + pub accepted_id_merkle_root: Hash, + pub utxo_commitment: Hash, + /// Timestamp is in milliseconds + pub timestamp: u64, + pub bits: u32, + pub nonce: u64, + pub daa_score: u64, + pub blue_work: BlueWorkType, + pub blue_score: u64, + pub pruning_point: Hash, +} + #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "camelCase")] pub struct RpcHeader { @@ -179,3 +198,134 @@ impl Deserializer for RpcHeader { }) } } + +impl From for Header { + fn from(header: RpcRawHeader) -> Self { + Self::new_finalized( + header.version, + header.parents_by_level, + header.hash_merkle_root, + header.accepted_id_merkle_root, + header.utxo_commitment, + header.timestamp, + header.bits, + header.nonce, + header.daa_score, + header.blue_work, + header.blue_score, + header.pruning_point, + ) + } +} + +impl From<&RpcRawHeader> for Header { + fn from(header: &RpcRawHeader) -> Self { + Self::new_finalized( + header.version, + header.parents_by_level.clone(), + header.hash_merkle_root, + header.accepted_id_merkle_root, + header.utxo_commitment, + header.timestamp, + header.bits, + header.nonce, + header.daa_score, + header.blue_work, + header.blue_score, + header.pruning_point, + ) + } +} + +impl From<&Header> for RpcRawHeader { + fn from(header: &Header) -> Self { + Self { + version: header.version, + parents_by_level: header.parents_by_level.clone(), + hash_merkle_root: header.hash_merkle_root, + accepted_id_merkle_root: header.accepted_id_merkle_root, + utxo_commitment: header.utxo_commitment, + timestamp: header.timestamp, + bits: header.bits, + nonce: header.nonce, + daa_score: header.daa_score, + blue_work: header.blue_work, + blue_score: header.blue_score, + pruning_point: header.pruning_point, + } + } +} + +impl From
for RpcRawHeader { + fn from(header: Header) -> Self { + Self { + version: header.version, + parents_by_level: header.parents_by_level, + hash_merkle_root: header.hash_merkle_root, + accepted_id_merkle_root: header.accepted_id_merkle_root, + utxo_commitment: header.utxo_commitment, + timestamp: header.timestamp, + bits: header.bits, + nonce: header.nonce, + daa_score: header.daa_score, + blue_work: header.blue_work, + blue_score: header.blue_score, + pruning_point: header.pruning_point, + } + } +} + +impl Serializer for RpcRawHeader { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u16, &1, writer)?; + + store!(u16, &self.version, writer)?; + store!(Vec>, &self.parents_by_level, writer)?; + store!(Hash, &self.hash_merkle_root, writer)?; + store!(Hash, &self.accepted_id_merkle_root, writer)?; + store!(Hash, &self.utxo_commitment, writer)?; + store!(u64, &self.timestamp, writer)?; + store!(u32, &self.bits, writer)?; + store!(u64, &self.nonce, writer)?; + store!(u64, &self.daa_score, writer)?; + store!(BlueWorkType, &self.blue_work, writer)?; + store!(u64, &self.blue_score, writer)?; + store!(Hash, &self.pruning_point, writer)?; + + Ok(()) + } +} + +impl Deserializer for RpcRawHeader { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u16, reader)?; + + let version = load!(u16, reader)?; + let parents_by_level = load!(Vec>, reader)?; + let hash_merkle_root = load!(Hash, reader)?; + let accepted_id_merkle_root = load!(Hash, reader)?; + let utxo_commitment = load!(Hash, reader)?; + let timestamp = load!(u64, reader)?; + let bits = load!(u32, reader)?; + let nonce = load!(u64, reader)?; + let daa_score = load!(u64, reader)?; + let blue_work = load!(BlueWorkType, reader)?; + let blue_score = load!(u64, reader)?; + let pruning_point = load!(Hash, reader)?; + + Ok(Self { + version, + parents_by_level, + hash_merkle_root, + accepted_id_merkle_root, + utxo_commitment, + timestamp, + bits, + nonce, + daa_score, + blue_work, + blue_score, + pruning_point, + }) + } +} diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 353be4ff3f..8a3f4be923 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -20,12 +20,12 @@ pub type RpcExtraData = Vec; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubmitBlockRequest { - pub block: RpcBlock, + pub block: RpcRawBlock, #[serde(alias = "allowNonDAABlocks")] pub allow_non_daa_blocks: bool, } impl SubmitBlockRequest { - pub fn new(block: RpcBlock, allow_non_daa_blocks: bool) -> Self { + pub fn new(block: RpcRawBlock, allow_non_daa_blocks: bool) -> Self { Self { block, allow_non_daa_blocks } } } @@ -33,7 +33,7 @@ impl SubmitBlockRequest { impl Serializer for SubmitBlockRequest { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - serialize!(RpcBlock, &self.block, writer)?; + serialize!(RpcRawBlock, &self.block, writer)?; store!(bool, &self.allow_non_daa_blocks, writer)?; Ok(()) @@ -43,7 +43,7 @@ impl Serializer for SubmitBlockRequest { impl Deserializer for SubmitBlockRequest { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let block = deserialize!(RpcBlock, reader)?; + let block = deserialize!(RpcRawBlock, reader)?; let allow_non_daa_blocks = load!(bool, reader)?; Ok(Self { block, allow_non_daa_blocks }) @@ -152,7 +152,7 @@ impl Deserializer for GetBlockTemplateRequest { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetBlockTemplateResponse { - pub block: RpcBlock, + pub block: RpcRawBlock, /// Whether kaspad thinks that it's synced. /// Callers are discouraged (but not forbidden) from solving blocks when kaspad is not synced. @@ -164,7 +164,7 @@ pub struct GetBlockTemplateResponse { impl Serializer for GetBlockTemplateResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; - serialize!(RpcBlock, &self.block, writer)?; + serialize!(RpcRawBlock, &self.block, writer)?; store!(bool, &self.is_synced, writer)?; Ok(()) @@ -174,7 +174,7 @@ impl Serializer for GetBlockTemplateResponse { impl Deserializer for GetBlockTemplateResponse { fn deserialize(reader: &mut R) -> std::io::Result { let _version = load!(u16, reader)?; - let block = deserialize!(RpcBlock, reader)?; + let block = deserialize!(RpcRawBlock, reader)?; let is_synced = load!(bool, reader)?; Ok(Self { block, is_synced }) diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index 25b278849a..bbac58949e 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -155,6 +155,25 @@ mod mockery { } } + impl Mock for RpcRawHeader { + fn mock() -> Self { + RpcRawHeader { + version: mock(), + timestamp: mock(), + bits: mock(), + nonce: mock(), + hash_merkle_root: mock(), + accepted_id_merkle_root: mock(), + utxo_commitment: mock(), + parents_by_level: vec![mock()], + daa_score: mock(), + blue_score: mock(), + blue_work: mock(), + pruning_point: mock(), + } + } + } + impl Mock for RpcBlockVerboseData { fn mock() -> Self { RpcBlockVerboseData { @@ -178,6 +197,12 @@ mod mockery { } } + impl Mock for RpcRawBlock { + fn mock() -> Self { + RpcRawBlock { header: mock(), transactions: mock() } + } + } + impl Mock for RpcTransactionInputVerboseData { fn mock() -> Self { RpcTransactionInputVerboseData {} diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index 9517561a0e..1b9bddcba3 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -1373,7 +1373,7 @@ declare! { try_from! ( args: SubmitTransactionReplacementResponse, ISubmitTransactionReplacementResponse, { let transaction_id = args.transaction_id; - let replaced_transaction = cctx::Transaction::try_from(&args.replaced_transaction)?; + let replaced_transaction = cctx::Transaction::try_from(args.replaced_transaction)?; let replaced_transaction = Transaction::from(replaced_transaction); let response = ISubmitTransactionReplacementResponse::default(); diff --git a/rpc/grpc/core/src/convert/block.rs b/rpc/grpc/core/src/convert/block.rs index 8429f3256d..6ab9e37fa0 100644 --- a/rpc/grpc/core/src/convert/block.rs +++ b/rpc/grpc/core/src/convert/block.rs @@ -15,6 +15,14 @@ from!(item: &kaspa_rpc_core::RpcBlock, protowire::RpcBlock, { } }); +from!(item: &kaspa_rpc_core::RpcRawBlock, protowire::RpcBlock, { + Self { + header: Some(protowire::RpcBlockHeader::from(&item.header)), + transactions: item.transactions.iter().map(protowire::RpcTransaction::from).collect(), + verbose_data: None, + } +}); + from!(item: &kaspa_rpc_core::RpcBlockVerboseData, protowire::RpcBlockVerboseData, { Self { hash: item.hash.to_string(), @@ -46,6 +54,17 @@ try_from!(item: &protowire::RpcBlock, kaspa_rpc_core::RpcBlock, { } }); +try_from!(item: &protowire::RpcBlock, kaspa_rpc_core::RpcRawBlock, { + Self { + header: item + .header + .as_ref() + .ok_or_else(|| RpcError::MissingRpcFieldError("RpcBlock".to_string(), "header".to_string()))? + .try_into()?, + transactions: item.transactions.iter().map(kaspa_rpc_core::RpcTransaction::try_from).collect::, _>>()?, + } +}); + try_from!(item: &protowire::RpcBlockVerboseData, kaspa_rpc_core::RpcBlockVerboseData, { Self { hash: RpcHash::from_str(&item.hash)?, diff --git a/rpc/grpc/core/src/convert/header.rs b/rpc/grpc/core/src/convert/header.rs index 66296a169e..5d763034a7 100644 --- a/rpc/grpc/core/src/convert/header.rs +++ b/rpc/grpc/core/src/convert/header.rs @@ -25,6 +25,23 @@ from!(item: &kaspa_rpc_core::RpcHeader, protowire::RpcBlockHeader, { } }); +from!(item: &kaspa_rpc_core::RpcRawHeader, protowire::RpcBlockHeader, { + Self { + version: item.version.into(), + parents: item.parents_by_level.iter().map(protowire::RpcBlockLevelParents::from).collect(), + hash_merkle_root: item.hash_merkle_root.to_string(), + accepted_id_merkle_root: item.accepted_id_merkle_root.to_string(), + utxo_commitment: item.utxo_commitment.to_string(), + timestamp: item.timestamp.try_into().expect("timestamp is always convertible to i64"), + bits: item.bits, + nonce: item.nonce, + daa_score: item.daa_score, + blue_work: item.blue_work.to_rpc_hex(), + blue_score: item.blue_score, + pruning_point: item.pruning_point.to_string(), + } +}); + from!(item: &Vec, protowire::RpcBlockLevelParents, { Self { parent_hashes: item.iter().map(|x| x.to_string()).collect() } }); // ---------------------------------------------------------------------------- @@ -32,9 +49,6 @@ from!(item: &Vec, protowire::RpcBlockLevelParents, { Self { parent_hash // ---------------------------------------------------------------------------- try_from!(item: &protowire::RpcBlockHeader, kaspa_rpc_core::RpcHeader, { - - // TODO - restructure using dual structs (with and without hash) - // We re-hash the block to remain as most trustless as possible let header = Header::new_finalized( item.version.try_into()?, @@ -54,6 +68,23 @@ try_from!(item: &protowire::RpcBlockHeader, kaspa_rpc_core::RpcHeader, { header.into() }); +try_from!(item: &protowire::RpcBlockHeader, kaspa_rpc_core::RpcRawHeader, { + Self { + version: item.version.try_into()?, + parents_by_level: item.parents.iter().map(Vec::::try_from).collect::>>>()?, + hash_merkle_root: RpcHash::from_str(&item.hash_merkle_root)?, + accepted_id_merkle_root: RpcHash::from_str(&item.accepted_id_merkle_root)?, + utxo_commitment: RpcHash::from_str(&item.utxo_commitment)?, + timestamp: item.timestamp.try_into()?, + bits: item.bits, + nonce: item.nonce, + daa_score: item.daa_score, + blue_work: kaspa_rpc_core::RpcBlueWorkType::from_rpc_hex(&item.blue_work)?, + blue_score: item.blue_score, + pruning_point: RpcHash::from_str(&item.pruning_point)?, + } +}); + try_from!(item: &protowire::RpcBlockLevelParents, Vec, { item.parent_hashes.iter().map(|x| RpcHash::from_str(x)).collect::, _>>()? }); @@ -61,8 +92,8 @@ try_from!(item: &protowire::RpcBlockLevelParents, Vec, { #[cfg(test)] mod tests { use crate::protowire; - use kaspa_consensus_core::header::Header; - use kaspa_rpc_core::{RpcHash, RpcHeader}; + use kaspa_consensus_core::{block::Block, header::Header}; + use kaspa_rpc_core::{RpcBlock, RpcHash, RpcHeader}; fn new_unique() -> RpcHash { use std::sync::atomic::{AtomicU64, Ordering}; @@ -142,4 +173,42 @@ mod tests { assert_eq!(r.hash, r2.hash); assert_eq!(p, p2); } + + #[test] + fn test_rpc_block() { + let h = Header::new_finalized( + 0, + vec![vec![new_unique(), new_unique(), new_unique()], vec![new_unique()], vec![new_unique(), new_unique()]], + new_unique(), + new_unique(), + new_unique(), + 123, + 12345, + 98765, + 120055, + 459912.into(), + 1928374, + new_unique(), + ); + let b = Block::from_header(h); + let r: RpcBlock = (&b).into(); + let p: protowire::RpcBlock = (&r).into(); + let r2: RpcBlock = (&p).try_into().unwrap(); + let b2: Block = r2.clone().try_into().unwrap(); + let r3: RpcBlock = (&b2).into(); + let p2: protowire::RpcBlock = (&r3).into(); + + assert_eq!(r.header.parents_by_level, r2.header.parents_by_level); + assert_eq!(p.header.as_ref().unwrap().parents, p2.header.as_ref().unwrap().parents); + test_parents_by_level_rxr(&r.header.parents_by_level, &r2.header.parents_by_level); + test_parents_by_level_rxr(&r.header.parents_by_level, &r3.header.parents_by_level); + test_parents_by_level_rxr(&b.header.parents_by_level, &r2.header.parents_by_level); + test_parents_by_level_rxr(&b.header.parents_by_level, &b2.header.parents_by_level); + test_parents_by_level_rxp(&r.header.parents_by_level, &p.header.as_ref().unwrap().parents); + test_parents_by_level_rxp(&r.header.parents_by_level, &p2.header.as_ref().unwrap().parents); + test_parents_by_level_rxp(&r2.header.parents_by_level, &p2.header.as_ref().unwrap().parents); + + assert_eq!(b.hash(), b2.hash()); + assert_eq!(p, p2); + } } diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index e61cdc78b1..ff9f9b8e6d 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -302,7 +302,7 @@ impl RpcApi for RpcCoreService { return Ok(SubmitBlockResponse { report: SubmitBlockReport::Reject(SubmitBlockRejectReason::IsInIBD) }); } - let try_block: RpcResult = (&request.block).try_into(); + let try_block: RpcResult = request.block.try_into(); if let Err(err) = &try_block { trace!("incoming SubmitBlockRequest with block conversion error: {}", err); // error = format!("Could not parse block: {0}", err) @@ -380,7 +380,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and let is_nearly_synced = self.config.is_nearly_synced(block_template.selected_parent_timestamp, block_template.selected_parent_daa_score); Ok(GetBlockTemplateResponse { - block: (&block_template.block).into(), + block: block_template.block.into(), is_synced: self.has_sufficient_peer_connectivity() && is_nearly_synced, }) } @@ -528,7 +528,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and warn!("SubmitTransaction RPC command called with AllowOrphan enabled while node in safe RPC mode -- switching to ForbidOrphan."); } - let transaction: Transaction = (&request.transaction).try_into()?; + let transaction: Transaction = request.transaction.try_into()?; let transaction_id = transaction.id(); let session = self.consensus_manager.consensus().unguarded_session(); let orphan = match allow_orphan { @@ -548,7 +548,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and _connection: Option<&DynRpcConnection>, request: SubmitTransactionReplacementRequest, ) -> RpcResult { - let transaction: Transaction = (&request.transaction).try_into()?; + let transaction: Transaction = request.transaction.try_into()?; let transaction_id = transaction.id(); let session = self.consensus_manager.consensus().unguarded_session(); let replaced_transaction = diff --git a/testing/integration/src/common/utils.rs b/testing/integration/src/common/utils.rs index ab03411a8f..10fb9cb671 100644 --- a/testing/integration/src/common/utils.rs +++ b/testing/integration/src/common/utils.rs @@ -3,6 +3,7 @@ use itertools::Itertools; use kaspa_addresses::Address; use kaspa_consensus_core::{ constants::TX_VERSION, + header::Header, sign::sign, subnets::SUBNETWORK_ID_NATIVE, tx::{ @@ -187,7 +188,8 @@ pub async fn mine_block(pay_address: Address, submitting_client: &GrpcClient, li // Mine a block let template = submitting_client.get_block_template(pay_address.clone(), vec![]).await.unwrap(); - let block_hash = template.block.header.hash; + let header: Header = (&template.block.header).into(); + let block_hash = header.hash; submitting_client.submit_block(template.block, false).await.unwrap(); // Wait for each listening client to get notified the submitted block was added to the DAG diff --git a/testing/integration/src/daemon_integration_tests.rs b/testing/integration/src/daemon_integration_tests.rs index 29f74e75ee..a923202942 100644 --- a/testing/integration/src/daemon_integration_tests.rs +++ b/testing/integration/src/daemon_integration_tests.rs @@ -7,6 +7,7 @@ use crate::common::{ use kaspa_addresses::Address; use kaspa_alloc::init_allocator_with_default_settings; use kaspa_consensus::params::SIMNET_PARAMS; +use kaspa_consensus_core::header::Header; use kaspa_consensusmanager::ConsensusManager; use kaspa_core::{task::runtime::AsyncRuntime, trace}; use kaspa_grpc_client::GrpcClient; @@ -77,7 +78,8 @@ async fn daemon_mining_test() { .get_block_template(Address::new(kaspad1.network.into(), kaspa_addresses::Version::PubKey, &[0; 32]), vec![]) .await .unwrap(); - last_block_hash = Some(template.block.header.hash); + let header: Header = (&template.block.header).into(); + last_block_hash = Some(header.hash); rpc_client1.submit_block(template.block, false).await.unwrap(); while let Ok(notification) = match tokio::time::timeout(Duration::from_secs(1), event_receiver.recv()).await { @@ -180,7 +182,8 @@ async fn daemon_utxos_propagation_test() { let mut last_block_hash = None; for i in 0..initial_blocks { let template = rpc_client1.get_block_template(miner_address.clone(), vec![]).await.unwrap(); - last_block_hash = Some(template.block.header.hash); + let header: Header = (&template.block.header).into(); + last_block_hash = Some(header.hash); rpc_client1.submit_block(template.block, false).await.unwrap(); while let Ok(notification) = match tokio::time::timeout(Duration::from_secs(1), event_receiver1.recv()).await { diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs index c1d0c0b96e..c0e2e48925 100644 --- a/testing/integration/src/rpc_tests.rs +++ b/testing/integration/src/rpc_tests.rs @@ -4,7 +4,7 @@ use crate::common::{client_notify::ChannelNotify, daemon::Daemon}; use futures_util::future::try_join_all; use kaspa_addresses::{Address, Prefix, Version}; use kaspa_consensus::params::SIMNET_GENESIS; -use kaspa_consensus_core::{constants::MAX_SOMPI, subnets::SubnetworkId, tx::Transaction}; +use kaspa_consensus_core::{constants::MAX_SOMPI, header::Header, subnets::SubnetworkId, tx::Transaction}; use kaspa_core::info; use kaspa_grpc_core::ops::KaspadPayloadOps; use kaspa_hashes::Hash; @@ -115,6 +115,10 @@ async fn sanity_test() { .unwrap(); assert!(!is_synced); + // Compute the expected block hash for the received block + let header: Header = (&block.header).into(); + let block_hash = header.hash; + // Submit the template (no mining, in simnet PoW is skipped) let response = rpc_client.submit_block(block.clone(), false).await.unwrap(); assert_eq!(response.report, SubmitBlockReport::Success); @@ -138,7 +142,7 @@ async fn sanity_test() { // After submitting a first block, the sink is the submitted block, let response = rpc_client.get_sink_call(None, GetSinkRequest {}).await.unwrap(); - assert_eq!(response.sink, block.header.hash); + assert_eq!(response.sink, block_hash); // the block count is 1 let response = rpc_client.get_block_count_call(None, GetBlockCountRequest {}).await.unwrap(); @@ -155,7 +159,7 @@ async fn sanity_test() { ) .await .unwrap(); - assert!(response.added_chain_block_hashes.contains(&block.header.hash)); + assert!(response.added_chain_block_hashes.contains(&block_hash)); assert!(response.removed_chain_block_hashes.is_empty()); }) } diff --git a/testing/integration/src/tasks/block/miner.rs b/testing/integration/src/tasks/block/miner.rs index ae759801e5..2cf117028a 100644 --- a/testing/integration/src/tasks/block/miner.rs +++ b/testing/integration/src/tasks/block/miner.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; use kaspa_addresses::Address; use kaspa_core::warn; use kaspa_grpc_client::GrpcClient; -use kaspa_rpc_core::{api::rpc::RpcApi, GetBlockTemplateResponse, RpcBlock}; +use kaspa_rpc_core::{api::rpc::RpcApi, GetBlockTemplateResponse, RpcRawBlock}; use kaspa_utils::triggers::SingleTrigger; use parking_lot::Mutex; use rand::thread_rng; @@ -25,7 +25,7 @@ pub struct BlockMinerTask { client: Arc, bps: u64, block_count: usize, - sender: Sender, + sender: Sender, template: Arc>, pay_address: Address, tx_counter: Arc, @@ -38,7 +38,7 @@ impl BlockMinerTask { client: Arc, bps: u64, block_count: usize, - sender: Sender, + sender: Sender, template: Arc>, pay_address: Address, stopper: Stopper, @@ -60,7 +60,7 @@ impl BlockMinerTask { client: Arc, bps: u64, block_count: usize, - sender: Sender, + sender: Sender, template: Arc>, pay_address: Address, stopper: Stopper, @@ -68,7 +68,7 @@ impl BlockMinerTask { Arc::new(Self::new(client, bps, block_count, sender, template, pay_address, stopper)) } - pub fn sender(&self) -> Sender { + pub fn sender(&self) -> Sender { self.sender.clone() } diff --git a/testing/integration/src/tasks/block/submitter.rs b/testing/integration/src/tasks/block/submitter.rs index b57d032696..49bf9d83e5 100644 --- a/testing/integration/src/tasks/block/submitter.rs +++ b/testing/integration/src/tasks/block/submitter.rs @@ -6,18 +6,18 @@ use async_channel::Sender; use async_trait::async_trait; use kaspa_core::warn; use kaspa_grpc_client::ClientPool; -use kaspa_rpc_core::{api::rpc::RpcApi, RpcBlock}; +use kaspa_rpc_core::{api::rpc::RpcApi, RpcRawBlock}; use kaspa_utils::triggers::SingleTrigger; use std::{sync::Arc, time::Duration}; use tokio::{task::JoinHandle, time::sleep}; pub struct BlockSubmitterTask { - pool: ClientPool, + pool: ClientPool, stopper: Stopper, } impl BlockSubmitterTask { - pub fn new(pool: ClientPool, stopper: Stopper) -> Self { + pub fn new(pool: ClientPool, stopper: Stopper) -> Self { Self { pool, stopper } } @@ -26,7 +26,7 @@ impl BlockSubmitterTask { Arc::new(Self::new(pool, stopper)) } - pub fn sender(&self) -> Sender { + pub fn sender(&self) -> Sender { self.pool.sender() } } @@ -35,7 +35,7 @@ impl BlockSubmitterTask { impl Task for BlockSubmitterTask { fn start(&self, stop_signal: SingleTrigger) -> Vec> { warn!("Block submitter task starting..."); - let mut tasks = self.pool.start(|c, block: RpcBlock| async move { + let mut tasks = self.pool.start(|c, block: RpcRawBlock| async move { loop { match c.submit_block(block.clone(), false).await { Ok(response) => { From 12cab5fffb4553efde35572e226c22bcae58b57f Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 20 Aug 2024 10:41:08 +0300 Subject: [PATCH 149/158] WASM - Update types for Mnemonic::random() --- wallet/bip32/src/mnemonic/phrase.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wallet/bip32/src/mnemonic/phrase.rs b/wallet/bip32/src/mnemonic/phrase.rs index 56693002dd..95fd921892 100644 --- a/wallet/bip32/src/mnemonic/phrase.rs +++ b/wallet/bip32/src/mnemonic/phrase.rs @@ -85,8 +85,8 @@ impl Mnemonic { } #[wasm_bindgen(js_name = random)] - pub fn create_random_js(word_count: JsValue) -> Result { - let word_count = word_count.as_f64().unwrap_or(24.0) as usize; + pub fn create_random_js(word_count: Option) -> Result { + let word_count = word_count.unwrap_or(24) as usize; Mnemonic::random(word_count.try_into()?, Default::default()) } From 33c53decea46f35702b63b6232dc48bbda3caf01 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 20 Aug 2024 10:53:48 +0300 Subject: [PATCH 150/158] WASM update deprecated methods in web-sys --- Cargo.lock | 136 ++++++++++++------ Cargo.toml | 58 ++++---- .../src/storage/local/transaction/indexdb.rs | 8 +- wallet/core/src/wasm/tx/generator/pending.rs | 8 ++ 4 files changed, 137 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06c35b62a7..0ac1f495eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2411,9 +2411,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -4172,6 +4172,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minicov" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -6546,11 +6556,12 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if 1.0.0", + "once_cell", "serde", "serde_json", "wasm-bindgen-macro", @@ -6558,9 +6569,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -6573,9 +6584,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -6585,9 +6596,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6595,9 +6606,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", @@ -6608,18 +6619,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-bindgen-test" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" dependencies = [ "console_error_panic_hook", "js-sys", + "minicov", "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", @@ -6628,9 +6640,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", @@ -6639,9 +6651,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -6878,7 +6890,9 @@ dependencies = [ [[package]] name = "workflow-chrome" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97134771e915c188b83d7a704b7ab626ef7a5c403deb5f09e89387536a1f3156" dependencies = [ "cfg-if 1.0.0", "chrome-sys", @@ -6891,7 +6905,9 @@ dependencies = [ [[package]] name = "workflow-core" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff42951d05b777c35c0650fa1d16bef24f37e4aae539d06463bcedb2d8125e90" dependencies = [ "async-channel 2.2.1", "async-std", @@ -6922,7 +6938,9 @@ dependencies = [ [[package]] name = "workflow-core-macros" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "137266453ee5696254d8cc0cf1a535ad931be18860b657749f1ea79431c95129" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6937,7 +6955,9 @@ dependencies = [ [[package]] name = "workflow-dom" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f632b72a510fbd06711ac00edd41800f5ff789af66ef295736a0b74fa288fde" dependencies = [ "futures", "js-sys", @@ -6953,7 +6973,9 @@ dependencies = [ [[package]] name = "workflow-http" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac8974f653aa090c4627738082a3e532c5b70ef75bc5d8bdc00e9df93a84c6a8" dependencies = [ "cfg-if 1.0.0", "reqwest", @@ -6967,7 +6989,9 @@ dependencies = [ [[package]] name = "workflow-log" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a0f71b5bd0fc4fe6e256cce3c9ba3b888f154533a9c91bec647bac715e9c37" dependencies = [ "cfg-if 1.0.0", "console", @@ -6981,7 +7005,9 @@ dependencies = [ [[package]] name = "workflow-macro-tools" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058b63fd7d255d04a5da8a4e2aab57bd8c9693dfd09a31c82ef47a65d807ca75" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -6992,7 +7018,9 @@ dependencies = [ [[package]] name = "workflow-node" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c6227ecff04a63c6cd42f8d179526e49a33708b6216abcd803e9a3b3ae94ac" dependencies = [ "borsh", "futures", @@ -7011,7 +7039,9 @@ dependencies = [ [[package]] name = "workflow-nw" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9014be650ede0fdcc10e5cdeece72f30db7e35846fd4c1e9e7a0dbb2e4a11815" dependencies = [ "ahash", "async-trait", @@ -7033,7 +7063,9 @@ dependencies = [ [[package]] name = "workflow-panic-hook" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8909242a7d2f15d15d4f20f6cdc05932b394c6f34bd122de0a94eeddde493" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen", @@ -7056,7 +7088,9 @@ dependencies = [ [[package]] name = "workflow-rpc" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e26bdfb04aee86d80206160d817132966a4cca636b3565d597171412c3a570a" dependencies = [ "ahash", "async-std", @@ -7084,7 +7118,9 @@ dependencies = [ [[package]] name = "workflow-rpc-macros" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8bd0c19bab1fc8606fd8a7b89b76bbc1c70b5389a4895c7ab5c81199b31578" dependencies = [ "parse-variants", "proc-macro-error", @@ -7095,7 +7131,9 @@ dependencies = [ [[package]] name = "workflow-serializer" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a7e3a8965373eb92d4dde26078a08677c4aebb422096d96f36ad89c33ddadb" dependencies = [ "ahash", "borsh", @@ -7104,7 +7142,9 @@ dependencies = [ [[package]] name = "workflow-store" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "782c42bf9ea4eff8a518c6c65b000f4d10179dedfa19348a26d34f4637571437" dependencies = [ "async-std", "base64 0.22.1", @@ -7130,7 +7170,9 @@ dependencies = [ [[package]] name = "workflow-task" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dbc4593b9c3a635b11952e35e33b273db513ce304af368bfb2e59eca47e77f8" dependencies = [ "futures", "thiserror", @@ -7140,7 +7182,9 @@ dependencies = [ [[package]] name = "workflow-task-macros" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee242b444bc37cf4d338c3c2994a3302b82ca2fa6a33537037bfed09e1583a0" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7154,7 +7198,9 @@ dependencies = [ [[package]] name = "workflow-terminal" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b181672f3debb02b0c1db672164a8ae827d0ae064a77c008c7beb126341b6" dependencies = [ "async-std", "async-trait", @@ -7181,7 +7227,9 @@ dependencies = [ [[package]] name = "workflow-terminal-macros" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ac116e1d603e4462496ba2ec4133c43af29e7729493ff0ed8ea91319221cf06" dependencies = [ "convert_case 0.6.0", "parse-variants", @@ -7195,7 +7243,9 @@ dependencies = [ [[package]] name = "workflow-wasm" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a878ee79b2a117277ae24d87525686f50006b87f43cf2052401e12c330fddbbc" dependencies = [ "cfg-if 1.0.0", "faster-hex 0.9.0", @@ -7214,7 +7264,9 @@ dependencies = [ [[package]] name = "workflow-wasm-macros" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9268b990788e1bf1a307e1bc9d9f498e9ee2cb6d4ba3292d1c7b0b5d3d0d9e46" dependencies = [ "js-sys", "proc-macro-error", @@ -7226,7 +7278,9 @@ dependencies = [ [[package]] name = "workflow-websocket" -version = "0.16.0" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d03ec3c5ad42ba1a2bfba7e752be890ecacabf73c6da300ad78a2ffa1e3c5a" dependencies = [ "ahash", "async-channel 2.2.1", diff --git a/Cargo.toml b/Cargo.toml index 430d055809..da84ba04c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -189,7 +189,7 @@ indexmap = "2.1.0" intertrait = "0.2.2" ipnet = "2.9.0" itertools = "0.11.0" -js-sys = "0.3.67" +js-sys = "0.3.70" keccak = "0.1.4" local-ip-address = "0.5.6" log = "0.4.20" @@ -250,10 +250,10 @@ tonic = { version = "0.10.2", features = ["tls", "gzip", "transport"] } tonic-build = { version = "0.10.2", features = ["prost"] } triggered = "0.1.2" uuid = { version = "1.5.0", features = ["v4", "fast-rng", "serde"] } -wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] } -wasm-bindgen-futures = "0.4.40" -wasm-bindgen-test = "0.3.37" -web-sys = "0.3.67" +wasm-bindgen = { version = "0.2.93", features = ["serde-serialize"] } +wasm-bindgen-futures = "0.4.43" +wasm-bindgen-test = "0.3.43" +web-sys = "0.3.70" xxhash-rust = { version = "0.8.7", features = ["xxh3"] } zeroize = { version = "1.6.0", default-features = false, features = ["alloc"] } pin-project-lite = "0.2.13" @@ -272,34 +272,34 @@ workflow-perf-monitor = "0.0.2" nw-sys = "0.1.6" # workflow dependencies -# workflow-core = { version = "0.15.0" } -# workflow-d3 = { version = "0.15.0" } -# workflow-dom = { version = "0.15.0" } -# workflow-http = { version = "0.15.0" } -# workflow-log = { version = "0.15.0" } -# workflow-node = { version = "0.15.0" } -# workflow-nw = { version = "0.15.0" } -# workflow-rpc = { version = "0.15.0" } -# workflow-serializer = { version = "0.15.0" } -# workflow-store = { version = "0.15.0" } -# workflow-terminal = { version = "0.15.0" } -# workflow-wasm = { version = "0.15.0" } +workflow-core = { version = "0.17.0" } +workflow-d3 = { version = "0.17.0" } +workflow-dom = { version = "0.17.0" } +workflow-http = { version = "0.17.0" } +workflow-log = { version = "0.17.0" } +workflow-node = { version = "0.17.0" } +workflow-nw = { version = "0.17.0" } +workflow-rpc = { version = "0.17.0" } +workflow-serializer = { version = "0.17.0" } +workflow-store = { version = "0.17.0" } +workflow-terminal = { version = "0.17.0" } +workflow-wasm = { version = "0.17.0" } # if below is enabled, this means that there is an ongoing work # on the workflow-rs crate. This requires that you clone workflow-rs # into a sibling folder from https://github.com/workflow-rs/workflow-rs -workflow-core = { path = "../workflow-rs/core" } -workflow-d3 = { path = "../workflow-rs/d3" } -workflow-dom = { path = "../workflow-rs/dom" } -workflow-http = { path = "../workflow-rs/http" } -workflow-log = { path = "../workflow-rs/log" } -workflow-node = { path = "../workflow-rs/node" } -workflow-nw = { path = "../workflow-rs/nw" } -workflow-rpc = { path = "../workflow-rs/rpc" } -workflow-serializer = { path = "../workflow-rs/serializer" } -workflow-store = { path = "../workflow-rs/store" } -workflow-terminal = { path = "../workflow-rs/terminal" } -workflow-wasm = { path = "../workflow-rs/wasm" } +# workflow-core = { path = "../workflow-rs/core" } +# workflow-d3 = { path = "../workflow-rs/d3" } +# workflow-dom = { path = "../workflow-rs/dom" } +# workflow-http = { path = "../workflow-rs/http" } +# workflow-log = { path = "../workflow-rs/log" } +# workflow-node = { path = "../workflow-rs/node" } +# workflow-nw = { path = "../workflow-rs/nw" } +# workflow-rpc = { path = "../workflow-rs/rpc" } +# workflow-serializer = { path = "../workflow-rs/serializer" } +# workflow-store = { path = "../workflow-rs/store" } +# workflow-terminal = { path = "../workflow-rs/terminal" } +# workflow-wasm = { path = "../workflow-rs/wasm" } # --- # workflow-core = { git = "https://github.com/workflow-rs/workflow-rs.git", branch = "master" } diff --git a/wallet/core/src/storage/local/transaction/indexdb.rs b/wallet/core/src/storage/local/transaction/indexdb.rs index 5631e002a5..6a1dacfc63 100644 --- a/wallet/core/src/storage/local/transaction/indexdb.rs +++ b/wallet/core/src/storage/local/transaction/indexdb.rs @@ -34,20 +34,22 @@ impl Inner { let old_version = evt.old_version(); if old_version < 1.0 { let object_store = evt.db().create_object_store(TRANSACTIONS_STORE_NAME)?; + let db_index_params = IdbIndexParameters::new(); + db_index_params.set_unique(true); object_store.create_index_with_params( TRANSACTIONS_STORE_ID_INDEX, &IdbKeyPath::str(TRANSACTIONS_STORE_ID_INDEX), - IdbIndexParameters::new().unique(true), + &db_index_params, )?; object_store.create_index_with_params( TRANSACTIONS_STORE_TIMESTAMP_INDEX, &IdbKeyPath::str(TRANSACTIONS_STORE_TIMESTAMP_INDEX), - IdbIndexParameters::new().unique(false), + &db_index_params, )?; object_store.create_index_with_params( TRANSACTIONS_STORE_DATA_INDEX, &IdbKeyPath::str(TRANSACTIONS_STORE_DATA_INDEX), - IdbIndexParameters::new().unique(false), + &db_index_params, )?; // these changes are not required for new db diff --git a/wallet/core/src/wasm/tx/generator/pending.rs b/wallet/core/src/wasm/tx/generator/pending.rs index 8ec04e6b3e..ee3a9a5d9f 100644 --- a/wallet/core/src/wasm/tx/generator/pending.rs +++ b/wallet/core/src/wasm/tx/generator/pending.rs @@ -122,6 +122,14 @@ impl PendingTransaction { /// {@link UtxoContext} if one was used to create the transaction /// and will return UTXOs back to {@link UtxoContext} in case of /// a failed submission. + /// + /// # Important + /// + /// Make sure to consume the returned `txid` value. Always invoke this method + /// as follows `let txid = await pendingTransaction.submit(rpc);`. If you do not + /// consume the returned value and the rpc object is temporary, the GC will + /// collect the `rpc` object passed to submit() potentially causing a panic. + /// /// @see {@link RpcClient.submitTransaction} pub async fn submit(&self, wasm_rpc_client: &RpcClient) -> Result { let rpc: Arc = wasm_rpc_client.client().clone(); From 6b7600e70fc032d6f23a5f3ea908d4c725c4ca47 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 20 Aug 2024 10:54:19 +0300 Subject: [PATCH 151/158] Add bip32 Mnemonic class to kaspa-wasm-sdk-keys build package --- wasm/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index e0e1f07cf5..77c5e16ea8 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -208,6 +208,7 @@ cfg_if::cfg_if! { pub use kaspa_addresses::{Address, Version as AddressVersion}; pub use kaspa_wallet_keys::prelude::*; + pub use kaspa_bip32::*; pub use kaspa_wasm_core::types::*; } From 7766ebd9ce161a6cf1cb873ffea12034506c984c Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 20 Aug 2024 12:12:08 +0300 Subject: [PATCH 152/158] misc dependency updates --- Cargo.lock | 274 ++++++++++-------- Cargo.toml | 30 +- .../src/storage/local/transaction/indexdb.rs | 2 +- 3 files changed, 167 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ac1f495eb..5973f9016c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -394,15 +394,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -549,17 +540,16 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.65.1" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "cexpr", "clang-sys", + "itertools 0.12.1", "lazy_static", "lazycell", - "peeking_take_while", - "prettyplease", "proc-macro2", "quote", "regex", @@ -1065,12 +1055,6 @@ dependencies = [ "itertools 0.10.5", ] -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - [[package]] name = "crossbeam-channel" version = "0.5.12" @@ -1252,11 +1236,12 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.3" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" dependencies = [ "cfg-if 1.0.0", + "crossbeam-utils", "hashbrown 0.14.5", "lock_api", "once_cell", @@ -1335,15 +1320,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -1432,9 +1417,9 @@ dependencies = [ [[package]] name = "duration-string" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcc1d9ae294a15ed05aeae8e11ee5f2b3fe971c077d45a42fb20825fba6ee13" +checksum = "2334658684d7c213e18602aa72ce37e94d1c9b535882ef6e30bc444b7514a1ee" [[package]] name = "either" @@ -1459,13 +1444,13 @@ dependencies = [ [[package]] name = "enum-primitive-derive" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e" +checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c" dependencies = [ "num-traits", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -1566,12 +1551,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "faster-hex" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e2ce894d53b295cf97b05685aa077950ff3e8541af83217fc720a6437169f8" - [[package]] name = "faster-hex" version = "0.9.0" @@ -1875,9 +1854,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", "bytes", @@ -1904,9 +1883,9 @@ dependencies = [ [[package]] name = "hash32" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ "byteorder", ] @@ -1928,14 +1907,11 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.17" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "atomic-polyfill", "hash32", - "rustc_version", - "spin", "stable_deref_trait", ] @@ -2116,7 +2092,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -2203,7 +2179,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -2252,16 +2228,16 @@ dependencies = [ [[package]] name = "indexed_db_futures" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc2083760572ee02385ab8b7c02c20925d2dd1f97a1a25a8737a238608f1152" +checksum = "43315957678a70eb21fb0d2384fe86dde0d6c859a01e24ce127eb65a0143d28c" dependencies = [ "accessory", "cfg-if 1.0.0", "delegate-display", "fancy_constructor", "js-sys", - "uuid 1.6.1", + "uuid 1.10.0", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2394,6 +2370,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -2441,7 +2426,7 @@ version = "0.14.1" dependencies = [ "borsh", "igd-next", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-consensus-core", "kaspa-core", "kaspa-database", @@ -2471,7 +2456,7 @@ version = "0.14.1" dependencies = [ "borsh", "bs58", - "faster-hex 0.6.1", + "faster-hex", "getrandom 0.2.14", "hmac", "js-sys", @@ -2502,7 +2487,7 @@ dependencies = [ "convert_case 0.6.0", "dashmap", "downcast", - "faster-hex 0.6.1", + "faster-hex", "futures", "hex", "js-sys", @@ -2545,7 +2530,7 @@ version = "0.14.1" dependencies = [ "duration-string", "futures-util", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-addressmanager", "kaspa-core", "kaspa-p2p-lib", @@ -2565,11 +2550,11 @@ dependencies = [ "bincode", "criterion", "crossbeam-channel", - "faster-hex 0.6.1", + "faster-hex", "flate2", "futures-util", "indexmap 2.2.6", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-consensus-core", "kaspa-consensus-notify", "kaspa-consensusmanager", @@ -2605,9 +2590,9 @@ version = "0.14.1" dependencies = [ "ahash", "cfg-if 1.0.0", - "faster-hex 0.6.1", + "faster-hex", "hex", - "itertools 0.11.0", + "itertools 0.13.0", "js-sys", "kaspa-addresses", "kaspa-consensus-core", @@ -2636,10 +2621,10 @@ dependencies = [ "borsh", "cfg-if 1.0.0", "criterion", - "faster-hex 0.6.1", + "faster-hex", "futures-util", "getrandom 0.2.14", - "itertools 0.11.0", + "itertools 0.13.0", "js-sys", "kaspa-addresses", "kaspa-core", @@ -2689,7 +2674,7 @@ name = "kaspa-consensus-wasm" version = "0.14.1" dependencies = [ "cfg-if 1.0.0", - "faster-hex 0.6.1", + "faster-hex", "js-sys", "kaspa-addresses", "kaspa-consensus-client", @@ -2715,7 +2700,7 @@ dependencies = [ "duration-string", "futures", "futures-util", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-consensus-core", "kaspa-consensus-notify", "kaspa-core", @@ -2772,9 +2757,9 @@ version = "0.14.1" dependencies = [ "bincode", "enum-primitive-derive", - "faster-hex 0.6.1", + "faster-hex", "indexmap 2.2.6", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-hashes", "kaspa-utils", "num-traits", @@ -2795,11 +2780,11 @@ dependencies = [ "async-channel 2.2.1", "async-stream", "async-trait", - "faster-hex 0.6.1", + "faster-hex", "futures", "futures-util", - "h2 0.3.26", - "itertools 0.11.0", + "h2 0.4.6", + "itertools 0.13.0", "kaspa-core", "kaspa-grpc-core", "kaspa-notify", @@ -2826,9 +2811,9 @@ dependencies = [ "async-channel 2.2.1", "async-stream", "async-trait", - "faster-hex 0.6.1", + "faster-hex", "futures", - "h2 0.3.26", + "h2 0.4.6", "kaspa-consensus-core", "kaspa-core", "kaspa-notify", @@ -2855,10 +2840,10 @@ dependencies = [ "async-channel 2.2.1", "async-stream", "async-trait", - "faster-hex 0.6.1", + "faster-hex", "futures", - "h2 0.3.26", - "itertools 0.11.0", + "h2 0.4.6", + "itertools 0.13.0", "kaspa-consensus-core", "kaspa-core", "kaspa-grpc-client", @@ -2880,7 +2865,7 @@ dependencies = [ "tokio-stream", "tonic", "triggered", - "uuid 1.6.1", + "uuid 1.10.0", ] [[package]] @@ -2891,7 +2876,7 @@ dependencies = [ "borsh", "cc", "criterion", - "faster-hex 0.6.1", + "faster-hex", "js-sys", "kaspa-utils", "keccak", @@ -2957,7 +2942,7 @@ version = "0.14.1" dependencies = [ "borsh", "criterion", - "faster-hex 0.6.1", + "faster-hex", "js-sys", "kaspa-utils", "malachite-base", @@ -3001,7 +2986,7 @@ version = "0.14.1" dependencies = [ "criterion", "futures-util", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-addresses", "kaspa-consensus-core", "kaspa-consensusmanager", @@ -3055,7 +3040,7 @@ dependencies = [ "futures", "futures-util", "indexmap 2.2.6", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-addresses", "kaspa-alloc", "kaspa-consensus-core", @@ -3087,7 +3072,7 @@ dependencies = [ "chrono", "futures", "indexmap 2.2.6", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-addressmanager", "kaspa-connectionmanager", "kaspa-consensus-core", @@ -3107,7 +3092,7 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", - "uuid 1.6.1", + "uuid 1.10.0", ] [[package]] @@ -3117,9 +3102,9 @@ dependencies = [ "borsh", "ctrlc", "futures", - "h2 0.3.26", + "h2 0.4.6", "hex", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-consensus-core", "kaspa-core", "kaspa-hashes", @@ -3138,7 +3123,7 @@ dependencies = [ "tokio-stream", "tonic", "tonic-build", - "uuid 1.6.1", + "uuid 1.10.0", ] [[package]] @@ -3179,7 +3164,7 @@ dependencies = [ "cfg-if 1.0.0", "derive_more", "downcast", - "faster-hex 0.6.1", + "faster-hex", "hex", "js-sys", "kaspa-addresses", @@ -3204,7 +3189,7 @@ dependencies = [ "serde_json", "smallvec", "thiserror", - "uuid 1.6.1", + "uuid 1.10.0", "wasm-bindgen", "workflow-core", "workflow-serializer", @@ -3264,11 +3249,11 @@ dependencies = [ "criterion", "crossbeam-channel", "dhat", - "faster-hex 0.6.1", + "faster-hex", "flate2", "futures-util", "indexmap 2.2.6", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-addresses", "kaspa-alloc", "kaspa-bip32", @@ -3323,7 +3308,7 @@ dependencies = [ "hex", "hexplay", "indexmap 2.2.6", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-addresses", "kaspa-consensus-core", "kaspa-hashes", @@ -3365,10 +3350,10 @@ dependencies = [ "criterion", "duct", "event-listener 2.5.3", - "faster-hex 0.6.1", + "faster-hex", "futures-util", "ipnet", - "itertools 0.11.0", + "itertools 0.13.0", "log", "mac_address", "num_cpus", @@ -3383,7 +3368,7 @@ dependencies = [ "thiserror", "tokio", "triggered", - "uuid 1.6.1", + "uuid 1.10.0", "wasm-bindgen", ] @@ -3458,7 +3443,7 @@ dependencies = [ "async-channel 2.2.1", "async-std", "async-trait", - "base64 0.21.7", + "base64 0.22.1", "borsh", "cfb-mode", "cfg-if 1.0.0", @@ -3469,7 +3454,7 @@ dependencies = [ "derivative", "downcast", "evpkdf", - "faster-hex 0.6.1", + "faster-hex", "fixedstr", "futures", "heapless", @@ -3477,7 +3462,7 @@ dependencies = [ "hmac", "home", "indexed_db_futures", - "itertools 0.11.0", + "itertools 0.13.0", "js-sys", "kaspa-addresses", "kaspa-bip32", @@ -3536,7 +3521,7 @@ dependencies = [ "async-trait", "borsh", "downcast", - "faster-hex 0.6.1", + "faster-hex", "hmac", "js-sys", "kaspa-addresses", @@ -3633,7 +3618,7 @@ dependencies = [ name = "kaspa-wasm-core" version = "0.14.1" dependencies = [ - "faster-hex 0.6.1", + "faster-hex", "hexplay", "js-sys", "wasm-bindgen", @@ -3885,11 +3870,11 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.11.0+8.1.1" +version = "0.16.0+8.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c" dependencies = [ - "bindgen 0.65.1", + "bindgen 0.69.4", "bzip2-sys", "cc", "glob", @@ -3944,9 +3929,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "local-ip-address" -version = "0.5.7" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612ed4ea9ce5acfb5d26339302528a5e1e59dfed95e9e11af3c083236ff1d15d" +checksum = "136ef34e18462b17bf39a7826f8f3bbc223341f8e83822beb8b77db9a3d49696" dependencies = [ "libc", "neli", @@ -5116,7 +5101,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.0", "http-body-util", @@ -5183,9 +5168,9 @@ dependencies = [ [[package]] name = "rocksdb" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +checksum = "6bd13e55d6d7b8cd0ea569161127567cd587676c99f4472f779a0279aa60a7a7" dependencies = [ "libc", "librocksdb-sys", @@ -5198,8 +5183,8 @@ dependencies = [ "async-channel 2.2.1", "clap 4.5.4", "criterion", - "faster-hex 0.6.1", - "itertools 0.11.0", + "faster-hex", + "itertools 0.13.0", "kaspa-addresses", "kaspa-consensus-core", "kaspa-core", @@ -5397,9 +5382,9 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.28.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" dependencies = [ "rand 0.8.5", "secp256k1-sys", @@ -5408,9 +5393,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" dependencies = [ "cc", ] @@ -5677,7 +5662,7 @@ dependencies = [ "futures", "futures-util", "indexmap 2.2.6", - "itertools 0.11.0", + "itertools 0.13.0", "kaspa-alloc", "kaspa-consensus", "kaspa-consensus-core", @@ -5761,9 +5746,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "stable_deref_trait" @@ -5870,15 +5852,14 @@ checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "sysinfo" -version = "0.30.13" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab" dependencies = [ - "cfg-if 1.0.0", "core-foundation-sys", "libc", + "memchr", "ntapi", - "once_cell", "rayon", "windows", ] @@ -6468,9 +6449,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom 0.2.14", "rand 0.8.5", @@ -6704,11 +6685,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.52.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ - "windows-core", + "windows-core 0.57.0", "windows-targets 0.52.5", ] @@ -6721,6 +6702,49 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -6916,7 +6940,7 @@ dependencies = [ "cfg-if 1.0.0", "chrono", "dirs", - "faster-hex 0.9.0", + "faster-hex", "futures", "getrandom 0.2.14", "instant", @@ -7150,7 +7174,7 @@ dependencies = [ "base64 0.22.1", "cfg-if 1.0.0", "chrome-sys", - "faster-hex 0.9.0", + "faster-hex", "filetime", "home", "js-sys", @@ -7248,7 +7272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a878ee79b2a117277ae24d87525686f50006b87f43cf2052401e12c330fddbbc" dependencies = [ "cfg-if 1.0.0", - "faster-hex 0.9.0", + "faster-hex", "futures", "js-sys", "serde", diff --git a/Cargo.toml b/Cargo.toml index da84ba04c8..9be64f39e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ async-channel = "2.0.0" async-std = { version = "1.12.0", features = ['attributes'] } async-stream = "0.3.5" async-trait = "0.1.74" -base64 = "0.21.5" +base64 = "0.22.1" bincode = { version = "1.3.3", default-features = false } blake2b_simd = "1.0.2" borsh = { version = "1.5.1", features = ["derive", "rc"] } @@ -159,26 +159,29 @@ criterion = { version = "0.5.1", default-features = false } crossbeam-channel = "0.5.8" ctrlc = "3.4.1" crypto_box = { version = "0.9.1", features = ["chacha20"] } -dashmap = "5.5.3" +dashmap = "6.0.1" derivative = "2.2.0" derive_builder = "0.20.0" derive_more = "0.99.17" +# derive_more = { version = "1.0.0", features = ["full"] } dhat = "0.3.2" dirs = "5.0.1" downcast = "0.11.0" downcast-rs = "1.2.0" -duration-string = "0.3.0" -enum-primitive-derive = "0.2.2" +duration-string = "0.4.0" +enum-primitive-derive = "0.3.0" event-listener = "2.5.3" # TODO "3.0.1" evpkdf = "0.2.0" -faster-hex = "0.6.1" # TODO "0.8.1" - fails unit tests +faster-hex = "0.9.0" fixedstr = { version = "0.5.4", features = ["serde"] } flate2 = "1.0.28" futures = { version = "0.3.29" } futures-util = { version = "0.3.29", default-features = false, features = ["alloc"] } getrandom = { version = "0.2.10", features = ["js"] } -h2 = "0.3.21" -heapless = "0.7.16" +h2 = "0.4.6" +# h2 = "0.3.21" +heapless = "0.8.0" +# heapless = "0.7.16" hex = { version = "0.4.3", features = ["serde"] } hex-literal = "0.4.1" hexplay = "0.3.0" @@ -188,10 +191,10 @@ igd-next = { version = "0.14.2", features = ["aio_tokio"] } indexmap = "2.1.0" intertrait = "0.2.2" ipnet = "2.9.0" -itertools = "0.11.0" +itertools = "0.13.0" js-sys = "0.3.70" keccak = "0.1.4" -local-ip-address = "0.5.6" +local-ip-address = "0.6.1" log = "0.4.20" log4rs = "1.2.0" mac_address = "1.1.7" @@ -208,6 +211,7 @@ paste = "1.0.14" pbkdf2 = "0.12.2" portable-atomic = { version = "1.5.1", features = ["float"] } prost = "0.12.1" +# prost = "0.13.1" rand = "0.8.5" rand_chacha = "0.3.1" rand_core = { version = "0.6.4", features = ["std"] } @@ -216,8 +220,8 @@ rayon = "1.8.0" regex = "1.10.2" ripemd = { version = "0.1.3", default-features = false } rlimit = "0.10.1" -rocksdb = "0.21.0" -secp256k1 = { version = "0.28.2", features = [ +rocksdb = "0.22.0" +secp256k1 = { version = "0.29.0", features = [ "global-context", "rand-std", "serde", @@ -239,7 +243,7 @@ sorted-insert = "0.2.3" statest = "0.2.2" statrs = "0.13.0" # TODO "0.16.0" subtle = { version = "2.5.0", default-features = false } -sysinfo = "0.30.12" +sysinfo = "0.31.2" tempfile = "3.8.1" textwrap = "0.16.0" thiserror = "1.0.50" @@ -264,7 +268,7 @@ tower-http = { version = "0.4.4", features = [ tower = "0.4.7" hyper = "0.14.27" chrono = "0.4.31" -indexed_db_futures = "0.4.1" +indexed_db_futures = "0.5.0" # workflow dependencies that are not a part of core libraries # workflow-perf-monitor = { path = "../../../workflow-perf-monitor-rs" } diff --git a/wallet/core/src/storage/local/transaction/indexdb.rs b/wallet/core/src/storage/local/transaction/indexdb.rs index 6a1dacfc63..463508f611 100644 --- a/wallet/core/src/storage/local/transaction/indexdb.rs +++ b/wallet/core/src/storage/local/transaction/indexdb.rs @@ -390,7 +390,7 @@ impl TransactionRecordStore for TransactionStore { let inner = inner_guard.lock().unwrap().clone(); call_async_no_send!(async move { - for (db_name, items) in &items.into_iter().group_by(|item| item.db_name.clone()) { + for (db_name, items) in &items.into_iter().chunk_by(|item| item.db_name.clone()) { let db = inner.open_db(db_name).await?; let idb_tx = db From f5fd25adf66c3e27260e242e1b0a0000e40d49b8 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Tue, 20 Aug 2024 18:37:06 +0300 Subject: [PATCH 153/158] Introduce profile data to GetConnections RPC method --- Cargo.lock | 1 + cli/src/modules/rpc.rs | 2 +- rpc/core/src/api/rpc.rs | 4 ++-- rpc/core/src/model/message.rs | 32 ++++++++++++++++++++++------ rpc/core/src/model/tests.rs | 4 ++-- rpc/grpc/core/proto/rpc.proto | 7 ++++++ rpc/grpc/core/src/convert/message.rs | 9 +++++--- rpc/grpc/core/src/convert/metrics.rs | 12 +++++++++++ rpc/service/src/service.rs | 14 ++++++++---- testing/integration/src/rpc_tests.rs | 2 +- wasm/Cargo.toml | 1 + 11 files changed, 68 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5973f9016c..fe6f33a8a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3594,6 +3594,7 @@ dependencies = [ "cfg-if 1.0.0", "js-sys", "kaspa-addresses", + "kaspa-bip32", "kaspa-consensus-core", "kaspa-consensus-wasm", "kaspa-core", diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index 68cb7f7456..db4fd3383b 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -46,7 +46,7 @@ impl Rpc { self.println(&ctx, result); } RpcApiOps::GetConnections => { - let result = rpc.get_connections().await?; + let result = rpc.get_connections(true).await?; self.println(&ctx, result); } RpcApiOps::GetServerInfo => { diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 9825a97380..9f92e3e5a6 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -40,8 +40,8 @@ pub trait RpcApi: Sync + Send + AnySync { // --- - async fn get_connections(&self) -> RpcResult { - self.get_connections_call(None, GetConnectionsRequest {}).await + async fn get_connections(&self, include_profile_data: bool) -> RpcResult { + self.get_connections_call(None, GetConnectionsRequest { include_profile_data }).await } async fn get_connections_call( &self, diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 8a3f4be923..4d935011c0 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -1799,30 +1799,45 @@ impl Deserializer for PingRequest { pub struct PingResponse {} impl Serializer for PingResponse { - fn serialize(&self, _writer: &mut W) -> std::io::Result<()> { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; Ok(()) } } impl Deserializer for PingResponse { - fn deserialize(_reader: &mut R) -> std::io::Result { + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; Ok(Self {}) } } +#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConnectionsProfileData { + pub cpu_usage: f32, + pub memory_usage: u64, +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct GetConnectionsRequest {} +pub struct GetConnectionsRequest { + pub include_profile_data: bool, +} impl Serializer for GetConnectionsRequest { - fn serialize(&self, _writer: &mut W) -> std::io::Result<()> { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + store!(u8, &1, writer)?; + store!(bool, &self.include_profile_data, writer)?; Ok(()) } } impl Deserializer for GetConnectionsRequest { - fn deserialize(_reader: &mut R) -> std::io::Result { - Ok(Self {}) + fn deserialize(reader: &mut R) -> std::io::Result { + let _version = load!(u8, reader)?; + let include_profile_data = load!(bool, reader)?; + Ok(Self { include_profile_data }) } } @@ -1831,6 +1846,7 @@ impl Deserializer for GetConnectionsRequest { pub struct GetConnectionsResponse { pub clients: u32, pub peers: u16, + pub profile_data: Option, } impl Serializer for GetConnectionsResponse { @@ -1838,6 +1854,7 @@ impl Serializer for GetConnectionsResponse { store!(u16, &1, writer)?; store!(u32, &self.clients, writer)?; store!(u16, &self.peers, writer)?; + store!(Option, &self.profile_data, writer)?; Ok(()) } } @@ -1847,7 +1864,8 @@ impl Deserializer for GetConnectionsResponse { let _version = load!(u16, reader)?; let clients = load!(u32, reader)?; let peers = load!(u16, reader)?; - Ok(Self { clients, peers }) + let extra = load!(Option, reader)?; + Ok(Self { clients, peers, profile_data: extra }) } } diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs index bbac58949e..bc3369ee0f 100644 --- a/rpc/core/src/model/tests.rs +++ b/rpc/core/src/model/tests.rs @@ -933,7 +933,7 @@ mod mockery { impl Mock for GetConnectionsRequest { fn mock() -> Self { - GetConnectionsRequest {} + GetConnectionsRequest { include_profile_data: false } } } @@ -941,7 +941,7 @@ mod mockery { impl Mock for GetConnectionsResponse { fn mock() -> Self { - GetConnectionsResponse { clients: mock(), peers: mock() } + GetConnectionsResponse { clients: mock(), peers: mock(), profile_data: None } } } diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto index 48cde0208b..d5c441927a 100644 --- a/rpc/grpc/core/proto/rpc.proto +++ b/rpc/grpc/core/proto/rpc.proto @@ -826,11 +826,18 @@ message StorageMetrics{ } message GetConnectionsRequestMessage{ + bool includeProfileData = 1; +} + +message ConnectionsProfileData { + double cpuUsage = 1; + uint64 memoryUsage = 2; } message GetConnectionsResponseMessage{ uint32 clients = 1; uint32 peers = 2; + ConnectionsProfileData profileData = 3; RPCError error = 1000; } diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index f7cfa8c7e2..5a0e946103 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -448,14 +448,16 @@ from!(item: RpcResult<&kaspa_rpc_core::GetMetricsResponse>, protowire::GetMetric } }); -from!(_item: &kaspa_rpc_core::GetConnectionsRequest, protowire::GetConnectionsRequestMessage, { +from!(item: &kaspa_rpc_core::GetConnectionsRequest, protowire::GetConnectionsRequestMessage, { Self { + include_profile_data : item.include_profile_data, } }); from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetConnectionsResponseMessage, { Self { clients: item.clients, peers: item.peers as u32, + profile_data: item.profile_data.as_ref().map(|x| x.into()), error: None, } }); @@ -920,13 +922,14 @@ try_from!(item: &protowire::GetMetricsResponseMessage, RpcResult, { Self { clients: item.clients, peers: item.peers as u16, + profile_data: item.profile_data.as_ref().map(|x| x.try_into()).transpose()?, } }); diff --git a/rpc/grpc/core/src/convert/metrics.rs b/rpc/grpc/core/src/convert/metrics.rs index 4402eea53b..5037b370f4 100644 --- a/rpc/grpc/core/src/convert/metrics.rs +++ b/rpc/grpc/core/src/convert/metrics.rs @@ -6,6 +6,14 @@ use kaspa_rpc_core::RpcError; // rpc_core to protowire // ---------------------------------------------------------------------------- +from!(item: &kaspa_rpc_core::ConnectionsProfileData, protowire::ConnectionsProfileData, { + Self { + cpu_usage: item.cpu_usage as f64, + memory_usage: item.memory_usage, + + } +}); + from!(item: &kaspa_rpc_core::ProcessMetrics, protowire::ProcessMetrics, { Self { resident_set_size: item.resident_set_size, @@ -76,6 +84,10 @@ from!(item: &kaspa_rpc_core::StorageMetrics, protowire::StorageMetrics, { // protowire to rpc_core // ---------------------------------------------------------------------------- +try_from!(item: &protowire::ConnectionsProfileData, kaspa_rpc_core::ConnectionsProfileData, { + Self { cpu_usage : item.cpu_usage as f32, memory_usage : item.memory_usage } +}); + try_from!(item: &protowire::ProcessMetrics, kaspa_rpc_core::ProcessMetrics, { Self { resident_set_size: item.resident_set_size, diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs index ff9f9b8e6d..52a38f45b8 100644 --- a/rpc/service/src/service.rs +++ b/rpc/service/src/service.rs @@ -927,13 +927,19 @@ NOTE: This error usually indicates an RPC conversion error between the node and async fn get_connections_call( &self, _connection: Option<&DynRpcConnection>, - _req: GetConnectionsRequest, + req: GetConnectionsRequest, ) -> RpcResult { let clients = (self.wrpc_borsh_counters.active_connections.load(Ordering::Relaxed) + self.wrpc_json_counters.active_connections.load(Ordering::Relaxed)) as u32; let peers = self.flow_context.hub().active_peers_len() as u16; - Ok(GetConnectionsResponse { clients, peers }) + let profile_data = req.include_profile_data.then(|| { + let CountersSnapshot { resident_set_size: memory_usage, cpu_usage, .. } = self.perf_monitor.snapshot(); + + ConnectionsProfileData { cpu_usage: cpu_usage as f32, memory_usage } + }); + + Ok(GetConnectionsResponse { clients, peers, profile_data }) } async fn get_metrics_call(&self, _connection: Option<&DynRpcConnection>, req: GetMetricsRequest) -> RpcResult { @@ -961,7 +967,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and disk_io_write_per_sec: disk_io_write_per_sec as f32, }); - let connection_metrics = req.connection_metrics.then_some(ConnectionMetrics { + let connection_metrics = req.connection_metrics.then(|| ConnectionMetrics { borsh_live_connections: self.wrpc_borsh_counters.active_connections.load(Ordering::Relaxed) as u32, borsh_connection_attempts: self.wrpc_borsh_counters.total_connections.load(Ordering::Relaxed) as u64, borsh_handshake_failures: self.wrpc_borsh_counters.handshake_failures.load(Ordering::Relaxed) as u64, @@ -972,7 +978,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and active_peers: self.flow_context.hub().active_peers_len() as u32, }); - let bandwidth_metrics = req.bandwidth_metrics.then_some(BandwidthMetrics { + let bandwidth_metrics = req.bandwidth_metrics.then(|| BandwidthMetrics { borsh_bytes_tx: self.wrpc_borsh_counters.tx_bytes.load(Ordering::Relaxed) as u64, borsh_bytes_rx: self.wrpc_borsh_counters.rx_bytes.load(Ordering::Relaxed) as u64, json_bytes_tx: self.wrpc_json_counters.tx_bytes.load(Ordering::Relaxed) as u64, diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs index c0e2e48925..c4e5b4ce0f 100644 --- a/testing/integration/src/rpc_tests.rs +++ b/testing/integration/src/rpc_tests.rs @@ -485,7 +485,7 @@ async fn sanity_test() { KaspadPayloadOps::GetConnections => { let rpc_client = client.clone(); tst!(op, { - let _ = rpc_client.get_connections_call(None, GetConnectionsRequest {}).await.unwrap(); + let _ = rpc_client.get_connections_call(None, GetConnectionsRequest { include_profile_data: true }).await.unwrap(); }) } diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 136d81f151..77b14c39dd 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -18,6 +18,7 @@ crate-type = ["cdylib"] cfg-if.workspace = true js-sys.workspace = true kaspa-addresses.workspace = true +kaspa-bip32.workspace = true kaspa-consensus-core.workspace = true kaspa-consensus-wasm.workspace = true kaspa-core.workspace = true From 18012e80a11c8ae7dfc70a21d375b0f395107a23 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 22 Aug 2024 12:47:21 +0300 Subject: [PATCH 154/158] WASM update TS declarations for wallet events --- wallet/core/src/wasm/notify.rs | 107 ++++++++++++--------------------- 1 file changed, 38 insertions(+), 69 deletions(-) diff --git a/wallet/core/src/wasm/notify.rs b/wallet/core/src/wasm/notify.rs index 5172d33f85..3383e1353e 100644 --- a/wallet/core/src/wasm/notify.rs +++ b/wallet/core/src/wasm/notify.rs @@ -59,13 +59,13 @@ cfg_if! { * * @category Wallet API */ + export type UtxoProcessorEvent = { [K in T]: { type: K, data: UtxoProcessorEventMap[K] } }[T]; - /** * {@link UtxoProcessor} notification callback type. @@ -77,6 +77,7 @@ cfg_if! { * * @category Wallet SDK */ + export type UtxoProcessorNotificationCallback = (event: UtxoProcessorEvent) => void; "#; @@ -132,85 +133,53 @@ cfg_if! { Error = "error", } - - /** - * {@link Wallet} notification event data payload. - * @category Wallet API - */ - export type WalletEventData = IConnectEvent - | IDisconnectEvent - | IUtxoIndexNotEnabledEvent - | ISyncStateEvent - | IWalletHintEvent - | IWalletOpenEvent - | IWalletCreateEvent - | IWalletReloadEvent - | IWalletErrorEvent - // | IWalletCloseEvent - | IPrvKeyDataCreateEvent - | IAccountActivationEvent - | IAccountDeactivationEvent - | IAccountSelectionEvent - | IAccountCreateEvent - | IAccountUpdateEvent - | IServerStatusEvent - // | IUtxoProcStartEvent - // | IUtxoProcStopEvent - | IUtxoProcErrorEvent - | IDaaScoreChangeEvent - | IPendingEvent - | IReorgEvent - | IStasisEvent - | IMaturityEvent - | IDiscoveryEvent - | IBalanceEvent - | IErrorEvent - | undefined - ; - /** * Wallet notification event data map. * @see {@link Wallet.addEventListener} * @category Wallet API */ export type WalletEventMap = { - "connect": IConnectEvent, - "disconnect": IDisconnectEvent, - "utxo-index-not-enabled": IUtxoIndexNotEnabledEvent, - "sync-state": ISyncStateEvent, - "wallet-hint": IWalletHintEvent, - "wallet-open": IWalletOpenEvent, - "wallet-create": IWalletCreateEvent, - "wallet-reload": IWalletReloadEvent, - "wallet-error": IWalletErrorEvent, - "wallet-close": undefined, - "prv-key-data-create": IPrvKeyDataCreateEvent, - "account-activation": IAccountActivationEvent, - "account-deactivation": IAccountDeactivationEvent, - "account-selection": IAccountSelectionEvent, - "account-create": IAccountCreateEvent, - "account-update": IAccountUpdateEvent, - "server-status": IServerStatusEvent, - "utxo-proc-start": undefined, - "utxo-proc-stop": undefined, - "utxo-proc-error": IUtxoProcErrorEvent, - "daa-score-change": IDaaScoreChangeEvent, - "pending": IPendingEvent, - "reorg": IReorgEvent, - "stasis": IStasisEvent, - "maturity": IMaturityEvent, - "discovery": IDiscoveryEvent, - "balance": IBalanceEvent, - "error": IErrorEvent, + "connect": IConnectEvent, + "disconnect": IDisconnectEvent, + "utxo-index-not-enabled": IUtxoIndexNotEnabledEvent, + "sync-state": ISyncStateEvent, + "wallet-hint": IWalletHintEvent, + "wallet-open": IWalletOpenEvent, + "wallet-create": IWalletCreateEvent, + "wallet-reload": IWalletReloadEvent, + "wallet-error": IWalletErrorEvent, + "wallet-close": undefined, + "prv-key-data-create": IPrvKeyDataCreateEvent, + "account-activation": IAccountActivationEvent, + "account-deactivation": IAccountDeactivationEvent, + "account-selection": IAccountSelectionEvent, + "account-create": IAccountCreateEvent, + "account-update": IAccountUpdateEvent, + "server-status": IServerStatusEvent, + "utxo-proc-start": undefined, + "utxo-proc-stop": undefined, + "utxo-proc-error": IUtxoProcErrorEvent, + "daa-score-change": IDaaScoreChangeEvent, + "pending": IPendingEvent, + "reorg": IReorgEvent, + "stasis": IStasisEvent, + "maturity": IMaturityEvent, + "discovery": IDiscoveryEvent, + "balance": IBalanceEvent, + "error": IErrorEvent, } /** * {@link Wallet} notification event interface. * @category Wallet API */ - export type IWalletEvent = { - [K in keyof WalletEventMap]: { type: K, data: WalletEventMap[K] } - }[keyof WalletEventMap]; + export type IWalletEvent = { + [K in T]: { + type: K, + data: WalletEventMap[K] + } + }[T]; + /** * Wallet notification callback type. @@ -222,7 +191,7 @@ cfg_if! { * * @category Wallet API */ - export type WalletNotificationCallback = (event: IWalletEvent) => void; + export type WalletNotificationCallback = (event: IWalletEvent) => void; "#; #[wasm_bindgen] From 45c413debc68a19a0a354a68ae35afce37128821 Mon Sep 17 00:00:00 2001 From: IgorKhomenko <70977170+IgorKhomenko@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:27:49 +0300 Subject: [PATCH 155/158] fix WASM sdk submitTransaction API (#96) --- consensus/client/src/transaction.rs | 4 ++-- rpc/core/src/wasm/message.rs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index 7562c4bd3b..6c09bae4eb 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -214,12 +214,12 @@ impl Transaction { self.inner().version = v; } - #[wasm_bindgen(getter, js_name = lock_time)] + #[wasm_bindgen(getter, js_name = lockTime)] pub fn get_lock_time(&self) -> u64 { self.inner().lock_time } - #[wasm_bindgen(setter, js_name = lock_time)] + #[wasm_bindgen(setter, js_name = lockTime)] pub fn set_lock_time(&self, v: u64) { self.inner().lock_time = v; } diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index 0455b8292a..639ba22aed 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -1414,7 +1414,11 @@ try_from! ( args: ISubmitTransactionRequest, SubmitTransactionRequest, { allow_orphan, } } else { - from_value(transaction)? + let tx = Transaction::try_cast_from(&transaction)?; + SubmitTransactionRequest { + transaction : tx.as_ref().into(), + allow_orphan, + } }; Ok(request) }); From 7d0016974baf9028ccca9dda628ea9f501f4b5b5 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 23 Aug 2024 13:02:28 +0300 Subject: [PATCH 156/158] Add custom Debug to GetSystemInfoResponse --- rpc/core/src/model/message.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs index 4d935011c0..6a2253d8e8 100644 --- a/rpc/core/src/model/message.rs +++ b/rpc/core/src/model/message.rs @@ -3,6 +3,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use kaspa_consensus_core::api::stats::BlockCount; use kaspa_core::debug; use kaspa_notify::subscription::{context::SubscriptionContext, single::UtxosChangedSubscription, Command}; +use kaspa_utils::hex::ToHex; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::{ @@ -1889,7 +1890,7 @@ impl Deserializer for GetSystemInfoRequest { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetSystemInfoResponse { pub version: String, @@ -1900,6 +1901,19 @@ pub struct GetSystemInfoResponse { pub fd_limit: u32, } +impl std::fmt::Debug for GetSystemInfoResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GetSystemInfoResponse") + .field("version", &self.version) + .field("system_id", &self.system_id.as_ref().map(|id| id.to_hex())) + .field("git_hash", &self.git_hash.as_ref().map(|hash| hash.to_hex())) + .field("cpu_physical_cores", &self.cpu_physical_cores) + .field("total_memory", &self.total_memory) + .field("fd_limit", &self.fd_limit) + .finish() + } +} + impl Serializer for GetSystemInfoResponse { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { store!(u16, &1, writer)?; From b3a55f5c7514422d4870b2fda587bf2a2e309ba4 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 23 Aug 2024 13:03:33 +0300 Subject: [PATCH 157/158] Add HexString type to ITransactionOutput::scriptPublicKey --- consensus/client/src/output.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/client/src/output.rs b/consensus/client/src/output.rs index 1e1e23e078..8f335c47d7 100644 --- a/consensus/client/src/output.rs +++ b/consensus/client/src/output.rs @@ -9,7 +9,7 @@ const TS_TRANSACTION_OUTPUT: &'static str = r#" */ export interface ITransactionOutput { value: bigint; - scriptPublicKey: IScriptPublicKey; + scriptPublicKey: IScriptPublicKey | HexString; /** Optional verbose data provided by RPC */ verboseData?: ITransactionOutputVerboseData; From 9cec07787edf473a68f5a300842fab81747ba67e Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Mon, 26 Aug 2024 21:24:52 +0300 Subject: [PATCH 158/158] fix camelCase on RpcTransactionOutpoint --- rpc/core/src/model/tx.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rpc/core/src/model/tx.rs b/rpc/core/src/model/tx.rs index cdd0bcbf8c..ad1fea8a31 100644 --- a/rpc/core/src/model/tx.rs +++ b/rpc/core/src/model/tx.rs @@ -79,6 +79,7 @@ impl Deserializer for RpcUtxoEntry { /// Represents a Kaspa transaction outpoint #[derive(Eq, Hash, PartialEq, Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct RpcTransactionOutpoint { #[serde(with = "serde_bytes_fixed_ref")] pub transaction_id: TransactionId, @@ -100,13 +101,11 @@ impl From for TransactionOutpoint { impl From for RpcTransactionOutpoint { fn from(outpoint: kaspa_consensus_client::TransactionOutpoint) -> Self { TransactionOutpoint::from(outpoint).into() - // Self { transaction_id: outpoint.transaction_id, index: outpoint.index } } } impl From for kaspa_consensus_client::TransactionOutpoint { fn from(outpoint: RpcTransactionOutpoint) -> Self { - // Self { transaction_id: outpoint.transaction_id, index: outpoint.index } TransactionOutpoint::from(outpoint).into() } }