-
Notifications
You must be signed in to change notification settings - Fork 778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Metro-Byzantium Working Branch #161
Changes from 60 commits
759d1c8
68a4e42
0d63b7f
a3ed328
c01dc1a
6b45b87
574221d
3bb73a2
7125fe8
ed0c97d
86fbca2
030c5fb
93c3088
c7ea4dd
a12732c
dbf8717
8b46e42
9f1e0c8
6ddcb6a
a83479b
a648dc1
2c65fc1
e6dde03
0060a42
13987e2
1a2d84d
9b6d9df
5aa403a
b712f30
89254ad
fd46227
27d9ed6
35226b7
a4887f8
0dd9f41
4aa2f54
c2b00a9
ce11c83
7498eb4
ab90781
18c74d4
f35ef3e
b2ce0a7
b68374c
1e77ce9
8a88d7f
497288f
e24908e
63f6cdb
3b288b5
5e9d115
b4b8a2f
a7179eb
5d7377f
9b4e205
0b2774a
a8f86f6
bc0ad30
73c0e8c
5ed376f
4a3c0f6
fba7652
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -298,6 +298,22 @@ module.exports = { | |
cb(null) | ||
}) | ||
}, | ||
RETURNDATASIZE: function (runState) { | ||
return utils.intToBuffer(runState.lastReturned.length) | ||
}, | ||
RETURNDATACOPY: function (memOffset, returnDataOffset, length, runState) { | ||
memOffset = utils.bufferToInt(memOffset) | ||
returnDataOffset = utils.bufferToInt(returnDataOffset) | ||
length = utils.bufferToInt(length) | ||
|
||
if (returnDataOffset + length > runState.lastReturned.length) { | ||
trap(ERROR.OUT_OF_GAS) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gasolin I just took this from the EIP specification, there There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all EVM exceptions have the same behavior (state changes are reverted and remaining gas is consumed). The error message only determines what gets printed in the EVM trace log, though it would be good to standardize that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cdetrio Makes a lot of sense |
||
} | ||
|
||
memStore(runState, memOffset, utils.toBuffer(runState.lastReturned), returnDataOffset, length, false) | ||
// sub the COPY fee | ||
subGas(runState, new BN(fees.copyGas.v).imuln(Math.ceil(length / 32))) | ||
}, | ||
GASPRICE: function (runState) { | ||
return utils.setLengthLeft(runState.gasPrice, 32) | ||
}, | ||
|
@@ -361,6 +377,9 @@ module.exports = { | |
}) | ||
}, | ||
SSTORE: function (key, val, runState, cb) { | ||
if (runState.static) { | ||
trap(ERROR.STATIC_STATE_CHANGE) | ||
} | ||
var stateManager = runState.stateManager | ||
var address = runState.address | ||
key = utils.setLengthLeft(key, 32) | ||
|
@@ -465,6 +484,10 @@ module.exports = { | |
LOG: function (memOffset, memLength) { | ||
var args = Array.prototype.slice.call(arguments, 0) | ||
var runState = args.pop() | ||
if (runState.static) { | ||
trap(ERROR.STATIC_STATE_CHANGE) | ||
} | ||
|
||
var topics = args.slice(2) | ||
topics = topics.map(function (a) { | ||
return utils.setLengthLeft(a, 32) | ||
|
@@ -487,6 +510,10 @@ module.exports = { | |
|
||
// '0xf0' range - closures | ||
CREATE: function (value, offset, length, runState, done) { | ||
if (runState.static) { | ||
trap(ERROR.STATIC_STATE_CHANGE) | ||
} | ||
|
||
value = new BN(value) | ||
offset = utils.bufferToInt(offset) | ||
length = utils.bufferToInt(length) | ||
|
@@ -519,13 +546,18 @@ module.exports = { | |
outOffset = utils.bufferToInt(outOffset) | ||
outLength = utils.bufferToInt(outLength) | ||
|
||
if (runState.static && !value.isZero()) { | ||
trap(ERROR.STATIC_STATE_CHANGE) | ||
} | ||
|
||
var data = memLoad(runState, inOffset, inLength) | ||
|
||
var options = { | ||
gasLimit: gasLimit, | ||
value: value, | ||
to: toAddress, | ||
data: data | ||
data: data, | ||
static: runState.static | ||
} | ||
|
||
var localOpts = { | ||
|
@@ -595,7 +627,8 @@ module.exports = { | |
gasLimit: gas, | ||
value: value, | ||
data: data, | ||
to: runState.address | ||
to: runState.address, | ||
static: runState.static | ||
} | ||
|
||
const localOpts = { | ||
|
@@ -620,7 +653,7 @@ module.exports = { | |
// load the code | ||
stateManager.getAccount(toAddress, function (err, account) { | ||
if (err) return done(err) | ||
if (utils.isPrecompiled(toAddress)) { | ||
if (runState._precompiled[toAddress.toString('hex')]) { | ||
options.compiled = true | ||
options.code = runState._precompiled[toAddress.toString('hex')] | ||
makeCall(runState, options, localOpts, done) | ||
|
@@ -652,7 +685,8 @@ module.exports = { | |
data: data, | ||
to: runState.address, | ||
caller: runState.caller, | ||
delegatecall: true | ||
delegatecall: true, | ||
static: runState.static | ||
} | ||
|
||
const localOpts = { | ||
|
@@ -668,7 +702,7 @@ module.exports = { | |
// load the code | ||
stateManager.getAccount(toAddress, function (err, account) { | ||
if (err) return done(err) | ||
if (utils.isPrecompiled(toAddress)) { | ||
if (runState._precompiled[toAddress.toString('hex')]) { | ||
options.compiled = true | ||
options.code = runState._precompiled[toAddress.toString('hex')] | ||
makeCall(runState, options, localOpts, done) | ||
|
@@ -682,13 +716,75 @@ module.exports = { | |
} | ||
}) | ||
}, | ||
STATICCALL: function (gasLimit, toAddress, inOffset, inLength, outOffset, outLength, runState, done) { | ||
var stateManager = runState.stateManager | ||
gasLimit = new BN(gasLimit) | ||
toAddress = utils.setLengthLeft(toAddress, 20) | ||
var value = new BN(0) | ||
inOffset = utils.bufferToInt(inOffset) | ||
inLength = utils.bufferToInt(inLength) | ||
outOffset = utils.bufferToInt(outOffset) | ||
outLength = utils.bufferToInt(outLength) | ||
|
||
var data = memLoad(runState, inOffset, inLength) | ||
|
||
var options = { | ||
gasLimit: gasLimit, | ||
value: value, | ||
to: toAddress, | ||
data: data, | ||
static: true | ||
} | ||
|
||
var localOpts = { | ||
inOffset: inOffset, | ||
inLength: inLength, | ||
outOffset: outOffset, | ||
outLength: outLength | ||
} | ||
|
||
stateManager.exists(toAddress, function (err, exists) { | ||
if (err) { | ||
done(err) | ||
return | ||
} | ||
|
||
stateManager.accountIsEmpty(toAddress, function (err, empty) { | ||
if (err) { | ||
done(err) | ||
return | ||
} | ||
|
||
try { | ||
checkCallMemCost(runState, options, localOpts) | ||
checkOutOfGas(runState, options) | ||
} catch (e) { | ||
done(e.error) | ||
return | ||
} | ||
|
||
makeCall(runState, options, localOpts, done) | ||
}) | ||
}) | ||
}, | ||
RETURN: function (offset, length, runState) { | ||
offset = utils.bufferToInt(offset) | ||
length = utils.bufferToInt(length) | ||
runState.returnValue = memLoad(runState, offset, length) | ||
}, | ||
REVERT: function (offset, length, runState) { | ||
offset = utils.bufferToInt(offset) | ||
length = utils.bufferToInt(length) | ||
|
||
runState.stopped = true | ||
runState.returnValue = runState.lastReturned = memLoad(runState, offset, length) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not necessary to set |
||
trap(ERROR.REVERT) | ||
}, | ||
// '0x70', range - other | ||
SELFDESTRUCT: function (selfdestructToAddress, runState, cb) { | ||
if (runState.static) { | ||
trap(ERROR.STATIC_STATE_CHANGE) | ||
} | ||
var stateManager = runState.stateManager | ||
var contract = runState.contract | ||
var contractAddress = runState.address | ||
|
@@ -870,11 +966,15 @@ function makeCall (runState, callOptions, localOpts, cb) { | |
callOptions.gasPrice = runState.gasPrice | ||
callOptions.block = runState.block | ||
callOptions.populateCache = false | ||
callOptions.static = callOptions.static || false | ||
callOptions.selfdestruct = runState.selfdestruct | ||
|
||
// increment the runState.depth | ||
callOptions.depth = runState.depth + 1 | ||
|
||
// empty the return data buffer | ||
runState.lastReturned = new Buffer([]) | ||
|
||
// check if account has enough ether | ||
// Note: in the case of delegatecall, the value is persisted and doesn't need to be deducted again | ||
if (runState.depth >= fees.stackLimit.v || (callOptions.delegatecall !== true && new BN(runState.contract.balance).lt(callOptions.value))) { | ||
|
@@ -906,12 +1006,25 @@ function makeCall (runState, callOptions, localOpts, cb) { | |
// this should always be safe | ||
runState.gasLeft.isub(results.gasUsed) | ||
|
||
if (!results.vm.exceptionError) { | ||
// save results to memory | ||
if (results.vm.return) { | ||
memStore(runState, localOpts.outOffset, results.vm.return, 0, localOpts.outLength, false) | ||
// save results to memory | ||
if (results.vm.return && (!results.vm.exceptionError || results.vm.exceptionError === ERROR.REVERT)) { | ||
memStore(runState, localOpts.outOffset, results.vm.return, 0, localOpts.outLength, false) | ||
|
||
if (results.vm.exceptionError === ERROR.REVERT && runState.opName === 'CREATE') { | ||
runState.lastReturned = results.vm.return | ||
} | ||
|
||
switch (runState.opName) { | ||
case 'CALL': | ||
case 'CALLCODE': | ||
case 'DELEGATECALL': | ||
case 'STATICCALL': | ||
runState.lastReturned = results.vm.return | ||
break | ||
} | ||
} | ||
|
||
if (!results.vm.exceptionError) { | ||
// update stateRoot on current contract | ||
runState.stateManager.getAccount(runState.address, function (err, account) { | ||
if (err) return cb(err) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, also
isPrecompiled()
must be updated in ethereumjs-util.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that we're still using ethereumjs-util v4.5, not the latest v5.0