Skip to content

Commit

Permalink
add support for CREATE2
Browse files Browse the repository at this point in the history
trap if not constantinople

add check if nonce or code at address

Fix lint errors

Refactoring

Fix lint

Use invalid init code on colission

Fix lint
  • Loading branch information
jwasinger authored and rmeissner committed Nov 3, 2018
1 parent 41e3f63 commit 21cf775
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 33 deletions.
31 changes: 31 additions & 0 deletions 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 hashingq
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
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: 55 additions & 33 deletions lib/runCall.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const BN = ethUtil.BN
const exceptions = require('./exceptions.js')

const ERROR = exceptions.ERROR
const EMPTY_CODE_HASH = ethUtil.keccak256()

/**
* runs a CALL operation
Expand Down Expand Up @@ -47,6 +48,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 +79,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 +96,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(EMPTY_CODE_HASH) !== 0) {
toAccount = account
code = new Buffer('0xfe', '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

0 comments on commit 21cf775

Please sign in to comment.