Skip to content
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ If you find a bug that disrupts you, please file an issue with its impact to you
| CALLCODE | ✅ | ❌ |
| RETURN | ✅ |✅ |
| DELEGATECALL | ✅ |✅ |
| CREATE2 | ✅ | |
| CREATE2 | ✅ | |
| STATICCALL | ✅ |✅ |
| REVERT | ✅ |✅ |
| INVALID | ✅ |❓ |
Expand Down
2 changes: 1 addition & 1 deletion include/ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ OP(0xf1,CALL,7,1,G_ACCESS) \
OP(0xf2,CALLCODE,7,1,G_ACCESS) \
OP(0xf3,RETURN,2,0,G_ZERO) \
OP(0xf4,DELEGATECALL,6,1,G_ACCESS) \
OP(0xf5,CREATE2,4,1,G_ACCESS) \
OP(0xf5,CREATE2,4,1,G_CREATE) \
OP(0xf6,ASSERT_0xf6,4,1,G_AUTH) \
OP(0xf7,ASSERT_0xf7,8,1,G_ACCESS) \
OP(0xf8,ASSERT_0xf8,6,1,G_ZERO) \
Expand Down
73 changes: 73 additions & 0 deletions src/evm.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,21 @@ static account_t *createNewAccount(account_t *from) {
return result;
}

// EIP-1014: keccak256(0xff ++ sender ++ salt ++ keccak256(initcode))[12:]
static account_t *createNewAccount2(account_t *from, const uint256_t *salt, const data_t *initcode) {
uint8_t inputBuffer[85];
inputBuffer[0] = 0xff;
memcpy(inputBuffer + 1, from->address.address, 20);
dumpu256BE(salt, inputBuffer + 21);
keccak_256(inputBuffer + 53, 32, initcode->content, initcode->size);
addressHashResult_t hashResult;
keccak_256((uint8_t *)&hashResult, sizeof(hashResult), inputBuffer, 85);
account_t *result = getAccount(hashResult.bottom160);
result->nonce = 1;
result->warm = evmIteration;
return result;
}

