From e27f6d35413acaf9dc54e90da90820ecd29d1813 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 Feb 2014 20:07:52 +0000 Subject: [PATCH] Separated VM from rest of code. --- libethereum/Exceptions.h | 2 + libethereum/FeeStructure.cpp | 49 +++ libethereum/FeeStructure.h | 43 +++ libethereum/State.cpp | 590 +--------------------------------- libethereum/State.h | 80 ++++- libethereum/VM.cpp | 60 ++++ libethereum/VM.h | 592 +++++++++++++++++++++++++++++++++++ 7 files changed, 817 insertions(+), 599 deletions(-) create mode 100644 libethereum/FeeStructure.cpp create mode 100644 libethereum/FeeStructure.h create mode 100644 libethereum/VM.cpp create mode 100644 libethereum/VM.h diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h index 3aa73fcac..7bee4699f 100644 --- a/libethereum/Exceptions.h +++ b/libethereum/Exceptions.h @@ -17,6 +17,8 @@ class BadHexCharacter: public Exception {}; class NotEnoughCash: public Exception {}; class VMException: public Exception {}; +class StepsDone: public VMException {}; +class BreakPointHit: public VMException {}; class BadInstruction: public VMException {}; class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; diff --git a/libethereum/FeeStructure.cpp b/libethereum/FeeStructure.cpp new file mode 100644 index 000000000..afcd31751 --- /dev/null +++ b/libethereum/FeeStructure.cpp @@ -0,0 +1,49 @@ +/* + 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 . +*/ +/** @file FeeStructure.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "FeeStructure.h" + +using namespace std; +using namespace eth; + +u256 const c_stepFee = 1; +u256 const c_dataFee = 20; +u256 const c_memoryFee = 0;//5; memoryFee is 0 for PoC-3 +u256 const c_extroFee = 40; +u256 const c_cryptoFee = 20; +u256 const c_newContractFee = 100; +u256 const c_txFee = 100; + +void FeeStructure::setMultiplier(u256 _x) +{ + m_stepFee = c_stepFee * _x; + m_dataFee = c_dataFee * _x; + m_memoryFee = c_memoryFee * _x; + m_extroFee = c_extroFee * _x; + m_cryptoFee = c_cryptoFee * _x; + m_newContractFee = c_newContractFee * _x; + m_txFee = c_txFee * _x; +} + +u256 FeeStructure::multiplier() const +{ + return m_stepFee / c_stepFee; +} diff --git a/libethereum/FeeStructure.h b/libethereum/FeeStructure.h new file mode 100644 index 000000000..56b8e9c0b --- /dev/null +++ b/libethereum/FeeStructure.h @@ -0,0 +1,43 @@ +/* + 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 . +*/ +/** @file FeeStructure.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include "Common.h" + +namespace eth +{ + +struct FeeStructure +{ + /// The fee structure. Values yet to be agreed on... + void setMultiplier(u256 _x); ///< The current block multiplier. + u256 multiplier() const; + u256 m_stepFee; + u256 m_dataFee; + u256 m_memoryFee; + u256 m_extroFee; + u256 m_cryptoFee; + u256 m_newContractFee; + u256 m_txFee; +}; + +} diff --git a/libethereum/State.cpp b/libethereum/State.cpp index f5a7c92be..b3c16f66a 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -23,19 +23,6 @@ #include #include -#if WIN32 -#pragma warning(push) -#pragma warning(disable:4244) -#else -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -#include -#include -#include -#if WIN32 -#pragma warning(pop) -#else -#endif #include #include #include "BlockChain.h" @@ -43,17 +30,10 @@ #include "Exceptions.h" #include "Dagger.h" #include "Defaults.h" +#include "VM.h" using namespace std; using namespace eth; -u256 const c_stepFee = 1; -u256 const c_dataFee = 20; -u256 const c_memoryFee = 0;//5; memoryFee is 0 for PoC-3 -u256 const c_extroFee = 40; -u256 const c_cryptoFee = 20; -u256 const c_newContractFee = 100; -u256 const c_txFee = 100; - u256 eth::c_genesisDifficulty = (u256)1 << 22; std::map const& eth::genesisState() @@ -138,22 +118,6 @@ State& State::operator=(State const& _s) return *this; } -void FeeStructure::setMultiplier(u256 _x) -{ - m_stepFee = c_stepFee * _x; - m_dataFee = c_dataFee * _x; - m_memoryFee = c_memoryFee * _x; - m_extroFee = c_extroFee * _x; - m_cryptoFee = c_cryptoFee * _x; - m_newContractFee = c_newContractFee * _x; - m_txFee = c_txFee * _x; -} - -u256 FeeStructure::multiplier() const -{ - return m_stepFee / c_stepFee; -} - void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const { auto it = m_cache.find(_a); @@ -736,554 +700,10 @@ void State::executeBare(Transaction const& _t, Address _sender) } } -// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. -// Currently we just pull out the left (high-order in BE) 160-bits. -// TODO: CHECK: check that this is correct. -inline Address asAddress(u256 _item) -{ - return right160(h256(_item)); -} - void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* _totalFee) { - std::vector stack; - - // Set up some local functions. - auto require = [&](u256 _n) - { - if (stack.size() < _n) - throw StackTooSmall(_n, stack.size()); - }; - ensureCached(_myAddress, true, true); - auto& myStore = m_cache[_myAddress].memory(); - - auto store = [&](u256 _n) -> u256 - { - auto i = myStore.find(_n); - return i == myStore.end() ? 0 : i->second; - }; - auto setStore = [&](u256 _n, u256 _v) - { - if (_v) - { -#ifdef __clang__ - auto it = myStore.find(_n); - if (it == myStore.end()) - myStore.insert(make_pair(_n, _v)); - else - myStore.at(_n) = _v; -#else - myStore[_n] = _v; -#endif - } - else - myStore.erase(_n); - }; - - map tempMem; - - u256 curPC = 0; - u256 nextPC = 1; - u256 stepCount = 0; - for (bool stopped = false; !stopped; curPC = nextPC, nextPC = curPC + 1) - { - stepCount++; - - bigint minerFee = stepCount > 16 ? m_fees.m_stepFee : 0; - bigint voidFee = 0; - - auto rawInst = store(curPC); - if (rawInst > 0xff) - throw BadInstruction(); - Instruction inst = (Instruction)(uint8_t)rawInst; - - switch (inst) - { - case Instruction::SSTORE: - require(2); - if (!store(stack.back()) && stack[stack.size() - 2]) - voidFee += m_fees.m_memoryFee; - if (store(stack.back()) && !stack[stack.size() - 2]) - voidFee -= m_fees.m_memoryFee; - // continue on to... - case Instruction::SLOAD: - minerFee += m_fees.m_dataFee; - break; - - case Instruction::EXTRO: - case Instruction::BALANCE: - minerFee += m_fees.m_extroFee; - break; - - case Instruction::MKTX: - minerFee += m_fees.m_txFee; - break; - - case Instruction::SHA256: - case Instruction::RIPEMD160: - case Instruction::ECMUL: - case Instruction::ECADD: - case Instruction::ECSIGN: - case Instruction::ECRECOVER: - case Instruction::ECVALID: - minerFee += m_fees.m_cryptoFee; - break; - default: - break; - } - - if (minerFee + voidFee > balance(_myAddress)) - throw NotEnoughCash(); - 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); - 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); - stack[stack.size() - 2] *= stack.back(); - stack.pop_back(); - break; - case Instruction::SUB: - require(2); - stack[stack.size() - 2] = stack.back() - stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::DIV: - require(2); - stack[stack.size() - 2] = stack.back() / stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::SDIV: - require(2); - (s256&)stack[stack.size() - 2] = (s256&)stack.back() / (s256&)stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::MOD: - require(2); - stack[stack.size() - 2] = stack.back() % stack[stack.size() - 2]; - stack.pop_back(); - break; - case Instruction::SMOD: - require(2); - (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 = stack.back(); - auto x = stack[stack.size() - 2]; - stack.pop_back(); - for (u256 i = 0; i < x; ++i) - n *= n; - stack.back() = n; - break; - } - case Instruction::NEG: - require(1); - stack.back() = ~(stack.back() - 1); - break; - case Instruction::LT: - require(2); - stack[stack.size() - 2] = stack.back() < stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::LE: - require(2); - stack[stack.size() - 2] = stack.back() <= stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::GT: - require(2); - stack[stack.size() - 2] = stack.back() > stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::GE: - require(2); - stack[stack.size() - 2] = stack.back() >= stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::EQ: - require(2); - stack[stack.size() - 2] = stack.back() == stack[stack.size() - 2] ? 1 : 0; - stack.pop_back(); - break; - case Instruction::NOT: - require(1); - stack.back() = stack.back() ? 0 : 1; - stack.pop_back(); - break; - case Instruction::MYADDRESS: - stack.push_back((u160)_myAddress); - break; - case Instruction::TXSENDER: - stack.push_back((u160)_txSender); - break; - case Instruction::TXVALUE: - stack.push_back(_txValue); - break; - case Instruction::TXDATAN: - stack.push_back(_txData.size()); - break; - case Instruction::TXDATA: - require(1); - stack.back() = stack.back() < _txData.size() ? _txData[(uint)stack.back()] : 0; - break; - case Instruction::BLK_PREVHASH: - stack.push_back(m_previousBlock.hash); - break; - case Instruction::BLK_COINBASE: - stack.push_back((u160)m_currentBlock.coinbaseAddress); - break; - case Instruction::BLK_TIMESTAMP: - stack.push_back(m_previousBlock.timestamp); - break; - case Instruction::BLK_NUMBER: - stack.push_back(m_currentNumber); - break; - case Instruction::BLK_DIFFICULTY: - stack.push_back(m_currentBlock.difficulty); - break; - case Instruction::BLK_NONCE: - stack.push_back(m_previousBlock.nonce); - break; - case Instruction::BASEFEE: - stack.push_back(m_fees.multiplier()); - break; - case Instruction::SHA256: - { - uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); - stack.pop_back(); - - CryptoPP::SHA256 digest; - uint i = 0; - for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) - { - bytes b = toBigEndian(stack.back()); - digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 - stack.pop_back(); - } - array final; - digest.TruncatedFinal(final.data(), 32); - stack.push_back(fromBigEndian(final)); - break; - } - case Instruction::RIPEMD160: - { - uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); - stack.pop_back(); - - CryptoPP::RIPEMD160 digest; - uint i = 0; - for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) - { - bytes b = toBigEndian(stack.back()); - digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 - stack.pop_back(); - } - array final; - digest.TruncatedFinal(final.data(), 20); - // 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. - stack.push_back((u256)fromBigEndian(final)); - break; - } - case Instruction::ECMUL: - { - // ECMUL - pops three items. - // If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity. - // Otherwise, pushes (0,0). - require(3); - - bytes pub(1, 4); - pub += toBigEndian(stack[stack.size() - 2]); - pub += toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - - bytes x = toBigEndian(stack.back()); - stack.pop_back(); - - if (secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size())) // TODO: Check both are less than P. - { - secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), (int)pub.size(), x.data()); - stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); - stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); - } - else - { - stack.push_back(0); - stack.push_back(0); - } - break; - } - case Instruction::ECADD: - { - // ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0). - require(4); - - bytes pub(1, 4); - pub += toBigEndian(stack[stack.size() - 2]); - pub += toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - - bytes tweak(1, 4); - tweak += toBigEndian(stack[stack.size() - 2]); - tweak += toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - - if (secp256k1_ecdsa_pubkey_verify(pub.data(),(int) pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(),(int) tweak.size())) - { - secp256k1_ecdsa_pubkey_tweak_add(pub.data(), (int)pub.size(), tweak.data()); - stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); - stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); - } - else - { - stack.push_back(0); - stack.push_back(0); - } - break; - } - case Instruction::ECSIGN: - { - require(2); - bytes sig(64); - int v = 0; - - u256 msg = stack.back(); - stack.pop_back(); - u256 priv = stack.back(); - stack.pop_back(); - bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv)); - - if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v)) - throw InvalidSignature(); - - stack.push_back(v + 27); - stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(0, 32))); - stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(32))); - break; - } - case Instruction::ECRECOVER: - { - require(4); - - bytes sig = toBigEndian(stack[stack.size() - 2]) + toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - int v = (int)stack.back(); - stack.pop_back(); - bytes msg = toBigEndian(stack.back()); - stack.pop_back(); - - byte pubkey[65]; - int pubkeylen = 65; - if (secp256k1_ecdsa_recover_compact(msg.data(), (int)msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27)) - { - stack.push_back(0); - stack.push_back(0); - } - else - { - stack.push_back(fromBigEndian(bytesConstRef(&pubkey[1], 32))); - stack.push_back(fromBigEndian(bytesConstRef(&pubkey[33], 32))); - } - break; - } - case Instruction::ECVALID: - { - require(2); - bytes pub(1, 4); - pub += toBigEndian(stack[stack.size() - 2]); - pub += toBigEndian(stack.back()); - stack.pop_back(); - stack.pop_back(); - - stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size()) ? 1 : 0; - break; - } - case Instruction::SHA3: - { - uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); - stack.pop_back(); - - CryptoPP::SHA3_256 digest; - uint i = 0; - for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) - { - bytes b = toBigEndian(stack.back()); - digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 - stack.pop_back(); - } - array final; - digest.TruncatedFinal(final.data(), 32); - stack.push_back(fromBigEndian(final)); - break; - } - case Instruction::PUSH: - { - stack.push_back(store(curPC + 1)); - nextPC = curPC + 2; - break; - } - case Instruction::POP: - require(1); - stack.pop_back(); - break; - case Instruction::DUP: - require(1); - stack.push_back(stack.back()); - break; - /*case Instruction::DUPN: - { - auto s = store(curPC + 1); - 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 = stack.back(); - stack.back() = stack[stack.size() - 2]; - stack[stack.size() - 2] = d; - break; - } - /*case Instruction::SWAPN: - { - require(1); - auto d = stack.back(); - auto s = store(curPC + 1); - 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::MLOAD: - { - require(1); -#ifdef __clang__ - auto mFinder = tempMem.find(stack.back()); - if (mFinder != tempMem.end()) - stack.back() = mFinder->second; - else - stack.back() = 0; -#else - stack.back() = tempMem[stack.back()]; -#endif - break; - } - case Instruction::MSTORE: - { - require(2); -#ifdef __clang__ - auto mFinder = tempMem.find(stack.back()); - if (mFinder == tempMem.end()) - tempMem.insert(make_pair(stack.back(), stack[stack.size() - 2])); - else - mFinder->second = stack[stack.size() - 2]; -#else - tempMem[stack.back()] = stack[stack.size() - 2]; -#endif - stack.pop_back(); - stack.pop_back(); - break; - } - case Instruction::SLOAD: - require(1); - stack.back() = store(stack.back()); - break; - case Instruction::SSTORE: - require(2); - setStore(stack.back(), stack[stack.size() - 2]); - stack.pop_back(); - stack.pop_back(); - break; - case Instruction::JMP: - require(1); - nextPC = stack.back(); - stack.pop_back(); - break; - case Instruction::JMPI: - require(2); - if (stack.back()) - nextPC = stack[stack.size() - 2]; - stack.pop_back(); - stack.pop_back(); - break; - case Instruction::IND: - stack.push_back(curPC); - break; - case Instruction::EXTRO: - { - require(2); - auto memoryAddress = stack.back(); - stack.pop_back(); - Address contractAddress = asAddress(stack.back()); - stack.back() = contractMemory(contractAddress, memoryAddress); - break; - } - case Instruction::BALANCE: - { - require(1); - stack.back() = balance(asAddress(stack.back())); - break; - } - case Instruction::MKTX: - { - require(4); - - Transaction t; - t.receiveAddress = asAddress(stack.back()); - stack.pop_back(); - t.value = stack.back(); - stack.pop_back(); - - auto itemCount = stack.back(); - stack.pop_back(); - if (stack.size() < itemCount) - throw OperandOutOfRange(0, stack.size(), itemCount); - t.data.reserve((uint)itemCount); - for (auto i = 0; i < itemCount; ++i) - { - t.data.push_back(stack.back()); - stack.pop_back(); - } - - t.nonce = transactionsFrom(_myAddress); - executeBare(t, _myAddress); - - break; - } - case Instruction::SUICIDE: - { - require(1); - Address dest = asAddress(stack.back()); - u256 minusVoidFee = myStore.size() * m_fees.m_memoryFee; - addBalance(dest, balance(_myAddress) + minusVoidFee); - m_cache[_myAddress].kill(); - // ...follow through to... - } - case Instruction::STOP: - return; - default: - throw BadInstruction(); - } - } + VM vm; + ExtVM evm(*this, _myAddress, _txSender, _txValue, _txData); + vm.go(evm); + *_totalFee = vm.runFee(); } diff --git a/libethereum/State.h b/libethereum/State.h index 54554d1a1..26b3993e5 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -32,7 +32,9 @@ #include "AddressState.h" #include "Transaction.h" #include "TrieDB.h" +#include "FeeStructure.h" #include "Dagger.h" +#include "ExtVMFace.h" namespace eth { @@ -44,24 +46,12 @@ std::map const& genesisState(); #define ETH_SENDER_PAYS_SETUP 1 -struct FeeStructure -{ - /// The fee structure. Values yet to be agreed on... - void setMultiplier(u256 _x); ///< The current block multiplier. - u256 multiplier() const; - u256 m_stepFee; - u256 m_dataFee; - u256 m_memoryFee; - u256 m_extroFee; - u256 m_cryptoFee; - u256 m_newContractFee; - u256 m_txFee; -}; - template class UnitTest {}; static const std::map EmptyMapU256U256; +class ExtVM; + /** * @brief Model of the current state of the ledger. * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). @@ -70,6 +60,8 @@ static const std::map EmptyMapU256U256; class State { template friend class UnitTest; + friend class ExtVM; + public: /// Construct state object. State(Address _coinbaseAddress, Overlay const& _db); @@ -254,6 +246,66 @@ private: friend std::ostream& operator<<(std::ostream& _out, State const& _s); }; +class ExtVM: public ExtVMFace +{ +public: + ExtVM(State& _s, Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData): + ExtVMFace(_myAddress, _txSender, _txValue, _txData, _s.m_fees, _s.m_previousBlock, _s.m_currentBlock, _s.m_currentNumber), m_s(_s) + { + m_s.ensureCached(_myAddress, true, true); + m_store = &(m_s.m_cache[_myAddress].memory()); + } + + u256 store(u256 _n) + { + auto i = m_store->find(_n); + return i == m_store->end() ? 0 : i->second; + } + void setStore(u256 _n, u256 _v) + { + if (_v) + { +#ifdef __clang__ + auto it = m_store->find(_n); + if (it == m_store->end()) + m_store->insert(make_pair(_n, _v)); + else + m_store->at(_n) = _v; +#else + (*m_store)[_n] = _v; +#endif + } + else + m_store->erase(_n); + } + + void payFee(bigint _f) + { + if (_f > m_s.balance(myAddress)) + throw NotEnoughCash(); + m_s.subBalance(myAddress, _f); + } + + void mktx(Transaction& _t) + { + _t.nonce = m_s.transactionsFrom(myAddress); + m_s.executeBare(_t, myAddress); + } + u256 balance(Address _a) { return m_s.balance(_a); } + u256 txCount(Address _a) { return m_s.transactionsFrom(_a); } + u256 extro(Address _a, u256 _pos) { return m_s.contractMemory(_a, _pos); } + u256 extroPrice(Address _a) { return 0; } + void suicide(Address _a) + { + m_s.addBalance(_a, m_s.balance(myAddress) + m_store->size() * fees.m_memoryFee); + m_s.m_cache[myAddress].kill(); + } + +private: + State& m_s; + std::map* m_store; +}; + inline std::ostream& operator<<(std::ostream& _out, State const& _s) { _out << "--- " << _s.rootHash() << std::endl; diff --git a/libethereum/VM.cpp b/libethereum/VM.cpp new file mode 100644 index 000000000..727559751 --- /dev/null +++ b/libethereum/VM.cpp @@ -0,0 +1,60 @@ +/* + 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 . +*/ +/** @file VM.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "VM.h" + +#include +#include +#if WIN32 +#pragma warning(push) +#pragma warning(disable:4244) +#else +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include +#include +#include +#if WIN32 +#pragma warning(pop) +#else +#endif +#include +#include +#include "BlockChain.h" +#include "Instruction.h" +#include "Exceptions.h" +#include "Dagger.h" +#include "Defaults.h" +using namespace std; +using namespace eth; + +VM::VM() +{ + reset(); +} + +void VM::reset() +{ + m_curPC = 0; + m_nextPC = 1; + m_stepCount = 0; + m_runFee = 0; +} diff --git a/libethereum/VM.h b/libethereum/VM.h new file mode 100644 index 000000000..b80c9a0a8 --- /dev/null +++ b/libethereum/VM.h @@ -0,0 +1,592 @@ +/* + 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 . +*/ +/** @file VM.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#if WIN32 +#pragma warning(push) +#pragma warning(disable:4244) +#else +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include +#include +#include +#if WIN32 +#pragma warning(pop) +#else +#endif +#include "Common.h" +#include "Exceptions.h" +#include "FeeStructure.h" +#include "Instruction.h" +#include "BlockInfo.h" +#include "ExtVMFace.h" + +namespace eth +{ + +// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. +// Currently we just pull out the right (low-order in BE) 160-bits. +inline Address asAddress(u256 _item) +{ + return right160(h256(_item)); +} + +inline u256 fromAddress(Address _a) +{ + return (u160)_a; +} + +/** + */ +class VM +{ + template friend class UnitTest; + +public: + /// Construct VM object. + VM(); + + void reset(); + + template + void go(Ext& _ext, uint64_t _steps = (uint64_t)-1); + + void require(u256 _n) { if (m_stack.size() < _n) throw StackTooSmall(_n, m_stack.size()); } + u256 runFee() const { return m_runFee; } + +private: + u256 m_curPC = 0; + u256 m_nextPC = 1; + uint64_t m_stepCount = 0; + std::map m_temp; + std::vector m_stack; + u256 m_runFee = 0; +}; + +} + +// INLINE: +template void eth::VM::go(Ext& _ext, uint64_t _steps) +{ + for (bool stopped = false; !stopped && _steps--; m_curPC = m_nextPC, m_nextPC = m_curPC + 1) + { + m_stepCount++; + + // INSTRUCTION... + auto rawInst = _ext.store(m_curPC); + if (rawInst > 0xff) + throw BadInstruction(); + Instruction inst = (Instruction)(uint8_t)rawInst; + + // FEES... + bigint runFee = m_stepCount > 16 ? _ext.fees.m_stepFee : 0; + bigint storeCostDelta = 0; + switch (inst) + { + case Instruction::SSTORE: + require(2); + if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) + storeCostDelta += _ext.fees.m_memoryFee; + if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) + storeCostDelta -= _ext.fees.m_memoryFee; + // continue on to... + case Instruction::SLOAD: + runFee += _ext.fees.m_dataFee; + break; + + case Instruction::EXTRO: + case Instruction::BALANCE: + runFee += _ext.fees.m_extroFee; + break; + + case Instruction::MKTX: + runFee += _ext.fees.m_txFee; + break; + + case Instruction::SHA256: + case Instruction::RIPEMD160: + case Instruction::ECMUL: + case Instruction::ECADD: + case Instruction::ECSIGN: + case Instruction::ECRECOVER: + case Instruction::ECVALID: + runFee += _ext.fees.m_cryptoFee; + break; + default: + break; + } + _ext.payFee(runFee + storeCostDelta); + m_runFee += (u256)runFee; + + // EXECUTE... + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + for (u256 i = 0; i < x; ++i) + n *= n; + m_stack.back() = n; + break; + } + case Instruction::NEG: + require(1); + m_stack.back() = ~(m_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(); + 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(); + 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(); + 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(); + 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(); + break; + case Instruction::NOT: + require(1); + m_stack.back() = m_stack.back() ? 0 : 1; + m_stack.pop_back(); + break; + case Instruction::MYADDRESS: + m_stack.push_back(fromAddress(_ext.myAddress)); + break; + case Instruction::TXSENDER: + m_stack.push_back(fromAddress(_ext.txSender)); + break; + case Instruction::TXVALUE: + m_stack.push_back(_ext.txValue); + break; + case Instruction::TXDATAN: + m_stack.push_back(_ext.txData.size()); + break; + case Instruction::TXDATA: + require(1); + m_stack.back() = m_stack.back() < _ext.txData.size() ? _ext.txData[(uint)m_stack.back()] : 0; + break; + case Instruction::BLK_PREVHASH: + m_stack.push_back(_ext.previousBlock.hash); + break; + case Instruction::BLK_COINBASE: + m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress); + break; + case Instruction::BLK_TIMESTAMP: + m_stack.push_back(_ext.currentBlock.timestamp); + break; + case Instruction::BLK_NUMBER: + m_stack.push_back(_ext.currentNumber); + break; + case Instruction::BLK_DIFFICULTY: + m_stack.push_back(_ext.currentBlock.difficulty); + break; + case Instruction::BLK_NONCE: + m_stack.push_back(_ext.previousBlock.nonce); + break; + case Instruction::BASEFEE: + m_stack.push_back(_ext.fees.multiplier()); + break; + case Instruction::SHA256: + { + uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32); + m_stack.pop_back(); + + CryptoPP::SHA256 digest; + uint i = 0; + for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) + { + bytes b = toBigEndian(m_stack.back()); + digest.Update(b.data(), (int)std::min(32, s)); // b.size() == 32 + m_stack.pop_back(); + } + std::array final; + digest.TruncatedFinal(final.data(), 32); + m_stack.push_back(fromBigEndian(final)); + break; + } + case Instruction::RIPEMD160: + { + uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32); + m_stack.pop_back(); + + CryptoPP::RIPEMD160 digest; + uint i = 0; + for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) + { + bytes b = toBigEndian(m_stack.back()); + digest.Update(b.data(), (int)std::min(32, s)); // b.size() == 32 + m_stack.pop_back(); + } + std::array final; + digest.TruncatedFinal(final.data(), 20); + // 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.push_back((u256)fromBigEndian(final)); + break; + } + case Instruction::ECMUL: + { + // ECMUL - pops three items. + // If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity. + // Otherwise, pushes (0,0). + require(3); + + bytes pub(1, 4); + pub += toBigEndian(m_stack[m_stack.size() - 2]); + pub += toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + + bytes x = toBigEndian(m_stack.back()); + m_stack.pop_back(); + + if (secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size())) // TODO: Check both are less than P. + { + secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), (int)pub.size(), x.data()); + m_stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); + m_stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); + } + else + { + m_stack.push_back(0); + m_stack.push_back(0); + } + break; + } + case Instruction::ECADD: + { + // ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0). + require(4); + + bytes pub(1, 4); + pub += toBigEndian(m_stack[m_stack.size() - 2]); + pub += toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + + bytes tweak(1, 4); + tweak += toBigEndian(m_stack[m_stack.size() - 2]); + tweak += toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + + if (secp256k1_ecdsa_pubkey_verify(pub.data(),(int) pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(),(int) tweak.size())) + { + secp256k1_ecdsa_pubkey_tweak_add(pub.data(), (int)pub.size(), tweak.data()); + m_stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); + m_stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); + } + else + { + m_stack.push_back(0); + m_stack.push_back(0); + } + break; + } + case Instruction::ECSIGN: + { + require(2); + bytes sig(64); + int v = 0; + + u256 msg = m_stack.back(); + m_stack.pop_back(); + u256 priv = m_stack.back(); + m_stack.pop_back(); + bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv)); + + if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v)) + throw InvalidSignature(); + + m_stack.push_back(v + 27); + m_stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(0, 32))); + m_stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(32))); + break; + } + case Instruction::ECRECOVER: + { + require(4); + + bytes sig = toBigEndian(m_stack[m_stack.size() - 2]) + toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + int v = (int)m_stack.back(); + m_stack.pop_back(); + bytes msg = toBigEndian(m_stack.back()); + m_stack.pop_back(); + + byte pubkey[65]; + int pubkeylen = 65; + if (secp256k1_ecdsa_recover_compact(msg.data(), (int)msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27)) + { + m_stack.push_back(0); + m_stack.push_back(0); + } + else + { + m_stack.push_back(fromBigEndian(bytesConstRef(&pubkey[1], 32))); + m_stack.push_back(fromBigEndian(bytesConstRef(&pubkey[33], 32))); + } + break; + } + case Instruction::ECVALID: + { + require(2); + bytes pub(1, 4); + pub += toBigEndian(m_stack[m_stack.size() - 2]); + pub += toBigEndian(m_stack.back()); + m_stack.pop_back(); + m_stack.pop_back(); + + m_stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size()) ? 1 : 0; + break; + } + case Instruction::SHA3: + { + uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32); + m_stack.pop_back(); + + CryptoPP::SHA3_256 digest; + uint i = 0; + for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) + { + bytes b = toBigEndian(m_stack.back()); + digest.Update(b.data(), (int)std::min(32, s)); // b.size() == 32 + m_stack.pop_back(); + } + std::array final; + digest.TruncatedFinal(final.data(), 32); + m_stack.push_back(fromBigEndian(final)); + break; + } + case Instruction::PUSH: + { + m_stack.push_back(_ext.store(m_curPC + 1)); + m_nextPC = m_curPC + 2; + break; + } + case Instruction::POP: + require(1); + m_stack.pop_back(); + break; + case Instruction::DUP: + require(1); + m_stack.push_back(m_stack.back()); + break; + /*case Instruction::DUPN: + { + auto s = store(curPC + 1); + 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; + break; + } + /*case Instruction::SWAPN: + { + require(1); + auto d = stack.back(); + auto s = store(curPC + 1); + 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::MLOAD: + { + require(1); +#ifdef __clang__ + auto mFinder = tempMem.find(stack.back()); + if (mFinder != tempMem.end()) + stack.back() = mFinder->second; + else + stack.back() = 0; +#else + m_stack.back() = m_temp[m_stack.back()]; +#endif + break; + } + case Instruction::MSTORE: + { + require(2); +#ifdef __clang__ + auto mFinder = tempMem.find(stack.back()); + if (mFinder == tempMem.end()) + tempMem.insert(make_pair(stack.back(), stack[stack.size() - 2])); + else + mFinder->second = stack[stack.size() - 2]; +#else + m_temp[m_stack.back()] = m_stack[m_stack.size() - 2]; +#endif + m_stack.pop_back(); + m_stack.pop_back(); + break; + } + case Instruction::SLOAD: + require(1); + m_stack.back() = _ext.store(m_stack.back()); + break; + case Instruction::SSTORE: + require(2); + _ext.setStore(m_stack.back(), m_stack[m_stack.size() - 2]); + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::JMP: + require(1); + m_nextPC = m_stack.back(); + m_stack.pop_back(); + break; + case Instruction::JMPI: + require(2); + if (m_stack.back()) + m_nextPC = m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + m_stack.pop_back(); + break; + case Instruction::IND: + m_stack.push_back(m_curPC); + break; + case Instruction::EXTRO: + { + require(2); + auto memoryAddress = m_stack.back(); + m_stack.pop_back(); + Address contractAddress = asAddress(m_stack.back()); + m_stack.back() = _ext.extro(contractAddress, memoryAddress); + break; + } + case Instruction::BALANCE: + { + require(1); + m_stack.back() = _ext.balance(asAddress(m_stack.back())); + break; + } + case Instruction::MKTX: + { + require(3); + + Transaction t; + t.receiveAddress = asAddress(m_stack.back()); + m_stack.pop_back(); + t.value = m_stack.back(); + m_stack.pop_back(); + + auto itemCount = m_stack.back(); + m_stack.pop_back(); + if (m_stack.size() < itemCount) + throw OperandOutOfRange(0, m_stack.size(), itemCount); + t.data.reserve((uint)itemCount); + for (auto i = 0; i < itemCount; ++i) + { + t.data.push_back(m_stack.back()); + m_stack.pop_back(); + } + + _ext.mktx(t); + break; + } + case Instruction::SUICIDE: + { + require(1); + Address dest = asAddress(m_stack.back()); + _ext.suicide(dest); + // ...follow through to... + } + case Instruction::STOP: + return; + default: + throw BadInstruction(); + } + } + if (_steps == (unsigned)-1) + throw StepsDone(); +} +