Skip to content

Commit

Permalink
a very big change
Browse files Browse the repository at this point in the history
- change project structure to use a cargo workspace, library is now in lib/
- optimize some string formatting
- add script conversion, bytes->script, asm->bytes and script, script->asm, etc
- add hex encode/decode utils
- add 3 different script formatting methods (space separated, newline separated and newline with indentation)
- copy index.html from the typescript version
- remove webcrypto warning as it is not needed anymore
- make web code
- use wasm pack (for now)
  • Loading branch information
antonilol committed Feb 8, 2024
1 parent 35d993f commit 07279e7
Show file tree
Hide file tree
Showing 33 changed files with 1,143 additions and 525 deletions.
24 changes: 7 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
[package]
name = "bitcoin-script-analyzer"
version = "0.1.0"
edition = "2021"
license = "MIT"
description = "Bitcoin script analyzer"
homepage = "https://github.com/antonilol/bitcoin-script-analyzer"
repository = "https://github.com/antonilol/bitcoin-script-analyzer"
keywords = ["bitcoin"]
categories = ["cryptography::cryptocurrencies"]

[features]
threads = []

[dependencies]
bitcoin_hashes = { version = "0.12.0", default-features = false }
time = { version = "0.3.22", features = ["formatting"] }
[workspace]
members = [
"lib",
"cli",
"web",
]
resolver = "2"

[profile.release]
lto = true
79 changes: 0 additions & 79 deletions cli/Cargo.lock

This file was deleted.

6 changes: 1 addition & 5 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,4 @@ version = "0.1.0"
edition = "2021"

[dependencies]
bitcoin-script-analyzer = { path = ".." }
hex = "0.4.3"

[profile.release]
lto = true
bitcoin-script-analyzer = { path = "../lib" }
13 changes: 6 additions & 7 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bitcoin_script_analyzer::{
analyze_script, parse_script, ScriptContext, ScriptRules, ScriptVersion,
analyze_script, util::decode_hex_in_place, OwnedScript, ScriptContext, ScriptRules,
ScriptVersion,
};

