Skip to content

Commit

Permalink
Add OP_SHA256 (#72)
Browse files Browse the repository at this point in the history
Co-authored-by: Jonatan Chaverri <[email protected]>
  • Loading branch information
Jonatan-Chaverri and Jonatan Chaverri committed Sep 20, 2024
1 parent 15f24e2 commit d11b780
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ pie showData
| opWithin | 0xa5 || Returns 1 if x is within the specified range (left-inclusive), 0 otherwise. |
| opRipeMd160 | 0xa6 || The input is hashed using RIPEMD-160. |
| opSha1 | 0xa7 || The input is hashed using SHA-1. |
| opSha256 | 0xa8 | | The input is hashed using SHA-256. |
| opSha256 | 0xa8 | | The input is hashed using SHA-256. |
| opHash160 | 0xa9 || The input is hashed twice: first with SHA-256 and then with RIPEMD-160. |
| opHash256 | 0xaa || The input is hashed two times with SHA-256. |
| opCodeSeparator | 0xab || All of the signature checking words will only match signatures to the data after the most recently-executed opCODESEPARATOR. |
Expand Down
40 changes: 40 additions & 0 deletions src/script/engine.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const Opcode = @import("opcodes/constant.zig").Opcode;
const isUnnamedPushNDataOpcode = @import("opcodes/constant.zig").isUnnamedPushNDataOpcode;
const EngineError = @import("lib.zig").EngineError;
const ripemd160 = @import("bitcoin-primitives").hashes.Ripemd160;
const Sha256 = std.crypto.hash.sha2.Sha256;
/// Engine is the virtual machine that executes Bitcoin scripts
pub const Engine = struct {
/// The script being executed
Expand Down Expand Up @@ -144,6 +145,7 @@ pub const Engine = struct {
Opcode.OP_MAX => try arithmetic.opMax(self),
Opcode.OP_WITHIN => try arithmetic.opWithin(self),
Opcode.OP_RIPEMD160 => try self.opRipemd160(),
Opcode.OP_SHA256 => try self.opSha256(),
Opcode.OP_HASH160 => try self.opHash160(),
Opcode.OP_CHECKSIG => try self.opCheckSig(),
Opcode.OP_NIP => try self.opNip(),
Expand Down Expand Up @@ -381,6 +383,21 @@ pub const Engine = struct {
try self.opVerify();
}

/// OP_Sha256: The input is hashed with SHA-256.
///
/// # Returns
/// - `EngineError`: If an error occurs during execution
fn opSha256(self: *Engine) !void {
const arr = try self.stack.pop();
defer self.allocator.free(arr);

// Create a digest buffer to hold the hash result
var hash: [Sha256.digest_length]u8 = undefined;
Sha256.hash(arr, &hash, .{});

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

/// OP_HASH160: Hash the top stack item with SHA256 and RIPEMD160
///
/// # Returns
Expand Down Expand Up @@ -801,3 +818,26 @@ test "Script execution OP_RIPEMD160" {
try std.testing.expectEqualSlices(u8, &expected_output, result);
}
}

test "Script execution - OP_SHA256" {
const allocator = std.testing.allocator;

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

var engine = Engine.init(allocator, script, .{});
defer engine.deinit(); // Ensure engine is deinitialized and memory is freed

try engine.execute();

try std.testing.expectEqual(1, engine.stack.len());

const hash_bytes = try engine.stack.pop(); // Pop the result
defer engine.allocator.free(hash_bytes); // Free the popped byte array

try std.testing.expectEqual(Sha256.digest_length, hash_bytes.len);

var expected_hash: [Sha256.digest_length]u8 = undefined;
Sha256.hash(&[_]u8{1}, &expected_hash, .{});
try std.testing.expectEqualSlices(u8, expected_hash[0..], hash_bytes);
}

0 comments on commit d11b780

Please sign in to comment.