Skip to content
Open
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
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
zig: ["0.16.0", "master"]
steps:
- uses: actions/checkout@v5
- uses: mlugg/setup-zig@v2
with:
version: ${{ matrix.zig }}
- name: Build
run: zig build --summary all
- name: Test (ReleaseFast, UB checks enabled)
run: zig build test -Doptimize=ReleaseFast --summary all
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
![bsvz](assets/banner.png)

![CI](https://github.com/samooth/bsvz/actions/workflows/ci.yml/badge.svg)

# bsvz

BSV foundation library for Zig.
Expand Down Expand Up @@ -43,7 +45,7 @@ Crypto, keys, script, transactions, SPV, BEEF, and broadcast. 27 BRC standards c

## Getting Started

**Requirements:** Zig `0.15.2`
**Requirements:** Zig `0.16.0` or newer — CI continuously verifies both the latest stable release and current master (`0.17` dev).

Fetch the dependency:

Expand Down Expand Up @@ -205,7 +207,16 @@ var traced = bsvz.script.thread.verifyScriptsTraced(.{
}));
defer traced.deinit(allocator);

try traced.writeDebug(std.io.getStdOut().writer());
// Zig 0.16: stdout writing requires an Io instance and a buffer.
var threaded = std.Io.Threaded.init(allocator, .{ .environ = .empty });
defer threaded.deinit();

var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(threaded.io(), &stdout_buffer);
const stdout = &stdout_writer.interface;