fn unwrap_both<T>(res: Result<T, T>) -> T {
Expand All @@ -14,12 +15,10 @@ pub fn main() {
.expect("missing argument \"script\"");

println!("hex: {script_hex}");
let script_bytes = hex::decode(script_hex).unwrap();
let script = parse_script(&script_bytes).unwrap();
println!("script:");
for a in &script {
println!("{a}");
}
let mut script_hex = script_hex.into_bytes();
let script_bytes = decode_hex_in_place(&mut script_hex).unwrap();
let script = OwnedScript::parse_from_bytes(script_bytes).unwrap();
println!("script:\n{script}");
println!();
let res = analyze_script(
&script,
Expand Down
17 changes: 17 additions & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "bitcoin-script-analyzer"
version = "0.1.0"
edition = "2021"
license = "MIT"
description = "Bitcoin script analyzer"
homepage = "https://github.com/antonilol/bitcoin-script-analyzer"
repository = "https://github.com/antonilol/bitcoin-script-analyzer"
keywords = ["bitcoin"]
categories = ["cryptography::cryptocurrencies"]

[features]
threads = []

[dependencies]
bitcoin_hashes = { version = "0.12.0", default-features = false }
time = { version = "0.3.22", features = ["formatting"] }
64 changes: 30 additions & 34 deletions src/analyzer.rs → lib/src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ use crate::{
script::{
convert::{decode_bool, decode_int, encode_bool_expr, encode_int_expr},
stack::Stack,
ScriptElem, ScriptSlice,
Script, ScriptElem,
},
script_error::ScriptError,
util::locktime::{
locktime_to_string, locktime_type_equals, LocktimeType, SEQUENCE_LOCKTIME_MASK,
SEQUENCE_LOCKTIME_TYPE_FLAG,
},
};
use core::fmt;
use core::fmt::{self, Write};

struct LocktimeRequirement {
exprs: Vec<Expr>,
Expand Down Expand Up @@ -49,21 +49,23 @@ impl LocktimeRequirement {
None => "unknown",
};

let tmp;
Some(format!(
"type: {}, minValue: {}{}",
type_,
min_value,
if !self.exprs.is_empty() {
format!(
", stack elements: {:?}",
tmp = format!(
", stack elements: {}",
self.exprs
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join("\n")
)
.join(", ")
);
&tmp
} else {
"".to_string()
""
}
))
}
Expand All @@ -78,16 +80,14 @@ struct AnalyzerResult {

impl fmt::Display for AnalyzerResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tmp;
let stack_size = self.stack_size;

let mut tmp;
let stack_items_str = if !self.spending_conditions.is_empty() {
tmp = format!(
"\n{}",
self.spending_conditions
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join("\n")
);
tmp = String::new();
for s in &self.spending_conditions {
write!(tmp, "\n{s}").unwrap();
}
&tmp
} else {
" none"
Expand All @@ -108,12 +108,11 @@ impl fmt::Display for AnalyzerResult {

write!(
f,
"Stack size: {}\n\
"Stack size: {stack_size}\n\
Stack item requirements:\
{stack_items_str}\n\
Locktime requirement: {locktime_str}\n\
Sequence requirement: {sequence_str}",
self.stack_size,
Sequence requirement: {sequence_str}"
)
}
}
Expand All @@ -133,7 +132,7 @@ type ThreadPool<'a, 'f> = &'f crate::threadpool::ThreadPool<'a>;
type ThreadPool<'a, 'f> = ();

pub fn analyze_script(
script: ScriptSlice<'_>,
script: &Script<'_>,
ctx: ScriptContext,
worker_threads: usize,
) -> Result<String, String> {
Expand All @@ -143,7 +142,7 @@ pub fn analyze_script(
"Feature \"threads\" disabled, set `worker_threads` to 0 or enable the feature"
);

for op in script {
for &op in &**script {
if let ScriptElem::Op(op) = op {
if op.is_disabled() {
return Err(format!(
Expand Down Expand Up @@ -178,7 +177,7 @@ pub fn analyze_script(
};

// TODO does not run on multiple threads yet
let results: Vec<_> = results
let mut results = results
.into_iter()
.filter_map(|mut a| {
a.calculate_locktime_requirements()
Expand All @@ -190,34 +189,31 @@ pub fn analyze_script(
spending_conditions: a.spending_conditions,
})
})
.collect();
.peekable();

if results.is_empty() {
if results.peek().is_none() {
return Err("Script is unspendable".to_string());
}

Ok(format!(
"Spending paths:\n\n{}",
results
.into_iter()
.map(|res| res.to_string())
.collect::<Vec<_>>()
.join("\n\n")
))
let mut s = String::from("Spending paths:");
for res in results {
write!(s, "\n\n{res}").unwrap();
}
Ok(s)
}

#[derive(Clone)]
pub struct ScriptAnalyzer<'a> {
stack: Stack,
altstack: Vec<Expr>,
spending_conditions: Vec<Expr>,
script: ScriptSlice<'a>,
script: &'a Script<'a>,
script_offset: usize,
cs: ConditionStack,
}

impl<'a> ScriptAnalyzer<'a> {
fn from_script(script: ScriptSlice<'a>) -> Self {
fn from_script(script: &'a Script<'a>) -> Self {
Self {
stack: Stack::new(),
altstack: Vec::new(),
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions src/context.rs → lib/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScriptVersion {
Legacy,
SegwitV0,
SegwitV1,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScriptRules {
ConsensusOnly,
All,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScriptContext {
pub version: ScriptVersion,
pub rules: ScriptRules,
Expand Down
10 changes: 9 additions & 1 deletion src/expr/bytes.rs → lib/src/expr/bytes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use core::{fmt, ops::Deref, ops::Index, slice::SliceIndex};
use core::{
fmt,
ops::{Deref, Index},
slice::SliceIndex,
};

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct BytesExprBox(Box<[u8]>);
Expand Down Expand Up @@ -45,6 +49,10 @@ impl BytesExpr {
unsafe { &*(bytes as *const [u8] as *const BytesExpr) }
}

pub fn new_mut(bytes: &mut [u8]) -> &mut Self {
unsafe { &mut *(bytes as *mut [u8] as *mut BytesExpr) }
}

pub fn len(&self) -> usize {
self.0.len()
}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/lib.rs → lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ pub mod util;
pub use crate::{
analyzer::analyze_script,
context::{ScriptContext, ScriptRules, ScriptVersion},
script::{convert as script_convert, parse_script},
script::{convert as script_convert, OwnedScript, ParseScriptError, Script, ScriptElem},
};
6 changes: 2 additions & 4 deletions src/opcode.rs → lib/src/opcode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::fmt;

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Opcode {
pub opcode: u8,
Expand All @@ -13,9 +13,7 @@ impl fmt::Display for Opcode {
}

macro_rules! opcodes {
(
$($k:ident:$v:literal,)*
) => {
($($k:ident: $v:literal),* $(,)?) => {
pub mod opcodes {
use super::Opcode;

Expand Down
Loading

0 comments on commit 07279e7

Please sign in to comment.