ethereumjs-vm
Advanced tools
Comparing version 2.3.5 to 2.4.0
@@ -8,2 +8,49 @@ # Changelog | ||
## [2.4.0] - 2018-07-27 | ||
With the ``2.4.x`` release series we now start to gradually add ``Constantinople`` features with the | ||
bitwise shifting instructions from [EIP 145](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md) | ||
making the start being introduced in the ``v2.4.0`` release. | ||
Since both the scope of the ``Constantinople`` hardfork as well as the state of at least some of the EIPs | ||
to be included are not yet finalized, this is only meant for ``EXPERIMENTAL`` purposes, e.g. for developer | ||
tools to give users early access and make themself familiar with dedicated features. | ||
Once scope and EIPs from ``Constantinople`` are final we will target a ``v2.5.0`` release which will officially | ||
introduce ``Constantinople`` support with all the changes bundled together. | ||
Note that from this release on we also introduce new ``chain`` (default: ``mainnet``) and ``hardfork`` | ||
(default: ``byzantium``) initialization parameters, which make use of our new [ethereumjs-common](https://github.com/ethereumjs/ethereumjs-common) library and in the future will allow | ||
for parallel hardfork support from ``Byzantium`` onwards. | ||
Since ``hardfork`` default might be changed or dropped in future releases, you might want to explicitly | ||
set this to ``byzantium`` on your next update to avoid future unexpected behavior. | ||
All the changes from this release: | ||
**FEATURES/FUNCTIONALITY** | ||
- Improved chain and fork support, see PR [#304](https://github.com/ethereumjs/ethereumjs-vm/pull/304) | ||
- Support for the ``Constantinople`` bitwise shifiting instructions ``SHL``, ``SHR`` and ``SAR``, see PR [#251](https://github.com/ethereumjs/ethereumjs-vm/pull/251) | ||
- New ``newContract`` event which can be used to do interrupting tasks on contract/address creation, see PR [#306](https://github.com/ethereumjs/ethereumjs-vm/pull/306) | ||
- Alignment of behavior of bloom filter hashing to go along with mainnet compatible clients *BREAKING*, see PR [#295](https://github.com/ethereumjs/ethereumjs-vm/pull/295) | ||
**UPDATES/TESTING** | ||
- Usage of the latest ``rustbn.js`` API, see PR [#312](https://github.com/ethereumjs/ethereumjs-vm/pull/312) | ||
- Some cleanup in precompile error handling, see PR [#318](https://github.com/ethereumjs/ethereumjs-vm/pull/318) | ||
- Some cleanup for ``StateManager``, see PR [#266](https://github.com/ethereumjs/ethereumjs-vm/pull/266) | ||
- Renaming of ``util.sha3`` usages to ``util.keccak256`` and bump ``ethereumjs-util`` to ``v5.2.0`` (you should do to if you use ``ethereumjs-util``) | ||
- Parallel testing of the``Byzantium`` and ``Constantinople`` state tests, see PR [#317](https://github.com/ethereumjs/ethereumjs-vm/pull/317) | ||
- For lower build times our CI configuration now runs solely on ``CircleCI`` and support for ``Travis`` have been dropped, see PR [#316](https://github.com/ethereumjs/ethereumjs-vm/pull/316) | ||
**BUG FIXES** | ||
- Programmatic runtime errors in the VM execution context (within an opcode) are no longer absorbed and displayed as a VMError but explicitly thrown, allowing for easier discovery of implementation bugs, see PR [#307](https://github.com/ethereumjs/ethereumjs-vm/pull/307) | ||
- Fix of the ``Bloom.check()`` method not working properly, see PR [#311](https://github.com/ethereumjs/ethereumjs-vm/pull/311) | ||
- Fix a bug when ``REVERT`` is used within a ``CREATE`` context, see PR [#297](https://github.com/ethereumjs/ethereumjs-vm/pull/297) | ||
- Fix a bug in ``FakeBlockChain`` error handing, see PR [#320](https://github.com/ethereumjs/ethereumjs-vm/pull/320) | ||
[2.4.0]: https://github.com/ethereumjs/ethereumjs-vm/compare/v2.3.5...v2.4.0 | ||
## [2.3.5] - 2018-04-25 | ||
@@ -10,0 +57,0 @@ |
@@ -9,2 +9,15 @@ 'use strict'; | ||
/** | ||
* drops leading zeroes from a buffer | ||
* @function dropLeadingZeroes | ||
* @param {Buffer} buff | ||
*/ | ||
function dropLeadingZeroes(buff) { | ||
for (var i = 0; i < buff.length; i++) { | ||
if (buff[i] !== 0) { | ||
return buff.slice(i); | ||
} | ||
} | ||
} | ||
/** | ||
* Represents a Bloom | ||
@@ -29,3 +42,3 @@ * @constructor | ||
Bloom.prototype.add = function (e) { | ||
e = utils.sha3(e); | ||
e = utils.keccak256(dropLeadingZeroes(e)); | ||
var mask = 2047; // binary 11111111111 | ||
@@ -48,4 +61,4 @@ | ||
Bloom.prototype.check = function (e) { | ||
e = utils.sha3(e); | ||
var mask = 511; // binary 111111111 | ||
e = utils.keccak256(dropLeadingZeroes(e)); | ||
var mask = 2047; // binary 11111111111 | ||
var match = true; | ||
@@ -52,0 +65,0 @@ |
@@ -16,2 +16,3 @@ 'use strict'; | ||
this.error = error; | ||
this.errorType = 'VmError'; | ||
} | ||
@@ -18,0 +19,0 @@ |
@@ -11,7 +11,7 @@ 'use strict'; | ||
if (Buffer.isBuffer(blockTag)) { | ||
_hash = utils.sha3(blockTag); | ||
_hash = utils.keccak256(blockTag); | ||
} else if (Number.isInteger(blockTag)) { | ||
_hash = utils.sha3('0x' + utils.toBuffer(blockTag).toString('hex')); | ||
_hash = utils.keccak256('0x' + utils.toBuffer(blockTag).toString('hex')); | ||
} else { | ||
cb(new Error('Unknown blockTag type')); | ||
return cb(new Error('Unknown blockTag type')); | ||
} | ||
@@ -18,0 +18,0 @@ |
@@ -7,2 +7,3 @@ 'use strict'; | ||
var StateManager = require('./stateManager.js'); | ||
var Common = require('ethereumjs-common'); | ||
var Account = require('ethereumjs-account'); | ||
@@ -36,2 +37,4 @@ var AsyncEventEmitter = require('async-eventemitter'); | ||
* @param {Blockchain} [opts.blockchain] A blockchain object for storing/retrieving blocks (ignored if stateManager is passed) | ||
* @param {String|Number} opts.chain The chain the VM operates on [default: 'mainnet'] | ||
* @param {String} opts.hardfork Hardfork rules to be used [default: 'byzantium', supported: 'byzantium' (will throw on unsupported)] | ||
* @param {Boolean} [opts.activatePrecompiles] Create entries in the state tree for the precompiled contracts | ||
@@ -45,2 +48,9 @@ * @param {Boolean} [opts.allowUnlimitedContractSize] Allows unlimited contract sizes while debugging (default: false; ONLY use during debugging) | ||
var chain = opts.chain ? opts.chain : 'mainnet'; | ||
var hardfork = opts.hardfork ? opts.hardfork : 'byzantium'; | ||
var supportedHardforks = ['byzantium', // Officially supported | ||
'constantinople' // No official support yet, only for testing reasons | ||
]; | ||
this._common = new Common(chain, hardfork, supportedHardforks); | ||
if (opts.stateManager) { | ||
@@ -51,3 +61,4 @@ this.stateManager = opts.stateManager; | ||
trie: opts.state, | ||
blockchain: opts.blockchain | ||
blockchain: opts.blockchain, | ||
common: this._common | ||
}); | ||
@@ -58,7 +69,2 @@ } | ||
// temporary | ||
// this is here for a gradual transition to StateManager | ||
this.blockchain = this.stateManager.blockchain; | ||
this.trie = this.stateManager.trie; | ||
// precompiled contracts | ||
@@ -77,3 +83,3 @@ this._precompiled = {}; | ||
for (var i = 1; i <= 7; i++) { | ||
this.trie.put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize()); | ||
this.stateManager.trie.put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize()); | ||
} | ||
@@ -102,3 +108,3 @@ } | ||
VM.prototype.loadCompiled = function (address, src, cb) { | ||
this.trie.db.put(address, src, cb); | ||
this.stateManager.trie.db.put(address, src, cb); | ||
}; | ||
@@ -105,0 +111,0 @@ |
@@ -31,2 +31,5 @@ 'use strict'; | ||
0x1a: ['BYTE', 3, 2, 1, false], | ||
0x1b: ['SHL', 3, 2, 1, false], | ||
0x1c: ['SHR', 3, 2, 1, false], | ||
0x1d: ['SAR', 3, 2, 1, false], | ||
@@ -33,0 +36,0 @@ // 0x20 range - crypto |
@@ -5,3 +5,2 @@ 'use strict'; | ||
var async = require('async'); | ||
var fees = require('ethereum-common'); | ||
var utils = require('ethereumjs-util'); | ||
@@ -101,3 +100,3 @@ var BN = utils.BN; | ||
var bytes = 1 + logTable(exponent); | ||
subGas(runState, new BN(bytes).muln(fees.expByteGas.v)); | ||
subGas(runState, new BN(bytes).muln(runState._common.param('gasPrices', 'expByte'))); | ||
return base.redPow(exponent); | ||
@@ -165,2 +164,41 @@ } else { | ||
}, | ||
SHL: function SHL(a, b, runState) { | ||
if (!runState._common.gteHardfork('constantinople')) { | ||
trap(ERROR.INVALID_OPCODE); | ||
} | ||
if (a.gten(256)) { | ||
return new BN(0); | ||
} | ||
return b.shln(a.toNumber()).iand(utils.MAX_INTEGER); | ||
}, | ||
SHR: function SHR(a, b, runState) { | ||
if (!runState._common.gteHardfork('constantinople')) { | ||
trap(ERROR.INVALID_OPCODE); | ||
} | ||
if (a.gten(256)) { | ||
return new BN(0); | ||
} | ||
return b.shrn(a.toNumber()); | ||
}, | ||
SAR: function SAR(a, b, runState) { | ||
if (!runState._common.gteHardfork('constantinople')) { | ||
trap(ERROR.INVALID_OPCODE); | ||
} | ||
var isSigned = b.testn(255); | ||
if (a.gten(256)) { | ||
if (isSigned) { | ||
return new BN(utils.MAX_INTEGER); | ||
} else { | ||
return new BN(0); | ||
} | ||
} | ||
var c = b.shrn(a.toNumber()); | ||
if (isSigned) { | ||
var shiftedOutWidth = 255 - a.toNumber(); | ||
var mask = utils.MAX_INTEGER.shrn(shiftedOutWidth).shln(shiftedOutWidth); | ||
return c.ior(mask); | ||
} else { | ||
return c; | ||
} | ||
}, | ||
// 0x20 range - crypto | ||
@@ -170,4 +208,4 @@ SHA3: function SHA3(offset, length, runState) { | ||
// copy fee | ||
subGas(runState, new BN(fees.sha3WordGas.v).imul(length.divCeil(new BN(32)))); | ||
return new BN(utils.sha3(data)); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'sha3Word')).imul(length.divCeil(new BN(32)))); | ||
return new BN(utils.keccak256(data)); | ||
}, | ||
@@ -226,3 +264,3 @@ // 0x30 range - closure state | ||
// sub the COPY fee | ||
subGas(runState, new BN(fees.copyGas.v).imul(dataLength.divCeil(new BN(32)))); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'copy')).imul(dataLength.divCeil(new BN(32)))); | ||
}, | ||
@@ -235,3 +273,3 @@ CODESIZE: function CODESIZE(runState) { | ||
// sub the COPY fee | ||
subGas(runState, new BN(fees.copyGas.v).imul(length.divCeil(new BN(32)))); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'copy')).imul(length.divCeil(new BN(32)))); | ||
}, | ||
@@ -253,3 +291,3 @@ EXTCODESIZE: function EXTCODESIZE(address, runState, cb) { | ||
// copy fee | ||
subGas(runState, new BN(fees.copyGas.v).imul(length.divCeil(new BN(32)))); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'copy')).imul(length.divCeil(new BN(32)))); | ||
@@ -272,3 +310,3 @@ stateManager.getContractCode(address, function (err, code) { | ||
// sub the COPY fee | ||
subGas(runState, new BN(fees.copyGas.v).mul(length.divCeil(new BN(32)))); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'copy')).mul(length.divCeil(new BN(32)))); | ||
}, | ||
@@ -352,10 +390,10 @@ GASPRICE: function GASPRICE(runState) { | ||
if (value.length === 0 && !found.length) { | ||
subGas(runState, new BN(fees.sstoreResetGas.v)); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'sstoreReset'))); | ||
} else if (value.length === 0 && found.length) { | ||
subGas(runState, new BN(fees.sstoreResetGas.v)); | ||
runState.gasRefund.iaddn(fees.sstoreRefundGas.v); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'sstoreReset'))); | ||
runState.gasRefund.iaddn(runState._common.param('gasPrices', 'sstoreRefund')); | ||
} else if (value.length !== 0 && !found.length) { | ||
subGas(runState, new BN(fees.sstoreSetGas.v)); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'sstoreSet'))); | ||
} else if (value.length !== 0 && found.length) { | ||
subGas(runState, new BN(fees.sstoreResetGas.v)); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'sstoreReset'))); | ||
} | ||
@@ -459,3 +497,3 @@ } catch (e) { | ||
var mem = memLoad(runState, memOffset, memLength); | ||
subGas(runState, new BN(fees.logTopicGas.v).imuln(numOfTopics).iadd(memLength.muln(fees.logDataGas.v))); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'logTopic')).imuln(numOfTopics).iadd(memLength.muln(runState._common.param('gasPrices', 'logData')))); | ||
@@ -522,3 +560,3 @@ // add address | ||
if (!value.isZero()) { | ||
subGas(runState, new BN(fees.callValueTransferGas.v)); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'callValueTransfer'))); | ||
} | ||
@@ -541,3 +579,3 @@ | ||
try { | ||
subGas(runState, new BN(fees.callNewAccountGas.v)); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'callNewAccount'))); | ||
} catch (e) { | ||
@@ -559,4 +597,4 @@ done(e.error); | ||
if (!value.isZero()) { | ||
runState.gasLeft.iaddn(fees.callStipend.v); | ||
options.gasLimit.iaddn(fees.callStipend.v); | ||
runState.gasLeft.iaddn(runState._common.param('gasPrices', 'callStipend')); | ||
options.gasLimit.iaddn(runState._common.param('gasPrices', 'callStipend')); | ||
} | ||
@@ -590,3 +628,3 @@ | ||
if (!value.isZero()) { | ||
subGas(runState, new BN(fees.callValueTransferGas.v)); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'callValueTransfer'))); | ||
} | ||
@@ -598,4 +636,4 @@ | ||
if (!value.isZero()) { | ||
runState.gasLeft.iaddn(fees.callStipend.v); | ||
options.gasLimit.iaddn(fees.callStipend.v); | ||
runState.gasLeft.iaddn(runState._common.param('gasPrices', 'callStipend')); | ||
options.gasLimit.iaddn(runState._common.param('gasPrices', 'callStipend')); | ||
} | ||
@@ -744,3 +782,3 @@ | ||
try { | ||
subGas(runState, new BN(fees.callNewAccountGas.v)); | ||
subGas(runState, new BN(runState._common.param('gasPrices', 'callNewAccount'))); | ||
} catch (e) { | ||
@@ -755,3 +793,3 @@ cb(e.error); | ||
if (!runState.selfdestruct[contractAddress.toString('hex')]) { | ||
runState.gasRefund = runState.gasRefund.addn(fees.suicideRefundGas.v); | ||
runState.gasRefund = runState.gasRefund.addn(runState._common.param('gasPrices', 'selfdestructRefund')); | ||
} | ||
@@ -772,3 +810,3 @@ runState.selfdestruct[contractAddress.toString('hex')] = selfdestructToAddress; | ||
function describeLocation(runState) { | ||
var hash = utils.sha3(runState.code).toString('hex'); | ||
var hash = utils.keccak256(runState.code).toString('hex'); | ||
var address = runState.address.toString('hex'); | ||
@@ -806,4 +844,4 @@ var pc = runState.programCounter - 1; | ||
var words = newMemoryWordCount; | ||
var fee = new BN(fees.memoryGas.v); | ||
var quadCoeff = new BN(fees.quadCoeffDiv.v); | ||
var fee = new BN(runState._common.param('gasPrices', 'memory')); | ||
var quadCoeff = new BN(runState._common.param('gasPrices', 'quadCoeffDiv')); | ||
// words * 3 + words ^2 / 512 | ||
@@ -948,3 +986,3 @@ var cost = words.mul(fee).add(words.mul(words).div(quadCoeff)); | ||
// 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)) { | ||
if (runState.depth >= runState._common.param('vm', 'stackLimit') || callOptions.delegatecall !== true && new BN(runState.contract.balance).lt(callOptions.value)) { | ||
cb(null, new BN(0)); | ||
@@ -951,0 +989,0 @@ } else { |
@@ -6,3 +6,2 @@ 'use strict'; | ||
var error = require('../exceptions.js').ERROR; | ||
var fees = require('ethereum-common'); | ||
var assert = require('assert'); | ||
@@ -15,5 +14,6 @@ | ||
results.gasUsed = new BN(fees.ecrecoverGas.v); | ||
results.gasUsed = new BN(opts._common.param('gasPrices', 'ecRecover')); | ||
if (opts.gasLimit.lt(results.gasUsed)) { | ||
results.return = Buffer.alloc(0); | ||
results.gasUsed = opts.gasLimit; | ||
@@ -36,2 +36,4 @@ results.exception = 0; // 0 means VM fail (in this case because of OOG) | ||
} catch (e) { | ||
results.return = Buffer.alloc(0); | ||
results.exception = 1; | ||
return results; | ||
@@ -38,0 +40,0 @@ } |
@@ -6,3 +6,2 @@ 'use strict'; | ||
var error = require('../exceptions.js').ERROR; | ||
var fees = require('ethereum-common'); | ||
var assert = require('assert'); | ||
@@ -16,6 +15,7 @@ | ||
results.gasUsed = new BN(fees.sha256Gas.v); | ||
results.gasUsed.iadd(new BN(fees.sha256WordGas.v).imuln(Math.ceil(data.length / 32))); | ||
results.gasUsed = new BN(opts._common.param('gasPrices', 'sha256')); | ||
results.gasUsed.iadd(new BN(opts._common.param('gasPrices', 'sha256Word')).imuln(Math.ceil(data.length / 32))); | ||
if (opts.gasLimit.lt(results.gasUsed)) { | ||
results.return = Buffer.alloc(0); | ||
results.gasUsed = opts.gasLimit; | ||
@@ -22,0 +22,0 @@ results.exceptionError = error.OUT_OF_GAS; |
@@ -6,3 +6,2 @@ 'use strict'; | ||
var error = require('../exceptions.js').ERROR; | ||
var fees = require('ethereum-common'); | ||
var assert = require('assert'); | ||
@@ -16,6 +15,7 @@ | ||
results.gasUsed = new BN(fees.ripemd160Gas.v); | ||
results.gasUsed.iadd(new BN(fees.ripemd160WordGas.v).imuln(Math.ceil(data.length / 32))); | ||
results.gasUsed = new BN(opts._common.param('gasPrices', 'ripemd160')); | ||
results.gasUsed.iadd(new BN(opts._common.param('gasPrices', 'ripemd160Word')).imuln(Math.ceil(data.length / 32))); | ||
if (opts.gasLimit.lt(results.gasUsed)) { | ||
results.return = Buffer.alloc(0); | ||
results.gasUsed = opts.gasLimit; | ||
@@ -22,0 +22,0 @@ results.exceptionError = error.OUT_OF_GAS; |
@@ -5,3 +5,2 @@ 'use strict'; | ||
var BN = utils.BN; | ||
var fees = require('ethereum-common'); | ||
var error = require('../exceptions.js').ERROR; | ||
@@ -16,6 +15,7 @@ var assert = require('assert'); | ||
results.gasUsed = new BN(fees.identityGas.v); | ||
results.gasUsed.iadd(new BN(fees.identityWordGas.v).imuln(Math.ceil(data.length / 32))); | ||
results.gasUsed = new BN(opts._common.param('gasPrices', 'identity')); | ||
results.gasUsed.iadd(new BN(opts._common.param('gasPrices', 'identityWord')).imuln(Math.ceil(data.length / 32))); | ||
if (opts.gasLimit.lt(results.gasUsed)) { | ||
results.return = Buffer.alloc(0); | ||
results.gasUsed = opts.gasLimit; | ||
@@ -22,0 +22,0 @@ results.exceptionError = error.OUT_OF_GAS; |
@@ -6,7 +6,4 @@ 'use strict'; | ||
var error = require('../exceptions.js').ERROR; | ||
var fees = require('ethereum-common'); | ||
var assert = require('assert'); | ||
var Gquaddivisor = fees.modexpGquaddivisor.v; | ||
function multComplexity(x) { | ||
@@ -31,5 +28,10 @@ var fac1 = new BN(0); | ||
function getAdjustedExponentLength(data) { | ||
var baseLen = new BN(data.slice(0, 32)).toNumber(); | ||
var expBytesStart; | ||
try { | ||
var baseLen = new BN(data.slice(0, 32)).toNumber(); | ||
expBytesStart = 96 + baseLen; // 96 for base length, then exponent length, and modulus length, then baseLen for the base data, then exponent bytes start | ||
} catch (e) { | ||
expBytesStart = Number.MAX_SAFE_INTEGER - 32; | ||
} | ||
var expLen = new BN(data.slice(32, 64)); | ||
var expBytesStart = 96 + baseLen; // 96 for base length, then exponent length, and modulus length, then baseLen for the base data, then exponent bytes start | ||
var firstExpBytes = Buffer.from(data.slice(expBytesStart, expBytesStart + 32)); // first word of the exponent data | ||
@@ -72,2 +74,3 @@ firstExpBytes = utils.setLengthRight(firstExpBytes, 32); // reading past the data reads virtual zeros | ||
function getOOGResults(opts, results) { | ||
results.return = Buffer.alloc(0); | ||
results.gasUsed = opts.gasLimit; | ||
@@ -98,2 +101,3 @@ results.exception = 0; // 0 means VM fail (in this case because of OOG) | ||
} | ||
var Gquaddivisor = opts._common.param('gasPrices', 'modexpGquaddivisor'); | ||
var gasUsed = adjustedELen.mul(multComplexity(maxLen)).divn(Gquaddivisor); | ||
@@ -114,3 +118,3 @@ | ||
if (mLen.isZero()) { | ||
results.return = Buffer.from([0]); | ||
results.return = Buffer.alloc(0); | ||
results.exception = 1; | ||
@@ -117,0 +121,0 @@ return results; |
@@ -6,7 +6,5 @@ 'use strict'; | ||
var error = require('../exceptions.js').ERROR; | ||
var fees = require('ethereum-common'); | ||
var assert = require('assert'); | ||
var bn128Module = require('rustbn.js'); | ||
var ecAddPrecompile = bn128Module.cwrap('ec_add', 'string', ['string']); | ||
var bn128 = require('rustbn.js'); | ||
@@ -17,6 +15,5 @@ module.exports = function (opts) { | ||
var results = {}; | ||
var data = opts.data; | ||
var inputHexStr = data.toString('hex'); | ||
var inputData = opts.data; | ||
results.gasUsed = new BN(fees.ecAddGas.v); | ||
results.gasUsed = new BN(opts._common.param('gasPrices', 'ecAdd')); | ||
if (opts.gasLimit.lt(results.gasUsed)) { | ||
@@ -30,6 +27,6 @@ results.return = Buffer.alloc(0); | ||
var returnData = ecAddPrecompile(inputHexStr); | ||
var returnData = bn128.add(inputData); | ||
// check ecadd success or failure by comparing the output length | ||
if (returnData.length !== 128) { | ||
if (returnData.length !== 64) { | ||
results.return = Buffer.alloc(0); | ||
@@ -40,3 +37,3 @@ results.exception = 0; | ||
} else { | ||
results.return = Buffer.from(returnData, 'hex'); | ||
results.return = returnData; | ||
results.exception = 1; | ||
@@ -43,0 +40,0 @@ } |
@@ -6,7 +6,5 @@ 'use strict'; | ||
var BN = utils.BN; | ||
var fees = require('ethereum-common'); | ||
var assert = require('assert'); | ||
var bn128Module = require('rustbn.js'); | ||
var ecMulPrecompile = bn128Module.cwrap('ec_mul', 'string', ['string']); | ||
var bn128 = require('rustbn.js'); | ||
@@ -17,6 +15,5 @@ module.exports = function (opts) { | ||
var results = {}; | ||
var data = opts.data; | ||
var inputData = opts.data; | ||
var inputHexStr = data.toString('hex'); | ||
results.gasUsed = new BN(fees.ecMulGas.v); | ||
results.gasUsed = new BN(opts._common.param('gasPrices', 'ecMul')); | ||
@@ -31,10 +28,12 @@ if (opts.gasLimit.lt(results.gasUsed)) { | ||
var returnData = ecMulPrecompile(inputHexStr); | ||
var returnData = bn128.mul(inputData); | ||
// check ecmul success or failure by comparing the output length | ||
if (returnData.length !== 128) { | ||
if (returnData.length !== 64) { | ||
results.return = Buffer.alloc(0); | ||
results.exception = 0; | ||
results.gasUsed = new BN(opts.gasLimit); | ||
results.exceptionError = ERROR.OUT_OF_GAS; | ||
} else { | ||
results.return = Buffer.from(returnData, 'hex'); | ||
results.return = returnData; | ||
results.exception = 1; | ||
@@ -41,0 +40,0 @@ } |
@@ -6,7 +6,5 @@ 'use strict'; | ||
var error = require('../exceptions.js').ERROR; | ||
var fees = require('ethereum-common'); | ||
var assert = require('assert'); | ||
var bn128Module = require('rustbn.js'); | ||
var ecPairingPrecompile = bn128Module.cwrap('ec_pairing', 'string', ['string']); | ||
var bn128 = require('rustbn.js'); | ||
@@ -17,12 +15,12 @@ module.exports = function (opts) { | ||
var results = {}; | ||
var data = opts.data; | ||
var inputData = opts.data; | ||
var inputHexStr = data.toString('hex'); | ||
var inputData = Buffer.from(inputHexStr, 'hex'); | ||
// no need to care about non-divisible-by-192, because bn128.pairing will properly fail in that case | ||
var inputDataSize = Math.floor(inputData.length / 192); | ||
var gascost = fees.ecPairingGas.v + inputDataSize * fees.ecPairingWordGas.v; | ||
var gascost = opts._common.param('gasPrices', 'ecPairing') + inputDataSize * opts._common.param('gasPrices', 'ecPairingWord'); | ||
results.gasUsed = new BN(gascost); | ||
if (opts.gasLimit.ltn(gascost)) { | ||
results.return = Buffer.alloc(0); | ||
results.gasUsed = opts.gasLimit; | ||
@@ -34,6 +32,6 @@ results.exceptionError = error.OUT_OF_GAS; | ||
var returnData = ecPairingPrecompile(inputHexStr); | ||
var returnData = bn128.pairing(inputData); | ||
// check ecpairing success or failure by comparing the output length | ||
if (returnData.length !== 64) { | ||
if (returnData.length !== 32) { | ||
results.return = Buffer.alloc(0); | ||
@@ -44,3 +42,3 @@ results.gasUsed = opts.gasLimit; | ||
} else { | ||
results.return = Buffer.from(returnData, 'hex'); | ||
results.return = returnData; | ||
results.exception = 1; | ||
@@ -47,0 +45,0 @@ } |
@@ -7,3 +7,2 @@ 'use strict'; | ||
var Bloom = require('./bloom.js'); | ||
var common = require('ethereum-common'); | ||
var rlp = ethUtil.rlp; | ||
@@ -13,4 +12,2 @@ var Trie = require('merkle-patricia-tree'); | ||
var minerReward = new BN(common.minerReward.v); | ||
/** | ||
@@ -44,3 +41,3 @@ * process the transaction in a block and pays the miners | ||
this.trie.checkpoint(); | ||
self.stateManager.trie.checkpoint(); | ||
@@ -139,3 +136,3 @@ // run everything | ||
if (err) { | ||
self.trie.revert(); | ||
self.stateManager.trie.revert(); | ||
cb(err); | ||
@@ -150,6 +147,6 @@ return; | ||
if (generateStateRoot) { | ||
block.header.stateRoot = self.trie.root; | ||
block.header.stateRoot = self.stateManager.trie.root; | ||
} | ||
self.trie.commit(function (err) { | ||
self.stateManager.trie.commit(function (err) { | ||
self.stateManager.cache.flush(function () { | ||
@@ -166,3 +163,3 @@ if (validateStateRoot) { | ||
} | ||
if (self.trie.root.toString('hex') !== block.header.stateRoot.toString('hex')) { | ||
if (self.stateManager.trie.root.toString('hex') !== block.header.stateRoot.toString('hex')) { | ||
err = new Error((err || '') + 'invalid block stateRoot '); | ||
@@ -192,2 +189,3 @@ } | ||
// calculate nibling reward | ||
var minerReward = new BN(self._common.param('pow', 'minerReward')); | ||
var niblingReward = minerReward.divn(32); | ||
@@ -204,2 +202,3 @@ var totalNiblingReward = niblingReward.muln(ommers.length); | ||
// calculate reward | ||
var minerReward = new BN(self._common.param('pow', 'minerReward')); | ||
var heightDiff = new BN(block.header.number).sub(new BN(ommer.number)); | ||
@@ -206,0 +205,0 @@ var reward = new BN(8).sub(heightDiff).mul(minerReward.divn(8)); |
@@ -14,13 +14,12 @@ 'use strict'; | ||
self.blockchain = self.stateManager.blockchain; | ||
// parse arguments | ||
if (typeof blockchain === 'function') { | ||
cb = blockchain; | ||
} else if (blockchain) { | ||
self.blockchain = blockchain; | ||
blockchain = undefined; | ||
} | ||
blockchain = blockchain || self.stateManager.blockchain; | ||
// setup blockchain iterator | ||
self.blockchain.iterator('vm', processBlock, cb); | ||
blockchain.iterator('vm', processBlock, cb); | ||
function processBlock(block, reorg, cb) { | ||
@@ -33,3 +32,3 @@ async.series([getStartingState, runBlock], cb); | ||
if (!headBlock || reorg) { | ||
self.blockchain.getBlock(block.header.parentHash, function (err, parentBlock) { | ||
blockchain.getBlock(block.header.parentHash, function (err, parentBlock) { | ||
parentState = parentBlock.header.stateRoot; | ||
@@ -59,3 +58,3 @@ // generate genesis state if we are at the genesis block | ||
console.log('Invalid block error:', err); | ||
self.blockchain.delBlock(block.header.hash(), cb); | ||
blockchain.delBlock(block.header.hash(), cb); | ||
} else { | ||
@@ -62,0 +61,0 @@ // set as new head block |
@@ -7,3 +7,2 @@ 'use strict'; | ||
var BN = ethUtil.BN; | ||
var fees = require('ethereum-common'); | ||
var exceptions = require('./exceptions.js'); | ||
@@ -69,7 +68,24 @@ | ||
createdAddress = toAddress = ethUtil.generateAddress(caller, newNonce.toArray()); | ||
stateManager.getAccount(createdAddress, function (err, account) { | ||
toAccount = account; | ||
var NONCE_OFFSET = 1; | ||
toAccount.nonce = new BN(toAccount.nonce).addn(NONCE_OFFSET).toArrayLike(Buffer); | ||
done(err); | ||
stateManager.clearContractStorage(createdAddress, function (err) { | ||
if (err) { | ||
done(err); | ||
} | ||
async.series([newContractEvent, getAccount], done); | ||
function newContractEvent(callback) { | ||
self.emit('newContract', { | ||
address: createdAddress, | ||
code: code | ||
}, callback); | ||
} | ||
function getAccount(callback) { | ||
stateManager.getAccount(createdAddress, function (err, account) { | ||
toAccount = account; | ||
var NONCE_OFFSET = 1; | ||
toAccount.nonce = new BN(toAccount.nonce).addn(NONCE_OFFSET).toArrayLike(Buffer); | ||
callback(err); | ||
}); | ||
} | ||
}); | ||
@@ -161,3 +177,3 @@ } else { | ||
if (!results.runState.vmError) { | ||
var returnFee = new BN(results.return.length * fees.createDataGas.v); | ||
var returnFee = new BN(results.return.length * self._common.param('gasPrices', 'createData')); | ||
@@ -164,0 +180,0 @@ totalGas = totalGas.add(returnFee); |
@@ -83,3 +83,4 @@ 'use strict'; | ||
// temporary - to be factored out | ||
};runState._precompiled = self._precompiled; | ||
};runState._common = self._common; | ||
runState._precompiled = self._precompiled; | ||
runState._vm = self; | ||
@@ -202,4 +203,8 @@ | ||
} catch (e) { | ||
cb(e); | ||
return; | ||
if (e.errorType && e.errorType === 'VmError') { | ||
cb(e); | ||
return; | ||
} else { | ||
throw e; | ||
} | ||
} | ||
@@ -211,3 +216,3 @@ | ||
// opcode post-stack mismatch | ||
return cb(VmError(ERROR.INTERNAL_ERROR)); | ||
return cb(new VmError(ERROR.INTERNAL_ERROR)); | ||
} | ||
@@ -219,3 +224,3 @@ | ||
// opcode post-stack mismatch | ||
return cb(VmError(ERROR.INTERNAL_ERROR)); | ||
return cb(new VmError(ERROR.INTERNAL_ERROR)); | ||
} | ||
@@ -222,0 +227,0 @@ } |
@@ -7,7 +7,5 @@ 'use strict'; | ||
if (typeof opts.code === 'function') { | ||
opts._common = this._common; | ||
results = opts.code(opts); | ||
results.account = opts.account; | ||
if (results.exception === undefined) { | ||
results.exception = 1; | ||
} | ||
cb(results.exceptionError, results); | ||
@@ -14,0 +12,0 @@ } else { |
@@ -5,3 +5,4 @@ 'use strict'; | ||
var Trie = require('merkle-patricia-tree/secure.js'); | ||
var common = require('ethereum-common'); | ||
var Common = require('ethereumjs-common'); | ||
var genesisStates = require('ethereumjs-common/genesisStates'); | ||
var async = require('async'); | ||
@@ -17,19 +18,18 @@ var Account = require('ethereumjs-account'); | ||
function StateManager(opts) { | ||
function StateManager() { | ||
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var self = this; | ||
var trie = opts.trie; | ||
if (!trie) { | ||
trie = new Trie(trie); | ||
} | ||
self.blockchain = opts.blockchain || fakeBlockchain; | ||
self.trie = opts.trie || new Trie(); | ||
var blockchain = opts.blockchain; | ||
if (!blockchain) { | ||
blockchain = fakeBlockchain; | ||
var common = opts.common; | ||
if (!common) { | ||
common = new Common('mainnet', 'byzantium'); | ||
} | ||
self._common = common; | ||
self.blockchain = blockchain; | ||
self.trie = trie; | ||
self._storageTries = {}; // the storage trie cache | ||
self.cache = new Cache(trie); | ||
self.cache = new Cache(self.trie); | ||
self._touched = new Set(); | ||
@@ -167,3 +167,3 @@ } | ||
proto.putContractStorage = function (address, key, value, cb) { | ||
proto._modifyContractStorage = function (address, modifyTrie, cb) { | ||
var self = this; | ||
@@ -175,10 +175,3 @@ self._getStorageTrie(address, function (err, storageTrie) { | ||
if (value && value.length) { | ||
// format input | ||
var encodedValue = rlp.encode(value); | ||
storageTrie.put(key, encodedValue, finalize); | ||
} else { | ||
// deleting a value | ||
storageTrie.del(key, finalize); | ||
} | ||
modifyTrie(storageTrie, finalize); | ||
@@ -198,2 +191,24 @@ function finalize(err) { | ||
proto.putContractStorage = function (address, key, value, cb) { | ||
var self = this; | ||
self._modifyContractStorage(address, function (storageTrie, done) { | ||
if (value && value.length) { | ||
// format input | ||
var encodedValue = rlp.encode(value); | ||
storageTrie.put(key, encodedValue, done); | ||
} else { | ||
// deleting a value | ||
storageTrie.del(key, done); | ||
} | ||
}, cb); | ||
}; | ||
proto.clearContractStorage = function (address, cb) { | ||
var self = this; | ||
self._modifyContractStorage(address, function (storageTrie, done) { | ||
storageTrie.root = storageTrie.EMPTY_TRIE_ROOT; | ||
done(); | ||
}, cb); | ||
}; | ||
proto.commitContracts = function (cb) { | ||
@@ -302,3 +317,3 @@ var self = this; | ||
proto.hasGenesisState = function (cb) { | ||
var root = common.genesisStateRoot.v; | ||
var root = this._common.genesis().stateRoot; | ||
this.trie.checkRoot(root, cb); | ||
@@ -312,3 +327,3 @@ }; | ||
if (!genesis && !err) { | ||
self.generateGenesis(common.genesisState, cb); | ||
self.generateGenesis(genesisStates[self._common.chainName()], cb); | ||
} else { | ||
@@ -315,0 +330,0 @@ cb(err); |
{ | ||
"name": "ethereumjs-vm", | ||
"version": "2.3.5", | ||
"version": "2.4.0", | ||
"description": "An Ethereum VM implementation", | ||
@@ -13,3 +13,4 @@ "main": "dist/index.js", | ||
"testVM": "node ./tests/tester -v", | ||
"testState": "npm run build:dist && node ./tests/tester -s --dist", | ||
"testStateByzantium": "npm run build:dist && node ./tests/tester -s --fork='Byzantium' --dist", | ||
"testStateConstantinople": "npm run build:dist && node ./tests/tester -s --fork='Constantinople' --dist", | ||
"testBuildIntegrity": "npm run build:dist && node ./tests/tester -s --dist --test='stackOverflow'", | ||
@@ -37,10 +38,10 @@ "testBlockchain": "npm run build:dist && node --stack-size=1500 ./tests/tester -b --dist --excludeDir='GeneralStateTests'", | ||
"async-eventemitter": "^0.2.2", | ||
"ethereum-common": "0.2.0", | ||
"ethereumjs-account": "^2.0.3", | ||
"ethereumjs-block": "~1.7.0", | ||
"ethereumjs-util": "^5.1.3", | ||
"ethereumjs-common": "~0.4.0", | ||
"ethereumjs-util": "^5.2.0", | ||
"fake-merkle-patricia-tree": "^1.0.1", | ||
"functional-red-black-tree": "^1.0.1", | ||
"merkle-patricia-tree": "^2.1.2", | ||
"rustbn.js": "~0.1.1", | ||
"rustbn.js": "~0.2.0", | ||
"safe-buffer": "^5.1.1" | ||
@@ -53,3 +54,3 @@ }, | ||
"ethereumjs-blockchain": "~2.1.0", | ||
"ethereumjs-testing": "https://github.com/ethereumjs/ethereumjs-testing", | ||
"ethereumjs-testing": "git+https://github.com/ethereumjs/ethereumjs-testing.git#v1.1.1", | ||
"ethereumjs-tx": "1.3.3", | ||
@@ -56,0 +57,0 @@ "istanbul": "^0.4.5", |
@@ -14,6 +14,22 @@ # SYNOPSIS | ||
This library always only supports the currently active Ethereum mainnet fork rules with its latest release, old fork rules are dropped with new releases once a HF occured. | ||
With the ``2.4.x`` release series we now start to gradually add ``Constantinople`` features with the | ||
bitwise shifting instructions from [EIP 145](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md) | ||
making the start being introduced in the ``v2.4.0`` release. | ||
The current major [2.3.x](https://github.com/ethereumjs/ethereumjs-vm/releases) release series supports the [Byzantium](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-609.md) fork changes. For a [Spurious Dragon](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-607.md) compatible version of this library install the latest of the ``2.2.x`` series (see [Changelog](./CHANGELOG.md)). | ||
Since both the scope of the ``Constantinople`` hardfork as well as the state of at least some of the EIPs | ||
to be included are not yet finalized, this is only meant for ``EXPERIMENTAL`` purposes, e.g. for developer | ||
tools to give users early access and make themself familiar with dedicated features. | ||
Once scope and EIPs from ``Constantinople`` are final we will target a ``v2.5.0`` release which will officially | ||
introduce ``Constantinople`` support with all the changes bundled together. | ||
Note that from this release on we also introduce new ``chain`` (default: ``mainnet``) and ``hardfork`` | ||
(default: ``byzantium``) parameters, which make use of our new [ethereumjs-common](https://github.com/ethereumjs/ethereumjs-common) library and in the future will allow | ||
for parallel hardfork support from ``Byzantium`` onwards. | ||
Since ``hardfork`` default might be changed or dropped in future releases, you might want to explicitly | ||
set this to ``byzantium`` on your next update to avoid future unexpected behavior. | ||
If you are still looking for a [Spurious Dragon](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-607.md) compatible version of this library install the latest of the ``2.2.x`` series (see [Changelog](./CHANGELOG.md)). | ||
# INSTALL | ||
@@ -54,4 +70,9 @@ `npm install ethereumjs-vm` | ||
- [`vm.generateGenesis(cb)`](#vmgenerategenesiscb) | ||
- [`VM` debugging hooks](#vm-debugging-hooks) | ||
- [`vm.onStep`](#vmonstep) | ||
- [`VM` Events](#events) | ||
- [`step`](#step) | ||
- [`newContract`](#newcontract) | ||
- [`beforeBlock`](#beforeblock) | ||
- [`afterBlock`](#afterblock) | ||
- [`beforeTx`](#beforetx) | ||
- [`afterTx`](#aftertx) | ||
@@ -64,2 +85,4 @@ ### `new VM([opts])` | ||
- `blockchain` - A blockchain object for storing/retrieving blocks (ignored if `stateManager` is passed) | ||
- `chain` - The chain the VM operates on [default: 'mainnet'] | ||
- `hardfork` - Hardfork rules to be used [default: 'byzantium', supported: 'byzantium' (will throw on unsupported)] | ||
- `activatePrecompiles` - Create entries in the state tree for the precompiled contracts | ||
@@ -146,3 +169,3 @@ - `allowUnlimitedContractSize` - Allows unlimited contract sizes while debugging. By setting this to `true`, the check for contract size limit of 2KB (see [EIP-170](https://git.io/vxZkK)) is bypassed. (default: `false`; **ONLY** set to `true` during debugging). | ||
### `events` | ||
All events are instances of [async-eventemmiter](https://www.npmjs.com/package/async-eventemitter). If an event handler has an arity of 2 the VM will pause until the callback is called | ||
All events are instances of [async-eventemmiter](https://www.npmjs.com/package/async-eventemitter). If an event handler has an arity of 2 the VM will pause until the callback is called, otherwise the VM will treat the event handler as a synchronous function. | ||
@@ -162,2 +185,7 @@ #### `step` | ||
#### `newContract` | ||
The `newContract` event is given an `Object` and callback. The `Object` has the following properties. | ||
- `address`: The created address for the new contract (type `Buffer | Uint8Array`) | ||
- `code`: The deployment bytecode for reference (type `Buffer | Uint8Array`) | ||
#### `beforeBlock` | ||
@@ -164,0 +192,0 @@ Emits the block that is about to be processed. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
153222
3213
329
+ Addedethereumjs-common@~0.4.0
+ Addedethereumjs-common@0.4.1(transitive)
+ Addedrustbn.js@0.2.0(transitive)
- Removedethereum-common@0.2.0
- Removedrustbn.js@0.1.2(transitive)
Updatedethereumjs-util@^5.2.0
Updatedrustbn.js@~0.2.0