Browse Source

VM refactor: check requirements helper function.

Move an instruction requirements checking in VM to a separated helper function.
Instruction execution is now split into two parts what reduces OS stack memory usage.
cl-refactor
Paweł Bylica 10 years ago
parent
commit
c0ffe96826
  1. 270
      libevm/VM.cpp
  2. 2
      libevm/VM.h

270
libevm/VM.cpp

@ -45,163 +45,167 @@ static array<InstructionMetric, 256> metrics()
return s_ret; return s_ret;
} }
bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) void VM::checkRequirements(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, Instruction _inst)
{ {
// Reset leftovers from possible previous run static const auto c_metrics = metrics();
m_curPC = 0; auto& metric = c_metrics[static_cast<size_t>(_inst)];
m_jumpDests.clear();
m_stack.reserve((unsigned)c_stackLimit); if (metric.gasPriceTier == InvalidTier)
BOOST_THROW_EXCEPTION(BadInstruction());
unique_ptr<CallParameters> callParams; // FEES...
bigint runGas = c_tierStepGas[metric.gasPriceTier];
bigint newTempSize = m_temp.size();
bigint copySize = 0;
static const array<InstructionMetric, 256> c_metrics = metrics(); // should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird.
//m_onFail = std::function<void()>(onOperation);
auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; require(metric.args, metric.ret);
auto gasForMem = [](bigint _size) -> bigint
auto onOperation = [&]()
{ {
bigint s = _size / 32; if (_onOp)
return (bigint)c_memoryGas * s + s * s / c_quadCoeffDiv; _onOp(m_steps, _inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
}; };
if (m_jumpDests.empty()) auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
for (unsigned i = 0; i < _ext.code.size(); ++i)
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 (_ext.code[i] == (byte)Instruction::JUMPDEST) runGas = c_sstoreResetGas;
m_jumpDests.push_back(i); _ext.sub.refunds += c_sstoreRefundGas;
else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
} }
u256 nextPC = m_curPC + 1; else
for (uint64_t steps = 0; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++steps) 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:
{ {
// INSTRUCTION... unsigned n = (unsigned)_inst - (unsigned)Instruction::LOG0;
Instruction inst = (Instruction)_ext.getCode(m_curPC); runGas = c_logGas + c_logTopicGas * n + (bigint)c_logDataGas * m_stack[m_stack.size() - 2];
auto metric = c_metrics[(int)inst]; newTempSize = memNeed(m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]);
int gasPriceTier = metric.gasPriceTier; break;
}
if (gasPriceTier == InvalidTier)
BOOST_THROW_EXCEPTION(BadInstruction());
// FEES... case Instruction::CALL:
bigint runGas = c_tierStepGas[metric.gasPriceTier]; case Instruction::CALLCODE:
bigint newTempSize = m_temp.size(); runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1];
bigint copySize = 0; 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:;
}
// should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird. auto gasForMem = [](bigint _size) -> bigint
//m_onFail = std::function<void()>(onOperation); {
bigint s = _size / 32;
return (bigint)c_memoryGas * s + s * s / c_quadCoeffDiv;
};
require(metric.args, metric.ret); newTempSize = (newTempSize + 31) / 32 * 32;
if (newTempSize > m_temp.size())
runGas += gasForMem(newTempSize) - gasForMem(m_temp.size());
runGas += c_copyGas * ((copySize + 31) / 32);
auto onOperation = [&]() onOperation();
{
if (_onOp)
_onOp(steps, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
};
switch (inst) if (io_gas < runGas)
{ BOOST_THROW_EXCEPTION(OutOfGas());
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;
case Instruction::SLOAD: io_gas -= (u256)runGas;
runGas = c_sloadGas;
break;
// These all operate on memory and therefore potentially expand it: if (newTempSize > m_temp.size())
case Instruction::MSTORE: m_temp.resize((size_t)newTempSize);
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: bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
runGas = 1; {
break; // Reset leftovers from possible previous run
m_curPC = 0;
m_jumpDests.clear();
case Instruction::LOG0: m_stack.reserve((unsigned)c_stackLimit);
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;
}
case Instruction::CALL: unique_ptr<CallParameters> callParams;
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: if (m_jumpDests.empty())
{ for (unsigned i = 0; i < _ext.code.size(); ++i)
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]; if (_ext.code[i] == (byte)Instruction::JUMPDEST)
runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8)); m_jumpDests.push_back(i);
break; else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
} i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
default:;
} }
u256 nextPC = m_curPC + 1;
newTempSize = (newTempSize + 31) / 32 * 32; for (m_steps = 0; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++m_steps)
if (newTempSize > m_temp.size()) {
runGas += gasForMem(newTempSize) - gasForMem(m_temp.size()); // INSTRUCTION...
runGas += c_copyGas * ((copySize + 31) / 32); Instruction inst = (Instruction)_ext.getCode(m_curPC);
checkRequirements(io_gas, _ext, _onOp, inst);
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... // EXECUTE...
switch (inst) switch (inst)

2
libevm/VM.h

@ -60,10 +60,12 @@ public:
u256s const& stack() const { return m_stack; } u256s const& stack() const { return m_stack; }
private: 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 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); } } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 m_curPC = 0; u256 m_curPC = 0;
uint64_t m_steps = 0;
bytes m_temp; bytes m_temp;
u256s m_stack; u256s m_stack;
std::vector<uint64_t> m_jumpDests; std::vector<uint64_t> m_jumpDests;

Loading…
Cancel
Save