try traced.writeDebug(stdout);
try stdout.flush();
```

### Output serialization
Expand Down
67 changes: 39 additions & 28 deletions benchmarks/script_engine.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const engine = bsvz.script.engine;
const iterations = 10_000;
const fixture_allocator = std.heap.page_allocator;

fn bench(comptime name: []const u8, comptime run_fn: fn (std.mem.Allocator) void) void {
const sep_line: [90]u8 = @splat('=');

fn bench(io: std.Io, comptime name: []const u8, comptime run_fn: fn (std.mem.Allocator) void) void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();

Expand All @@ -17,14 +19,13 @@ fn bench(comptime name: []const u8, comptime run_fn: fn (std.mem.Allocator) void
run_fn(arena.allocator());
}

const start = std.time.nanoTimestamp();
const start = std.Io.Timestamp.now(io, .awake);
for (0..iterations) |_| {
_ = arena.reset(.retain_capacity);
run_fn(arena.allocator());
}
const end = std.time.nanoTimestamp();

const elapsed_ns: u64 = @intCast(end - start);
const end = std.Io.Timestamp.now(io, .awake);
const elapsed_ns: u64 = @intCast(end.nanoseconds - start.nanoseconds);
const per_iter_ns = elapsed_ns / iterations;
const per_iter_us = per_iter_ns / 1_000;
const ops_per_sec = if (per_iter_ns > 0) 1_000_000_000 / per_iter_ns else 0;
Expand All @@ -42,7 +43,7 @@ const branching_locking = [_]u8{ 0x51, 0x63, 0x52, 0x67, 0x53, 0x68, 0x52, 0x9c

const sha256_locking = [_]u8{
0x20,
} ++ [_]u8{0xab} ** 32 ++ [_]u8{
} ++ @as([32]u8, @splat(0xab)) ++ [_]u8{
0xa8,
0x82,
0x01, 0x20,
Expand All @@ -51,7 +52,7 @@ const sha256_locking = [_]u8{

const hash160_locking = [_]u8{
0x14,
} ++ [_]u8{0xcd} ** 20 ++ [_]u8{
} ++ @as([20]u8, @splat(0xcd)) ++ [_]u8{
0xa9,
0x82,
0x01, 0x14,
Expand Down Expand Up @@ -140,13 +141,13 @@ fn pairRunarArithmetic() ScriptPair {
return .{ .unlocking = &runar_arithmetic_unlocking, .locking = &runar_arithmetic_locking };
}

var p2pkh_fixture_once = std.once(initP2PKHFixture);
var p2pkh_fixture_initialized = false;
var p2pkh_fixture: PrevoutFixture = undefined;
var go_reference_p2pkh_fixture_once = std.once(initGoReferenceP2PKHFixture);
var go_reference_p2pkh_fixture_initialized = false;
var go_reference_p2pkh_fixture: ReferencePrevoutFixture = undefined;

fn initP2PKHFixture() void {
const key_bytes = [_]u8{0} ** 31 ++ [_]u8{1};
const key_bytes = @as([31]u8, @splat(0)) ++ [_]u8{1};
const private_key = bsvz.crypto.PrivateKey.fromBytes(key_bytes) catch unreachable;
const locking_script = Script.init(&p2pkh_locking);
const previous_satoshis: i64 = 100_000;
Expand All @@ -155,7 +156,7 @@ fn initP2PKHFixture() void {
var outputs = fixture_allocator.alloc(bsvz.transaction.Output, 1) catch unreachable;

inputs[0] = .{
.previous_outpoint = .{ .txid = .{ .bytes = [_]u8{0xaa} ** 32 }, .index = 0 },
.previous_outpoint = .{ .txid = .{ .bytes = @as([32]u8, @splat(0xaa)) }, .index = 0 },
.unlocking_script = Script.init(""),
.sequence = 0xffff_ffff,
};
Expand Down Expand Up @@ -211,7 +212,10 @@ fn initP2PKHFixture() void {
}

fn getP2PKHFixture() *const PrevoutFixture {
p2pkh_fixture_once.call();
if (!p2pkh_fixture_initialized) {
initP2PKHFixture();
p2pkh_fixture_initialized = true;
}
return &p2pkh_fixture;
}

Expand All @@ -232,7 +236,10 @@ fn initGoReferenceP2PKHFixture() void {
}

fn getGoReferenceP2PKHFixture() *const ReferencePrevoutFixture {
go_reference_p2pkh_fixture_once.call();
if (!go_reference_p2pkh_fixture_initialized) {
initGoReferenceP2PKHFixture();
go_reference_p2pkh_fixture_initialized = true;
}
return &go_reference_p2pkh_fixture;
}

Expand Down Expand Up @@ -333,20 +340,24 @@ pub fn main() !void {
_ = getP2PKHFixture();
_ = getGoReferenceP2PKHFixture();

var threaded = std.Io.Threaded.init(std.heap.page_allocator, .{ .environ = .empty });
defer threaded.deinit();
const io = threaded.io();

std.debug.print("\nbsvz script engine benchmarks ({d} iterations each)\n", .{iterations});
std.debug.print("{s}\n", .{"=" ** 90});

bench("arithmetic verify (2+3==5)", benchArithmetic);
bench("branching verify (if/else)", benchBranching);
bench("OP_SHA256 verify (32-byte input)", benchSha256);
bench("OP_HASH160 verify (20-byte input)", benchHash160);
bench("stack ops verify", benchStackOps);
bench("runar arithmetic verify", benchRunarArithmetic);
bench("P2PKH sighash only", benchP2PKHSighash);
bench("P2PKH secp verify only", benchP2PKHSecpVerify);
bench("P2PKH secp verify only (parsed)", benchP2PKHSecpVerifyParsed);
bench("P2PKH verify (synthetic fixture)", benchP2PKHVerify);
bench("P2PKH verify (Go reference tx)", benchGoReferenceP2PKHVerify);

std.debug.print("{s}\n", .{"=" ** 90});
std.debug.print("{s}\n", .{sep_line});

bench(io, "arithmetic verify (2+3==5)", benchArithmetic);
bench(io, "branching verify (if/else)", benchBranching);
bench(io, "OP_SHA256 verify (32-byte input)", benchSha256);
bench(io, "OP_HASH160 verify (20-byte input)", benchHash160);
bench(io, "stack ops verify", benchStackOps);
bench(io, "runar arithmetic verify", benchRunarArithmetic);
bench(io, "P2PKH sighash only", benchP2PKHSighash);
bench(io, "P2PKH secp verify only", benchP2PKHSecpVerify);
bench(io, "P2PKH secp verify only (parsed)", benchP2PKHSecpVerifyParsed);
bench(io, "P2PKH verify (synthetic fixture)", benchP2PKHVerify);
bench(io, "P2PKH verify (Go reference tx)", benchGoReferenceP2PKHVerify);

std.debug.print("{s}\n", .{sep_line});
}
15 changes: 15 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ pub fn build(b: *std.Build) void {
});
b.installArtifact(lib);

// C-compatible static library with exported symbols
const c_module = b.createModule(.{
.root_source_file = b.path("src/exports.zig"),
.target = target,
.optimize = optimize,
});
c_module.addImport("bsvz", root_module);

const c_lib = b.addLibrary(.{
.name = "bsvz_c",
.root_module = c_module,
.linkage = .static,
});
b.installArtifact(c_lib);

const lib_unit_tests = b.addTest(.{
.root_module = root_module,
});
Expand Down
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.{
.name = .bsvz,
.version = "0.1.0",
.minimum_zig_version = "0.15.2",
.minimum_zig_version = "0.16.0",
.fingerprint = 0x2f6970cff90bfbc2,
.dependencies = .{},
.paths = .{
Expand Down
11 changes: 10 additions & 1 deletion docs/examples/script-verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,16 @@ var traced = bsvz.script.thread.verifyScriptsTraced(
);
defer traced.deinit(allocator);

try traced.writeDebug(std.io.getStdOut().writer());
// Zig 0.16: stdout writing requires an Io instance and a buffer.
var threaded = std.Io.Threaded.init(allocator, .{ .environ = .empty });
defer threaded.deinit();

var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(threaded.io(), &stdout_buffer);
const stdout = &stdout_writer.interface;

try traced.writeDebug(stdout);
try stdout.flush();
```

