Skip to content

Commit

Permalink
Merge pull request #329 from ethereumjs/eip-1014
Browse files Browse the repository at this point in the history
EIP 1014 CREATE2
  • Loading branch information
holgerd77 authored Nov 6, 2018
2 parents 66c8a36 + 6efe3d1 commit 5fdce83
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 35 deletions.
37 changes: 36 additions & 1 deletion lib/opFns.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,37 @@ module.exports = {
checkOutOfGas(runState, options)
makeCall(runState, options, localOpts, done)
},
CREATE2: function (value, offset, length, salt, runState, done) {
if (!runState._common.gteHardfork('constantinople')) {
trap(ERROR.INVALID_OPCODE)
}

if (runState.static) {
trap(ERROR.STATIC_STATE_CHANGE)
}

var data = memLoad(runState, offset, length)

// set up config
var options = {
value: value,
data: data,
salt: salt.toBuffer('be', 32)
}

var localOpts = {
inOffset: offset,
inLength: length,
outOffset: new BN(0),
outLength: new BN(0)
}

// Deduct gas costs for hashing
subGas(runState, new BN(runState._common.param('gasPrices', 'sha3Word')).imul(length.divCeil(new BN(32))))
checkCallMemCost(runState, options, localOpts)
checkOutOfGas(runState, options)
makeCall(runState, options, localOpts, done)
},
CALL: function (gasLimit, toAddress, value, inOffset, inLength, outOffset, outLength, runState, done) {
var stateManager = runState.stateManager
toAddress = addressToBuffer(toAddress)
Expand Down Expand Up @@ -980,7 +1011,7 @@ function makeCall (runState, callOptions, localOpts, cb) {
if (results.vm.return && (!results.vm.exceptionError || results.vm.exceptionError.error === ERROR.REVERT)) {
memStore(runState, localOpts.outOffset, results.vm.return, new BN(0), localOpts.outLength, false)

if (results.vm.exceptionError && results.vm.exceptionError.error === ERROR.REVERT && runState.opName === 'CREATE') {
if (results.vm.exceptionError && results.vm.exceptionError.error === ERROR.REVERT && isCreateOpCode(runState.opName)) {
runState.lastReturned = results.vm.return
}

Expand Down Expand Up @@ -1018,6 +1049,10 @@ function makeCall (runState, callOptions, localOpts, cb) {
}
}

function isCreateOpCode (opName) {
return opName === 'CREATE' || opName === 'CREATE2'
}

function getContractStorage (runState, address, key, cb) {
if (runState._common.gteHardfork('constantinople')) {
async.parallel({
Expand Down
1 change: 1 addition & 0 deletions lib/opcodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ const codes = {
0xf2: ['CALLCODE', 700, 7, 1, true, true],
0xf3: ['RETURN', 0, 2, 0, false],
0xf4: ['DELEGATECALL', 700, 6, 1, true, true],
0xf5: ['CREATE2', 32000, 4, 1, true, true],
0xfa: ['STATICCALL', 700, 6, 1, true, true],
0xfd: ['REVERT', 0, 2, 0, false],

Expand Down
88 changes: 54 additions & 34 deletions lib/runCall.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = function (opts, cb) {
var selfdestruct = opts.selfdestruct || opts.suicides
var delegatecall = opts.delegatecall || false
var isStatic = opts.static || false
var salt = opts.salt || null

txValue = new BN(txValue)

Expand Down Expand Up @@ -77,41 +78,14 @@ module.exports = function (opts, cb) {
code = txData
txData = undefined
var newNonce = new BN(account.nonce).subn(1)
createdAddress = toAddress = ethUtil.generateAddress(caller, newNonce.toArray())
stateManager.clearContractStorage(createdAddress, function (err) {
if (err) {
done(err)
}

async.series([
newContractEvent,
getAccount
], done)

function newContractEvent (callback) {
/**
* The `newContract` event when a contract is created
*
* @event Event: newContract
* @type {Object}
* @property {Buffer} address the created address for the new contract (type `Buffer | Uint8Array`)
* @property {Buffer} code the deployment bytecode for reference (type `Buffer | Uint8Array`)
*/
self.emit('newContract', {
address: createdAddress,
code: code
}, callback)
}
if (salt) {
createdAddress = toAddress = ethUtil.generateAddress2(caller, salt, code)
} else {
createdAddress = toAddress = ethUtil.generateAddress(caller, newNonce.toArray())
}

function getAccount (callback) {
stateManager.getAccount(createdAddress, function (err, account) {
toAccount = account
const NONCE_OFFSET = 1
toAccount.nonce = new BN(toAccount.nonce).addn(NONCE_OFFSET).toArrayLike(Buffer)
callback(err)
})
}
})
checkAccountState(createdAddress, setupNewContract, done)
} else {
// else load the `to` account
stateManager.getAccount(toAddress, function (err, account) {
Expand All @@ -121,6 +95,53 @@ module.exports = function (opts, cb) {
}
}

function checkAccountState (address, next, done) {
stateManager.getAccount(address, function (err, account) {
if (err) {
done(err)
return
}

if ((account.nonce && new BN(account.nonce) > 0) || account.codeHash.compare(ethUtil.KECCAK256_NULL) !== 0) {
toAccount = account
code = new Buffer('fe', 'hex') // Invalid init code
done()
return
}

next(address, done)
})
}

function setupNewContract (address, done) {
stateManager.clearContractStorage(address, function (err) {
if (err) {
done(err)
return
}

async.series([
newContractEvent,
getAccount
], done)

function newContractEvent (callback) {
self.emit('newContract', {
address: address,
code: code
}, callback)
}

function getAccount (callback) {
stateManager.getAccount(address, function (err, account) {
toAccount = account
toAccount.nonce = new BN(toAccount.nonce).addn(1).toArrayLike(Buffer)
callback(err)
})
}
})
}

function subTxValue (cb) {
if (delegatecall) {
cb()
Expand Down Expand Up @@ -199,7 +220,6 @@ module.exports = function (opts, cb) {
var totalGas = results.gasUsed
if (!results.runState.vmError) {
var returnFee = new BN(results.return.length * self._common.param('gasPrices', 'createData'))

totalGas = totalGas.add(returnFee)
}
// if not enough gas
Expand Down

0 comments on commit 5fdce83

Please sign in to comment.