Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Varun/Added 6 Opcodes #25

Merged
merged 6 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
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
217 changes: 217 additions & 0 deletions src/script/engine.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 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,87 @@ 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;
}
_ = try self.stack.pop();
_ = try self.stack.pop();
}

/// 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;
}
_ = try self.stack.pop();
}

/// OP_DUP: Duplicate the top stack item
///
/// # Returns
Expand Down Expand Up @@ -360,3 +450,130 @@ 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();
const element0 = try engine.stack.peek(0);

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