From fa33a771b00ead5039d54e436a990a5b67ff7cc3 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 17 Jul 2018 16:39:01 +0200 Subject: [PATCH 1/6] CREATE2 implementation in LegacyVM --- libethereum/Executive.cpp | 2 +- libethereum/ExtVM.cpp | 7 +++++-- libevm/Instruction.h | 2 +- libevm/LegacyVMCalls.cpp | 21 ++++++--------------- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 186b6dd24ce..3187617181d 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -365,7 +365,7 @@ bool Executive::createOpcode(Address const& _sender, u256 const& _endowment, u25 bool Executive::create2Opcode(Address const& _sender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin, u256 const& _salt) { - m_newAddress = right160(sha3(_sender.asBytes() + toBigEndian(_salt) + sha3(_init).asBytes())); + m_newAddress = right160(sha3(_sender.asBytes() + toBigEndian(_salt) + _init)); return executeCreate(_sender, _endowment, _gasPrice, _gas, _init, _origin); } diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 005073ba623..005414bdd28 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -152,8 +152,11 @@ CreateResult ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, I bool result = false; if (_op == Instruction::CREATE) result = e.createOpcode(myAddress, _endowment, gasPrice, io_gas, _code, origin); - else - result = e.create2Opcode(myAddress, _endowment, gasPrice, io_gas, _code, origin, _salt); + else + { + assert(_op == Instruction::CREATE2); + result = e.create2Opcode(myAddress, _endowment, gasPrice, io_gas, _code, origin, _salt); + } if (!result) { diff --git a/libevm/Instruction.h b/libevm/Instruction.h index ed67039b478..c2f0306700f 100644 --- a/libevm/Instruction.h +++ b/libevm/Instruction.h @@ -222,7 +222,7 @@ enum class Instruction: uint8_t RETURN, ///< halt execution returning output data DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender STATICCALL = 0xfa, ///< like CALL except state changing operation are not permitted (will throw) - CREATE2 = 0xfb, ///< create a new account with associated code. sha3((sender + salt + sha3(code)) + CREATE2 = 0xf5, ///< create a new account with associated code. sha3((sender + salt + sha3(code)) REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas INVALID = 0xfe, ///< dedicated invalid instruction SUICIDE = 0xff ///< halt execution and register account for later deletion diff --git a/libevm/LegacyVMCalls.cpp b/libevm/LegacyVMCalls.cpp index 8a128e7c451..65b36764aaa 100644 --- a/libevm/LegacyVMCalls.cpp +++ b/libevm/LegacyVMCalls.cpp @@ -121,22 +121,13 @@ void LegacyVM::caseCreate() m_runGas = toInt63(m_schedule->createGas); // Collect arguments. - u256 endowment = m_SP[0]; - u256 salt; - u256 initOff; - u256 initSize; + u256 const endowment = m_SP[0]; + u256 const initOff = m_SP[1]; + u256 const initSize = m_SP[2]; - if (m_OP == Instruction::CREATE) - { - initOff = m_SP[1]; - initSize = m_SP[2]; - } - else - { - salt = m_SP[1]; - initOff = m_SP[2]; - initSize = m_SP[3]; - } + u256 salt; + if (m_OP == Instruction::CREATE2) + salt = m_SP[3]; updateMem(memNeed(initOff, initSize)); updateIOGas(); From 5a4813f1b6f059cc9089f89e0dd7c89762e1b929 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 18 Jul 2018 14:43:59 +0200 Subject: [PATCH 2/6] Unit test for CREATE2 implementation --- libethereum/LastBlockHashesFace.h | 2 + test/unittests/libevm/LegacyVMTest.cpp | 88 ++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 test/unittests/libevm/LegacyVMTest.cpp diff --git a/libethereum/LastBlockHashesFace.h b/libethereum/LastBlockHashesFace.h index 0cf14849355..2083b839117 100644 --- a/libethereum/LastBlockHashesFace.h +++ b/libethereum/LastBlockHashesFace.h @@ -20,6 +20,8 @@ #pragma once +#include + namespace dev { diff --git a/test/unittests/libevm/LegacyVMTest.cpp b/test/unittests/libevm/LegacyVMTest.cpp new file mode 100644 index 00000000000..85e5fb9e647 --- /dev/null +++ b/test/unittests/libevm/LegacyVMTest.cpp @@ -0,0 +1,88 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +using namespace dev; +using namespace dev::test; +using namespace dev::eth; + + +namespace +{ +class LastBlockHashes: public eth::LastBlockHashesFace +{ +public: + h256s precedingHashes(h256 const& /* _mostRecentHash */) const override + { + return h256s(256, h256()); + } + void clear() override {} +}; +} + +BOOST_FIXTURE_TEST_SUITE(LegacyVMSuite, TestOutputHelperFixture) + +BOOST_AUTO_TEST_CASE(create2) +{ + BlockHeader blockHeader; + blockHeader.setGasLimit(0x7fffffffffffffff); + blockHeader.setTimestamp(0); + + LastBlockHashes lastBlockHashes; + EnvInfo envInfo(blockHeader, lastBlockHashes, 0); + + Address address = KeyPair::create().address(); + State state(0); + state.addBalance(address, 1 * ether); + + std::unique_ptr se(ChainParams(genesisInfo(eth::Network::ConstantinopleTest)).createSealEngine()); + + + u256 value = 0; + u256 gasPrice = 1; + int depth = 0; + bool isCreate = true; + bool staticCall = false; + // mstore(0, 0x60) + // return(0, 0x20) + bytes inputData = fromHex("606060005260206000f3"); + // let s : = calldatasize() + // calldatacopy(0, 0, s) + // create2(0, 0, s, 0x123) + // pop + bytes code = fromHex("368060006000376101238160006000f55050"); + + ExtVM extVm(state, envInfo, *se, address, + address, address, value, gasPrice, ref(inputData), + ref(code), sha3(code), depth, isCreate, staticCall); + + LegacyVM vm; + + u256 gas = 1000000; + owning_bytes_ref res = vm.exec(gas, extVm, OnOpFunc{}); + + Address expectedAddress = right160(sha3(address.asBytes() + toBigEndian(0x123_cppui256) + inputData)); + BOOST_REQUIRE(state.addressHasCode(expectedAddress)); +} + +BOOST_AUTO_TEST_SUITE_END() From 7b88aa67aa29f1e757536118936d0c0c1b14dcc1 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 18 Jul 2018 14:50:00 +0200 Subject: [PATCH 3/6] Clang-format and convert tabs to spaces --- libethereum/ExtVM.cpp | 150 ++++----- libevm/Instruction.h | 432 ++++++++++++------------- libevm/LegacyVMCalls.cpp | 394 +++++++++++----------- test/unittests/libevm/LegacyVMTest.cpp | 15 +- 4 files changed, 496 insertions(+), 495 deletions(-) diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 005414bdd28..7ea89f1f936 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file ExtVM.cpp * @author Gav Wood @@ -38,11 +38,11 @@ static size_t const c_singleExecutionStackSize = 100 * 1024; /// Standard thread stack size. static size_t const c_defaultStackSize = #if defined(__linux) - 8 * 1024 * 1024; + 8 * 1024 * 1024; #elif defined(_WIN32) - 16 * 1024 * 1024; + 16 * 1024 * 1024; #else - 512 * 1024; // OSX and other OSs + 512 * 1024; // OSX and other OSs #endif /// Stack overhead prior to allocation. @@ -53,41 +53,41 @@ static unsigned const c_offloadPoint = (c_defaultStackSize - c_entryOverhead) / void goOnOffloadedStack(Executive& _e, OnOpFunc const& _onOp) { - // Set new stack size enouth to handle the rest of the calls up to the limit. - boost::thread::attributes attrs; - attrs.set_stack_size((c_depthLimit - c_offloadPoint) * c_singleExecutionStackSize); - - // Create new thread with big stack and join immediately. - // TODO: It is possible to switch the implementation to Boost.Context or similar when the API is stable. - boost::exception_ptr exception; - boost::thread{attrs, [&]{ - try - { - _e.go(_onOp); - } - catch (...) - { - exception = boost::current_exception(); // Catch all exceptions to be rethrown in parent thread. - } - }}.join(); - if (exception) - boost::rethrow_exception(exception); + // Set new stack size enouth to handle the rest of the calls up to the limit. + boost::thread::attributes attrs; + attrs.set_stack_size((c_depthLimit - c_offloadPoint) * c_singleExecutionStackSize); + + // Create new thread with big stack and join immediately. + // TODO: It is possible to switch the implementation to Boost.Context or similar when the API is stable. + boost::exception_ptr exception; + boost::thread{attrs, [&]{ + try + { + _e.go(_onOp); + } + catch (...) + { + exception = boost::current_exception(); // Catch all exceptions to be rethrown in parent thread. + } + }}.join(); + if (exception) + boost::rethrow_exception(exception); } void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp) { - // If in the offloading point we need to switch to additional separated stack space. - // Current stack is too small to handle more CALL/CREATE executions. - // It needs to be done only once as newly allocated stack space it enough to handle - // the rest of the calls up to the depth limit (c_depthLimit). - - if (_depth == c_offloadPoint) - { - cnote << "Stack offloading (depth: " << c_offloadPoint << ")"; - goOnOffloadedStack(_e, _onOp); - } - else - _e.go(_onOp); + // If in the offloading point we need to switch to additional separated stack space. + // Current stack is too small to handle more CALL/CREATE executions. + // It needs to be done only once as newly allocated stack space it enough to handle + // the rest of the calls up to the depth limit (c_depthLimit). + + if (_depth == c_offloadPoint) + { + cnote << "Stack offloading (depth: " << c_offloadPoint << ")"; + goOnOffloadedStack(_e, _onOp); + } + else + _e.go(_onOp); } evmc_status_code transactionExceptionToEvmcStatusCode(TransactionException ex) noexcept @@ -138,33 +138,33 @@ CallResult ExtVM::call(CallParameters& _p) size_t ExtVM::codeSizeAt(dev::Address _a) { - return m_s.codeSize(_a); + return m_s.codeSize(_a); } void ExtVM::setStore(u256 _n, u256 _v) { - m_s.setStorage(myAddress, _n, _v); + m_s.setStorage(myAddress, _n, _v); } CreateResult ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, Instruction _op, u256 _salt, OnOpFunc const& _onOp) { - Executive e{m_s, envInfo(), m_sealEngine, depth + 1}; - bool result = false; - if (_op == Instruction::CREATE) - result = e.createOpcode(myAddress, _endowment, gasPrice, io_gas, _code, origin); + Executive e{m_s, envInfo(), m_sealEngine, depth + 1}; + bool result = false; + if (_op == Instruction::CREATE) + result = e.createOpcode(myAddress, _endowment, gasPrice, io_gas, _code, origin); else { assert(_op == Instruction::CREATE2); result = e.create2Opcode(myAddress, _endowment, gasPrice, io_gas, _code, origin, _salt); } - if (!result) - { - go(depth, e, _onOp); - e.accrueSubState(sub); - } - io_gas = e.gas(); - return {transactionExceptionToEvmcStatusCode(e.getException()), e.takeOutput(), e.newAddress()}; + if (!result) + { + go(depth, e, _onOp); + e.accrueSubState(sub); + } + io_gas = e.gas(); + return {transactionExceptionToEvmcStatusCode(e.getException()), e.takeOutput(), e.newAddress()}; } void ExtVM::suicide(Address _a) @@ -180,26 +180,26 @@ void ExtVM::suicide(Address _a) h256 ExtVM::blockHash(u256 _number) { - u256 const currentNumber = envInfo().number(); + u256 const currentNumber = envInfo().number(); - if (_number >= currentNumber || _number < (std::max(256, currentNumber) - 256)) - return h256(); + if (_number >= currentNumber || _number < (std::max(256, currentNumber) - 256)) + return h256(); - if (currentNumber < m_sealEngine.chainParams().constantinopleForkBlock + 256) - { - h256 const parentHash = envInfo().header().parentHash(); - h256s const lastHashes = envInfo().lastHashes().precedingHashes(parentHash); + if (currentNumber < m_sealEngine.chainParams().constantinopleForkBlock + 256) + { + h256 const parentHash = envInfo().header().parentHash(); + h256s const lastHashes = envInfo().lastHashes().precedingHashes(parentHash); - assert(lastHashes.size() > (unsigned)(currentNumber - 1 - _number)); - return lastHashes[(unsigned)(currentNumber - 1 - _number)]; - } + assert(lastHashes.size() > (unsigned)(currentNumber - 1 - _number)); + return lastHashes[(unsigned)(currentNumber - 1 - _number)]; + } - u256 const nonce = m_s.getNonce(caller); - u256 const gas = 1000000; - Transaction tx(0, 0, gas, c_blockhashContractAddress, toBigEndian(_number), nonce); - tx.forceSender(caller); + u256 const nonce = m_s.getNonce(caller); + u256 const gas = 1000000; + Transaction tx(0, 0, gas, c_blockhashContractAddress, toBigEndian(_number), nonce); + tx.forceSender(caller); - ExecutionResult res; - std::tie(res, std::ignore) = m_s.execute(envInfo(), m_sealEngine, tx, Permanence::Reverted); - return h256(res.output); + ExecutionResult res; + std::tie(res, std::ignore) = m_s.execute(envInfo(), m_sealEngine, tx, Permanence::Reverted); + return h256(res.output); } diff --git a/libevm/Instruction.h b/libevm/Instruction.h index c2f0306700f..e80ce64972c 100644 --- a/libevm/Instruction.h +++ b/libevm/Instruction.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ #pragma once @@ -27,234 +27,234 @@ namespace eth /// Virtual machine bytecode instruction. enum class Instruction: uint8_t { - STOP = 0x00, ///< halts execution - ADD, ///< addition operation - MUL, ///< mulitplication operation - SUB, ///< subtraction operation - DIV, ///< integer division operation - SDIV, ///< signed integer division operation - MOD, ///< modulo remainder operation - SMOD, ///< signed modulo remainder operation - ADDMOD, ///< unsigned modular addition - MULMOD, ///< unsigned modular multiplication - EXP, ///< exponential operation - SIGNEXTEND, ///< extend length of signed integer + STOP = 0x00, ///< halts execution + ADD, ///< addition operation + MUL, ///< mulitplication operation + SUB, ///< subtraction operation + DIV, ///< integer division operation + SDIV, ///< signed integer division operation + MOD, ///< modulo remainder operation + SMOD, ///< signed modulo remainder operation + ADDMOD, ///< unsigned modular addition + MULMOD, ///< unsigned modular multiplication + EXP, ///< exponential operation + SIGNEXTEND, ///< extend length of signed integer - LT = 0x10, ///< less-than comparision - GT, ///< greater-than comparision - SLT, ///< signed less-than comparision - SGT, ///< signed greater-than comparision - EQ, ///< equality comparision - ISZERO, ///< simple not operator - AND, ///< bitwise AND operation - OR, ///< bitwise OR operation - XOR, ///< bitwise XOR operation - NOT, ///< bitwise NOT operation - BYTE, ///< retrieve single byte from word - SHL, ///< logical shift left operation - SHR, ///< logical shift right operation - SAR, ///< arithmetic shift right operation + LT = 0x10, ///< less-than comparision + GT, ///< greater-than comparision + SLT, ///< signed less-than comparision + SGT, ///< signed greater-than comparision + EQ, ///< equality comparision + ISZERO, ///< simple not operator + AND, ///< bitwise AND operation + OR, ///< bitwise OR operation + XOR, ///< bitwise XOR operation + NOT, ///< bitwise NOT operation + BYTE, ///< retrieve single byte from word + SHL, ///< logical shift left operation + SHR, ///< logical shift right operation + SAR, ///< arithmetic shift right operation - SHA3 = 0x20, ///< compute SHA3-256 hash + SHA3 = 0x20, ///< compute SHA3-256 hash - ADDRESS = 0x30, ///< get address of currently executing account - BALANCE, ///< get balance of the given account - ORIGIN, ///< get execution origination address - CALLER, ///< get caller address - CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution - CALLDATALOAD, ///< get input data of current environment - CALLDATASIZE, ///< get size of input data in current environment - CALLDATACOPY, ///< copy input data in current environment to memory - CODESIZE, ///< get size of code running in current environment - CODECOPY, ///< copy code running in current environment to memory - GASPRICE, ///< get price of gas in current environment - EXTCODESIZE, ///< get external code size (from another contract) - EXTCODECOPY, ///< copy external code (from another contract) - RETURNDATASIZE = 0x3d, ///< size of data returned from previous call - RETURNDATACOPY = 0x3e, ///< copy data returned from previous call to memory + ADDRESS = 0x30, ///< get address of currently executing account + BALANCE, ///< get balance of the given account + ORIGIN, ///< get execution origination address + CALLER, ///< get caller address + CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution + CALLDATALOAD, ///< get input data of current environment + CALLDATASIZE, ///< get size of input data in current environment + CALLDATACOPY, ///< copy input data in current environment to memory + CODESIZE, ///< get size of code running in current environment + CODECOPY, ///< copy code running in current environment to memory + GASPRICE, ///< get price of gas in current environment + EXTCODESIZE, ///< get external code size (from another contract) + EXTCODECOPY, ///< copy external code (from another contract) + RETURNDATASIZE = 0x3d, ///< size of data returned from previous call + RETURNDATACOPY = 0x3e, ///< copy data returned from previous call to memory - BLOCKHASH = 0x40, ///< get hash of most recent complete block - COINBASE, ///< get the block's coinbase address - TIMESTAMP, ///< get the block's timestamp - NUMBER, ///< get the block's number - DIFFICULTY, ///< get the block's difficulty - GASLIMIT, ///< get the block's gas limit - - POP = 0x50, ///< remove item from stack - MLOAD, ///< load word from memory - MSTORE, ///< save word to memory - MSTORE8, ///< save byte to memory - SLOAD, ///< load word from storage - SSTORE, ///< save word to storage - JUMP, ///< alter the program counter to a jumpdest - JUMPI, ///< conditionally alter the program counter - PC, ///< get the program counter - MSIZE, ///< get the size of active memory - GAS, ///< get the amount of available gas - JUMPDEST, ///< set a potential jump destination - - PUSH1 = 0x60, ///< place 1 byte item on stack - PUSH2, ///< place 2 byte item on stack - PUSH3, ///< place 3 byte item on stack - PUSH4, ///< place 4 byte item on stack - PUSH5, ///< place 5 byte item on stack - PUSH6, ///< place 6 byte item on stack - PUSH7, ///< place 7 byte item on stack - PUSH8, ///< place 8 byte item on stack - PUSH9, ///< place 9 byte item on stack - PUSH10, ///< place 10 byte item on stack - PUSH11, ///< place 11 byte item on stack - PUSH12, ///< place 12 byte item on stack - PUSH13, ///< place 13 byte item on stack - PUSH14, ///< place 14 byte item on stack - PUSH15, ///< place 15 byte item on stack - PUSH16, ///< place 16 byte item on stack - PUSH17, ///< place 17 byte item on stack - PUSH18, ///< place 18 byte item on stack - PUSH19, ///< place 19 byte item on stack - PUSH20, ///< place 20 byte item on stack - PUSH21, ///< place 21 byte item on stack - PUSH22, ///< place 22 byte item on stack - PUSH23, ///< place 23 byte item on stack - PUSH24, ///< place 24 byte item on stack - PUSH25, ///< place 25 byte item on stack - PUSH26, ///< place 26 byte item on stack - PUSH27, ///< place 27 byte item on stack - PUSH28, ///< place 28 byte item on stack - PUSH29, ///< place 29 byte item on stack - PUSH30, ///< place 30 byte item on stack - PUSH31, ///< place 31 byte item on stack - PUSH32, ///< place 32 byte item on stack - - DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack - DUP2, ///< copies the second highest item in the stack to the top of the stack - DUP3, ///< copies the third highest item in the stack to the top of the stack - DUP4, ///< copies the 4th highest item in the stack to the top of the stack - DUP5, ///< copies the 5th highest item in the stack to the top of the stack - DUP6, ///< copies the 6th highest item in the stack to the top of the stack - DUP7, ///< copies the 7th highest item in the stack to the top of the stack - DUP8, ///< copies the 8th highest item in the stack to the top of the stack - DUP9, ///< copies the 9th highest item in the stack to the top of the stack - DUP10, ///< copies the 10th highest item in the stack to the top of the stack - DUP11, ///< copies the 11th highest item in the stack to the top of the stack - DUP12, ///< copies the 12th highest item in the stack to the top of the stack - DUP13, ///< copies the 13th highest item in the stack to the top of the stack - DUP14, ///< copies the 14th highest item in the stack to the top of the stack - DUP15, ///< copies the 15th highest item in the stack to the top of the stack - DUP16, ///< copies the 16th highest item in the stack to the top of the stack + BLOCKHASH = 0x40, ///< get hash of most recent complete block + COINBASE, ///< get the block's coinbase address + TIMESTAMP, ///< get the block's timestamp + NUMBER, ///< get the block's number + DIFFICULTY, ///< get the block's difficulty + GASLIMIT, ///< get the block's gas limit + + POP = 0x50, ///< remove item from stack + MLOAD, ///< load word from memory + MSTORE, ///< save word to memory + MSTORE8, ///< save byte to memory + SLOAD, ///< load word from storage + SSTORE, ///< save word to storage + JUMP, ///< alter the program counter to a jumpdest + JUMPI, ///< conditionally alter the program counter + PC, ///< get the program counter + MSIZE, ///< get the size of active memory + GAS, ///< get the amount of available gas + JUMPDEST, ///< set a potential jump destination + + PUSH1 = 0x60, ///< place 1 byte item on stack + PUSH2, ///< place 2 byte item on stack + PUSH3, ///< place 3 byte item on stack + PUSH4, ///< place 4 byte item on stack + PUSH5, ///< place 5 byte item on stack + PUSH6, ///< place 6 byte item on stack + PUSH7, ///< place 7 byte item on stack + PUSH8, ///< place 8 byte item on stack + PUSH9, ///< place 9 byte item on stack + PUSH10, ///< place 10 byte item on stack + PUSH11, ///< place 11 byte item on stack + PUSH12, ///< place 12 byte item on stack + PUSH13, ///< place 13 byte item on stack + PUSH14, ///< place 14 byte item on stack + PUSH15, ///< place 15 byte item on stack + PUSH16, ///< place 16 byte item on stack + PUSH17, ///< place 17 byte item on stack + PUSH18, ///< place 18 byte item on stack + PUSH19, ///< place 19 byte item on stack + PUSH20, ///< place 20 byte item on stack + PUSH21, ///< place 21 byte item on stack + PUSH22, ///< place 22 byte item on stack + PUSH23, ///< place 23 byte item on stack + PUSH24, ///< place 24 byte item on stack + PUSH25, ///< place 25 byte item on stack + PUSH26, ///< place 26 byte item on stack + PUSH27, ///< place 27 byte item on stack + PUSH28, ///< place 28 byte item on stack + PUSH29, ///< place 29 byte item on stack + PUSH30, ///< place 30 byte item on stack + PUSH31, ///< place 31 byte item on stack + PUSH32, ///< place 32 byte item on stack + + DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack + DUP2, ///< copies the second highest item in the stack to the top of the stack + DUP3, ///< copies the third highest item in the stack to the top of the stack + DUP4, ///< copies the 4th highest item in the stack to the top of the stack + DUP5, ///< copies the 5th highest item in the stack to the top of the stack + DUP6, ///< copies the 6th highest item in the stack to the top of the stack + DUP7, ///< copies the 7th highest item in the stack to the top of the stack + DUP8, ///< copies the 8th highest item in the stack to the top of the stack + DUP9, ///< copies the 9th highest item in the stack to the top of the stack + DUP10, ///< copies the 10th highest item in the stack to the top of the stack + DUP11, ///< copies the 11th highest item in the stack to the top of the stack + DUP12, ///< copies the 12th highest item in the stack to the top of the stack + DUP13, ///< copies the 13th highest item in the stack to the top of the stack + DUP14, ///< copies the 14th highest item in the stack to the top of the stack + DUP15, ///< copies the 15th highest item in the stack to the top of the stack + DUP16, ///< copies the 16th highest item in the stack to the top of the stack - SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack - SWAP2, ///< swaps the highest and third highest value on the stack - SWAP3, ///< swaps the highest and 4th highest value on the stack - SWAP4, ///< swaps the highest and 5th highest value on the stack - SWAP5, ///< swaps the highest and 6th highest value on the stack - SWAP6, ///< swaps the highest and 7th highest value on the stack - SWAP7, ///< swaps the highest and 8th highest value on the stack - SWAP8, ///< swaps the highest and 9th highest value on the stack - SWAP9, ///< swaps the highest and 10th highest value on the stack - SWAP10, ///< swaps the highest and 11th highest value on the stack - SWAP11, ///< swaps the highest and 12th highest value on the stack - SWAP12, ///< swaps the highest and 13th highest value on the stack - SWAP13, ///< swaps the highest and 14th highest value on the stack - SWAP14, ///< swaps the highest and 15th highest value on the stack - SWAP15, ///< swaps the highest and 16th highest value on the stack - SWAP16, ///< swaps the highest and 17th highest value on the stack + SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack + SWAP2, ///< swaps the highest and third highest value on the stack + SWAP3, ///< swaps the highest and 4th highest value on the stack + SWAP4, ///< swaps the highest and 5th highest value on the stack + SWAP5, ///< swaps the highest and 6th highest value on the stack + SWAP6, ///< swaps the highest and 7th highest value on the stack + SWAP7, ///< swaps the highest and 8th highest value on the stack + SWAP8, ///< swaps the highest and 9th highest value on the stack + SWAP9, ///< swaps the highest and 10th highest value on the stack + SWAP10, ///< swaps the highest and 11th highest value on the stack + SWAP11, ///< swaps the highest and 12th highest value on the stack + SWAP12, ///< swaps the highest and 13th highest value on the stack + SWAP13, ///< swaps the highest and 14th highest value on the stack + SWAP14, ///< swaps the highest and 15th highest value on the stack + SWAP15, ///< swaps the highest and 16th highest value on the stack + SWAP16, ///< swaps the highest and 17th highest value on the stack - LOG0 = 0xa0, ///< Makes a log entry; no topics. - LOG1, ///< Makes a log entry; 1 topic. - LOG2, ///< Makes a log entry; 2 topics. - LOG3, ///< Makes a log entry; 3 topics. - LOG4, ///< Makes a log entry; 4 topics. + LOG0 = 0xa0, ///< Makes a log entry; no topics. + LOG1, ///< Makes a log entry; 1 topic. + LOG2, ///< Makes a log entry; 2 topics. + LOG3, ///< Makes a log entry; 3 topics. + LOG4, ///< Makes a log entry; 4 topics. - // these are generated by the interpreter - should never be in user code - PUSHC = 0xac, ///< push value from constant pool - JUMPC, ///< alter the program counter - pre-verified - JUMPCI, ///< conditionally alter the program counter - pre-verified + // these are generated by the interpreter - should never be in user code + PUSHC = 0xac, ///< push value from constant pool + JUMPC, ///< alter the program counter - pre-verified + JUMPCI, ///< conditionally alter the program counter - pre-verified - JUMPTO = 0xb0, ///< alter the program counter to a jumpdest - JUMPIF, ///< conditionally alter the program counter - JUMPSUB, ///< alter the program counter to a beginsub - JUMPV, ///< alter the program counter to a jumpdest - JUMPSUBV, ///< alter the program counter to a beginsub - BEGINSUB, ///< set a potential jumpsub destination - BEGINDATA, ///< begine the data section - RETURNSUB, ///< return to subroutine jumped from - PUTLOCAL, ///< pop top of stack to local variable - GETLOCAL, ///< push local variable to top of stack + JUMPTO = 0xb0, ///< alter the program counter to a jumpdest + JUMPIF, ///< conditionally alter the program counter + JUMPSUB, ///< alter the program counter to a beginsub + JUMPV, ///< alter the program counter to a jumpdest + JUMPSUBV, ///< alter the program counter to a beginsub + BEGINSUB, ///< set a potential jumpsub destination + BEGINDATA, ///< begine the data section + RETURNSUB, ///< return to subroutine jumped from + PUTLOCAL, ///< pop top of stack to local variable + GETLOCAL, ///< push local variable to top of stack - XADD = 0xc1, ///< addition operation - XMUL, ///< mulitplication operation - XSUB, ///< subtraction operation - XDIV, ///< integer division operation - XSDIV, ///< signed integer division operation - XMOD, ///< modulo remainder operation - XSMOD, ///< signed modulo remainder operation - XLT = 0xd0, ///< less-than comparision - XGT, ///< greater-than comparision - XSLT, ///< signed less-than comparision - XSGT, ///< signed greater-than comparision - XEQ, ///< equality comparision - XISZERO, ///< simple not operator - XAND, ///< bitwise AND operation - XOOR, ///< bitwise OR operation - XXOR, ///< bitwise XOR operation - XNOT, ///< bitwise NOT opertation - XSHL = 0xdb, ///< shift left opertation - XSHR, ///< shift right opertation - XSAR, ///< shift arithmetic right opertation - XROL, ///< rotate left opertation - XROR, ///< rotate right opertation - XPUSH = 0xe0, ///< push vector to stack - XMLOAD, ///< load vector from memory - XMSTORE, ///< save vector to memory - XSLOAD = 0xe4, ///< load vector from storage - XSSTORE, ///< save vector to storage - XVTOWIDE, ///< convert vector to wide integer - XWIDETOV, ///< convert wide integer to vector - XGET, ///< get data from vector - XPUT, ///< put data in vector - XSWIZZLE, ///< permute data in vector - XSHUFFLE, ///< permute data in two vectors + XADD = 0xc1, ///< addition operation + XMUL, ///< mulitplication operation + XSUB, ///< subtraction operation + XDIV, ///< integer division operation + XSDIV, ///< signed integer division operation + XMOD, ///< modulo remainder operation + XSMOD, ///< signed modulo remainder operation + XLT = 0xd0, ///< less-than comparision + XGT, ///< greater-than comparision + XSLT, ///< signed less-than comparision + XSGT, ///< signed greater-than comparision + XEQ, ///< equality comparision + XISZERO, ///< simple not operator + XAND, ///< bitwise AND operation + XOOR, ///< bitwise OR operation + XXOR, ///< bitwise XOR operation + XNOT, ///< bitwise NOT opertation + XSHL = 0xdb, ///< shift left opertation + XSHR, ///< shift right opertation + XSAR, ///< shift arithmetic right opertation + XROL, ///< rotate left opertation + XROR, ///< rotate right opertation + XPUSH = 0xe0, ///< push vector to stack + XMLOAD, ///< load vector from memory + XMSTORE, ///< save vector to memory + XSLOAD = 0xe4, ///< load vector from storage + XSSTORE, ///< save vector to storage + XVTOWIDE, ///< convert vector to wide integer + XWIDETOV, ///< convert wide integer to vector + XGET, ///< get data from vector + XPUT, ///< put data in vector + XSWIZZLE, ///< permute data in vector + XSHUFFLE, ///< permute data in two vectors - CREATE = 0xf0, ///< create a new account with associated code - CALL, ///< message-call into an account - CALLCODE, ///< message-call with another account's code only - RETURN, ///< halt execution returning output data - DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender - STATICCALL = 0xfa, ///< like CALL except state changing operation are not permitted (will throw) - CREATE2 = 0xf5, ///< create a new account with associated code. sha3((sender + salt + sha3(code)) - REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas - INVALID = 0xfe, ///< dedicated invalid instruction - SUICIDE = 0xff ///< halt execution and register account for later deletion + CREATE = 0xf0, ///< create a new account with associated code + CALL, ///< message-call into an account + CALLCODE, ///< message-call with another account's code only + RETURN, ///< halt execution returning output data + DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender + STATICCALL = 0xfa, ///< like CALL except state changing operation are not permitted (will throw) + CREATE2 = 0xf5, ///< create a new account with associated code. sha3((sender + salt + code) + REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas + INVALID = 0xfe, ///< dedicated invalid instruction + SUICIDE = 0xff ///< halt execution and register account for later deletion }; enum class Tier : unsigned { - Zero = 0, // 0, Zero - Base, // 2, Quick - VeryLow, // 3, Fastest - Low, // 5, Fast - Mid, // 8, Mid - High, // 10, Slow - Ext, // 20, Ext - Special, // multiparam or otherwise special - Invalid // Invalid. + Zero = 0, // 0, Zero + Base, // 2, Quick + VeryLow, // 3, Fastest + Low, // 5, Fast + Mid, // 8, Mid + High, // 10, Slow + Ext, // 20, Ext + Special, // multiparam or otherwise special + Invalid // Invalid. }; /// Information structure for a particular instruction. struct InstructionInfo { - char const* const name; ///< The name of the instruction. - int const args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack). - int const ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed. - Tier const gasPriceTier; ///< Tier for gas pricing. + char const* const name; ///< The name of the instruction. + int const args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack). + int const ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed. + Tier const gasPriceTier; ///< Tier for gas pricing. }; struct InstructionMetric { - Tier gasPriceTier; - int args; - int ret; + Tier gasPriceTier; + int args; + int ret; }; /// Information on all the instructions. diff --git a/libevm/LegacyVMCalls.cpp b/libevm/LegacyVMCalls.cpp index 65b36764aaa..47d42f11140 100644 --- a/libevm/LegacyVMCalls.cpp +++ b/libevm/LegacyVMCalls.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ #include "LegacyVM.h" @@ -24,17 +24,17 @@ using namespace dev::eth; void LegacyVM::copyDataToMemory(bytesConstRef _data, u256*_sp) { - auto offset = static_cast(_sp[0]); - s512 bigIndex = _sp[1]; - auto index = static_cast(bigIndex); - auto size = static_cast(_sp[2]); + auto offset = static_cast(_sp[0]); + s512 bigIndex = _sp[1]; + auto index = static_cast(bigIndex); + auto size = static_cast(_sp[2]); - size_t sizeToBeCopied = bigIndex + size > _data.size() ? _data.size() < bigIndex ? 0 : _data.size() - index : size; + size_t sizeToBeCopied = bigIndex + size > _data.size() ? _data.size() < bigIndex ? 0 : _data.size() - index : size; - if (sizeToBeCopied > 0) - std::memcpy(m_mem.data() + offset, _data.data() + index, sizeToBeCopied); - if (size > sizeToBeCopied) - std::memset(m_mem.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied); + if (sizeToBeCopied > 0) + std::memcpy(m_mem.data() + offset, _data.data() + index, sizeToBeCopied); + if (size > sizeToBeCopied) + std::memset(m_mem.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied); } @@ -42,22 +42,22 @@ void LegacyVM::copyDataToMemory(bytesConstRef _data, u256*_sp) void LegacyVM::throwOutOfGas() { - BOOST_THROW_EXCEPTION(OutOfGas()); + BOOST_THROW_EXCEPTION(OutOfGas()); } void LegacyVM::throwBadInstruction() { - BOOST_THROW_EXCEPTION(BadInstruction()); + BOOST_THROW_EXCEPTION(BadInstruction()); } void LegacyVM::throwBadJumpDestination() { - BOOST_THROW_EXCEPTION(BadJumpDestination()); + BOOST_THROW_EXCEPTION(BadJumpDestination()); } void LegacyVM::throwDisallowedStateChange() { - BOOST_THROW_EXCEPTION(DisallowedStateChange()); + BOOST_THROW_EXCEPTION(DisallowedStateChange()); } // throwBadStack is called from fetchInstruction() -> adjustStack() @@ -65,49 +65,49 @@ void LegacyVM::throwDisallowedStateChange() // so the call to m_onFail is needed here void LegacyVM::throwBadStack(unsigned _removed, unsigned _added) { - bigint size = m_stackEnd - m_SPP; - if (size < _removed) - { - if (m_onFail) - (this->*m_onFail)(); - BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_removed, size)); - } - else - { - if (m_onFail) - (this->*m_onFail)(); - BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_added - _removed), size)); - } + bigint size = m_stackEnd - m_SPP; + if (size < _removed) + { + if (m_onFail) + (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_removed, size)); + } + else + { + if (m_onFail) + (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_added - _removed), size)); + } } void LegacyVM::throwRevertInstruction(owning_bytes_ref&& _output) { - // We can't use BOOST_THROW_EXCEPTION here because it makes a copy of exception inside and RevertInstruction has no copy constructor - throw RevertInstruction(move(_output)); + // We can't use BOOST_THROW_EXCEPTION here because it makes a copy of exception inside and RevertInstruction has no copy constructor + throw RevertInstruction(move(_output)); } void LegacyVM::throwBufferOverrun(bigint const& _endOfAccess) { - // todo: disable this m_onFail, may result in duplicate log step in the trace - if (m_onFail) - (this->*m_onFail)(); - BOOST_THROW_EXCEPTION(BufferOverrun() << RequirementError(_endOfAccess, bigint(m_returnData.size()))); + // todo: disable this m_onFail, may result in duplicate log step in the trace + if (m_onFail) + (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(BufferOverrun() << RequirementError(_endOfAccess, bigint(m_returnData.size()))); } int64_t LegacyVM::verifyJumpDest(u256 const& _dest, bool _throw) { - // check for overflow - if (_dest <= 0x7FFFFFFFFFFFFFFF) { - - // check for within bounds and to a jump destination - // use binary search of array because hashtable collisions are exploitable - uint64_t pc = uint64_t(_dest); - if (std::binary_search(m_jumpDests.begin(), m_jumpDests.end(), pc)) - return pc; - } - if (_throw) - throwBadJumpDestination(); - return -1; + // check for overflow + if (_dest <= 0x7FFFFFFFFFFFFFFF) { + + // check for within bounds and to a jump destination + // use binary search of array because hashtable collisions are exploitable + uint64_t pc = uint64_t(_dest); + if (std::binary_search(m_jumpDests.begin(), m_jumpDests.end(), pc)) + return pc; + } + if (_throw) + throwBadJumpDestination(); + return -1; } @@ -117,158 +117,158 @@ int64_t LegacyVM::verifyJumpDest(u256 const& _dest, bool _throw) void LegacyVM::caseCreate() { - m_bounce = &LegacyVM::interpretCases; - m_runGas = toInt63(m_schedule->createGas); + m_bounce = &LegacyVM::interpretCases; + m_runGas = toInt63(m_schedule->createGas); - // Collect arguments. - u256 const endowment = m_SP[0]; - u256 const initOff = m_SP[1]; - u256 const initSize = m_SP[2]; + // Collect arguments. + u256 const endowment = m_SP[0]; + u256 const initOff = m_SP[1]; + u256 const initSize = m_SP[2]; u256 salt; if (m_OP == Instruction::CREATE2) - salt = m_SP[3]; - - updateMem(memNeed(initOff, initSize)); - updateIOGas(); - - // Clear the return data buffer. This will not free the memory. - m_returnData.clear(); - - if (m_ext->balance(m_ext->myAddress) >= endowment && m_ext->depth < 1024) - { - *m_io_gas_p = m_io_gas; - u256 createGas = *m_io_gas_p; - if (!m_schedule->staticCallDepthLimit()) - createGas -= createGas / 64; - u256 gas = createGas; - - // Get init code. Casts are safe because the memory cost has been paid. - auto off = static_cast(initOff); - auto size = static_cast(initSize); - bytesConstRef initCode{m_mem.data() + off, size}; - - - CreateResult result = m_ext->create(endowment, gas, initCode, m_OP, salt, m_onOp); - m_SPP[0] = (u160)result.address; // Convert address to integer. - m_returnData = result.output.toBytes(); - - *m_io_gas_p -= (createGas - gas); - m_io_gas = uint64_t(*m_io_gas_p); - } - else - m_SPP[0] = 0; - ++m_PC; + salt = m_SP[3]; + + updateMem(memNeed(initOff, initSize)); + updateIOGas(); + + // Clear the return data buffer. This will not free the memory. + m_returnData.clear(); + + if (m_ext->balance(m_ext->myAddress) >= endowment && m_ext->depth < 1024) + { + *m_io_gas_p = m_io_gas; + u256 createGas = *m_io_gas_p; + if (!m_schedule->staticCallDepthLimit()) + createGas -= createGas / 64; + u256 gas = createGas; + + // Get init code. Casts are safe because the memory cost has been paid. + auto off = static_cast(initOff); + auto size = static_cast(initSize); + bytesConstRef initCode{m_mem.data() + off, size}; + + + CreateResult result = m_ext->create(endowment, gas, initCode, m_OP, salt, m_onOp); + m_SPP[0] = (u160)result.address; // Convert address to integer. + m_returnData = result.output.toBytes(); + + *m_io_gas_p -= (createGas - gas); + m_io_gas = uint64_t(*m_io_gas_p); + } + else + m_SPP[0] = 0; + ++m_PC; } void LegacyVM::caseCall() { - m_bounce = &LegacyVM::interpretCases; - - // TODO: Please check if that does not actually increases the stack size. - // That was the case before. - unique_ptr callParams(new CallParameters()); - - // Clear the return data buffer. This will not free the memory. - m_returnData.clear(); - - bytesRef output; - if (caseCallSetup(callParams.get(), output)) - { - CallResult result = m_ext->call(*callParams); - result.output.copyTo(output); - - // Here we have 2 options: - // 1. Keep the whole returned memory buffer (owning_bytes_ref): - // higher memory footprint, no memory copy. - // 2. Copy only the return data from the returned memory buffer: - // minimal memory footprint, additional memory copy. - // Option 2 used: - m_returnData = result.output.toBytes(); - - m_SPP[0] = result.status == EVMC_SUCCESS ? 1 : 0; - } - else - m_SPP[0] = 0; - m_io_gas += uint64_t(callParams->gas); - ++m_PC; + m_bounce = &LegacyVM::interpretCases; + + // TODO: Please check if that does not actually increases the stack size. + // That was the case before. + unique_ptr callParams(new CallParameters()); + + // Clear the return data buffer. This will not free the memory. + m_returnData.clear(); + + bytesRef output; + if (caseCallSetup(callParams.get(), output)) + { + CallResult result = m_ext->call(*callParams); + result.output.copyTo(output); + + // Here we have 2 options: + // 1. Keep the whole returned memory buffer (owning_bytes_ref): + // higher memory footprint, no memory copy. + // 2. Copy only the return data from the returned memory buffer: + // minimal memory footprint, additional memory copy. + // Option 2 used: + m_returnData = result.output.toBytes(); + + m_SPP[0] = result.status == EVMC_SUCCESS ? 1 : 0; + } + else + m_SPP[0] = 0; + m_io_gas += uint64_t(callParams->gas); + ++m_PC; } bool LegacyVM::caseCallSetup(CallParameters *callParams, bytesRef& o_output) { - // Make sure the params were properly initialized. - assert(callParams->valueTransfer == 0); - assert(callParams->apparentValue == 0); - - m_runGas = toInt63(m_schedule->callGas); - - callParams->staticCall = (m_OP == Instruction::STATICCALL || m_ext->staticCall); - - bool const haveValueArg = m_OP == Instruction::CALL || m_OP == Instruction::CALLCODE; - - Address destinationAddr = asAddress(m_SP[1]); - if (m_OP == Instruction::CALL && !m_ext->exists(destinationAddr)) - if (m_SP[2] > 0 || m_schedule->zeroValueTransferChargesNewAccountGas()) - m_runGas += toInt63(m_schedule->callNewAccountGas); - - if (haveValueArg && m_SP[2] > 0) - m_runGas += toInt63(m_schedule->callValueTransferGas); - - size_t const sizesOffset = haveValueArg ? 3 : 2; - u256 inputOffset = m_SP[sizesOffset]; - u256 inputSize = m_SP[sizesOffset + 1]; - u256 outputOffset = m_SP[sizesOffset + 2]; - u256 outputSize = m_SP[sizesOffset + 3]; - uint64_t inputMemNeed = memNeed(inputOffset, inputSize); - uint64_t outputMemNeed = memNeed(outputOffset, outputSize); - - m_newMemSize = std::max(inputMemNeed, outputMemNeed); - updateMem(m_newMemSize); - updateIOGas(); - - // "Static" costs already applied. Calculate call gas. - if (m_schedule->staticCallDepthLimit()) - { - // With static call depth limit we just charge the provided gas amount. - callParams->gas = m_SP[0]; - } - else - { - // Apply "all but one 64th" rule. - u256 maxAllowedCallGas = m_io_gas - m_io_gas / 64; - callParams->gas = std::min(m_SP[0], maxAllowedCallGas); - } - - m_runGas = toInt63(callParams->gas); - updateIOGas(); - - if (haveValueArg && m_SP[2] > 0) - callParams->gas += m_schedule->callStipend; - - callParams->codeAddress = destinationAddr; - - if (haveValueArg) - { - callParams->valueTransfer = m_SP[2]; - callParams->apparentValue = m_SP[2]; - } - else if (m_OP == Instruction::DELEGATECALL) - // Forward VALUE. - callParams->apparentValue = m_ext->value; - - uint64_t inOff = (uint64_t)inputOffset; - uint64_t inSize = (uint64_t)inputSize; - uint64_t outOff = (uint64_t)outputOffset; - uint64_t outSize = (uint64_t)outputSize; - - if (m_ext->balance(m_ext->myAddress) >= callParams->valueTransfer && m_ext->depth < 1024) - { - callParams->onOp = m_onOp; - callParams->senderAddress = m_OP == Instruction::DELEGATECALL ? m_ext->caller : m_ext->myAddress; - callParams->receiveAddress = (m_OP == Instruction::CALL || m_OP == Instruction::STATICCALL) ? callParams->codeAddress : m_ext->myAddress; - callParams->data = bytesConstRef(m_mem.data() + inOff, inSize); - o_output = bytesRef(m_mem.data() + outOff, outSize); - return true; - } - return false; + // Make sure the params were properly initialized. + assert(callParams->valueTransfer == 0); + assert(callParams->apparentValue == 0); + + m_runGas = toInt63(m_schedule->callGas); + + callParams->staticCall = (m_OP == Instruction::STATICCALL || m_ext->staticCall); + + bool const haveValueArg = m_OP == Instruction::CALL || m_OP == Instruction::CALLCODE; + + Address destinationAddr = asAddress(m_SP[1]); + if (m_OP == Instruction::CALL && !m_ext->exists(destinationAddr)) + if (m_SP[2] > 0 || m_schedule->zeroValueTransferChargesNewAccountGas()) + m_runGas += toInt63(m_schedule->callNewAccountGas); + + if (haveValueArg && m_SP[2] > 0) + m_runGas += toInt63(m_schedule->callValueTransferGas); + + size_t const sizesOffset = haveValueArg ? 3 : 2; + u256 inputOffset = m_SP[sizesOffset]; + u256 inputSize = m_SP[sizesOffset + 1]; + u256 outputOffset = m_SP[sizesOffset + 2]; + u256 outputSize = m_SP[sizesOffset + 3]; + uint64_t inputMemNeed = memNeed(inputOffset, inputSize); + uint64_t outputMemNeed = memNeed(outputOffset, outputSize); + + m_newMemSize = std::max(inputMemNeed, outputMemNeed); + updateMem(m_newMemSize); + updateIOGas(); + + // "Static" costs already applied. Calculate call gas. + if (m_schedule->staticCallDepthLimit()) + { + // With static call depth limit we just charge the provided gas amount. + callParams->gas = m_SP[0]; + } + else + { + // Apply "all but one 64th" rule. + u256 maxAllowedCallGas = m_io_gas - m_io_gas / 64; + callParams->gas = std::min(m_SP[0], maxAllowedCallGas); + } + + m_runGas = toInt63(callParams->gas); + updateIOGas(); + + if (haveValueArg && m_SP[2] > 0) + callParams->gas += m_schedule->callStipend; + + callParams->codeAddress = destinationAddr; + + if (haveValueArg) + { + callParams->valueTransfer = m_SP[2]; + callParams->apparentValue = m_SP[2]; + } + else if (m_OP == Instruction::DELEGATECALL) + // Forward VALUE. + callParams->apparentValue = m_ext->value; + + uint64_t inOff = (uint64_t)inputOffset; + uint64_t inSize = (uint64_t)inputSize; + uint64_t outOff = (uint64_t)outputOffset; + uint64_t outSize = (uint64_t)outputSize; + + if (m_ext->balance(m_ext->myAddress) >= callParams->valueTransfer && m_ext->depth < 1024) + { + callParams->onOp = m_onOp; + callParams->senderAddress = m_OP == Instruction::DELEGATECALL ? m_ext->caller : m_ext->myAddress; + callParams->receiveAddress = (m_OP == Instruction::CALL || m_OP == Instruction::STATICCALL) ? callParams->codeAddress : m_ext->myAddress; + callParams->data = bytesConstRef(m_mem.data() + inOff, inSize); + o_output = bytesRef(m_mem.data() + outOff, outSize); + return true; + } + return false; } diff --git a/test/unittests/libevm/LegacyVMTest.cpp b/test/unittests/libevm/LegacyVMTest.cpp index 85e5fb9e647..7bbea735842 100644 --- a/test/unittests/libevm/LegacyVMTest.cpp +++ b/test/unittests/libevm/LegacyVMTest.cpp @@ -17,9 +17,9 @@ #include #include +#include #include #include -#include #include using namespace dev; @@ -29,7 +29,7 @@ using namespace dev::eth; namespace { -class LastBlockHashes: public eth::LastBlockHashesFace +class LastBlockHashes : public eth::LastBlockHashesFace { public: h256s precedingHashes(h256 const& /* _mostRecentHash */) const override @@ -38,7 +38,7 @@ class LastBlockHashes: public eth::LastBlockHashesFace } void clear() override {} }; -} +} // namespace BOOST_FIXTURE_TEST_SUITE(LegacyVMSuite, TestOutputHelperFixture) @@ -55,7 +55,8 @@ BOOST_AUTO_TEST_CASE(create2) State state(0); state.addBalance(address, 1 * ether); - std::unique_ptr se(ChainParams(genesisInfo(eth::Network::ConstantinopleTest)).createSealEngine()); + std::unique_ptr se( + ChainParams(genesisInfo(eth::Network::ConstantinopleTest)).createSealEngine()); u256 value = 0; @@ -72,8 +73,7 @@ BOOST_AUTO_TEST_CASE(create2) // pop bytes code = fromHex("368060006000376101238160006000f55050"); - ExtVM extVm(state, envInfo, *se, address, - address, address, value, gasPrice, ref(inputData), + ExtVM extVm(state, envInfo, *se, address, address, address, value, gasPrice, ref(inputData), ref(code), sha3(code), depth, isCreate, staticCall); LegacyVM vm; @@ -81,7 +81,8 @@ BOOST_AUTO_TEST_CASE(create2) u256 gas = 1000000; owning_bytes_ref res = vm.exec(gas, extVm, OnOpFunc{}); - Address expectedAddress = right160(sha3(address.asBytes() + toBigEndian(0x123_cppui256) + inputData)); + Address expectedAddress = + right160(sha3(address.asBytes() + toBigEndian(0x123_cppui256) + inputData)); BOOST_REQUIRE(state.addressHasCode(expectedAddress)); } From c950c7c2239fe99aeda0a30eddff8b7d2ef0d683 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 18 Jul 2018 18:39:44 +0200 Subject: [PATCH 4/6] Don't allow CREATE2 from a static call --- libevm/LegacyVM.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libevm/LegacyVM.cpp b/libevm/LegacyVM.cpp index 9e2ac7b5685..cf1934799bd 100644 --- a/libevm/LegacyVM.cpp +++ b/libevm/LegacyVM.cpp @@ -209,6 +209,8 @@ void LegacyVM::interpretCases() ON_OP(); if (!m_schedule->haveCreate2) throwBadInstruction(); + if (m_ext->staticCall) + throwDisallowedStateChange(); m_bounce = &LegacyVM::caseCreate; } From 4229006b0276afcdd61d3f50850682d6db734216 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 23 Jul 2018 12:37:04 +0200 Subject: [PATCH 5/6] Fix CREATE 2 in jumpTable used for VM opcode dispatch on linux & macos --- libevm/Instruction.h | 2 +- libevm/LegacyVMConfig.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevm/Instruction.h b/libevm/Instruction.h index e80ce64972c..5289fe1776e 100644 --- a/libevm/Instruction.h +++ b/libevm/Instruction.h @@ -221,8 +221,8 @@ enum class Instruction: uint8_t CALLCODE, ///< message-call with another account's code only RETURN, ///< halt execution returning output data DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender - STATICCALL = 0xfa, ///< like CALL except state changing operation are not permitted (will throw) CREATE2 = 0xf5, ///< create a new account with associated code. sha3((sender + salt + code) + STATICCALL = 0xfa, ///< like CALL except state changing operation are not permitted (will throw) REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas INVALID = 0xfe, ///< dedicated invalid instruction SUICIDE = 0xff ///< halt execution and register account for later deletion diff --git a/libevm/LegacyVMConfig.h b/libevm/LegacyVMConfig.h index 7e879e9d1eb..5d0ba26afb0 100644 --- a/libevm/LegacyVMConfig.h +++ b/libevm/LegacyVMConfig.h @@ -408,13 +408,13 @@ namespace dev &&CALLCODE, \ &&RETURN, \ &&DELEGATECALL, \ - &&INVALID, \ + &&CREATE2, \ &&INVALID, \ &&INVALID, \ &&INVALID, \ &&INVALID, \ &&STATICCALL, \ - &&CREATE2, \ + &&INVALID, \ &&INVALID, \ &&REVERT, \ &&INVALID, \ From bb70fd5be33d3ac43d89366efc2b93fa7087e540 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 23 Jul 2018 16:10:57 +0200 Subject: [PATCH 6/6] More unit tests for LegacyVM CREATE2 implementation --- test/unittests/libevm/LegacyVMTest.cpp | 92 +++++++++++++++++++++----- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/test/unittests/libevm/LegacyVMTest.cpp b/test/unittests/libevm/LegacyVMTest.cpp index 7bbea735842..86d4977c8f3 100644 --- a/test/unittests/libevm/LegacyVMTest.cpp +++ b/test/unittests/libevm/LegacyVMTest.cpp @@ -38,52 +38,108 @@ class LastBlockHashes : public eth::LastBlockHashesFace } void clear() override {} }; -} // namespace - -BOOST_FIXTURE_TEST_SUITE(LegacyVMSuite, TestOutputHelperFixture) -BOOST_AUTO_TEST_CASE(create2) +BlockHeader initBlockHeader() { BlockHeader blockHeader; blockHeader.setGasLimit(0x7fffffffffffffff); blockHeader.setTimestamp(0); + return blockHeader; +} - LastBlockHashes lastBlockHashes; - EnvInfo envInfo(blockHeader, lastBlockHashes, 0); - - Address address = KeyPair::create().address(); - State state(0); - state.addBalance(address, 1 * ether); - - std::unique_ptr se( - ChainParams(genesisInfo(eth::Network::ConstantinopleTest)).createSealEngine()); +class LegacyVMFCreate2TestFixture : public TestOutputHelperFixture +{ +public: + LegacyVMFCreate2TestFixture() { state.addBalance(address, 1 * ether); } + BlockHeader blockHeader{initBlockHeader()}; + LastBlockHashes lastBlockHashes; + EnvInfo envInfo{blockHeader, lastBlockHashes, 0}; + Address address{KeyPair::create().address()}; + State state{0}; + std::unique_ptr se{ + ChainParams(genesisInfo(Network::ConstantinopleTest)).createSealEngine()}; u256 value = 0; u256 gasPrice = 1; int depth = 0; bool isCreate = true; bool staticCall = false; + u256 gas = 1000000; + // mstore(0, 0x60) // return(0, 0x20) bytes inputData = fromHex("606060005260206000f3"); + // let s : = calldatasize() // calldatacopy(0, 0, s) // create2(0, 0, s, 0x123) // pop bytes code = fromHex("368060006000376101238160006000f55050"); + Address expectedAddress = + right160(sha3(address.asBytes() + toBigEndian(0x123_cppui256) + inputData)); + + LegacyVM vm; +}; + +} // namespace + +BOOST_FIXTURE_TEST_SUITE(LegacyVMSuite, TestOutputHelperFixture) +BOOST_FIXTURE_TEST_SUITE(LegacyVMCreate2Suite, LegacyVMFCreate2TestFixture) + +BOOST_AUTO_TEST_CASE(create2worksInConstantinople) +{ ExtVM extVm(state, envInfo, *se, address, address, address, value, gasPrice, ref(inputData), ref(code), sha3(code), depth, isCreate, staticCall); - LegacyVM vm; + vm.exec(gas, extVm, OnOpFunc{}); - u256 gas = 1000000; - owning_bytes_ref res = vm.exec(gas, extVm, OnOpFunc{}); + BOOST_REQUIRE(state.addressHasCode(expectedAddress)); +} + +BOOST_AUTO_TEST_CASE(create2isInvalidBeforeConstantinople) +{ + se.reset(ChainParams(genesisInfo(Network::ByzantiumTest)).createSealEngine()); + + ExtVM extVm(state, envInfo, *se, address, address, address, value, gasPrice, ref(inputData), + ref(code), sha3(code), depth, isCreate, staticCall); + + BOOST_REQUIRE_THROW(vm.exec(gas, extVm, OnOpFunc{}), BadInstruction); +} + +BOOST_AUTO_TEST_CASE(create2succeedsIfAddressHasEther) +{ + state.addBalance(expectedAddress, 1 * ether); + + ExtVM extVm(state, envInfo, *se, address, address, address, value, gasPrice, ref(inputData), + ref(code), sha3(code), depth, isCreate, staticCall); + + vm.exec(gas, extVm, OnOpFunc{}); - Address expectedAddress = - right160(sha3(address.asBytes() + toBigEndian(0x123_cppui256) + inputData)); BOOST_REQUIRE(state.addressHasCode(expectedAddress)); } +BOOST_AUTO_TEST_CASE(create2doesntChangeContractIfAddressExists) +{ + state.setCode(expectedAddress, bytes{inputData}); + + ExtVM extVm(state, envInfo, *se, address, address, address, value, gasPrice, ref(inputData), + ref(code), sha3(code), depth, isCreate, staticCall); + + vm.exec(gas, extVm, OnOpFunc{}); + BOOST_REQUIRE(state.code(expectedAddress) == inputData); +} + +BOOST_AUTO_TEST_CASE(create2isForbiddenInStaticCall) +{ + staticCall = true; + + ExtVM extVm(state, envInfo, *se, address, address, address, value, gasPrice, ref(inputData), + ref(code), sha3(code), depth, isCreate, staticCall); + + BOOST_REQUIRE_THROW(vm.exec(gas, extVm, OnOpFunc{}), DisallowedStateChange); +} + +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()