diff --git a/libethereum/Common.h b/libethereum/Common.h index a1e00cc05..4d4df6c31 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -61,7 +61,7 @@ inline std::string toBigEndianString(u256 _val) { std::string ret; ret.resize(32); - for (int i = 0; i <32; ++i, _val >>= 8) + for (int i = 0; i < 32; ++i, _val >>= 8) ret[31 - i] = (char)(uint8_t)_val; return ret; } diff --git a/libethereum/RLP.h b/libethereum/RLP.h index 3140254ac..1113fccba 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -59,6 +59,9 @@ public: /// Fits only into eth::u256 type. Use only toFatInt() or toBigInt() to read. bool isFatInt() const { assert(!isNull()); return m_data[0] >= 0x20 && m_data[0] < 0x38; } + /// Fits into eth::u256 type, though might fit into eth::uint type. + bool isFixedInt() const { assert(!isNull()); return m_data[0] < 0x38; } + /// Fits only into eth::bigint type. Use only toBigInt() to read. bool isBigInt() const { assert(!isNull()); return m_data[0] >= 0x38 && m_data[0] < 0x40; } @@ -95,23 +98,23 @@ public: return payload().cropped(0, items()).toString(); } - template _T toInt(_T _def = 0) const + template _T toInt() const { - if (!isInt()) - return _def; + if (!isString() && !isInt()) + return 0; if (isDirectValueInt()) return m_data[0]; _T ret = 0; - auto s = intSize() - intLengthSize(); - uint o = intLengthSize() + 1; + auto s = isInt() ? intSize() - lengthSize() : isString() ? items() : 0; + uint o = lengthSize() + 1; for (uint i = 0; i < s; ++i) ret = (ret << 8) | m_data[i + o]; return ret; } - uint toSlimInt(uint _def = 0) const { return toInt(_def); } - u256 toFatInt(u256 _def = 0) const { return toInt(_def); } - bigint toBigInt(bigint _def = 0) const { return toInt(_def); } + uint toSlimInt() const { return toInt(); } + u256 toFatInt() const { return toInt(); } + bigint toBigInt() const { return toInt(); } RLPs toList() const { @@ -159,9 +162,9 @@ private: return 0; } - uint intLengthSize() const { return isIndirectAddressedInt() ? m_data[0] - 0x37 : 0; } - uint intSize() const { return (!isInt() || isDirectValueInt()) ? 0 : isIndirectAddressedInt() ? intLengthSize() + items() : (m_data[0] - 0x17); } + uint intSize() const { return (!isInt() || isDirectValueInt()) ? 0 : isIndirectAddressedInt() ? lengthSize() + items() : (m_data[0] - 0x17); } + uint lengthSize() const { auto n = (m_data[0] & 0x3f); return n > 0x37 ? n - 0x37 : 0; } uint items() const { auto n = (m_data[0] & 0x3f); @@ -219,6 +222,7 @@ public: RLPStream& operator<<(char const* _s) { append(std::string(_s)); return *this; } RLPStream& operator<<(std::string const& _s) { append(_s); return *this; } RLPStream& operator<<(RLPList _l) { appendList(_l.count); return *this; } + template RLPStream& operator<<(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } bytes const& out() const { return m_out; } std::string str() const { return std::string((char const*)m_out.data(), (char const*)(m_out.data() + m_out.size())); } diff --git a/libethereum/VirtualMachine.cpp b/libethereum/VirtualMachine.cpp index 32bca9955..65e3cddbd 100644 --- a/libethereum/VirtualMachine.cpp +++ b/libethereum/VirtualMachine.cpp @@ -1,177 +1,370 @@ -#include "sha256.h" #include +#include +#include "sha256.h" #include "VirtualMachine.h" using namespace std; using namespace eth; -VirtualMachine::~VirtualMachine() +u256 const State::c_stepFee = 0; +u256 const State::c_dataFee = 0; +u256 const State::c_memoryFee = 0; +u256 const State::c_extroFee = 0; +u256 const State::c_cryptoFee = 0; +u256 const State::c_newContractFee = 0; + +u256 extractSender(u256 _v, u256 _r, u256 _s) { + // TODO... + return _s; } +template +inline _T low160(_T const& _t) +{ + return _t & ((((_T)1) << 160) - 1); +} -void VirtualMachine::go() +bool State::transact(bytes const& _rlp) { - u256 curPC = 0; - u256 nextPC = 1; - auto& memory = m_state->memory(m_myAddress); + RLP rlp(_rlp); + if (!rlp.isList()) + return false; + RLPs items = rlp.toList(); + +// if (!items[0].isFixedInt()) +// return false; + if (!items[0].isString()) + return false; + u256 nonce = items[0].toFatInt(); + +// if (!(items[1].isEmpty() || items[1].isFixedInt())) +// return false; + if (!items[1].isString()) + return false; + u256 address = items[1].toFatInt(); + + if (!items[2].isFixedInt()) + return false; + u256 value = items[2].toFatInt(); + + if (!items[3].isFixedInt()) + return false; + u256 fee = items[3].toFatInt(); + + if (!items[4].isList()) + return false; + u256s data; + data.reserve(items[4].itemCount()); + for (auto const& i: items[4].toList()) + if (i.isFixedInt()) + data.push_back(i.toFatInt()); + else + return false; + + if (!items[5].isString()) + return false; + u256 v = items[5].toFatInt(); + + if (!items[6].isString()) + return false; + u256 r = items[6].toFatInt(); + + if (!items[7].isString()) + return false; + u256 s = items[7].toFatInt(); + + u256 sender; + try + { + sender = extractSender(v, r, s); + } + catch (...) + { + // Invalid signiture. + // Error reporting? + return false; + } + + if (nonce != transactionsFrom(sender)) + { + // Nonce is wrong. + // Error reporting? + return false; + } + + if (balance(sender) < value + fee) + { + // Sender balance too low. + // Error reporting? + return false; + } + + if (address) + { + assert(subBalance(sender, value)); + addBalance(address, value); + + if (isContractAddress(address)) + { + bool ret = true; + u256 minerFee = 0; + + try + { + execute(address, sender, value, fee, data, &minerFee); + } + catch (...) + { + // Execution error. + // Error reporting? + ret = false; + } + + addBalance(m_minerAddress, minerFee); + return ret; + } + else + return true; + } + else + { + if (fee < data.size() * c_memoryFee + c_newContractFee) + { + // Fee too small. + // Error reporting? + return false; + } + + u256 newAddress = low160(sha256(_rlp)); + if (isContractAddress(newAddress)) + { + // Contract collision. + // Error reporting? + return false; + } + + auto& mem = m_contractMemory[newAddress]; + for (uint i = 0; i < data.size(); ++i) + mem[i] = data[i]; + assert(subBalance(sender, value)); + addBalance(newAddress, value); + return true; + } +} + +void State::execute(u256 _myAddress, u256 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee) +{ + std::vector stack; + auto& myMemory = ensureMemory(_myAddress); auto require = [&](u256 _n) { - if (m_stack.size() < _n) - throw StackTooSmall(_n, m_stack.size()); + if (stack.size() < _n) + throw StackTooSmall(_n, stack.size()); + }; + auto mem = [&](u256 _n) -> u256 + { + auto i = myMemory.find(_n); + return i == myMemory.end() ? 0 : i->second; }; - auto mem = [&](u256 _n) + auto setMem = [&](u256 _n, u256 _v) { - auto i = memory.find(_n); - return i == memory.end() ? 0 : i->second; + if (_v) + myMemory[_n] = _v; + else + myMemory.erase(_n); }; + u256 curPC = 0; + u256 nextPC = 1; + u256 stepCount = 0; for (bool stopped = false; !stopped; curPC = nextPC, nextPC = curPC + 1) { + stepCount++; + + bigint minerFee = stepCount > 16 ? c_stepFee : 0; + bigint voidFee = 0; + auto rawInst = mem(curPC); if (rawInst > 0xff) throw BadInstruction(); Instruction inst = (Instruction)(uint8_t)rawInst; + switch (inst) + { + case Instruction::STORE: + require(2); + if (!mem(stack.back()) && stack[stack.size() - 2]) + voidFee += c_memoryFee; + if (mem(stack.back()) && !stack[stack.size() - 2]) + voidFee -= c_memoryFee; + // continue on to... + case Instruction::LOAD: + minerFee += c_dataFee; + break; + + case Instruction::EXTRO: + case Instruction::BALANCE: + minerFee += c_extroFee; + break; + + case Instruction::SHA256: + case Instruction::RIPEMD160: + case Instruction::ECMUL: + case Instruction::ECADD: + case Instruction::ECSIGN: + case Instruction::ECRECOVER: + case Instruction::ECVALID: + minerFee += c_cryptoFee; + break; + default: + break; + } + + if (minerFee + voidFee > balance(_myAddress)) + return; + subBalance(_myAddress, minerFee + voidFee); + *_totalFee += (u256)minerFee; + switch (inst) { case Instruction::ADD: //pops two items and pushes S[-1] + S[-2] mod 2^256. require(2); - m_stack[m_stack.size() - 2] += m_stack.back(); - m_stack.pop_back(); + stack[stack.size() - 2] += stack.back(); + stack.pop_back(); break; case Instruction::MUL: //pops two items and pushes S[-1] * S[-2] mod 2^256. require(2); - m_stack[m_stack.size() - 2] *= m_stack.back(); - m_stack.pop_back(); + stack[stack.size() - 2] *= stack.back(); + stack.pop_back(); break; case Instruction::SUB: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() - m_stack[m_stack.size() - 2]; - m_stack.pop_back(); + stack[stack.size() - 2] = stack.back() - stack[stack.size() - 2]; + stack.pop_back(); break; case Instruction::DIV: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() / m_stack[m_stack.size() - 2]; - m_stack.pop_back(); + stack[stack.size() - 2] = stack.back() / stack[stack.size() - 2]; + stack.pop_back(); break; case Instruction::SDIV: require(2); - (s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() / (s256&)m_stack[m_stack.size() - 2]; - m_stack.pop_back(); + (s256&)stack[stack.size() - 2] = (s256&)stack.back() / (s256&)stack[stack.size() - 2]; + stack.pop_back(); break; case Instruction::MOD: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() % m_stack[m_stack.size() - 2]; - m_stack.pop_back(); + stack[stack.size() - 2] = stack.back() % stack[stack.size() - 2]; + stack.pop_back(); break; case Instruction::SMOD: require(2); - (s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() % (s256&)m_stack[m_stack.size() - 2]; - m_stack.pop_back(); + (s256&)stack[stack.size() - 2] = (s256&)stack.back() % (s256&)stack[stack.size() - 2]; + stack.pop_back(); break; case Instruction::EXP: { // TODO: better implementation? require(2); - auto n = m_stack.back(); - auto x = m_stack[m_stack.size() - 2]; - m_stack.pop_back(); + auto n = stack.back(); + auto x = stack[stack.size() - 2]; + stack.pop_back(); for (u256 i = 0; i < x; ++i) n *= n; - m_stack.back() = n; + stack.back() = n; break; } case Instruction::NEG: require(1); - m_stack.back() = ~(m_stack.back() - 1); + stack.back() = ~(stack.back() - 1); break; case Instruction::LT: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0; - m_stack.pop_back(); + stack[stack.size() - 2] = stack.back() < stack[stack.size() - 2] ? 1 : 0; + stack.pop_back(); break; case Instruction::LE: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() <= m_stack[m_stack.size() - 2] ? 1 : 0; - m_stack.pop_back(); + stack[stack.size() - 2] = stack.back() <= stack[stack.size() - 2] ? 1 : 0; + stack.pop_back(); break; case Instruction::GT: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() > m_stack[m_stack.size() - 2] ? 1 : 0; - m_stack.pop_back(); + stack[stack.size() - 2] = stack.back() > stack[stack.size() - 2] ? 1 : 0; + stack.pop_back(); break; case Instruction::GE: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() >= m_stack[m_stack.size() - 2] ? 1 : 0; - m_stack.pop_back(); + stack[stack.size() - 2] = stack.back() >= stack[stack.size() - 2] ? 1 : 0; + stack.pop_back(); break; case Instruction::EQ: require(2); - m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0; - m_stack.pop_back(); + stack[stack.size() - 2] = stack.back() == stack[stack.size() - 2] ? 1 : 0; + stack.pop_back(); break; case Instruction::NOT: require(1); - m_stack.back() = m_stack.back() ? 0 : 1; - m_stack.pop_back(); + stack.back() = stack.back() ? 0 : 1; + stack.pop_back(); break; case Instruction::MYADDRESS: - m_stack.push_back(m_myAddress); + stack.push_back(_myAddress); break; case Instruction::TXSENDER: - m_stack.push_back(m_txSender); + stack.push_back(_txSender); break; case Instruction::TXVALUE: - m_stack.push_back(m_txValue); + stack.push_back(_txValue); break; case Instruction::TXFEE: - m_stack.push_back(m_txFee); + stack.push_back(_txFee); break; case Instruction::TXDATAN: - m_stack.push_back(m_txData.size()); + stack.push_back(_txData.size()); break; case Instruction::TXDATA: require(1); - m_stack.back() = m_stack.back() < m_txData.size() ? m_txData[(uint)m_stack.back()] : 0; + stack.back() = stack.back() < _txData.size() ? _txData[(uint)stack.back()] : 0; break; case Instruction::BLK_PREVHASH: - m_stack.push_back(m_previousBlock.hash); + stack.push_back(m_previousBlock.hash); break; case Instruction::BLK_COINBASE: - m_stack.push_back(m_currentBlock.coinbase); + stack.push_back(m_currentBlock.coinbase); break; case Instruction::BLK_TIMESTAMP: - m_stack.push_back(m_currentBlock.timestamp); + stack.push_back(m_currentBlock.timestamp); break; case Instruction::BLK_NUMBER: - m_stack.push_back(m_currentBlock.number); + stack.push_back(m_currentBlock.number); break; case Instruction::BLK_DIFFICULTY: - m_stack.push_back(m_currentBlock.difficulty); + stack.push_back(m_currentBlock.difficulty); break; case Instruction::SHA256: case Instruction::RIPEMD160: { - uint s = (uint)min(m_stack.back(), (u256)(m_stack.size() - 1) * 32); + uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); bytes b(s); uint i = 0; for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) { - m_stack.pop_back(); - u256 v = m_stack.back(); + stack.pop_back(); + u256 v = stack.back(); int sz = (int)min(32, s) - 1; // sz is one fewer than the number of bytes we're interested in. v >>= ((31 - sz) * 8); // kill unused low-order bytes. for (int j = 0; j <= sz; ++j, v >>= 8) // cycle through bytes, starting at low-order end. b[i + sz - j] = (byte)(v & 0xff); // set each 32-byte (256-bit) chunk in reverse - (i.e. we want to put low-order last). } if (inst == Instruction::SHA256) - m_stack.back() = sha256(b); + stack.back() = sha256(b); else // NOTE: this aligns to right of 256-bit container (low-order bytes). This won't work if they're treated as byte-arrays and thus left-aligned in a 256-bit container. - m_stack.back() = ripemd160(&b); + stack.back() = ripemd160(&b); break; } case Instruction::ECMUL: @@ -183,117 +376,143 @@ void VirtualMachine::go() break; case Instruction::PUSH: { - m_stack.push_back(mem(curPC + 1)); + stack.push_back(mem(curPC + 1)); nextPC = curPC + 2; break; } case Instruction::POP: require(1); - m_stack.pop_back(); + stack.pop_back(); break; case Instruction::DUP: require(1); - m_stack.push_back(m_stack.back()); + stack.push_back(stack.back()); break; case Instruction::DUPN: { auto s = mem(curPC + 1); - if (s == 0 || s > m_stack.size()) - throw OperandOutOfRange(1, m_stack.size(), s); - m_stack.push_back(m_stack[m_stack.size() - (uint)s]); + if (s == 0 || s > stack.size()) + throw OperandOutOfRange(1, stack.size(), s); + stack.push_back(stack[stack.size() - (uint)s]); nextPC = curPC + 2; break; } case Instruction::SWAP: { require(2); - auto d = m_stack.back(); - m_stack.back() = m_stack[m_stack.size() - 2]; - m_stack[m_stack.size() - 2] = d; + auto d = stack.back(); + stack.back() = stack[stack.size() - 2]; + stack[stack.size() - 2] = d; break; } case Instruction::SWAPN: { require(1); - auto d = m_stack.back(); + auto d = stack.back(); auto s = mem(curPC + 1); - if (s == 0 || s > m_stack.size()) - throw OperandOutOfRange(1, m_stack.size(), s); - m_stack.back() = m_stack[m_stack.size() - (uint)s]; - m_stack[m_stack.size() - (uint)s] = d; + if (s == 0 || s > stack.size()) + throw OperandOutOfRange(1, stack.size(), s); + stack.back() = stack[stack.size() - (uint)s]; + stack[stack.size() - (uint)s] = d; nextPC = curPC + 2; break; } case Instruction::LOAD: require(1); - m_stack.back() = mem(m_stack.back()); + stack.back() = mem(stack.back()); break; case Instruction::STORE: require(2); - mem(m_stack.back()) = m_stack[m_stack.size() - 2]; - m_stack.pop_back(); - m_stack.pop_back(); + setMem(stack.back(), stack[stack.size() - 2]); + stack.pop_back(); + stack.pop_back(); break; case Instruction::JMP: require(1); - nextPC = m_stack.back(); - m_stack.pop_back(); + nextPC = stack.back(); + stack.pop_back(); break; case Instruction::JMPI: require(2); - if (m_stack.back()) - nextPC = m_stack[m_stack.size() - 2]; - m_stack.pop_back(); - m_stack.pop_back(); + if (stack.back()) + nextPC = stack[stack.size() - 2]; + stack.pop_back(); + stack.pop_back(); break; case Instruction::IND: - m_stack.push_back(curPC); + stack.push_back(curPC); break; case Instruction::EXTRO: { require(2); - auto memoryAddress = m_stack.back(); - m_stack.pop_back(); - auto contractAddress = m_stack.back(); - m_stack.back() = m_state->memory(contractAddress, memoryAddress); + auto memoryAddress = stack.back(); + stack.pop_back(); + auto contractAddress = stack.back(); + stack.back() = memory(contractAddress, memoryAddress); break; } case Instruction::BALANCE: { require(1); - m_stack.back() = m_state->balance(m_stack.back()); + stack.back() = balance(stack.back()); break; } case Instruction::MKTX: { require(4); - auto dest = m_stack.back(); - m_stack.pop_back(); + auto dest = stack.back(); + stack.pop_back(); - auto amount = m_stack.back(); - m_stack.pop_back(); + auto value = stack.back(); + stack.pop_back(); - auto fee = m_stack.back(); - m_stack.pop_back(); + auto fee = stack.back(); + stack.pop_back(); - auto itemCount = m_stack.back(); - m_stack.pop_back(); - if (m_stack.size() < itemCount) - throw OperandOutOfRange(0, m_stack.size(), itemCount); + auto itemCount = stack.back(); + stack.pop_back(); + if (stack.size() < itemCount) + throw OperandOutOfRange(0, stack.size(), itemCount); u256s data; data.reserve((uint)itemCount); for (auto i = 0; i < itemCount; ++i) { - data.push_back(m_stack.back()); - m_stack.pop_back(); + data.push_back(stack.back()); + stack.pop_back(); } - m_state->transact(m_myAddress, dest, amount, fee, data); + + u256 nonce = transactionsFrom(_myAddress); + + u256 v = 42; // TODO: turn our address into a v/r/s signature? + u256 r = 42; + u256 s = _myAddress; + // v/r/s are required to make the transaction hash (via the RLP serialisation) and thus are required in the creation of a contract. + + RLPStream rlp; + if (nonce) + rlp << nonce; + else + rlp << ""; + if (dest) + rlp << toBigEndianString(dest); + else + rlp << ""; + rlp << value << fee << data << toBigEndianString(v) << toBigEndianString(r) << toBigEndianString(s); + transact(rlp.out()); + break; } case Instruction::SUICIDE: - // TODO: Suicide... + { + require(1); + auto dest = stack.back(); + u256 minusVoidFee = m_contractMemory[_myAddress].size() * c_memoryFee; + addBalance(dest, balance(_myAddress) + minusVoidFee - _txFee); + m_balance.erase(_myAddress); + m_contractMemory.erase(_myAddress); + // ...follow through to... + } case Instruction::STOP: - // TODO: Cleanups... return; default: throw BadInstruction(); diff --git a/libethereum/VirtualMachine.h b/libethereum/VirtualMachine.h index 49937d64c..d0195b719 100644 --- a/libethereum/VirtualMachine.h +++ b/libethereum/VirtualMachine.h @@ -80,65 +80,47 @@ struct BlockInfo class State { public: - State() {} + explicit State(u256 _minerAddress): m_minerAddress(_minerAddress) {} + + bool transact(bytes const& _rlp); + +private: + bool isContractAddress(u256 _address) const { return m_contractMemory.count(_address); } + + u256 balance(u256 _id) const { auto it = m_balance.find(_id); return it == m_balance.end() ? 0 : it->second; } + void addBalance(u256 _id, u256 _amount) { auto it = m_balance.find(_id); if (it == m_balance.end()) it->second = _amount; else it->second += _amount; } + // bigint as we don't want any accidental problems with -ve numbers. + bool subBalance(u256 _id, bigint _amount) { auto it = m_balance.find(_id); if (it == m_balance.end() || (bigint)it->second < _amount) return false; it->second = (u256)((bigint)it->second - _amount); return true; } u256 memory(u256 _contract, u256 _memory) const { - auto m = m_memory.find(_contract); - if (m == m_memory.end()) + auto m = m_contractMemory.find(_contract); + if (m == m_contractMemory.end()) return 0; auto i = m->second.find(_memory); return i == m->second.end() ? 0 : i->second; } - std::map& memory(u256 _contract) - { - return m_memory[_contract]; - } - - u256 balance(u256 _id) const { return 0; } - bool transact(u256 _src, u256 _dest, u256 _amount, u256 _fee, u256s const& _data) { return false; } - -private: - std::map> m_memory; -}; - -class VirtualMachine -{ -public: - VirtualMachine(State& _s): m_state(&_s) {} - - ~VirtualMachine(); + u256 transactionsFrom(u256 _address) { return 0; } // TODO + void execute(u256 _myAddress, u256 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee); - void initMemory(RLP _contract); - void setMemory(RLP _state); + std::map& ensureMemory(u256 _contract) { return m_contractMemory[_contract]; } - void go(); - -private: - State* m_state; - - std::vector m_stack; - u256 m_stepCount; - u256 m_totalFee; - u256 m_stepFee; - u256 m_dataFee; - u256 m_memoryFee; - u256 m_extroFee; - u256 m_minerFee; - u256 m_voidFee; - - u256 m_myAddress; - u256 m_txSender; - u256 m_txValue; - u256 m_txFee; - std::vector m_txData; + std::map> m_contractMemory; + std::map m_balance; // for now - might end up using Trie? BlockInfo m_previousBlock; BlockInfo m_currentBlock; + u256 m_minerAddress; + static const u256 c_stepFee; + static const u256 c_dataFee; + static const u256 c_memoryFee; + static const u256 c_extroFee; + static const u256 c_cryptoFee; + static const u256 c_newContractFee; }; }