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] 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 16d198c36..dcaef44db 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 c897773f7..1c46d4919 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 47d3a90b6..48e4cdfbd 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 6084df0b2..c7f352e1b 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 000000000..7d45fb05e --- /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 77cef45bc..b145fb90e 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 000000000..4c8cb83f5 --- /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 c7aa05fee..f1d7d716a 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 000000000..ce5d3ba3c --- /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 000000000..e88e580c7 --- /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 7392b1d85..40492cc83 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 15ad78503..3eb617e39 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 78a747e19..136d81f15 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 000000000..4e09c8299 --- /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 912ed9428..e0e1f07cf 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