Skip to content

Commit

Permalink
Varun/Added 6 Opcodes (#25)
Browse files Browse the repository at this point in the history
* added 6 opcodes

* update:added tests

* clean up

* failing CI test fix

* fixed:CI build fail
  • Loading branch information
varun-doshi authored Sep 10, 2024
1 parent cf4c547 commit 7ae5693
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
zig-out

.bitcoin
.vscode
227 changes: 225 additions & 2 deletions src/script/engine.zig
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub const Engine = struct {
/// - `EngineError`: If an error occurs during execution
fn executeOpcode(self: *Engine, opcode: u8) !void {
self.log("Executing opcode: 0x{x:0>2}\n", .{opcode});
switch (opcode) {
try switch (opcode) {
0x00...0x4b => try self.pushData(opcode),
0x4c => try self.opPushData1(),
0x4d => try self.opPushData2(),
Expand All @@ -112,6 +112,12 @@ pub const Engine = struct {
0x61 => try self.opNop(),
0x69 => try self.opVerify(),
0x6a => try self.opReturn(),
0x6d => try self.op2Drop(),
0x6e => try self.op2Dup(),
0x6f => try self.op3Dup(),
0x73 => self.opIfDup(),
0x74 => self.opDepth(),
0x75 => try self.opDrop(),
0x76 => try self.opDup(),
0x87 => try self.opEqual(),
0x88 => try self.opEqualVerify(),
Expand All @@ -138,7 +144,7 @@ pub const Engine = struct {
0xa9 => try self.opHash160(),
0xac => try self.opCheckSig(),
else => return error.UnknownOpcode,
}
};
}

// Opcode implementations
Expand Down Expand Up @@ -234,6 +240,9 @@ pub const Engine = struct {
_ = self;
}

/// OP_IF: If the top stack value is not False, the statements are executed. The top stack value is removed.
///
/// # Returns
/// OP_VERIFY: Verify the top stack value
///
/// # Returns
Expand All @@ -255,6 +264,91 @@ pub const Engine = struct {
return error.EarlyReturn;
}

/// OP_2DROP: Drops top 2 stack items
///
/// # Returns
/// - "EngineError.StackUnderflow": if initial stack length < 2
fn op2Drop(self: *Engine) !void {
if (self.stack.len() < 2) {
return error.StackUnderflow;
}
const a = try self.stack.pop();
const b = try self.stack.pop();

defer self.allocator.free(a);
defer self.allocator.free(b);
}

/// OP_2DUP: Duplicates top 2 stack item
///
/// # Returns
/// - "EngineError.StackUnderflow": if initial stack length < 2
fn op2Dup(self: *Engine) !void {
if (self.stack.len() < 2) {
return error.StackUnderflow;
}

const second_item = try self.stack.peek(0);
const first_item = try self.stack.peek(1);
try self.stack.pushByteArray(first_item);
try self.stack.pushByteArray(second_item);
}

/// OP_3DUP: Duplicates top 3 stack item
///
/// # Returns
/// - "EngineError.StackUnderflow": if initial stack length < 3
fn op3Dup(self: *Engine) !void {
if (self.stack.len() < 3) {
return error.StackUnderflow;
}
const third_item = try self.stack.peek(2);
const second_item = try self.stack.peek(1);
const first_item = try self.stack.peek(0);
try self.stack.pushByteArray(third_item);
try self.stack.pushByteArray(second_item);
try self.stack.pushByteArray(first_item);
}

/// OP_DEPTH: Puts the number of stack items onto the stack.
///
/// # Returns
/// - "EngineError.StackUnderflow": if initial stack length == 0
fn opDepth(self: *Engine) !void {
if (self.stack.len() == 0) {
return error.StackUnderflow;
}
const stack_length = self.stack.len();
const u8_stack_length: u8 = @intCast(stack_length);
try self.stack.pushByteArray(&[_]u8{u8_stack_length});
}

/// OP_IFDUP: If the top stack value is not 0, duplicate itp
///
/// # Returns
/// - "EngineError.StackUnderflow": if initial stack length < 1
fn opIfDup(self: *Engine) !void {
if (self.stack.len() < 1) {
return error.StackUnderflow;
}
const value = try self.stack.peek(0);
if (value.len != 1 or value[0] != 0) {
try self.stack.pushByteArray(value);
}
}

/// OP_DROP: Drops top stack item
///
/// # Returns
/// - "EngineError.StackUnderflow": if initial stack length < 1
fn opDrop(self: *Engine) !void {
if (self.stack.len() < 1) {
return error.StackUnderflow;
}
const a = try self.stack.pop();
defer self.allocator.free(a);
}

/// OP_DUP: Duplicate the top stack item
///
/// # Returns
Expand Down Expand Up @@ -360,3 +454,132 @@ test "Script execution - OP_RETURN" {
try std.testing.expectEqualSlices(u8, &[_]u8{1}, item);
}
}

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

// Simple script: OP_1 OP_1 OP_EQUAL
const script_bytes = [_]u8{ 0x51, 0x51, 0x51, 0x6d };
const script = Script.init(&script_bytes);

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

try engine.execute();

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 1), engine.stack.len());
}

