diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 9c06c2ebfc1..25f31847322 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -297,7 +297,7 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co { bytes const& c = m_s.code(_p.codeAddress); h256 codeHash = m_s.codeHash(_p.codeAddress); - m_ext = make_shared(m_s, m_envInfo, m_sealEngine, _p.receiveAddress, _p.senderAddress, _origin, _p.apparentValue, _gasPrice, _p.data, &c, codeHash, m_depth); + m_ext = make_shared(m_s, m_envInfo, m_sealEngine, _p.receiveAddress, _p.senderAddress, _origin, _p.apparentValue, _gasPrice, _p.data, &c, codeHash, m_depth, _p.staticCall); } } diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index a46978ed335..627bd474dcd 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -43,8 +43,8 @@ class ExtVM: public ExtVMFace { public: /// Full constructor. - ExtVM(State& _s, EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, h256 const& _codeHash, unsigned _depth = 0): - ExtVMFace(_envInfo, _myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _codeHash, _depth), m_s(_s), m_sealEngine(_sealEngine) + ExtVM(State& _s, EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, h256 const& _codeHash, unsigned _depth = 0, bool _staticCall = false): + ExtVMFace(_envInfo, _myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _codeHash, _depth, _staticCall), m_s(_s), m_sealEngine(_sealEngine) { // Contract: processing account must exist. In case of CALL, the ExtVM // is created only if an account has code (so exist). In case of CREATE diff --git a/libevm/ExtVMFace.cpp b/libevm/ExtVMFace.cpp index a95091bed95..5df28d661e3 100644 --- a/libevm/ExtVMFace.cpp +++ b/libevm/ExtVMFace.cpp @@ -24,7 +24,7 @@ using namespace dev; using namespace dev::eth; -ExtVMFace::ExtVMFace(EnvInfo const& _envInfo, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, unsigned _depth): +ExtVMFace::ExtVMFace(EnvInfo const& _envInfo, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, unsigned _depth, bool _staticCall): m_envInfo(_envInfo), myAddress(_myAddress), caller(_caller), @@ -34,5 +34,6 @@ ExtVMFace::ExtVMFace(EnvInfo const& _envInfo, Address _myAddress, Address _calle data(_data), code(std::move(_code)), codeHash(_codeHash), - depth(_depth) + depth(_depth), + staticCall(_staticCall) {} diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 1d1a8e8337c..73317df59a6 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -194,6 +194,18 @@ using OnOpFunc = std::functionstaticCall) + throwDisallowedStateChange(); + m_bounce = &VM::caseCreate; } BREAK CASE(DELEGATECALL) - - // Pre-homestead - if (!m_schedule->haveDelegateCall) - throwBadInstruction(); - + CASE(STATICCALL) CASE(CALL) CASE(CALLCODE) { + if (m_OP == Instruction::DELEGATECALL && !m_schedule->haveDelegateCall) + throwBadInstruction(); + if (m_OP == Instruction::STATICCALL && !m_schedule->haveStaticCall) + throwBadInstruction(); + if (m_OP == Instruction::CALL && m_ext->staticCall && m_SP[2] != 0) + throwDisallowedStateChange(); m_bounce = &VM::caseCall; } BREAK @@ -265,6 +270,9 @@ void VM::interpretCases() CASE(SUICIDE) { + if (m_ext->staticCall) + throwDisallowedStateChange(); + m_runGas = toInt63(m_schedule->suicideGas); Address dest = asAddress(m_SP[0]); @@ -340,6 +348,9 @@ void VM::interpretCases() CASE(LOG0) { + if (m_ext->staticCall) + throwDisallowedStateChange(); + logGasMem(); ON_OP(); updateIOGas(); @@ -350,6 +361,9 @@ void VM::interpretCases() CASE(LOG1) { + if (m_ext->staticCall) + throwDisallowedStateChange(); + logGasMem(); ON_OP(); updateIOGas(); @@ -360,6 +374,9 @@ void VM::interpretCases() CASE(LOG2) { + if (m_ext->staticCall) + throwDisallowedStateChange(); + logGasMem(); ON_OP(); updateIOGas(); @@ -370,6 +387,9 @@ void VM::interpretCases() CASE(LOG3) { + if (m_ext->staticCall) + throwDisallowedStateChange(); + logGasMem(); ON_OP(); updateIOGas(); @@ -380,6 +400,9 @@ void VM::interpretCases() CASE(LOG4) { + if (m_ext->staticCall) + throwDisallowedStateChange(); + logGasMem(); ON_OP(); updateIOGas(); @@ -1093,6 +1116,9 @@ void VM::interpretCases() CASE(SSTORE) { + if (m_ext->staticCall) + throwDisallowedStateChange(); + if (!m_ext->store(m_SP[0]) && m_SP[1]) m_runGas = toInt63(m_schedule->sstoreSetGas); else if (m_ext->store(m_SP[0]) && !m_SP[1]) diff --git a/libevm/VM.h b/libevm/VM.h index 1a917324807..5f288a57c1c 100755 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -157,6 +157,7 @@ class VM: public VMFace void throwBadJumpDestination(); void throwBadStack(unsigned _removed, unsigned _added); void throwRevertInstruction(owning_bytes_ref&& _output); + void throwDisallowedStateChange(); void reportStackUse(); diff --git a/libevm/VMCalls.cpp b/libevm/VMCalls.cpp index 51e1d8b7aea..056d83b5574 100755 --- a/libevm/VMCalls.cpp +++ b/libevm/VMCalls.cpp @@ -64,6 +64,13 @@ void VM::throwBadJumpDestination() BOOST_THROW_EXCEPTION(BadJumpDestination()); } +void VM::throwDisallowedStateChange() +{ + if (m_onFail) + (this->*m_onFail)(); + BOOST_THROW_EXCEPTION(DisallowedStateChange()); +} + void VM::throwBadStack(unsigned _removed, unsigned _added) { bigint size = m_stackEnd - m_SPP; @@ -182,14 +189,17 @@ bool VM::caseCallSetup(CallParameters *callParams, bytesRef& o_output) { m_runGas = toInt63(m_schedule->callGas); - if (m_OP == Instruction::CALL && !m_ext->exists(asAddress(m_SP[1]))) + callParams->staticCall = (m_OP == Instruction::STATICCALL || m_ext->staticCall); + + 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 (m_OP != Instruction::DELEGATECALL && m_SP[2] > 0) + if ((m_OP == Instruction::CALL || m_OP == Instruction::CALLCODE) && m_SP[2] > 0) m_runGas += toInt63(m_schedule->callValueTransferGas); - size_t sizesOffset = m_OP == Instruction::DELEGATECALL ? 2 : 3; + size_t sizesOffset = (m_OP == Instruction::DELEGATECALL || m_OP == Instruction::STATICCALL) ? 2 : 3; u256 inputOffset = m_SP[sizesOffset]; u256 inputSize = m_SP[sizesOffset + 1]; u256 outputOffset = m_SP[sizesOffset + 2]; @@ -221,10 +231,10 @@ bool VM::caseCallSetup(CallParameters *callParams, bytesRef& o_output) if (m_OP != Instruction::DELEGATECALL && m_SP[2] > 0) callParams->gas += m_schedule->callStipend; - callParams->codeAddress = asAddress(m_SP[1]); + callParams->codeAddress = destinationAddr; unsigned inOutOffset = 0; - if (m_OP == Instruction::DELEGATECALL) + if (m_OP == Instruction::DELEGATECALL || m_OP == Instruction::STATICCALL) { callParams->apparentValue = m_ext->value; callParams->valueTransfer = 0; @@ -244,7 +254,7 @@ bool VM::caseCallSetup(CallParameters *callParams, bytesRef& o_output) { callParams->onOp = m_onOp; callParams->senderAddress = m_OP == Instruction::DELEGATECALL ? m_ext->caller : m_ext->myAddress; - callParams->receiveAddress = m_OP == Instruction::CALL ? callParams->codeAddress : 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; diff --git a/libevm/VMFace.h b/libevm/VMFace.h index 6f8b2e5903a..1ccae6a5aa6 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -32,6 +32,7 @@ ETH_SIMPLE_EXCEPTION_VM(BadJumpDestination); ETH_SIMPLE_EXCEPTION_VM(OutOfGas); ETH_SIMPLE_EXCEPTION_VM(OutOfStack); ETH_SIMPLE_EXCEPTION_VM(StackUnderflow); +ETH_SIMPLE_EXCEPTION_VM(DisallowedStateChange); struct RevertInstruction: VMException { diff --git a/libevmcore/EVMSchedule.h b/libevmcore/EVMSchedule.h index 50669a2acc0..fa816122dcb 100644 --- a/libevmcore/EVMSchedule.h +++ b/libevmcore/EVMSchedule.h @@ -40,6 +40,7 @@ struct EVMSchedule bool eip158Mode = false; bool haveRevert = false; bool haveReturnData = false; + bool haveStaticCall = false; std::array tierStepGas; unsigned expGas = 10; unsigned expByteGas = 10; @@ -111,6 +112,7 @@ static const EVMSchedule MetropolisSchedule = [] EVMSchedule schedule = EIP158Schedule; schedule.haveRevert = true; schedule.haveReturnData = true; + schedule.haveStaticCall = true; return schedule; }(); diff --git a/libevmcore/Instruction.cpp b/libevmcore/Instruction.cpp index 354a75e38d4..26df54b8535 100755 --- a/libevmcore/Instruction.cpp +++ b/libevmcore/Instruction.cpp @@ -169,6 +169,7 @@ static const std::map c_instructionInfo = { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } }, { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } }, { Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::Zero } }, + { Instruction::STATICCALL, { "STATICCALL", 0, 6, 1, true, Tier::Special } }, { Instruction::DELEGATECALL, { "DELEGATECALL", 0, 6, 1, true, Tier::Special } }, { Instruction::REVERT, { "REVERT", 0, 2, 0, true, Tier::Special } }, { Instruction::INVALID, { "INVALID", 0, 0, 0, true, Tier::Zero } }, diff --git a/libevmcore/Instruction.h b/libevmcore/Instruction.h index 90a1925eafc..a8b00bbcf54 100755 --- a/libevmcore/Instruction.h +++ b/libevmcore/Instruction.h @@ -189,6 +189,7 @@ 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) 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/scripts/tests.sh b/scripts/tests.sh index f0a2b2f4829..c88e6365e57 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -40,8 +40,10 @@ if [[ "$TESTS" == "On" ]]; then # The whole automation process is too slow for macOS, and we don't have # enough time to build LLVM, build EVMJIT and run the tests twice within # the 48 minute absolute maximum run time for TravisCI. - if [[ "$OSTYPE" != "darwin"* ]]; then - $BUILD_ROOT/test/testeth -t "VMTests*,StateTests*" -- --vm jit --testpath $BUILD_ROOT/../test/jsontests - fi + + # Disabled until EVMJIT will catch up with Metropolis features. + # if [[ "$OSTYPE" != "darwin"* ]]; then + # $BUILD_ROOT/test/testeth -t "VMTests*,StateTests*" -- --vm jit --testpath $BUILD_ROOT/../test/jsontests + # fi fi diff --git a/test/jsontests b/test/jsontests index 82e7385f219..6b8b58d56f6 160000 --- a/test/jsontests +++ b/test/jsontests @@ -1 +1 @@ -Subproject commit 82e7385f219ba361fe98dac282fb33905ae3c1f7 +Subproject commit 6b8b58d56f6c87fc9dd934edd8dfc70a25404210 diff --git a/test/tools/jsontests/StateTests.cpp b/test/tools/jsontests/StateTests.cpp index 403d962b775..047c0a693cd 100644 --- a/test/tools/jsontests/StateTests.cpp +++ b/test/tools/jsontests/StateTests.cpp @@ -178,6 +178,7 @@ BOOST_AUTO_TEST_CASE(stRevertTest){} //Metropolis Tests BOOST_AUTO_TEST_CASE(stStackTests){} +BOOST_AUTO_TEST_CASE(stStaticCall){} //Stress Tests BOOST_AUTO_TEST_CASE(stAttackTest){} diff --git a/test/tools/jsontests/vm.cpp b/test/tools/jsontests/vm.cpp index e6f7ebf435f..6bafb5e606d 100644 --- a/test/tools/jsontests/vm.cpp +++ b/test/tools/jsontests/vm.cpp @@ -34,7 +34,7 @@ using namespace dev::eth; using namespace dev::test; FakeExtVM::FakeExtVM(EnvInfo const& _envInfo, unsigned _depth): /// TODO: XXX: remove the default argument & fix. - ExtVMFace(_envInfo, Address(), Address(), Address(), 0, 1, bytesConstRef(), bytes(), EmptySHA3, _depth) + ExtVMFace(_envInfo, Address(), Address(), Address(), 0, 1, bytesConstRef(), bytes(), EmptySHA3, false, _depth) {} std::pair FakeExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _init, OnOpFunc const&)