Skip to content

Commit

Permalink
fead(opcodes): Add opcode OP_HASH256 (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
KhairallahA authored Oct 7, 2024
1 parent bf53548 commit add860c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ pie showData
| opSha1 | 0xa7 || The input is hashed using SHA-1. |
| 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. |
| 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. |
| opCheckSig | 0xac || The entire transaction's outputs, inputs, and script are hashed. The signature used by opCHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. |
| opCheckSigVerify | 0xad || Same as opCHECKSIG, but opVERIFY is executed afterward. |
Expand Down
58 changes: 58 additions & 0 deletions src/script/engine.zig
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ pub const Engine = struct {
Opcode.OP_RIPEMD160 => try self.opRipemd160(),
Opcode.OP_SHA256 => try self.opSha256(),
Opcode.OP_HASH160 => try self.opHash160(),
Opcode.OP_HASH256 => try self.opHash256(),
Opcode.OP_CHECKSIG => try self.opCheckSig(),
Opcode.OP_NIP => try self.opNip(),
Opcode.OP_OVER => try self.opOver(),
Expand Down Expand Up @@ -486,6 +487,18 @@ pub const Engine = struct {
try self.stack.pushByteArray(&hash_160);
}

/// OP_HASH256: The input is hashed two times with SHA-256.
fn opHash256(self: *Engine) EngineError!void {
const data = try self.stack.pop();
defer self.allocator.free(data);

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

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

/// OP_CHECKSIG: Verify a signature
///
/// # Returns
Expand Down Expand Up @@ -537,6 +550,51 @@ pub const Engine = struct {
}
};

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

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

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

try engine.execute();

const hex_string = "9C12CFDC04C74584D787AC3D23772132C18524BC7AB28DEC4219B8FC5B425F70";
var expected_output: [hex_string.len / 2]u8 = undefined;
_ = try std.fmt.hexToBytes(&expected_output, hex_string);

try std.testing.expectEqual(1, engine.stack.len());
try std.testing.expectEqualSlices(u8, &expected_output, try engine.stack.peek(0));
}

test "Script execution - OP_HASH256 (double)" {
const allocator = std.testing.allocator;

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

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

try engine.execute();

const hex_string = "26AA6C7A9B46E9C409F09C179F7DEFF54F7AF5571D38DE5E5D9BA3932B91F55B";
var expected_output: [hex_string.len / 2]u8 = undefined;
_ = try std.fmt.hexToBytes(&expected_output, hex_string);

try std.testing.expectEqual(1, engine.stack.len());
try std.testing.expectEqualSlices(u8, &expected_output, try engine.stack.peek(0));
}

// Testing SHA1 against known vectors
test "opSha1 function test" {
const test_cases = [_]struct {
Expand Down

0 comments on commit add860c

Please sign in to comment.