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

EIP 1014 CREATE2 #329

Merged
merged 3 commits into from
Nov 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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