diff --git a/src/script/engine.zig b/src/script/engine.zig index 1beda9d..d23da5f 100644 --- a/src/script/engine.zig +++ b/src/script/engine.zig @@ -153,6 +153,7 @@ pub const Engine = struct { Opcode.OP_CHECKSIG => try self.opCheckSig(), Opcode.OP_NIP => try self.opNip(), Opcode.OP_OVER => try self.opOver(), + Opcode.OP_ROLL => try self.opRoll(), Opcode.OP_PICK => try self.opPick(), Opcode.OP_SWAP => try self.opSwap(), Opcode.OP_TUCK => try self.opTuck(), @@ -351,6 +352,23 @@ pub const Engine = struct { try self.stack.pushByteArray(value); } + /// OP_ROLL: Pop the top stack element as N. Move the Nth stack element to the top. + fn opRoll(self: *Engine) !void { + const n = try self.stack.popInt(); + + const index : usize = @intCast(n); + if (index >= self.stack.items.items.len) { + 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); + + try self.stack.pushElement(value); + } + /// OP_SWAP: The top two items on the stack are swapped. fn opSwap(self: *Engine) !void { const first = try self.stack.pop(); @@ -820,6 +838,28 @@ test "Script execution OP_1 OP_2 OP_3 OP_OVER" { try std.testing.expectEqual(3, element1); } +test "Script execution OP_1 OP_2 OP_3 OP_2 OP_ROLL" { + const allocator = std.testing.allocator; + + // Simple script: OP_1 OP_2 OP_3 OP_2 OP_ROLL + const script_bytes = [_]u8{ Opcode.OP_1.toBytes(), Opcode.OP_2.toBytes(), Opcode.OP_3.toBytes(), Opcode.OP_2.toBytes(), Opcode.OP_ROLL.toBytes() }; + const script = Script.init(&script_bytes); + + var engine = Engine.init(allocator, script, .{}); + defer engine.deinit(); + + try engine.execute(); + try std.testing.expectEqual(3, engine.stack.len()); + + const element0 = try engine.stack.peekInt(0); + const element1 = try engine.stack.peekInt(1); + const element2 = try engine.stack.peekInt(2); + + try std.testing.expectEqual(1, element0); + try std.testing.expectEqual(3, element1); + try std.testing.expectEqual(2, element2); +} + test "Script execution OP_1 OP_2 OP_3 OP_SWAP" { const allocator = std.testing.allocator;