diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 36fba6e43..ed4cdb4dc 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -45,169 +45,165 @@ static array metrics() return s_ret; } -void VM::checkRequirements(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, Instruction _inst) +bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) { - static const auto c_metrics = metrics(); - auto& metric = c_metrics[static_cast(_inst)]; - - if (metric.gasPriceTier == InvalidTier) - BOOST_THROW_EXCEPTION(BadInstruction()); + // Reset leftovers from possible previous run + m_curPC = 0; + m_jumpDests.clear(); - // FEES... - bigint runGas = c_tierStepGas[metric.gasPriceTier]; - bigint newTempSize = m_temp.size(); - bigint copySize = 0; + m_stack.reserve((unsigned)c_stackLimit); - // should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird. - //m_onFail = std::function(onOperation); + unique_ptr callParams; - require(metric.args, metric.ret); + static const array c_metrics = metrics(); - auto onOperation = [&]() + auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; + auto gasForMem = [](bigint _size) -> bigint { - if (_onOp) - _onOp(m_steps, _inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext); + bigint s = _size / 32; + return (bigint)c_memoryGas * s + s * s / c_quadCoeffDiv; }; - auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; - - switch (_inst) - { - case Instruction::SSTORE: - if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) - runGas = c_sstoreSetGas; - else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) + if (m_jumpDests.empty()) + for (unsigned i = 0; i < _ext.code.size(); ++i) { - runGas = c_sstoreResetGas; - _ext.sub.refunds += c_sstoreRefundGas; + if (_ext.code[i] == (byte)Instruction::JUMPDEST) + m_jumpDests.push_back(i); + else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32) + i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1; } - else - runGas = c_sstoreResetGas; - break; - - case Instruction::SLOAD: - runGas = c_sloadGas; - break; - - // These all operate on memory and therefore potentially expand it: - case Instruction::MSTORE: - newTempSize = (bigint)m_stack.back() + 32; - break; - case Instruction::MSTORE8: - newTempSize = (bigint)m_stack.back() + 1; - break; - case Instruction::MLOAD: - newTempSize = (bigint)m_stack.back() + 32; - break; - case Instruction::RETURN: - newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); - break; - case Instruction::SHA3: - runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas; - newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); - break; - case Instruction::CALLDATACOPY: - copySize = m_stack[m_stack.size() - 3]; - newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); - break; - case Instruction::CODECOPY: - copySize = m_stack[m_stack.size() - 3]; - newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); - break; - case Instruction::EXTCODECOPY: - copySize = m_stack[m_stack.size() - 4]; - newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]); - break; - - case Instruction::JUMPDEST: - runGas = 1; - break; - - case Instruction::LOG0: - case Instruction::LOG1: - case Instruction::LOG2: - case Instruction::LOG3: - case Instruction::LOG4: + u256 nextPC = m_curPC + 1; + for (uint64_t steps = 0; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++steps) { - unsigned n = (unsigned)_inst - (unsigned)Instruction::LOG0; - runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2]; - newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]); - break; - } + // INSTRUCTION... + Instruction inst = (Instruction)_ext.getCode(m_curPC); + auto metric = c_metrics[(int)inst]; + int gasPriceTier = metric.gasPriceTier; - case Instruction::CALL: - case Instruction::CALLCODE: - runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1]; - if (_inst != Instruction::CALLCODE && !_ext.exists(asAddress(m_stack[m_stack.size() - 2]))) - runGas += c_callNewAccountGas; - if (m_stack[m_stack.size() - 3] > 0) - runGas += c_callValueTransferGas; - newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); - break; - - case Instruction::CREATE: - { - newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]); - runGas = c_createGas; - break; - } - case Instruction::EXP: - { - auto expon = m_stack[m_stack.size() - 2]; - runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8)); - break; - } - default:; - } + if (gasPriceTier == InvalidTier) + BOOST_THROW_EXCEPTION(BadInstruction()); - auto gasForMem = [](bigint _size) -> bigint - { - bigint s = _size / 32; - return (bigint)c_memoryGas * s + s * s / c_quadCoeffDiv; - }; + // FEES... + bigint runGas = c_tierStepGas[metric.gasPriceTier]; + bigint newTempSize = m_temp.size(); + bigint copySize = 0; - newTempSize = (newTempSize + 31) / 32 * 32; - if (newTempSize > m_temp.size()) - runGas += gasForMem(newTempSize) - gasForMem(m_temp.size()); - runGas += c_copyGas * ((copySize + 31) / 32); + // should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird. + //m_onFail = std::function(onOperation); - onOperation(); + require(metric.args, metric.ret); - if (io_gas < runGas) - BOOST_THROW_EXCEPTION(OutOfGas()); + auto onOperation = [&]() + { + if (_onOp) + _onOp(steps, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext); + }; - io_gas -= (u256)runGas; + switch (inst) + { + case Instruction::SSTORE: + if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) + runGas = c_sstoreSetGas; + else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) + { + runGas = c_sstoreResetGas; + _ext.sub.refunds += c_sstoreRefundGas; + } + else + runGas = c_sstoreResetGas; + break; - if (newTempSize > m_temp.size()) - m_temp.resize((size_t)newTempSize); -} + case Instruction::SLOAD: + runGas = c_sloadGas; + break; -bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) -{ - m_stack.reserve((unsigned)c_stackLimit); + // These all operate on memory and therefore potentially expand it: + case Instruction::MSTORE: + newTempSize = (bigint)m_stack.back() + 32; + break; + case Instruction::MSTORE8: + newTempSize = (bigint)m_stack.back() + 1; + break; + case Instruction::MLOAD: + newTempSize = (bigint)m_stack.back() + 32; + break; + case Instruction::RETURN: + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); + break; + case Instruction::SHA3: + runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); + break; + case Instruction::CALLDATACOPY: + copySize = m_stack[m_stack.size() - 3]; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); + break; + case Instruction::CODECOPY: + copySize = m_stack[m_stack.size() - 3]; + newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]); + break; + case Instruction::EXTCODECOPY: + copySize = m_stack[m_stack.size() - 4]; + newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]); + break; - for (size_t i = 0; i < _ext.code.size(); ++i) - { - if (_ext.code[i] == (byte)Instruction::JUMPDEST) - m_jumpDests.push_back(i); - else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32) - i += _ext.code[i] - (size_t)Instruction::PUSH1 + 1; - } + case Instruction::JUMPDEST: + runGas = 1; + break; - auto verifyJumpDest = [](u256 const& _dest, std::vector const& _validDests) - { - auto nextPC = static_cast(_dest); - if (!std::binary_search(_validDests.begin(), _validDests.end(), nextPC) || _dest > std::numeric_limits::max()) - BOOST_THROW_EXCEPTION(BadJumpDestination()); - return nextPC; - }; + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + unsigned n = (unsigned)inst - (unsigned)Instruction::LOG0; + runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2]; + newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]); + break; + } - m_steps = 0; - for (auto nextPC = m_curPC + 1; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++m_steps) - { - Instruction inst = (Instruction)_ext.getCode(m_curPC); - checkRequirements(io_gas, _ext, _onOp, inst); + case Instruction::CALL: + case Instruction::CALLCODE: + runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1]; + if (inst != Instruction::CALLCODE && !_ext.exists(asAddress(m_stack[m_stack.size() - 2]))) + runGas += c_callNewAccountGas; + if (m_stack[m_stack.size() - 3] > 0) + runGas += c_callValueTransferGas; + newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); + break; + + case Instruction::CREATE: + { + newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]); + runGas = c_createGas; + break; + } + case Instruction::EXP: + { + auto expon = m_stack[m_stack.size() - 2]; + runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8)); + break; + } + default:; + } + + newTempSize = (newTempSize + 31) / 32 * 32; + if (newTempSize > m_temp.size()) + runGas += gasForMem(newTempSize) - gasForMem(m_temp.size()); + runGas += c_copyGas * ((copySize + 31) / 32); + + onOperation(); + + if (io_gas < runGas) + BOOST_THROW_EXCEPTION(OutOfGas()); + io_gas -= (u256)runGas; + + if (newTempSize > m_temp.size()) + m_temp.resize((size_t)newTempSize); + + // EXECUTE... switch (inst) { case Instruction::ADD: @@ -303,7 +299,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) case Instruction::SIGNEXTEND: if (m_stack.back() < 31) { - auto testBit = static_cast(m_stack.back()) * 8 + 7; + unsigned const testBit(m_stack.back() * 8 + 7); u256& number = m_stack[m_stack.size() - 2]; u256 mask = ((u256(1) << testBit) - 1); if (boost::multiprecision::bit_test(number, testBit)) @@ -484,7 +480,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) case Instruction::DUP15: case Instruction::DUP16: { - auto n = 1 + (unsigned)inst - (unsigned)Instruction::DUP1; + auto n = 1 + (int)inst - (int)Instruction::DUP1; m_stack.push_back(m_stack[m_stack.size() - n]); break; } @@ -505,7 +501,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) case Instruction::SWAP15: case Instruction::SWAP16: { - auto n = (unsigned)inst - (unsigned)Instruction::SWAP1 + 2; + unsigned n = (int)inst - (int)Instruction::SWAP1 + 2; auto d = m_stack.back(); m_stack.back() = m_stack[m_stack.size() - n]; m_stack[m_stack.size() - n] = d; @@ -539,12 +535,18 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) m_stack.pop_back(); break; case Instruction::JUMP: - nextPC = verifyJumpDest(m_stack.back(), m_jumpDests); + nextPC = m_stack.back(); + if (find(m_jumpDests.begin(), m_jumpDests.end(), (uint64_t)nextPC) == m_jumpDests.end() || nextPC > numeric_limits::max() ) + BOOST_THROW_EXCEPTION(BadJumpDestination()); m_stack.pop_back(); break; case Instruction::JUMPI: if (m_stack[m_stack.size() - 2]) - nextPC = verifyJumpDest(m_stack.back(), m_jumpDests); + { + nextPC = m_stack.back(); + if (find(m_jumpDests.begin(), m_jumpDests.end(), (uint64_t)nextPC) == m_jumpDests.end() || nextPC > numeric_limits::max() ) + BOOST_THROW_EXCEPTION(BadJumpDestination()); + } m_stack.pop_back(); m_stack.pop_back(); break; @@ -596,7 +598,7 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) break; case Instruction::CREATE: { - auto endowment = m_stack.back(); + u256 endowment = m_stack.back(); m_stack.pop_back(); unsigned initOff = (unsigned)m_stack.back(); m_stack.pop_back(); @@ -612,14 +614,16 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) case Instruction::CALL: case Instruction::CALLCODE: { - CallParameters callParams; - callParams.gas = m_stack.back(); + if (!callParams) + callParams.reset(new CallParameters); + + callParams->gas = m_stack.back(); if (m_stack[m_stack.size() - 3] > 0) - callParams.gas += c_callStipend; + callParams->gas += c_callStipend; m_stack.pop_back(); - callParams.codeAddress = asAddress(m_stack.back()); + callParams->codeAddress = asAddress(m_stack.back()); m_stack.pop_back(); - callParams.value = m_stack.back(); + callParams->value = m_stack.back(); m_stack.pop_back(); unsigned inOff = (unsigned)m_stack.back(); @@ -631,19 +635,19 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) unsigned outSize = (unsigned)m_stack.back(); m_stack.pop_back(); - if (_ext.balance(_ext.myAddress) >= callParams.value && _ext.depth < 1024) + if (_ext.balance(_ext.myAddress) >= callParams->value && _ext.depth < 1024) { - callParams.onOp = _onOp; - callParams.senderAddress = _ext.myAddress; - callParams.receiveAddress = inst == Instruction::CALL ? callParams.codeAddress : callParams.senderAddress; - callParams.data = bytesConstRef(m_temp.data() + inOff, inSize); - callParams.out = bytesRef(m_temp.data() + outOff, outSize); - m_stack.push_back(_ext.call(callParams)); + callParams->onOp = _onOp; + callParams->senderAddress = _ext.myAddress; + callParams->receiveAddress = inst == Instruction::CALL ? callParams->codeAddress : callParams->senderAddress; + callParams->data = bytesConstRef(m_temp.data() + inOff, inSize); + callParams->out = bytesRef(m_temp.data() + outOff, outSize); + m_stack.push_back(_ext.call(*callParams)); } else m_stack.push_back(0); - io_gas += callParams.gas; + io_gas += callParams->gas; break; } case Instruction::RETURN: diff --git a/libevm/VM.h b/libevm/VM.h index 1931ad748..9084769cf 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -54,18 +54,16 @@ class VM: public VMFace public: virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; - uint64_t curPC() const { return m_curPC; } + u256 curPC() const { return m_curPC; } bytes const& memory() const { return m_temp; } u256s const& stack() const { return m_stack; } private: - void checkRequirements(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, Instruction _inst); void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } - uint64_t m_curPC = 0; - uint64_t m_steps = 0; + u256 m_curPC = 0; bytes m_temp; u256s m_stack; std::vector m_jumpDests;