static account_t *warmAccount(context_t *callContext, const address_t address) {
account_t *account = getAccount(address);
if (account->warm != evmIteration) {
Expand Down Expand Up @@ -680,6 +695,7 @@ static result_t evmStaticCall(address_t from, uint64_t gas, address_t to, data_t
static result_t evmDelegateCall(uint64_t gas, account_t *codeSource, data_t input);
static result_t evmCall(address_t from, uint64_t gas, address_t to, val_t value, data_t input);
static result_t evmCreate(account_t *fromAccount, uint64_t gas, val_t value, data_t input);
static result_t evmCreate2(account_t *fromAccount, uint64_t gas, val_t value, data_t input, const uint256_t *salt);

static result_t doCall(context_t *callContext) {
if (SHOW_CALLS) {
Expand Down Expand Up @@ -1526,6 +1542,47 @@ static result_t doCall(context_t *callContext) {
copy256(callContext->top - 1, &createResult.status);
}
break;
case CREATE2:
{
data_t input;
input.size = LOWER(LOWER_P(callContext->top));
uint64_t src = LOWER(LOWER_P(callContext->top + 1));
if (!ensureMemory(callContext, src + input.size)) {
OUT_OF_GAS;
}
input.content = callContext->memory.uint8s + src;
if (UPPER(UPPER_P(callContext->top + 2))
|| LOWER(UPPER_P(callContext->top + 2))
|| UPPER(LOWER_P(callContext->top + 2)) >> 32) {
callContext->returnData.size = 0;
clear256(callContext->top - 1);
break;
}
val_t value;
value[0] = UPPER(LOWER_P(callContext->top + 2));
value[1] = LOWER(LOWER_P(callContext->top + 2)) >> 32;
value[2] = LOWER(LOWER_P(callContext->top + 2));
const uint256_t *salt = callContext->top - 1;

// apply R function before L function; CREATE2 adds keccak word cost
uint64_t rGas = initcodeGas(&input) + G_KECCAK_WORD * ((input.size + 31) >> 5);
if (callContext->gas < rGas) {
OUT_OF_GAS;
}
callContext->gas -= rGas;
uint64_t gas = L(callContext->gas);
callContext->gas -= gas;

result_t createResult = evmCreate2(callContext->account, gas, value, input, salt);
callContext->gas += createResult.gasRemaining;
mergeStateChanges(&result.stateChanges, createResult.stateChanges);
callContext->returnData = createResult.returnData;
if (!zero256(&createResult.status)) {
callContext->returnData.size = 0; // EIP-211: success = empty buffer
}
copy256(callContext->top - 1, &createResult.status);
}
break;
case CALL:
{
data_t input;
Expand Down Expand Up @@ -1967,6 +2024,22 @@ result_t evmCreate(account_t *fromAccount, uint64_t gas, val_t value, data_t inp
return _evmConstruct(fromAccount->address, createNewAccount(fromAccount), gas, value, input);
}

static result_t evmCreate2(account_t *fromAccount, uint64_t gas, val_t value, data_t input, const uint256_t *salt) {
if (!BalanceSub(fromAccount->balance, value)) {
fprintf(stderr, "Insufficient balance [0x%08x%08x%08x] for create2 (need [0x%08x%08x%08x])\n",
fromAccount->balance[0], fromAccount->balance[1], fromAccount->balance[2],
value[0], value[1], value[2]
);
result_t result;
result.gasRemaining = gas;
result.stateChanges = NULL;
clear256(&result.status);
result.returnData.size = 0;
return result;
}
return _evmConstruct(fromAccount->address, createNewAccount2(fromAccount, salt, &input), gas, value, input);
}

result_t txCreate(address_t from, uint64_t gas, val_t value, data_t input) {
account_t *fromAccount = getAccount(from);
fromAccount->warm = evmIteration;
Expand Down
14 changes: 14 additions & 0 deletions tst/create2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"construct": "tst/in/create2.evm",
"address": "0xdeadbeef00000000000000000000000000000000",
"tests": [
{
"name": "EIP-1014 vector 1: salt=0 init=0x00",
"gasUsed": "0xcf35",
"input": "0x00",
"output": "0x000000000000000000000000b928f69bb1d91cd65274e3c79d8986362984fda3"
}
]
}
]
71 changes: 71 additions & 0 deletions tst/evm.c
Original file line number Diff line number Diff line change
Expand Up @@ -2225,6 +2225,75 @@ void test_createOutOfGas() {
evmFinalize();
}

void test_create2() {
evmInit();

address_t from = AddressFromHex42("0xf000000000000000000000000000000000000000");
address_t to = AddressFromHex42("0xdeadbeef00000000000000000000000000000000");
uint64_t gas = 1000000;
val_t value;
data_t empty = {0, NULL};
value[0] = 0; value[1] = 0; value[2] = 0;
empty.size = 0;

op_t code[] = {
PUSH0, PUSH1, 0x01, PUSH0, PUSH0, CREATE2,
PUSH0, MSTORE,
PUSH1, 20, PUSH1, 12, RETURN,
};
data_t code_data = {sizeof(code), code};
evmMockCode(to, code_data);

result_t result = txCall(from, gas, to, value, empty, NULL);

assert(result.returnData.size == 20);
uint8_t expected[20] = {
0xb9, 0x28, 0xf6, 0x9b, 0xb1, 0xd9, 0x1c, 0xd6, 0x52, 0x74, 0xe3, 0xc7,
0x9d, 0x89, 0x86, 0x36, 0x29, 0x84, 0xfd, 0xa3,
};
assert(memcmp(result.returnData.content, expected, 20) == 0);
// https://hoodi.etherscan.io/tx/0xcf535e989d23766f325ca3d98f609c990b1eb72586abcb70a31101b1a91d8b76
assert(gas - result.gasRemaining == 53031);

evmMockCode(to, empty);
evmFinalize();
}

void test_create2InsufficientBalance() {
evmInit();

address_t from = AddressFromHex42("0xf000000000000000000000000000000000000000");
address_t to = AddressFromHex42("0xdeadbeef00000000000000000000000000000000");
uint64_t gas = 1000000;
val_t value;
data_t input = {0, NULL};
value[0] = 0; value[1] = 0; value[2] = 0;
input.size = 0;

op_t code[] = {
PUSH0, PUSH1, 0x01, DUP2, DUP2, CREATE2,
PUSH0, MSTORE,
PUSH1, 20, PUSH1, 12, RETURN,
};
data_t code_data = {sizeof(code), code};
evmMockCode(to, code_data);

assertStderr(
"Insufficient balance [0x000000000000000000000000] for create2 (need [0x000000000000000000000001])\n",
result_t result = txCall(from, gas, to, value, input, NULL)
);

assert(result.returnData.size == 20);
uint8_t zeroes[20];
bzero(zeroes, sizeof(zeroes));
assert(memcmp(result.returnData.content, zeroes, 20) == 0);
// https://hoodi.etherscan.io/tx/0xf3c3de8400562fe80b140a455b5842dbc971e090c9ddf67cbb3629bd3548bb02
assert(gas - result.gasRemaining == 53033);

evmMockCode(to, input);
evmFinalize();
}

void test_returnDataCopyOOB() {
evmInit();

Expand Down Expand Up @@ -2459,6 +2528,8 @@ int main() {
test_staticcallSstore();
test_createInsufficientBalance();
test_createOutOfGas();
test_create2();
test_create2InsufficientBalance();

for (op_t PUSHx = PUSH0; PUSHx <= PUSH32; PUSHx++) {
test_jumpForwardScan(PUSHx);
Expand Down
3 changes: 3 additions & 0 deletions tst/in/create2.evm
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CALLDATACOPY(0, 0, CALLDATASIZE)
MSTORE(0, CREATE2(0, 0, CALLDATASIZE, 0))
RETURN(0, 32)
1 change: 1 addition & 0 deletions tst/out/create2.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
365f5f375f365f5ff55f5260205ff3