Skip to content
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

Merged
merged 62 commits into from
Sep 25, 2017
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
759d1c8
change test fork to metropolis
jwasinger Jul 28, 2017
68a4e42
Added Call1MB1024Calldepth test to the tests to skip (slow)
Jul 30, 2017
0d63b7f
Minor (removed name)
Jul 30, 2017
a3ed328
Added base files for bn128 curve operations and set precompiled contr…
Jul 31, 2017
c01dc1a
Moved precompiled contracts for alt_btn128 curve operations from addr…
Aug 2, 2017
6b45b87
add REVERT opcode/functionality
jwasinger Aug 5, 2017
574221d
Added debug option to StateTestRunner
Aug 7, 2017
3bb73a2
Add stack to the StateTest debug output
Aug 7, 2017
7125fe8
Added vmError checking to returnFee calculation and saveContract to f…
Aug 8, 2017
ed0c97d
Added memory to GeneralStateTestRunner debug output
Aug 8, 2017
86fbca2
Save results to memory after CALL ended with REVERT
Aug 8, 2017
030c5fb
downgrade tape to hide stack trace in test runner
cdetrio Aug 8, 2017
93c3088
EIP-211:implement OPCODE RETURNDATASIZE and RETURNDATACOPY
gasolin Aug 8, 2017
c7ea4dd
lint fix
gasolin Aug 8, 2017
a12732c
Fixed a bug in GeneralStateTestsRunner debug code
holgerd77 Aug 9, 2017
dbf8717
Fix overflow checking for RETURNDATACOPY
holgerd77 Aug 9, 2017
8b46e42
Switched test fork configuration to Byzantium
holgerd77 Aug 9, 2017
9f1e0c8
use ethereumjs-testing metro-byzantium branch
cdetrio Aug 9, 2017
6ddcb6a
Add STATICCALL opcode to opcode list, add basic code frame taken from…
holgerd77 Aug 23, 2017
a83479b
Added static flag to runState
holgerd77 Aug 23, 2017
a648dc1
Added STATICCALL restrictions for state changing opcodes, corrected m…
holgerd77 Aug 23, 2017
2c65fc1
Temporarily added static_ versions of slow tests to the test skip list
holgerd77 Aug 23, 2017
e6dde03
Pass static flag from runState to callOptions in CALL, CALLCODE and D…
holgerd77 Aug 23, 2017
0060a42
Added WIP Byzantium version of ethereumjs-block to package.json depen…
holgerd77 Aug 31, 2017
13987e2
Added the success case of new receipt status field (EIP 658)
holgerd77 Sep 1, 2017
1a2d84d
Added modexp precompile code (EIP198) (courtesy of @axic)
holgerd77 Sep 4, 2017
9b6d9df
Removed ethereumjs-util isPrecompile usage to reduce dependency when …
holgerd77 Sep 4, 2017
5aa403a
Updated modexp precompile contract according to the latest specification
holgerd77 Sep 5, 2017
b712f30
lint fixes
cdetrio Sep 8, 2017
89254ad
fix modexp typo
cdetrio Sep 8, 2017
fd46227
elliptic curve pairing precompiles
cdetrio Sep 8, 2017
27d9ed6
skip slow staticcall tests
cdetrio Sep 8, 2017
35226b7
ecpairing exceptions should consume all gas
cdetrio Sep 8, 2017
a4887f8
Added ethereum-common block-reward-reduction branch (courtesy @jwasin…
holgerd77 Sep 11, 2017
0dd9f41
Switched from ethereumjs-block personal contributors fork (@holgerd77…
holgerd77 Sep 11, 2017
4aa2f54
Switched from ethereumjs-testing metro-byzantium branch back to maste…
holgerd77 Sep 11, 2017
c2b00a9
correctly handle transaction success or failure in transaction receipt
jwasinger Sep 12, 2017
ce11c83
Reset return data buffer on every new call, add STATICCALL to include…
holgerd77 Sep 14, 2017
7498eb4
Compare to ERROR.REVERT directly instead of the ERROR.REVERT string v…
holgerd77 Sep 14, 2017
ab90781
Also add the REVERT error message to the returndata buffer (no change…
holgerd77 Sep 14, 2017
18c74d4
Update Gquaddivisor in modexp precompile from 100 to 20 according to …
holgerd77 Sep 14, 2017
f35ef3e
Update gas costs for EC mult precompile
holgerd77 Sep 14, 2017
b2ce0a7
Fixed double import
holgerd77 Sep 15, 2017
b68374c
Renamed curve operation precompile files to a simpler/more consistent…
holgerd77 Sep 15, 2017
1e77ce9
Replaced hard-coded gas costs for new precompiles with constants from…
holgerd77 Sep 15, 2017
8a88d7f
fix modexp when modulus is one
cdetrio Sep 16, 2017
497288f
fix remaining modexp test case failures
cdetrio Sep 16, 2017
e24908e
lint fixes
cdetrio Sep 16, 2017
63f6cdb
Add error message to returndata buffer for CREATE call in case of REV…
holgerd77 Sep 16, 2017
3b288b5
don't run precompile if gasLimit=0
jwasinger Sep 17, 2017
5e9d115
add better check to make sure that precompiles have enough gas to exe…
jwasinger Sep 18, 2017
b4b8a2f
Added Byzantium ethereumjs-blockchain GitHub version (temporary)
holgerd77 Sep 20, 2017
a7179eb
correctly calculate uncle and nephew reward
jwasinger Sep 21, 2017
5d7377f
Readded ethUtil in GeneralStateTestsRunner
holgerd77 Sep 22, 2017
9b4e205
Fix linting
holgerd77 Sep 22, 2017
0b2774a
Fix run-blockchain example
holgerd77 Sep 22, 2017
a8f86f6
Added a note on Byzantium support to README
holgerd77 Sep 22, 2017
bc0ad30
Updated CHANGELOG entry for Unreleased version
holgerd77 Sep 22, 2017
73c0e8c
Fix linting
holgerd77 Sep 25, 2017
5ed376f
Temporarily removed randomStatetest642 state test
holgerd77 Sep 25, 2017
4a3c0f6
simplify revert return data
cdetrio Sep 25, 2017
fba7652
update deps for byzantium release
cdetrio Sep 25, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
(modification: no type change headlines) and this project adheres to
[Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [2.3.0] - Unreleased
- TODO
## [2.3.0] - Unreleased (``master`` branch)
- ``Byzantium`` compatible
- New opcodes ``REVERT``, ``RETURNDATA`` and ``STATICCALL``
- Precompiles for curve operations and bigint mod exp
- Transaction return data in receipts
- Detailed list of changes in PR [#161](https://github.com/ethereumjs/ethereumjs-vm/pull/161)
- For a ``Spurious Dragon``/``EIP 150`` compatible version of this library install latest version of ``2.2.x``

## [2.2.2] - 2017-09-19
- Fixed [JS number issues](https://github.com/ethereumjs/ethereumjs-vm/pull/168)
Expand All @@ -22,7 +27,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
[2.2.1]: https://github.com/ethereumjs/ethereumjs-vm/compare/v2.2.0...v2.2.1

## [2.2.0] - 2017-07-28
- Spurious Dragon & EIP 150 compatible
- ``Spurious Dragon`` & ``EIP 150`` compatible
- Detailed list of changes in pull requests [#147](https://github.com/ethereumjs/ethereumjs-vm/pull/147) and [#143](https://github.com/ethereumjs/ethereumjs-vm/pull/143)
- Removed ``enableHomestead`` option when creating a [ new VM object](https://github.com/ethereumjs/ethereumjs-vm#new-vmstatetrie-blockchain) (pre-Homestead fork rules not supported any more)

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@

Implements Ethereum's VM in JS

#### Note on Byzantium Support:

The ``master`` branch of this repository has now been updated with the latest
[Byzantium changes](https://github.com/ethereumjs/ethereumjs-vm/pull/161) please install
directly from GitHub if you want to try out the latest ``VM`` version and report
issues on our [Gitter channel](https://gitter.im/ethereum/ethereumjs-lib).

For a ``Spurious Dragon``/``EIP 150`` compatible version of this library install the
latest of the ``2.2.x`` series (see [Changelog](./CHANGELOG.md)).

# INSTALL
`npm install ethereumjs-vm`

Expand Down
3 changes: 2 additions & 1 deletion examples/run-blockchain/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ function setupPreConditions (state, testData, done) {
storageTrie.root = null

async.series([

function (cb2) {
var keys = Object.keys(acctData.storage)

Expand All @@ -130,7 +131,7 @@ function setupPreConditions (state, testData, done) {
if (testData.exec && key === testData.exec.address) {
testData.root = storageTrie.root
}
state.put(Buffer.from(key, 'hex'), account.serialize(), function () {
state.put(Buffer.from(utils.stripHexPrefix(key), 'hex'), account.serialize(), function () {
cb2()
})
}
Expand Down
243 changes: 91 additions & 152 deletions examples/run-blockchain/test-data.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ exports.ERROR = {
STACK_UNDERFLOW: 'stack underflow',
STACK_OVERFLOW: 'stack overflow',
INVALID_JUMP: 'invalid JUMP',
INVALID_OPCODE: 'invalid opcode'
INVALID_OPCODE: 'invalid opcode',
REVERT: 'revert',
STATIC_STATE_CHANGE: 'static state change'
}
10 changes: 9 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const num01 = require('./precompiled/01-ecrecover.js')
const num02 = require('./precompiled/02-sha256.js')
const num03 = require('./precompiled/03-ripemd160.js')
const num04 = require('./precompiled/04-identity.js')
const num05 = require('./precompiled/05-modexp.js')
const num06 = require('./precompiled/06-ecadd.js')
const num07 = require('./precompiled/07-ecmul.js')
const num08 = require('./precompiled/08-ecpairing.js')

module.exports = VM

Expand Down Expand Up @@ -46,9 +50,13 @@ function VM (opts = {}) {
this._precompiled['0000000000000000000000000000000000000002'] = num02
this._precompiled['0000000000000000000000000000000000000003'] = num03
this._precompiled['0000000000000000000000000000000000000004'] = num04
this._precompiled['0000000000000000000000000000000000000005'] = num05
this._precompiled['0000000000000000000000000000000000000006'] = num06
this._precompiled['0000000000000000000000000000000000000007'] = num07
this._precompiled['0000000000000000000000000000000000000008'] = num08

if (this.opts.activatePrecompiles) {
for (var i = 1; i <= 4; i++) {
for (var i = 1; i <= 7; i++) {
Copy link
Member

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.

Copy link
Contributor

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

this.trie.put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize())
}
}
Expand Down
131 changes: 122 additions & 9 deletions lib/opFns.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think RETURNDATACOPY Out of range is not the same as Out of Gas

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gasolin I just took this from the EIP specification, there out-of-gas is mentioned: https://github.com/ethereum/EIPs/pull/211/files

Copy link
Contributor

@gasolin gasolin Aug 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"if `start + length` overflows or results in a value larger than `RETURNDATASIZE`, the current call stops in an out-of-gas condition" it looks right. I referred pyethereum implementation and they seems showing the different exception https://github.com/ethereum/pyethereum/blob/develop/ethereum/fastvm.py#L415

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The 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)
},
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -595,7 +627,8 @@ module.exports = {
gasLimit: gas,
value: value,
data: data,
to: runState.address
to: runState.address,
static: runState.static
}

const localOpts = {
Expand All @@ -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)
Expand Down Expand Up @@ -652,7 +685,8 @@ module.exports = {
data: data,
to: runState.address,
caller: runState.caller,
delegatecall: true
delegatecall: true,
static: runState.static
}

const localOpts = {
Expand All @@ -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)
Expand All @@ -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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not necessary to set runState.lastReturned here. The return data buffer is already set at https://github.com/jwasinger/ethereumjs-vm/blob/wip/metropolis/lib/opFns.js#L1022

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
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions lib/opcodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const codes = {
0x3a: ['GASPRICE', 2, 0, 1, false],
0x3b: ['EXTCODESIZE', 700, 1, 1, true, true],
0x3c: ['EXTCODECOPY', 700, 4, 0, true, true],
0x3d: ['RETURNDATASIZE', 2, 0, 1, true],
0x3e: ['RETURNDATACOPY', 3, 3, 0, true],

// '0x40' range - block operations
0x40: ['BLOCKHASH', 20, 1, 1, true, true],
Expand Down Expand Up @@ -147,6 +149,8 @@ const codes = {
0xf2: ['CALLCODE', 700, 7, 1, true, true],
0xf3: ['RETURN', 0, 2, 0, false],
0xf4: ['DELEGATECALL', 700, 6, 1, true, true],
0xfa: ['STATICCALL', 700, 6, 1, true, true],
0xfd: ['REVERT', 0, 2, 0, false],

// '0x70', range - other
0xfe: ['INVALID', 0, 0, 0, false],
Expand Down
Loading