Skip to content

Commit

Permalink
feat(opcodes): ophash160 (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
supreme2580 authored Sep 27, 2024
1 parent 698dffd commit bfeec7a
Showing 1 changed file with 59 additions and 14 deletions.
73 changes: 59 additions & 14 deletions src/script/engine.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const EngineError = @import("lib.zig").EngineError;
const sha1 = std.crypto.hash.Sha1;
const ripemd160 = @import("bitcoin-primitives").hashes.Ripemd160;
const Sha256 = std.crypto.hash.sha2.Sha256;
const hash160 = @import("bitcoin-primitives").hashes.Hash160;
/// Engine is the virtual machine that executes Bitcoin scripts
pub const Engine = struct {
/// The script being executed
Expand Down Expand Up @@ -278,7 +279,7 @@ pub const Engine = struct {
const value = try self.stack.pop();
try self.alt_stack.pushElement(value);
}

/// OP_FROMALTSTACK: Puts the value onto the top of the main stack, and removes it from the alt stack.
fn opFromAltStack(self: *Engine) EngineError!void {
const value = try self.alt_stack.pop();
Expand Down Expand Up @@ -371,13 +372,13 @@ pub const Engine = struct {
fn opRoll(self: *Engine) !void {
const n = try self.stack.popInt();

const index : usize = @intCast(n);
const index: usize = @intCast(n);
if (index >= self.stack.items.items.len) {
return error.StackUnderflow;
return error.StackUnderflow;
}

const actualIndex = self.stack.items.items.len - 1 - index;

// Use orderedRemove to get the item
const value = self.stack.items.orderedRemove(actualIndex);

Expand Down Expand Up @@ -453,11 +454,16 @@ pub const Engine = struct {
/// # Returns
/// - `EngineError`: If an error occurs during execution
fn opHash160(self: *Engine) !void {
_ = try self.stack.pop();
// For now, just set the hash to a dummy value
const hash: [20]u8 = [_]u8{0x00} ** 20;
// TODO: Implement SHA256 and RIPEMD160
try self.stack.pushByteArray(&hash);
const data = try self.stack.pop();
defer self.allocator.free(data);

var hash: [Sha256.digest_length]u8 = undefined;
Sha256.hash(data, &hash, .{});

var hash_160: [ripemd160.digest_length]u8 = undefined;
ripemd160.hash(&hash, &hash_160, .{});

try self.stack.pushByteArray(&hash_160);
}

/// OP_CHECKSIG: Verify a signature
Expand All @@ -475,7 +481,7 @@ pub const Engine = struct {
}

/// OP_PICK: The item idx back in the stack is copied to the top.
///
///
/// # Returns
/// - `EngineError`: If an error occurs during execution
fn opPick(self: *Engine) !void {
Expand Down Expand Up @@ -670,7 +676,7 @@ test "Script execution - OP_1 OP_2 OP_3 OP_4 OP_2OVER" {
const allocator = std.testing.allocator;

// Simple script: OP_1 OP_2 OP_3 OP_4 OP_2OVER
const script_bytes = [_]u8{
const script_bytes = [_]u8{
Opcode.OP_1.toBytes(),
Opcode.OP_2.toBytes(),
Opcode.OP_3.toBytes(),
Expand Down Expand Up @@ -704,7 +710,7 @@ test "Script execution - OP_1 OP_2 OP_3 OP_4 OP_2SWAP" {
const allocator = std.testing.allocator;

// Simple script: OP_1 OP_2 OP_3 OP_4 OP_2SWAP
const script_bytes = [_]u8{
const script_bytes = [_]u8{
Opcode.OP_1.toBytes(),
Opcode.OP_2.toBytes(),
Opcode.OP_3.toBytes(),
Expand Down Expand Up @@ -822,7 +828,7 @@ test "Script execution - OP_PICK" {
const allocator = std.testing.allocator;

// Simple script: OP_1 OP_2 OP_3 OP_2 OP_PICK
const script_bytes = [_]u8{
const script_bytes = [_]u8{
Opcode.OP_1.toBytes(),
Opcode.OP_2.toBytes(),
Opcode.OP_3.toBytes(),
Expand Down Expand Up @@ -1064,3 +1070,42 @@ test "Script execution - OP_SHA256" {
Sha256.hash(&[_]u8{1}, &expected_hash, .{});
try std.testing.expectEqualSlices(u8, expected_hash[0..], hash_bytes);
}

test "Script execution = OP_HASH160" {
const test_cases = [_]struct {
input: []const u8,
expected: []const u8,
}{
.{ .input = "hello", .expected = "b6a9c8c230722b7c748331a8b450f05566dc7d0f" },
.{ .input = "blockchain", .expected = "755f6f4af6e11c5cf642f0ed6ecda89d8619cee7" },
.{ .input = "abc", .expected = "bb1be98c142444d7a56aa3981c3942a978e4dc33" },
.{ .input = "bitcoin", .expected = "6b2904910f9b40b2244eed93a7b8d992b22f8d32" },
};

for (test_cases) |case| {
const allocator = std.testing.allocator;

const script_bytes = [_]u8{Opcode.OP_HASH160.toBytes()};
const script = Script.init(&script_bytes);

var engine = Engine.init(allocator, script, .{});
defer engine.deinit();

// Push the input onto the stack
try engine.stack.pushByteArray(case.input);

// Call opHash160
try engine.execute();

// Pop the result from the stack
const result = try engine.stack.pop();
defer engine.allocator.free(result); // Free the result after use

// Convert expected hash to bytes
var expected_output: [ripemd160.digest_length]u8 = undefined;
_ = try std.fmt.hexToBytes(&expected_output, case.expected);

// Compare the result with the expected hash
try std.testing.expectEqualSlices(u8, &expected_output, result);
}
}

0 comments on commit bfeec7a

Please sign in to comment.