import { describe, it, expect } from 'vitest';
import { mcopy } from './0x5e_MCOPY.js';
describe('MCOPY (0x5e)', () => {
it('copies memory from source to destination', () => {
const frame = createFrame();
for (let i = 0; i < 32; i++) {
frame.memory.set(i, i + 1);
}
frame.stack.push(32n); // len
frame.stack.push(0n); // src
frame.stack.push(64n); // dest
expect(mcopy(frame)).toBeNull();
for (let i = 0; i < 32; i++) {
expect(frame.memory.get(64 + i)).toBe(i + 1);
}
expect(frame.pc).toBe(1);
});
it('handles zero-length copy', () => {
const frame = createFrame();
frame.stack.push(0n); // len = 0
frame.stack.push(0n); // src
frame.stack.push(0n); // dest
expect(mcopy(frame)).toBeNull();
expect(frame.memorySize).toBe(0); // No expansion
expect(frame.gasRemaining).toBe(999997n); // Only base gas
});
it('handles forward overlap correctly', () => {
const frame = createFrame();
for (let i = 0; i < 64; i++) {
frame.memory.set(i, i);
}
frame.stack.push(32n); // len
frame.stack.push(0n); // src
frame.stack.push(16n); // dest (overlap)
expect(mcopy(frame)).toBeNull();
for (let i = 0; i < 32; i++) {
expect(frame.memory.get(16 + i)).toBe(i);
}
});
it('handles backward overlap correctly', () => {
const frame = createFrame();
for (let i = 0; i < 64; i++) {
frame.memory.set(16 + i, i + 100);
}
frame.stack.push(32n); // len
frame.stack.push(16n); // src
frame.stack.push(0n); // dest (backward overlap)
expect(mcopy(frame)).toBeNull();
for (let i = 0; i < 32; i++) {
expect(frame.memory.get(i)).toBe(i + 100);
}
});
it('charges correct gas for copy', () => {
const frame = createFrame({ gasRemaining: 1000n, memorySize: 128 });
frame.stack.push(32n); // 1 word
frame.stack.push(0n);
frame.stack.push(64n);
expect(mcopy(frame)).toBeNull();
// Base: 3, Copy: 1 word * 3 = 3
expect(frame.gasRemaining).toBe(994n);
});
it('returns InvalidOpcode before Cancun', () => {
const frame = createFrame();
frame.evm.hardfork = 'Shanghai';
frame.stack.push(32n);
frame.stack.push(0n);
frame.stack.push(0n);
expect(mcopy(frame)).toEqual({ type: "InvalidOpcode" });
});
it('returns OutOfGas when insufficient', () => {
const frame = createFrame({ gasRemaining: 2n });
frame.stack.push(32n);
frame.stack.push(0n);
frame.stack.push(0n);
expect(mcopy(frame)).toEqual({ type: "OutOfGas" });
});
it('returns StackUnderflow with only 2 items', () => {
const frame = createFrame();
frame.stack.push(0n);
frame.stack.push(0n);
expect(mcopy(frame)).toEqual({ type: "StackUnderflow" });
});
});