Skip to content

Commit 2f33a01

Browse files
committed
Implement CODESIZE and CODECOPY
1 parent f54214b commit 2f33a01

6 files changed

Lines changed: 90 additions & 4 deletions

File tree

packages/evm/src/Memory.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class Memory {
1515
}
1616

1717
getBytes (offset: number, length: number) {
18-
this.onMemoryAccess(offset, length)
18+
this.useGasForAccess(offset, length)
1919
if (length === 0) {
2020
return []
2121
}
@@ -24,7 +24,7 @@ export class Memory {
2424
}
2525

2626
setBytes (offset: number, bytes: Byte[]) {
27-
this.onMemoryAccess(offset, bytes.length)
27+
this.useGasForAccess(offset, bytes.length)
2828
if (bytes.length === 0) {
2929
return
3030
}
@@ -34,7 +34,7 @@ export class Memory {
3434
}
3535
}
3636

37-
private onMemoryAccess (offset: number, length: number) {
37+
useGasForAccess (offset: number, length: number) {
3838
if (length === 0) {
3939
return
4040
}

packages/evm/src/opcodes/code.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ExecutionContext } from '../ExecutionContext'
2+
import { GasCost } from './gasCosts'
3+
import { Bytes32 } from '../Bytes32'
4+
import { Byte } from '../Byte'
5+
6+
export function opCODESIZE (ctx: ExecutionContext) {
7+
ctx.useGas(GasCost.BASE)
8+
ctx.stack.push(Bytes32.fromNumber(ctx.message.code.length))
9+
}
10+
11+
export function opCODECOPY (ctx: ExecutionContext) {
12+
const memoryOffset = ctx.stack.pop().toUnsignedNumber()
13+
const codeOffset = ctx.stack.pop().toUnsignedNumber()
14+
const memorySize = ctx.stack.pop().toUnsignedNumber()
15+
16+
ctx.useGas(GasCost.VERYLOW + GasCost.COPY * Math.ceil(memorySize / 32))
17+
// we subtract the gas early in case of OutOfGas
18+
ctx.memory.useGasForAccess(memoryOffset, memorySize)
19+
20+
const code = ctx.message.code.slice(codeOffset, codeOffset + memorySize)
21+
while (code.length < memorySize) {
22+
code.push(0x00 as Byte)
23+
}
24+
ctx.memory.setBytes(memoryOffset, code)
25+
}

packages/evm/src/opcodes/gasCosts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export const GasCost = {
22
ZERO: 0,
33
BASE: 2,
44
VERYLOW: 3,
5+
COPY: 3,
56
LOW: 5,
67
MID: 8,
78
HIGH: 10,

packages/evm/src/opcodes/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { makeOpDUP, makeOpSWAP, opPOP } from './stack'
3939
import { opMSIZE, opMLOAD, opMSTORE, opMSTORE8 } from './memory'
4040
import { opSSTORE, opSLOAD } from './storage'
4141
import { Byte } from '../Byte'
42+
import { opCODESIZE, opCODECOPY } from './code'
4243

4344
export { opUnreachable } from './invalid'
4445
export { makeOpPUSH } from './stack'
@@ -76,6 +77,8 @@ const OP_CODES: Record<number, Opcode | undefined> = {
7677
0x1b: opSHL,
7778
0x1c: opSHR,
7879
0x1d: opSAR,
80+
0x38: opCODESIZE,
81+
0x39: opCODECOPY,
7982
0x50: opPOP,
8083
0x51: opMLOAD,
8184
0x52: opMSTORE,

packages/evm/test/helpers/executeAssembly.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function executeAssembly (
2929
return executeCode({ ...DEFAULT_MESSAGE, ...params, code }, state)
3030
}
3131

32-
function assemblyToBytecode (code: string): Byte[] {
32+
export function assemblyToBytecode (code: string): Byte[] {
3333
const instructions = code.trim().split(/\s+/)
3434
const result: Byte[] = []
3535
for (const instruction of instructions) {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {
2+
expectStorage,
3+
Int256,
4+
expectGas,
5+
expectReturn,
6+
assemblyToBytecode,
7+
memoryGas,
8+
expectUnderflow,
9+
} from '../helpers'
10+
import { GasCost } from '../../src/opcodes'
11+
12+
describe('CODESIZE opcode', () => {
13+
it('returns the code size of the current environment', () => {
14+
expectStorage('CODESIZE PUSH1 00 SSTORE', {
15+
[Int256.of(0)]: Int256.of(4),
16+
})
17+
})
18+
19+
it(`costs ${GasCost.BASE} gas`, () => {
20+
expectGas('CODESIZE', GasCost.BASE)
21+
})
22+
})
23+
24+
describe('CODECOPY opcode', () => {
25+
it('copies the code to the memory', () => {
26+
const assembly = `
27+
PUSH1 0C
28+
PUSH1 00
29+
PUSH1 42
30+
CODECOPY
31+
PUSH1 0C
32+
PUSH1 42
33+
RETURN
34+
`
35+
expectReturn(assembly, assemblyToBytecode(assembly))
36+
})
37+
38+
it('uses a formula to calculate gas cost', () => {
39+
const assembly = `
40+
PUSH1 42
41+
PUSH1 00
42+
PUSH1 69
43+
CODECOPY
44+
`
45+
const gas = (
46+
GasCost.VERYLOW * 3 +
47+
GasCost.VERYLOW +
48+
GasCost.COPY * Math.ceil(0x42 / 32) +
49+
memoryGas(0x69 + 0x42)
50+
)
51+
expectGas(assembly, gas)
52+
})
53+
54+
it('can cause stack underflow', () => {
55+
expectUnderflow('CODECOPY', 3)
56+
})
57+
})

0 commit comments

Comments
 (0)