Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
target
web-root
**/.idea/
**/.zed/
/rust-toolchain
/.vscode/
**/db-*
Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion crypto/txscript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@ repository.workspace = true
[[example]]
name = "kip-10"

[[example]]
name = "script-viewer"

[features]
wasm32-core = []
wasm32-sdk = []

[dependencies]
blake2b_simd.workspace = true
borsh.workspace = true
bs58.workspace = true
cfg-if.workspace = true
faster-hex.workspace = true
hex.workspace = true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use faster hex. i think we should use the library everywhere instead of mixing them

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hexplay.workspace = true
indexmap.workspace = true
itertools.workspace = true
Expand All @@ -41,11 +47,11 @@ smallvec.workspace = true
thiserror.workspace = true
wasm-bindgen.workspace = true
workflow-wasm.workspace = true
js-sys.workspace = true

[dev-dependencies]
criterion.workspace = true
smallvec.workspace = true
hex.workspace = true
serde_json.workspace = true

[[bench]]
Expand Down
2 changes: 2 additions & 0 deletions crypto/txscript/errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ pub enum TxScriptError {
Serialization(#[from] SerializationError),
#[error("sig op count exceeds passed limit of {0}")]
ExceededSigOpLimit(u8),
#[error("expected push opcode")]
NonPushData,
}

#[derive(Error, PartialEq, Eq, Debug, Clone, Copy)]
Expand Down
19 changes: 19 additions & 0 deletions crypto/txscript/examples/script-viewer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use kaspa_consensus_core::{hashing::sighash::SigHashReusedValuesSync, tx::ValidatedTransaction};
use kaspa_txscript::{script_builder::ScriptBuilder, viewer::ScriptViewerOptions};

fn main() {
let script_vec = hex::decode("4130ef124590e4e6627078a658e2eb0b89fe4733f40d8cbfe0d077ae16bb90afb0a5f10e5693352e4b9d19d77a98fe75e395ce60988a0750ab8603a252c9c7290401412294d292317d03d1a5f49a8204c35486da84bbbea604209637e2bbfb5bbfabb36bcb37fc90aeb9836ed950a42b87382880fbd926b362cdbca16e9db9891918850141c2d76d4c64c9b8a8a64fa34a69f7cea953c4f0e564463226d931481ee1fbccafd7c20500a699fc8a10d01d03219d25944081750cdbba89e6a5a64b3224f58a5a014c875320b0a2f302b97271d6d1f20f2168e8b86b037d42a52aaf7ca959bea8a8bbf859a220e040996f44024491881ad4d2f59d4397a5a1f2e169c55624cb9509693fbb7a14204e518f0ecb51eef7db45042e441bb4d99f2c68277359bea369fcb7c80bee5b0120924013135715c9a8076141a33d6528a13fa2e816d3f006897b6d6c8b1da90fd754ae").unwrap();

// build the script from hex
let mut s = ScriptBuilder::new();
s.script_mut().extend_from_slice(&script_vec);

// print the hexadecimal form
println!("{}", s.hex_view(0, 30));

// print the human readable form
println!(
"{}",
s.string_view::<ValidatedTransaction, SigHashReusedValuesSync>(ScriptViewerOptions { contains_redeem_script: false })
);
}
2 changes: 2 additions & 0 deletions crypto/txscript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ extern crate core;
pub mod caches;
mod data_stack;
pub mod error;
pub mod multi_sig;
pub mod opcodes;
pub mod result;
pub mod script_builder;
pub mod script_class;
pub mod standard;
pub mod viewer;
#[cfg(feature = "wasm32-sdk")]
pub mod wasm;

Expand Down
63 changes: 63 additions & 0 deletions crypto/txscript/src/multi_sig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use crate::opcodes::to_small_int;
use kaspa_consensus_core::{hashing::sighash::SigHashReusedValues, tx::VerifiableTransaction};
use kaspa_txscript_errors::TxScriptError;

use crate::opcodes::OpCodeImplementation;

#[derive(Debug)]
pub struct MultiSigSchnorrScriptParameters {
pub required_signatures_count: u8,
pub signers_count: u8,
pub signers_pubkey: Vec<secp256k1::XOnlyPublicKey>,
}

/// Extract parameters from a standard multisig script (schnorr):
/// OP_m <pubkey1> ... <pubkeyn> OP_n OP_CHECKMULTISIG
pub fn get_schnorr_multisig_params<T: VerifiableTransaction, Reused: SigHashReusedValues>(
opcodes: &[Box<dyn OpCodeImplementation<T, Reused>>],
checkmultisig_index: usize,
) -> Result<MultiSigSchnorrScriptParameters, TxScriptError> {
let op_n = opcodes
.get(checkmultisig_index.checked_sub(1).ok_or_else(|| TxScriptError::InvalidState("index out of bounds".into()))?)
.ok_or_else(|| TxScriptError::InvalidState("index out of bounds".into()))?;

let n = to_small_int(op_n);
if n == 0 {
return Err(TxScriptError::InvalidPubKeyCount("n must be >= 1".into()));
}
let n_usize = n as usize;

let pubkeys_end = checkmultisig_index.checked_sub(1).ok_or_else(|| TxScriptError::InvalidState("index out of bounds".into()))?;
let pubkeys_start = pubkeys_end.checked_sub(n_usize).ok_or_else(|| TxScriptError::InvalidState("index out of bounds".into()))?;

let pubkeys_ops =
opcodes.get(pubkeys_start..pubkeys_end).ok_or_else(|| TxScriptError::InvalidState("index out of bounds".into()))?;

let mut pubkeys = Vec::with_capacity(n_usize);
for op in pubkeys_ops {
if !op.is_push_opcode() {
return Err(TxScriptError::InvalidOpcode("expected push opcode for pubkey".into()));
}
let data = op.get_data();

match data.len() {
32 => {
let pk = secp256k1::XOnlyPublicKey::from_slice(data).map_err(|_| TxScriptError::PubKeyFormat)?;
pubkeys.push(pk);
}
_ => return Err(TxScriptError::PubKeyFormat),
}
}

let op_m = opcodes
.get(pubkeys_start.checked_sub(1).ok_or_else(|| TxScriptError::InvalidState("index out of bounds".into()))?)
.ok_or_else(|| TxScriptError::InvalidState("index out of bounds".into()))?;

let m = to_small_int(op_m);

if m > n {
return Err(TxScriptError::InvalidSignatureCount("m must be <= n".into()));
}

Ok(MultiSigSchnorrScriptParameters { required_signatures_count: m, signers_count: n, signers_pubkey: pubkeys })
}
12 changes: 9 additions & 3 deletions crypto/txscript/src/opcodes/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ macro_rules! opcode_init {
}

macro_rules! opcode_impl {
($name: ident, $num: literal, $length: tt, $code: expr, $self:ident, $vm:ident ) => {
($name: ident, $str_rep: literal, $num: literal, $length: tt, $code: expr, $self:ident, $vm:ident ) => {
type $name = OpCode<$num>;

impl OpcodeSerialization for $name {
Expand All @@ -83,11 +83,17 @@ macro_rules! opcode_impl {
}

impl<T: VerifiableTransaction, Reused: SigHashReusedValues> OpCodeImplementation<T, Reused> for $name {}

impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, $str_rep)
}
}
}
}

macro_rules! opcode_list {
( $( opcode $(|$alias:ident|)? $name:ident<$num:literal, $length:tt>($self:ident, $vm:ident) $code: expr ) *) => {
( $( opcode $(|$alias:ident|)? $name:ident<$str_rep:literal, $num:literal, $length:tt>($self:ident, $vm:ident) $code: expr ) *) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think $name:ident can be used instead on str_rep directly and it will be consistent with enum

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's try this 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with macros, but compiler complains on introduced $name:ident with error duplicate matcher binding

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name:ident already there. You can reuse it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I tried re-using this "variable" (not sure the proper term for macros)

Image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No
You are trying to introduce new one with the same name. Reusing it means remove the newly added variable and use the first "name"

pub mod codes {
$(
#[allow(non_upper_case_globals)]
Expand All @@ -103,7 +109,7 @@ macro_rules! opcode_list {
}

$(
opcode_impl!($name, $num, $length, $code, $self, $vm);
opcode_impl!($name, $str_rep, $num, $length, $code, $self, $vm);

$(
#[allow(dead_code)]
Expand Down
Loading
Loading