Skip to content

Commit

Permalink
Implement CODESIZE and CODECOPY
Browse files Browse the repository at this point in the history
  • Loading branch information
sz-piotr committed Mar 5, 2020
1 parent 4327d4f commit f4b3f0b
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 4 deletions.
6 changes: 3 additions & 3 deletions packages/evm/src/Memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class Memory {
}

getBytes (offset: number, length: number) {
this.onMemoryAccess(offset, length)
this.useGasForAccess(offset, length)
if (length === 0) {
return []
}
Expand All @@ -24,7 +24,7 @@ export class Memory {
}

setBytes (offset: number, bytes: Byte[]) {
this.onMemoryAccess(offset, bytes.length)
this.useGasForAccess(offset, bytes.length)
if (bytes.length === 0) {
return
}
Expand All @@ -34,7 +34,7 @@ export class Memory {
}
}

private onMemoryAccess (offset: number, length: number) {
useGasForAccess (offset: number, length: number) {
if (length === 0) {
return
}
Expand Down
25 changes: 25 additions & 0 deletions packages/evm/src/opcodes/code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ExecutionContext } from '../ExecutionContext'
import { GasCost } from './gasCosts'
import { Bytes32 } from '../Bytes32'
import { Byte } from '../Byte'

export function opCODESIZE (ctx: ExecutionContext) {
ctx.useGas(GasCost.BASE)
ctx.stack.push(Bytes32.fromNumber(ctx.message.code.length))
}

export function opCODECOPY (ctx: ExecutionContext) {
const memoryOffset = ctx.stack.pop().toUnsignedNumber()
const codeOffset = ctx.stack.pop().toUnsignedNumber()
const memorySize = ctx.stack.pop().toUnsignedNumber()

ctx.useGas(GasCost.VERYLOW + GasCost.COPY * Math.ceil(memorySize / 32))
// we subtract the gas early in case of OutOfGas
ctx.memory.useGasForAccess(memoryOffset, memorySize)

const code = ctx.message.code.slice(codeOffset, codeOffset + memorySize)
while (code.length < memorySize) {
code.push(0x00 as Byte)
}
ctx.memory.setBytes(memoryOffset, code)
}
1 change: 1 addition & 0 deletions packages/evm/src/opcodes/gasCosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const GasCost = {
ZERO: 0,
BASE: 2,
VERYLOW: 3,
COPY: 3,
LOW: 5,
MID: 8,
HIGH: 10,
Expand Down
3 changes: 3 additions & 0 deletions packages/evm/src/opcodes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { makeOpDUP, makeOpSWAP, opPOP } from './stack'
import { opMSIZE, opMLOAD, opMSTORE, opMSTORE8 } from './memory'
import { opSSTORE, opSLOAD } from './storage'
import { Byte } from '../Byte'
import { opCODESIZE, opCODECOPY } from './code'

export { opUnreachable } from './invalid'
export { makeOpPUSH } from './stack'
Expand Down Expand Up @@ -76,6 +77,8 @@ const OP_CODES: Record<number, Opcode | undefined> = {
0x1b: opSHL,
0x1c: opSHR,
0x1d: opSAR,
0x38: opCODESIZE,
0x39: opCODECOPY,
0x50: opPOP,
0x51: opMLOAD,
0x52: opMSTORE,
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/test/helpers/executeAssembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function executeAssembly (
return executeCode({ ...DEFAULT_MESSAGE, ...params, code }, state)
}

function assemblyToBytecode (code: string): Byte[] {
export function assemblyToBytecode (code: string): Byte[] {
const instructions = code.trim().split(/\s+/)
const result: Byte[] = []
for (const instruction of instructions) {
Expand Down
57 changes: 57 additions & 0 deletions packages/evm/test/opcodes/code.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
expectStorage,
Int256,
expectGas,
expectReturn,
assemblyToBytecode,
memoryGas,
expectUnderflow,
} from '../helpers'
import { GasCost } from '../../src/opcodes'

describe('CODESIZE opcode', () => {
it('returns the code size of the current environment', () => {
expectStorage('CODESIZE PUSH1 00 SSTORE', {
[Int256.of(0)]: Int256.of(4),
})
})

it(`costs ${GasCost.BASE} gas`, () => {
expectGas('CODESIZE', GasCost.BASE)
})
})

describe('CODECOPY opcode', () => {
it('copies the code to the memory', () => {
const assembly = `
PUSH1 0C
PUSH1 00
PUSH1 42
CODECOPY
PUSH1 0C
PUSH1 42
RETURN
`
expectReturn(assembly, assemblyToBytecode(assembly))
})

it('uses a formula to calculate gas cost', () => {
const assembly = `
PUSH1 42
PUSH1 00
PUSH1 69
CODECOPY
`
const gas = (
GasCost.VERYLOW * 3 +
GasCost.VERYLOW +
GasCost.COPY * Math.ceil(0x42 / 32) +
memoryGas(0x69 + 0x42)
)
expectGas(assembly, gas)
})

it('can cause stack underflow', () => {
expectUnderflow('CODECOPY', 3)
})
})

0 comments on commit f4b3f0b

Please sign in to comment.