Skip to content

Commit a67918b

Browse files
authored
Merge pull request ethereumjs#378 from Agusx1211/master
Add emitFreeLogs option - Used for debugging/coverage
2 parents c33283c + bd72339 commit a67918b

File tree

5 files changed

+73
-3
lines changed

5 files changed

+73
-3
lines changed

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ VM Class, `new VM(opts)` creates a new VM object
5454
- `opts.hardfork` **[String][32]** hardfork rules to be used [default: 'byzantium', supported: 'byzantium' (will throw on unsupported)]
5555
- `opts.activatePrecompiles` **[Boolean][34]** create entries in the state tree for the precompiled contracts
5656
- `opts.allowUnlimitedContractSize` **[Boolean][34]** allows unlimited contract sizes while debugging. By setting this to `true`, the check for contract size limit of 24KB (see [EIP-170][35]) is bypassed. (default: `false`; ONLY set to `true` during debugging)
57+
- `opts.emitFreeLogs` **[Boolean][34]** Changes the behavior of the LOG opcode, the gas cost of the opcode becomes zero and calling it using STATICCALL won't throw. (default: `false`; ONLY set to `true` during debugging)
5758

5859
## vm.runBlock
5960

lib/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ VM.deps = {
3838
* @param {String} opts.hardfork hardfork rules to be used [default: 'byzantium', supported: 'byzantium' (will throw on unsupported)]
3939
* @param {Boolean} opts.activatePrecompiles create entries in the state tree for the precompiled contracts
4040
* @param {Boolean} opts.allowUnlimitedContractSize allows unlimited contract sizes while debugging. By setting this to `true`, the check for contract size limit of 24KB (see [EIP-170](https://git.io/vxZkK)) is bypassed. (default: `false`; ONLY set to `true` during debugging)
41+
* @param {Boolean} opts.emitFreeLogs Changes the behavior of the LOG opcode, the gas cost of the opcode becomes zero and calling it using STATICCALL won't throw. (default: `false`; ONLY set to `true` during debugging)
4142
*/
4243
function VM (opts = {}) {
4344
this.opts = opts
@@ -63,6 +64,7 @@ function VM (opts = {}) {
6364
this.blockchain = opts.blockchain || fakeBlockchain
6465

6566
this.allowUnlimitedContractSize = opts.allowUnlimitedContractSize === undefined ? false : opts.allowUnlimitedContractSize
67+
this.emitFreeLogs = opts.emitFreeLogs === undefined ? false : opts.emitFreeLogs
6668

6769
// precompiled contracts
6870
this._precompiled = {}

lib/opcodes.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ const codes = {
160160
0xff: ['SELFDESTRUCT', 5000, 1, 0, false, true]
161161
}
162162

163-
module.exports = function (op, full) {
163+
module.exports = function (op, full, freeLogs) {
164164
var code = codes[op] ? codes[op] : ['INVALID', 0, 0, 0, false, false]
165165
var opcode = code[0]
166166

@@ -182,5 +182,11 @@ module.exports = function (op, full) {
182182
}
183183
}
184184

185+
if (freeLogs) {
186+
if (opcode === 'LOG') {
187+
code[1] = 0
188+
}
189+
}
190+
185191
return {name: opcode, opcode: op, fee: code[1], in: code[2], out: code[3], dynamic: code[4], async: code[5]}
186192
}

lib/runCode.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ module.exports = function (opts, cb) {
127127

128128
function iterateVm (done) {
129129
var opCode = runState.code[runState.programCounter]
130-
var opInfo = lookupOpInfo(opCode)
130+
var opInfo = lookupOpInfo(opCode, false, self.emitFreeLogs)
131131
var opName = opInfo.name
132132
var opFn = opFns[opName]
133133

@@ -145,7 +145,7 @@ module.exports = function (opts, cb) {
145145
var eventObj = {
146146
pc: runState.programCounter,
147147
gasLeft: runState.gasLeft,
148-
opcode: lookupOpInfo(opCode, true),
148+
opcode: lookupOpInfo(opCode, true, self.emitFreeLogs),
149149
stack: runState.stack,
150150
depth: runState.depth,
151151
address: runState.address,
@@ -230,6 +230,12 @@ module.exports = function (opts, cb) {
230230
})
231231
}
232232

233+
// if opcode is log and emitFreeLogs is enabled, remove static context
234+
let prevStatic = runState.static
235+
if (self.emitFreeLogs && opName === 'LOG') {
236+
runState.static = false
237+
}
238+
233239
try {
234240
// run the opcode
235241
var result = opFn.apply(null, args)
@@ -242,6 +248,9 @@ module.exports = function (opts, cb) {
242248
}
243249
}
244250

251+
// restore previous static context
252+
runState.static = prevStatic
253+
245254
// save result to the stack
246255
if (result !== undefined) {
247256
if (retNum !== 1) {

tests/api/freeLogs.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const tape = require('tape')
2+
const VM = require('../../lib/index')
3+
4+
/*
5+
contract Contract1 {
6+
event Event();
7+
function() external {
8+
emit Event();
9+
}
10+
}
11+
*/
12+
13+
const code = Buffer.from('6080604052348015600f57600080fd5b506040517f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e90600090a10000a165627a7a72305820f80265dc41ca5376abe548a02070b68b91119b77dc54c76563b9c19a758cf26f0029', 'hex')
14+
15+
tape('VM with free logs', async (t) => {
16+
t.test('should run code without charging for log opcode', async (st) => {
17+
const vm = new VM({ emitFreeLogs: true })
18+
vm.runCode({
19+
code: code,
20+
gasLimit: 810
21+
}, function (err, val) {
22+
st.notOk(err)
23+
st.ok(val.runState.gasLeft >= 0x177, 'should expend less gas')
24+
st.ok(val.logs.length === 1, 'should emit event')
25+
st.end()
26+
})
27+
})
28+
t.test('should emit event on static context', async (st) => {
29+
const vm = new VM({ emitFreeLogs: true })
30+
vm.runCode({
31+
code: code,
32+
gasLimit: 24000,
33+
static: true
34+
}, function (err, val) {
35+
st.notOk(err)
36+
st.ok(val.logs.length === 1, 'should emit event')
37+
st.end()
38+
})
39+
})
40+
t.test('should not emit event on static context if flag is not set', async (st) => {
41+
const vm = new VM()
42+
vm.runCode({
43+
code: code,
44+
gasLimit: 24000,
45+
static: true
46+
}, function (err, val) {
47+
st.ok(err)
48+
st.ok(val.logs.length === 0, 'should emit zero events')
49+
st.end()
50+
})
51+
})
52+
})

0 commit comments

Comments
 (0)