test "Script execution - OP_1 OP_2 OP_2Dup" {
const allocator = std.testing.allocator;

// Simple script: OP_1 OP_1 OP_EQUAL
const script_bytes = [_]u8{ 0x51, 0x52, 0x6e };
const script = Script.init(&script_bytes);

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

try engine.execute();
const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element2 = try engine.stack.peek(2);
const element3 = try engine.stack.peek(3);

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 4), engine.stack.len());
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{1}, element1);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element2);
try std.testing.expectEqualSlices(u8, &[_]u8{1}, element3);
}

test "Script execution - OP_1 OP_2 OP_3 OP_4 OP_3Dup" {
const allocator = std.testing.allocator;

// Simple script: OP_1 OP_1 OP_EQUAL
const script_bytes = [_]u8{ 0x51, 0x52, 0x53, 0x54, 0x6f };
const script = Script.init(&script_bytes);

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

try engine.execute();
const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element2 = try engine.stack.peek(2);
const element3 = try engine.stack.peek(3);
const element4 = try engine.stack.peek(4);
const element5 = try engine.stack.peek(5);
const element6 = try engine.stack.peek(6);

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 7), engine.stack.len());
try std.testing.expectEqualSlices(u8, &[_]u8{4}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element1);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element2);
try std.testing.expectEqualSlices(u8, &[_]u8{4}, element3);
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element4);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element5);
try std.testing.expectEqualSlices(u8, &[_]u8{1}, element6);
}

test "Script execution - OP_1 OP_2 OP_IFDUP" {
const allocator = std.testing.allocator;

// Simple script: OP_1 OP_1 OP_EQUAL
const script_bytes = [_]u8{ 0x51, 0x52, 0x73 };
const script = Script.init(&script_bytes);

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

try engine.execute();
const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 3), engine.stack.len());
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element1);
}

test "Script execution - OP_1 OP_2 OP_DEPTH" {
const allocator = std.testing.allocator;

// Simple script: OP_1 OP_1 OP_EQUAL
const script_bytes = [_]u8{ 0x51, 0x52, 0x74 };
const script = Script.init(&script_bytes);

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

try engine.execute();
const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 3), engine.stack.len());
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element1);
}

test "Script execution - OP_1 OP_2 OP_DROP" {
const allocator = std.testing.allocator;

// Simple script: OP_1 OP_1 OP_EQUAL
const script_bytes = [_]u8{ 0x51, 0x52, 0x75 };
const script = Script.init(&script_bytes);

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

try engine.execute();
try std.testing.expectEqual(@as(usize, 1), engine.stack.len());

const element0 = try engine.stack.peek(0);

// Ensure the stack is empty after popping the result
try std.testing.expectEqualSlices(u8, &[_]u8{1}, element0);
}

0 comments on commit 7ae5693

Please sign in to comment.