Runnable examples:
Expand Down
5 changes: 4 additions & 1 deletion examples/gorillapool_arc_demo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ pub fn main() !void {
.api_url = "https://arc.gorillapool.io",
};

var result = try broadcaster.broadcast(allocator, &tx);
var threaded = std.Io.Threaded.init(allocator, .{ .environ = .empty });
defer threaded.deinit();

var result = try broadcaster.broadcast(allocator, threaded.io(), &tx);
defer result.deinit(allocator);

switch (result) {
Expand Down
14 changes: 11 additions & 3 deletions examples/prevout_trace_demo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn main() !void {
.inputs = &[_]bsvz.transaction.Input{
.{
.previous_outpoint = .{
.txid = .{ .bytes = [_]u8{0} ** 32 },
.txid = .{ .bytes = @as([32]u8, @splat(0)) },
.index = 0,
},
.unlocking_script = bsvz.script.Script.init(&[_]u8{}),
Expand All @@ -36,6 +36,14 @@ pub fn main() !void {
});
defer traced.deinit(allocator);

try traced.writeDebug(std.io.getStdOut().writer());
try std.io.getStdOut().writer().writeByte('\n');
var threaded = std.Io.Threaded.init(allocator, .{ .environ = .empty });
defer threaded.deinit();

var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(threaded.io(), &stdout_buffer);
const stdout = &stdout_writer.interface;

try traced.writeDebug(stdout);
try stdout.writeByte('\n');
try stdout.flush();
}
12 changes: 10 additions & 2 deletions examples/script_trace_demo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ pub fn main() !void {
}));
defer traced.deinit(allocator);

try traced.writeDebug(std.io.getStdOut().writer());
try std.io.getStdOut().writer().writeByte('\n');
var threaded = std.Io.Threaded.init(allocator, .{ .environ = .empty });
defer threaded.deinit();

var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(threaded.io(), &stdout_buffer);
const stdout = &stdout_writer.interface;

try traced.writeDebug(stdout);
try stdout.writeByte('\n');
try stdout.flush();
}
Loading