diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2181124f7..3a1a22165 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1228,6 +1228,7 @@ void Main::on_blocks_currentItemChanged() s << "
Nonce: " << info.nonce << ""; s << "
Parent: " << info.parentHash << ""; s << "
Bloom: " << details.bloom << ""; + s << "
Log Bloom: " << info.logBloom << ""; s << "
Transactions: " << block[1].itemCount() << " @" << info.transactionsRoot << ""; s << "
Uncles: " << block[2].itemCount() << " @" << info.sha3Uncles << ""; if (info.parentHash) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 97c1d32c7..a32125354 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.7.6"; +char const* Version = "0.7.7"; } diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 11850fa69..5f14f38f8 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -190,7 +190,7 @@ void pushFront(_T& _t, _U _e) _t[0] = _e; } -/// Concatenate two vectors of elements. _T must be POD. +/// Concatenate two vectors of elements of POD types. template inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) { @@ -201,30 +201,38 @@ inline std::vector<_T>& operator+=(std::vector -inline std::vector<_T> operator+(std::vector::value, _T>::type> const& _a, std::vector<_T> const& _b) +inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) +{ + _a.reserve(_a.size() + _b.size()); + for (auto& i: _b) + _a.push_back(i); + return _a; +} + +/// Concatenate two vectors of elements. +template +inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b) { std::vector<_T> ret(_a); return ret += _b; } -/// Concatenate two vectors of elements. _T must be POD. +/// Merge two sets of elements. template -inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) +inline std::set<_T>& operator+=(std::set<_T>& _a, std::set<_T> const& _b) { - _a.reserve(_a.size() + _b.size()); for (auto& i: _b) - _a.push_back(i); + _a.insert(i); return _a; - } -/// Concatenate two vectors of elements. _T must be POD. +/// Merge two sets of elements. template -inline std::vector<_T> operator+(std::vector::value, _T>::type> const& _a, std::vector<_T> const& _b) +inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b) { - std::vector<_T> ret(_a); + std::set<_T> ret(_a); return ret += _b; } diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 0032a1314..41d140cad 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -158,6 +158,25 @@ public: return ret; } + template inline FixedHash& shiftBloom(FixedHash const& _h) { return (*this |= _h.template nbloom()); } + + template inline FixedHash nbloom() const + { + static const unsigned c_bloomBytes = (M + 7) / 8; + unsigned mask = (1 << c_bloomBytes) - 1; + FixedHash ret; + byte const* p = data(); + for (unsigned i = 0; i < P; ++i) + { + unsigned index = 0; + for (unsigned j = 0; j < c_bloomBytes; ++j, ++p) + index = (index << 8) | *p; + index &= mask; + ret[N - 1 - index / 8] |= (1 << (index % 8)); + } + return ret; + } + /// Returns the index of the first bit set to one, or size() * 8 if no bits are set. inline unsigned firstBitSet() const { diff --git a/libdevcrypto/SHA3.cpp b/libdevcrypto/SHA3.cpp index eaabae0ff..7c2cc01a3 100644 --- a/libdevcrypto/SHA3.cpp +++ b/libdevcrypto/SHA3.cpp @@ -30,7 +30,6 @@ namespace dev { h256 EmptySHA3 = sha3(bytesConstRef()); -h256 ZeroRLPSHA3 = sha3(rlp(bytesConstRef())); std::string sha3(std::string const& _input, bool _hex) { diff --git a/libdevcrypto/SHA3.h b/libdevcrypto/SHA3.h index 5948cb3bd..fc2cfcfc3 100644 --- a/libdevcrypto/SHA3.h +++ b/libdevcrypto/SHA3.h @@ -57,7 +57,6 @@ inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef((bytes*)&_inpu inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); } extern h256 EmptySHA3; -extern h256 ZeroRLPSHA3; // Other crypto convenience routines diff --git a/libdevcrypto/TrieDB.cpp b/libdevcrypto/TrieDB.cpp index 168b2fdf7..674f21aa4 100644 --- a/libdevcrypto/TrieDB.cpp +++ b/libdevcrypto/TrieDB.cpp @@ -26,6 +26,7 @@ using namespace dev; #if !ETH_LANGUAGES -const h256 dev::c_shaNull = sha3(rlp("")); +h256 const dev::c_shaNull = sha3(rlp("")); +h256 const dev::EmptyTrie = c_shaNull; #endif diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index 0ca0b9d98..b42d67aea 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -45,6 +45,7 @@ struct TrieDBChannel: public LogChannel { static const char* name() { return "- struct InvalidTrie: virtual dev::Exception {}; extern const h256 c_shaNull; +extern const h256 EmptyTrie; /** * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree. diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 6e4af247f..d87e6f5df 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -37,9 +37,9 @@ BlockInfo::BlockInfo(): timestamp(Invalid256) { } -BlockInfo::BlockInfo(bytesConstRef _block) +BlockInfo::BlockInfo(bytesConstRef _block, bool _checkNonce) { - populate(_block); + populate(_block, _checkNonce); } BlockInfo BlockInfo::fromHeader(bytesConstRef _block) @@ -52,16 +52,16 @@ BlockInfo BlockInfo::fromHeader(bytesConstRef _block) h256 BlockInfo::headerHashWithoutNonce() const { RLPStream s; - fillStream(s, false); + streamRLP(s, false); return sha3(s.out()); } auto static const c_sha3EmptyList = sha3(RLPEmptyList); -void BlockInfo::fillStream(RLPStream& _s, bool _nonce) const +void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const { - _s.appendList(_nonce ? 13 : 12) - << parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot + _s.appendList(_nonce ? 15 : 14) + << parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom << difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData; if (_nonce) _s << nonce; @@ -84,17 +84,19 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce) coinbaseAddress = _header[field = 2].toHash
(); stateRoot = _header[field = 3].toHash(); transactionsRoot = _header[field = 4].toHash(); - difficulty = _header[field = 5].toInt(); - number = _header[field = 6].toInt(); - minGasPrice = _header[field = 7].toInt(); - gasLimit = _header[field = 8].toInt(); - gasUsed = _header[field = 9].toInt(); - timestamp = _header[field = 10].toInt(); - extraData = _header[field = 11].toBytes(); - nonce = _header[field = 12].toHash(); + receiptsRoot = _header[field = 5].toHash(); + logBloom = _header[field = 6].toHash(); + difficulty = _header[field = 7].toInt(); + number = _header[field = 8].toInt(); + minGasPrice = _header[field = 9].toInt(); + gasLimit = _header[field = 10].toInt(); + gasUsed = _header[field = 11].toInt(); + timestamp = _header[field = 12].toInt(); + extraData = _header[field = 13].toBytes(); + nonce = _header[field = 14].toHash(); } - catch (Exception & _e) + catch (Exception const& _e) { _e << errinfo_name("invalid block header format") << BadFieldError(field, toHex(_header[field].data().toBytes())); throw; @@ -140,7 +142,7 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const { bytes k = rlp(i); t.insert(&k, tr.data()); - u256 gp = tr[0][1].toInt(); + u256 gp = tr[1].toInt(); mgp = min(mgp, gp); ++i; } diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index 283f11b88..d91ff244d 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -47,7 +47,7 @@ extern u256 c_genesisDifficulty; * corresponding RLP block created with createGenesisBlock(). * * The difficulty and gas-limit derivations may be calculated with the calculateDifficulty() - * and calculateGasLimit() and the object serialised to RLP with fillStream. To determine the + * and calculateGasLimit() and the object serialised to RLP with streamRLP. To determine the * header hash without the nonce (for mining), the method headerHashWithoutNonce() is provided. * * The default constructor creates an empty object, which can be tested against with the boolean @@ -62,6 +62,8 @@ public: Address coinbaseAddress; h256 stateRoot; h256 transactionsRoot; + h256 receiptsRoot; + h512 logBloom; // TODO LogBloom - get include u256 difficulty; u256 number; u256 minGasPrice; @@ -73,7 +75,7 @@ public: BlockInfo(); explicit BlockInfo(bytes const& _block): BlockInfo(&_block) {} - explicit BlockInfo(bytesConstRef _block); + explicit BlockInfo(bytesConstRef _block, bool _checkNonce = true); static h256 headerHash(bytes const& _block) { return headerHash(&_block); } static h256 headerHash(bytesConstRef _block); @@ -89,6 +91,8 @@ public: coinbaseAddress == _cmp.coinbaseAddress && stateRoot == _cmp.stateRoot && transactionsRoot == _cmp.transactionsRoot && + receiptsRoot == _cmp.receiptsRoot && + logBloom == _cmp.logBloom && difficulty == _cmp.difficulty && number == _cmp.number && minGasPrice == _cmp.minGasPrice && @@ -112,13 +116,14 @@ public: /// No-nonce sha3 of the header only. h256 headerHashWithoutNonce() const; - void fillStream(RLPStream& _s, bool _nonce) const; + void streamRLP(RLPStream& _s, bool _nonce) const; }; inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi) { _out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " << - _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " << _bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce; + _bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " << + _bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce; return _out; } diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index 8e4fcb2d2..5a07407d2 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -57,7 +57,8 @@ struct InvalidDifficulty: virtual dev::Exception {}; class InvalidGasLimit: virtual public dev::Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual const char* what() const noexcept; }; class InvalidMinGasPrice: virtual public dev::Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual const char* what() const noexcept; }; struct InvalidTransactionGasUsed: virtual dev::Exception {}; -struct InvalidTransactionStateRoot: virtual dev::Exception {}; +struct InvalidTransactionsStateRoot: virtual dev::Exception {}; +struct InvalidReceiptsStateRoot: virtual dev::Exception {}; struct InvalidTimestamp: virtual dev::Exception {}; class InvalidNonce: virtual public dev::Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual const char* what() const noexcept; }; class InvalidBlockNonce: virtual public dev::Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual const char* what() const noexcept; }; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index b08179f9e..a9bdbf247 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -101,8 +101,9 @@ bytes BlockChain::createGenesisBlock() stateRoot = state.root(); } - block.appendList(13) - << h256() << c_shaNull << h160() << stateRoot << c_shaNull << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); + block.appendList(15) + // TODO: maybe make logbloom correct? + << h256() << EmptySHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList); return block.out(); @@ -305,7 +306,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) // Get total difficulty increase and update state, checking it. State s(bi.coinbaseAddress, _db); auto tdIncrease = s.enactOn(&_block, bi, *this); - auto b = s.bloom(); + auto b = s.oldBloom(); BlockBlooms bb; BlockTraces bt; for (unsigned i = 0; i < s.pending().size(); ++i) diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index 9cb49a21c..90bf65bdd 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -66,7 +66,7 @@ struct BlockTraces { BlockTraces() {} BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); } - bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamOut(s); return s.out(); } + bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamRLP(s); return s.out(); } Manifests traces; }; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index dc36957ee..63e093b17 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -159,7 +159,7 @@ void Client::clearPending() if (!m_postMine.pending().size()) return; for (unsigned i = 0; i < m_postMine.pending().size(); ++i) - appendFromNewPending(m_postMine.bloom(i), changeds); + appendFromNewPending(m_postMine.oldBloom(i), changeds); changeds.insert(PendingChangedFilter); m_postMine = m_preMine; } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 193010cfa..5a983b66e 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -123,7 +123,11 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms); } else + { m_endGas = _gas; + if (m_ext) + m_ext->sub.logs.push_back(LogEntry(_receiveAddress, {u256((u160)_senderAddress) + 1}, bytes())); + } return !m_ext; } @@ -172,6 +176,8 @@ bool Executive::go(OnOpFunc const& _onOp) try { m_out = m_vm->go(*m_ext, _onOp); + if (m_ext) + m_endGas += min((m_t.gas - m_endGas) / 2, m_ext->sub.refunds); m_endGas = m_vm->gas(); } catch (StepsDone const&) @@ -187,14 +193,17 @@ bool Executive::go(OnOpFunc const& _onOp) { clog(StateChat) << "VM Exception: " << diagnostic_information(_e); m_endGas = m_vm->gas(); + revert = true; } catch (Exception const& _e) { - clog(StateChat) << "Exception in VM: " << diagnostic_information(_e); + // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. + cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e); } catch (std::exception const& _e) { - clog(StateChat) << "std::exception in VM: " << _e.what(); + // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. + cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what(); } cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas); @@ -236,6 +245,6 @@ void Executive::finalize(OnOpFunc const&) // Suicides... if (m_ext) - for (auto a: m_ext->suicides) + for (auto a: m_ext->sub.suicides) m_s.m_cache[a].kill(); } diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 82b7df7e9..cdfe23966 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -61,6 +61,7 @@ public: bytesConstRef out() const { return m_out; } h160 newAddress() const { return m_newAddress; } + LogEntries const& logs() const { return m_logs; } VM const& vm() const { return *m_vm; } State const& state() const { return m_s; } @@ -77,6 +78,8 @@ private: Transaction m_t; Address m_sender; u256 m_endGas; + + LogEntries m_logs; }; } diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index fc76d56b0..59b6eb2ab 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -61,7 +61,7 @@ public: m_s.noteSending(myAddress); if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); + auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; @@ -72,7 +72,7 @@ public: { if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); + auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; diff --git a/libethereum/Manifest.cpp b/libethereum/Manifest.cpp index 2d1e4aa6e..9e6d2f651 100644 --- a/libethereum/Manifest.cpp +++ b/libethereum/Manifest.cpp @@ -37,10 +37,10 @@ Manifest::Manifest(bytesConstRef _r) internal.emplace_back(i.data()); } -void Manifest::streamOut(RLPStream& _s) const +void Manifest::streamRLP(RLPStream& _s) const { _s.appendList(7) << from << to << value << altered << input << output; _s.appendList(internal.size()); for (auto const& i: internal) - i.streamOut(_s); + i.streamRLP(_s); } diff --git a/libethereum/Manifest.h b/libethereum/Manifest.h index 12c40b3a9..e0e512c92 100644 --- a/libethereum/Manifest.h +++ b/libethereum/Manifest.h @@ -41,7 +41,7 @@ struct Manifest { Manifest() {} Manifest(bytesConstRef _r); - void streamOut(RLPStream& _s) const; + void streamRLP(RLPStream& _s) const; h256 bloom() const { h256 ret = from.bloom() | to.bloom(); for (auto const& i: internal) ret |= i.bloom(); for (auto const& i: altered) ret |= h256(i).bloom(); return ret; } diff --git a/libethereum/MessageFilter.cpp b/libethereum/MessageFilter.cpp index 951bfec98..fc6af1308 100644 --- a/libethereum/MessageFilter.cpp +++ b/libethereum/MessageFilter.cpp @@ -27,7 +27,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -void MessageFilter::fillStream(RLPStream& _s) const +void MessageFilter::streamRLP(RLPStream& _s) const { _s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip; } @@ -35,7 +35,7 @@ void MessageFilter::fillStream(RLPStream& _s) const h256 MessageFilter::sha3() const { RLPStream s; - fillStream(s); + streamRLP(s); return dev::sha3(s.out()); } diff --git a/libethereum/MessageFilter.h b/libethereum/MessageFilter.h index c9fb4e51c..83cbb7237 100644 --- a/libethereum/MessageFilter.h +++ b/libethereum/MessageFilter.h @@ -39,7 +39,7 @@ class MessageFilter public: MessageFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {} - void fillStream(RLPStream& _s) const; + void streamRLP(RLPStream& _s) const; h256 sha3() const; int earliest() const { return m_earliest; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 9ec835833..11400b755 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -161,6 +161,7 @@ State::State(State const& _s): m_db(_s.m_db), m_state(&m_db, _s.m_state.root()), m_transactions(_s.m_transactions), + m_receipts(_s.m_receipts), m_transactionSet(_s.m_transactionSet), m_cache(_s.m_cache), m_previousBlock(_s.m_previousBlock), @@ -192,6 +193,7 @@ State& State::operator=(State const& _s) m_db = _s.m_db; m_state.open(&m_db, _s.m_state.root()); m_transactions = _s.m_transactions; + m_receipts = _s.m_receipts; m_transactionSet = _s.m_transactionSet; m_cache = _s.m_cache; m_previousBlock = _s.m_previousBlock; @@ -353,7 +355,7 @@ void State::ensureCached(std::map& _cache, Address _a, bo RLP state(stateBack); AddressState s; if (state.isNull()) - s = AddressState(0, 0, ZeroRLPSHA3, EmptySHA3); + s = AddressState(0, 0, EmptyTrie, EmptySHA3); else s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].toHash()); bool ok; @@ -484,6 +486,7 @@ map State::addresses() const void State::resetCurrent() { m_transactions.clear(); + m_receipts.clear(); m_transactionSet.clear(); m_cache.clear(); m_currentBlock = BlockInfo(); @@ -547,7 +550,7 @@ h256s State::sync(TransactionQueue& _tq, bool* o_transactionQueueChanged) { uncommitToMine(); execute(i.second); - ret.push_back(m_transactions.back().changes.bloom()); + ret.push_back(m_receipts.back().changes().bloom()); _tq.noteGood(i); ++goodTxs; } @@ -588,7 +591,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce) // m_currentBlock is assumed to be prepopulated and reset. #if !ETH_RELEASE - BlockInfo bi(_block); + BlockInfo bi(_block, _checkNonce); assert(m_previousBlock.hash == bi.parentHash); assert(m_currentBlock.parentHash == bi.parentHash); assert(rootHash() == m_previousBlock.stateRoot); @@ -605,16 +608,32 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce) // cnote << m_state; MemoryDB tm; - GenericTrieDB transactionManifest(&tm); - transactionManifest.init(); + GenericTrieDB transactionsTrie(&tm); + transactionsTrie.init(); + + MemoryDB rm; + GenericTrieDB receiptsTrie(&rm); + receiptsTrie.init(); // All ok with the block generally. Play back the transactions now... unsigned i = 0; for (auto const& tr: RLP(_block)[1]) { + RLPStream k; + k << i; + + RLPStream txrlp; + m_transactions[i].streamRLP(txrlp); + transactionsTrie.insert(&k.out(), tr.data()); + // cnote << m_state.root() << m_state; // cnote << *this; - execute(tr[0].data()); + execute(tr.data()); + + RLPStream receiptrlp; + m_receipts.back().streamRLP(receiptrlp); + receiptsTrie.insert(&k.out(), &receiptrlp.out()); +/* if (tr[1].toHash() != m_state.root()) { // Invalid state root @@ -625,15 +644,20 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce) } if (tr[2].toInt() != gasUsed()) BOOST_THROW_EXCEPTION(InvalidTransactionGasUsed()); - bytes k = rlp(i); - transactionManifest.insert(&k, tr.data()); +*/ ++i; } - if (m_currentBlock.transactionsRoot && transactionManifest.root() != m_currentBlock.transactionsRoot) + if (transactionsTrie.root() != m_currentBlock.transactionsRoot) { cwarn << "Bad transactions state root!"; - BOOST_THROW_EXCEPTION(InvalidTransactionStateRoot()); + BOOST_THROW_EXCEPTION(InvalidTransactionsStateRoot()); + } + + if (receiptsTrie.root() != m_currentBlock.receiptsRoot) + { + cwarn << "Bad receipts state root!"; + BOOST_THROW_EXCEPTION(InvalidReceiptsStateRoot()); } // Initialise total difficulty calculation. @@ -713,7 +737,7 @@ void State::uncommitToMine() if (!m_transactions.size()) m_state.setRoot(m_previousBlock.stateRoot); else - m_state.setRoot(m_transactions[m_transactions.size() - 1].stateRoot); + m_state.setRoot(m_receipts[m_receipts.size() - 1].stateRoot()); m_db = m_lastTx; paranoia("Uncommited to mine", true); m_currentBlock.sha3Uncles = h256(); @@ -730,7 +754,7 @@ bool State::amIJustParanoid(BlockChain const& _bc) // Compile block: RLPStream block; block.appendList(3); - m_currentBlock.fillStream(block, true); + m_currentBlock.streamRLP(block, true); block.appendRaw(m_currentTxs); block.appendRaw(m_currentUncles); @@ -757,11 +781,20 @@ bool State::amIJustParanoid(BlockChain const& _bc) return false; } -h256 State::bloom() const +h256 State::oldBloom() const { h256 ret = m_currentBlock.coinbaseAddress.bloom(); - for (auto const& i: m_transactions) - ret |= i.changes.bloom(); + for (auto const& i: m_receipts) + ret |= i.changes().bloom(); + return ret; +} + +LogBloom State::logBloom() const +{ + LogBloom ret; + ret.shiftBloom<3>(sha3(m_currentBlock.coinbaseAddress.ref())); + for (TransactionReceipt const& i: m_receipts) + ret |= i.bloom(); return ret; } @@ -797,7 +830,7 @@ void State::commitToMine(BlockChain const& _bc) if (!knownUncles.count(u)) // ignore any uncles/mainline blocks that we know about. { BlockInfo ubi(_bc.block(u)); - ubi.fillStream(unclesData, true); + ubi.streamRLP(unclesData, true); ++unclesCount; uncleAddresses.push_back(ubi.coinbaseAddress); } @@ -805,8 +838,12 @@ void State::commitToMine(BlockChain const& _bc) } MemoryDB tm; - GenericTrieDB transactionReceipts(&tm); - transactionReceipts.init(); + GenericTrieDB transactionsTrie(&tm); + transactionsTrie.init(); + + MemoryDB rm; + GenericTrieDB receiptsTrie(&rm); + receiptsTrie.init(); RLPStream txs; txs.appendList(m_transactions.size()); @@ -815,17 +852,25 @@ void State::commitToMine(BlockChain const& _bc) { RLPStream k; k << i; - RLPStream v; - m_transactions[i].fillStream(v); - transactionReceipts.insert(&k.out(), &v.out()); - txs.appendRaw(v.out()); + + RLPStream receiptrlp; + m_receipts[i].streamRLP(receiptrlp); + receiptsTrie.insert(&k.out(), &receiptrlp.out()); + + RLPStream txrlp; + m_transactions[i].streamRLP(txrlp); + transactionsTrie.insert(&k.out(), &txrlp.out()); + + txs.appendRaw(txrlp.out()); } txs.swapOut(m_currentTxs); RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles); - m_currentBlock.transactionsRoot = transactionReceipts.root(); + m_currentBlock.transactionsRoot = transactionsTrie.root(); + m_currentBlock.receiptsRoot = receiptsTrie.root(); + m_currentBlock.logBloom = logBloom(); m_currentBlock.sha3Uncles = sha3(m_currentUncles); // Apply rewards last of all. @@ -853,6 +898,10 @@ MineInfo State::mine(unsigned _msTimeout, bool _turbo) if (!ret.completed) m_currentBytes.clear(); + else + { + cnote << "Completed" << m_currentBlock.headerHashWithoutNonce().abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHashWithoutNonce(), m_currentBlock.nonce, m_currentBlock.difficulty); + } return ret; } @@ -865,7 +914,7 @@ void State::completeMine() // Compile block: RLPStream ret; ret.appendList(3); - m_currentBlock.fillStream(ret, true); + m_currentBlock.streamRLP(ret, true); ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentUncles); ret.swapOut(m_currentBytes); @@ -875,6 +924,7 @@ void State::completeMine() // Quickly reset the transactions. // TODO: Leave this in a better state than this limbo, or at least record that it's in limbo. m_transactions.clear(); + m_receipts.clear(); m_transactionSet.clear(); m_lastTx = m_db; } @@ -1114,12 +1164,13 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit) // TODO: CHECK TRIE after level DB flush to make sure exactly the same. // Add to the user-originated transactions that we've executed. - m_transactions.push_back(TransactionReceipt(e.t(), rootHash(), startGasUsed + e.gasUsed(), ms)); + m_transactions.push_back(e.t()); + m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs(), ms)); m_transactionSet.insert(e.t().sha3()); return e.gasUsed(); } -bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_originAddress) _originAddress = _senderAddress; @@ -1154,9 +1205,8 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA { auto out = vm.go(evm, _onOp); memcpy(_out.data(), out.data(), std::min(out.size(), _out.size())); - if (o_suicides) - for (auto i: evm.suicides) - o_suicides->insert(i); + if (o_sub) + *o_sub += evm.sub; if (o_ms) o_ms->output = out.toBytes(); } @@ -1168,6 +1218,7 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA catch (VMException const& _e) { clog(StateChat) << "VM Exception: " << diagnostic_information(_e); + revert = true; } catch (Exception const& _e) { @@ -1186,10 +1237,16 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA return !revert; } + else + { + // non-contract call + if (o_sub) + o_sub->logs.push_back(LogEntry(_receiveAddress, {u256((u160)_senderAddress) + 1}, bytes())); + } return true; } -h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_origin) _origin = _sender; @@ -1218,9 +1275,8 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, out = vm.go(evm, _onOp); if (o_ms) o_ms->output = out.toBytes(); - if (o_suicides) - for (auto i: evm.suicides) - o_suicides->insert(i); + if (o_sub) + *o_sub += evm.sub; } catch (OutOfGas const& /*_e*/) { @@ -1230,25 +1286,32 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, catch (VMException const& _e) { clog(StateChat) << "VM Exception: " << diagnostic_information(_e); + revert = true; } catch (Exception const& _e) { - clog(StateChat) << "Exception in VM: " << diagnostic_information(_e); + // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. + cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e); } catch (std::exception const& _e) { - clog(StateChat) << "std::exception in VM: " << _e.what(); + // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does. + cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what(); } - // TODO: CHECK: IS THIS CORRECT?! (esp. given account created prior to revertion init.) + // TODO: CHECK: AUDIT: IS THIS CORRECT?! (esp. given account created prior to revertion init.) // Write state out only in the case of a non-out-of-gas transaction. if (revert) + { evm.revert(); - - // Set code. - if (addressInUse(newAddress)) - m_cache[newAddress].setCode(out); + m_cache.erase(newAddress); + newAddress = Address(); + } + else + // Set code. + if (addressInUse(newAddress)) + m_cache[newAddress].setCode(out); *_gas = vm.gas(); @@ -1263,11 +1326,12 @@ State State::fromPending(unsigned _i) const if (!_i) ret.m_state.setRoot(m_previousBlock.stateRoot); else - ret.m_state.setRoot(m_transactions[_i - 1].stateRoot); + ret.m_state.setRoot(m_receipts[_i - 1].stateRoot()); while (ret.m_transactions.size() > _i) { - ret.m_transactionSet.erase(ret.m_transactions.back().transaction.sha3()); + ret.m_transactionSet.erase(ret.m_transactions.back().sha3()); ret.m_transactions.pop_back(); + ret.m_receipts.pop_back(); } return ret; } diff --git a/libethereum/State.h b/libethereum/State.h index 6e65bfd53..b32bb6c7c 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -52,25 +52,37 @@ struct StateChat: public LogChannel { static const char* name() { return "-S-"; struct StateTrace: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 7; }; struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; }; -struct TransactionReceipt +class TransactionReceipt { - TransactionReceipt(Transaction const& _t, h256 _root, u256 _gasUsed, Manifest const& _ms): transaction(_t), stateRoot(_root), gasUsed(_gasUsed), changes(_ms) {} +public: + TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log, Manifest const& _ms): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log), m_changes(_ms) {} + + Manifest const& changes() const { return m_changes; } -// Manifest const& changes() const { return changes; } + h256 const& stateRoot() const { return m_stateRoot; } + u256 const& gasUsed() const { return m_gasUsed; } + LogBloom const& bloom() const { return m_bloom; } + LogEntries const& log() const { return m_log; } - void fillStream(RLPStream& _s) const + void streamRLP(RLPStream& _s) const { - _s.appendList(3); - transaction.fillStream(_s); - _s.append(stateRoot, false, true) << gasUsed; + _s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom; + _s.appendList(m_log.size()); + for (LogEntry const& l: m_log) + l.streamRLP(_s); } - Transaction transaction; - h256 stateRoot; - u256 gasUsed; - Manifest changes; +private: + h256 m_stateRoot; + u256 m_gasUsed; + LogBloom m_bloom; + LogEntries m_log; + + Manifest m_changes; ///< TODO: PoC-7: KILL }; +using TransactionReceipts = std::vector; + struct PrecompiledAddress { unsigned gas; @@ -226,16 +238,28 @@ public: h256 rootHash() const { return m_state.root(); } /// Get the list of pending transactions. - Transactions pending() const { Transactions ret; for (auto const& t: m_transactions) ret.push_back(t.transaction); return ret; } + Transactions const& pending() const { return m_transactions; } + + /// Get the list of pending transactions. TODO: PoC-7: KILL + Manifest changesFromPending(unsigned _i) const { return m_receipts[_i].changes(); } + + /// Get the bloom filter of all changes happened in the block. TODO: PoC-7: KILL + h256 oldBloom() const; + + /// Get the bloom filter of a particular transaction that happened in the block. TODO: PoC-7: KILL + h256 oldBloom(unsigned _i) const { return m_receipts[_i].changes().bloom(); } + + /// Get the transaction receipt for the transaction of the given index. + TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; } /// Get the list of pending transactions. - Manifest changesFromPending(unsigned _i) const { return m_transactions[_i].changes; } + LogEntries const& log(unsigned _i) const { return m_receipts[_i].log(); } - /// Get the bloom filter of all changes happened in the block. - h256 bloom() const; + /// Get the bloom filter of all logs that happened in the block. + LogBloom logBloom() const; /// Get the bloom filter of a particular transaction that happened in the block. - h256 bloom(unsigned _i) const { return m_transactions[_i].changes.bloom(); } + LogBloom const& logBloom(unsigned _i) const { return m_receipts[_i].bloom(); } /// Get the State immediately after the given number of pending transactions have been applied. /// If (_i == 0) returns the initial state of the block. @@ -288,12 +312,12 @@ private: // We assume all instrinsic fees are paid up before this point. /// Execute a contract-creation transaction. - h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Execute a call. /// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. /// @returns false if the call ran out of gas before completion. true otherwise. - bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); + bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); @@ -304,14 +328,15 @@ private: void refreshManifest(RLPStream* _txs = nullptr); /// @returns gas used by transactions thus far executed. - u256 gasUsed() const { return m_transactions.size() ? m_transactions.back().gasUsed : 0; } + u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; } bool isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const; void paranoia(std::string const& _when, bool _enforceRefs = false) const; OverlayDB m_db; ///< Our overlay for the state tree. TrieDB m_state; ///< Our state tree, as an OverlayDB DB. - std::vector m_transactions; ///< The current list of transactions that we've included in the state. + Transactions m_transactions; ///< The current list of transactions that we've included in the state. + TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts. std::set m_transactionSet; ///< The set of transaction hashes that we've included in the state. OverlayDB m_lastTx; diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index b707efde3..bdc8bf34d 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -85,7 +85,7 @@ void Transaction::sign(Secret _priv) vrs = *(SignatureStruct const*)&sig; } -void Transaction::fillStream(RLPStream& _s, bool _sig) const +void Transaction::streamRLP(RLPStream& _s, bool _sig) const { _s.appendList((_sig ? 3 : 0) + 6); _s << nonce << gasPrice << gas; diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index ca73ba06a..2492e32bc 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -64,11 +64,11 @@ struct Transaction static h256 kFromMessage(h256 _msg, h256 _priv); - void fillStream(RLPStream& _s, bool _sig = true) const; - bytes rlp(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return s.out(); } + void streamRLP(RLPStream& _s, bool _sig = true) const; + bytes rlp(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return s.out(); } std::string rlpString(bool _sig = true) const { return asString(rlp(_sig)); } - h256 sha3(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return dev::sha3(s.out()); } - bytes sha3Bytes(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return dev::sha3Bytes(s.out()); } + h256 sha3(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return dev::sha3(s.out()); } + bytes sha3Bytes(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return dev::sha3Bytes(s.out()); } private: mutable Address m_sender; diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 67fec9321..1b1ae7455 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -33,13 +36,53 @@ namespace dev namespace eth { -struct Post +using LogBloom = h512; + +struct LogEntry { + LogEntry() {} + LogEntry(RLP const& _r) { from = (Address)_r[0]; topics = (h256s)_r[1]; data = (bytes)_r[2]; } + LogEntry(Address const& _f, h256s&& _ts, bytes&& _d): from(_f), topics(std::move(_ts)), data(std::move(_d)) {} + + void streamRLP(RLPStream& _s) const { _s.appendList(3) << from << topics << data; } + + LogBloom bloom() const + { + LogBloom ret; + ret.shiftBloom<3, 32>(sha3(from.ref())); + for (auto t: topics) + ret.shiftBloom<3, 32>(sha3(t.ref())); + return ret; + } + Address from; - Address to; - u256 value; + h256s topics; bytes data; - u256 gas; +}; + +using LogEntries = std::vector; + +inline LogBloom bloom(LogEntries const& _logs) +{ + LogBloom ret; + for (auto const& l: _logs) + ret |= l.bloom(); + return ret; +} + +struct SubState +{ + std::set
suicides; ///< Any accounts that have suicided. + LogEntries logs; ///< Any logs. + u256 refunds; ///< Refund counter of SSTORE nonzero->zero. + + SubState& operator+=(SubState const& _s) + { + suicides += _s.suicides; + refunds += _s.refunds; + suicides += _s.suicides; + return *this; + } }; using OnOpFunc = std::function; @@ -80,7 +123,7 @@ public: virtual u256 txCount(Address) { return 0; } /// Suicide the associated contract and give proceeds to the given address. - virtual void suicide(Address) { suicides.insert(myAddress); } + virtual void suicide(Address) { sub.suicides.insert(myAddress); } /// Create a new (contract) account. virtual h160 create(u256, u256*, bytesConstRef, OnOpFunc const&) { return h160(); } @@ -88,6 +131,9 @@ public: /// Make a new message call. virtual bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; } + /// Revert any changes made (by any of the other calls). + virtual void log(h256s&& _topics, bytesConstRef _data) { sub.logs.push_back(LogEntry(myAddress, std::move(_topics), _data.toBytes())); } + /// Revert any changes made (by any of the other calls). virtual void revert() {} @@ -103,7 +149,7 @@ public: bytesConstRef code; ///< Current code that is executing. BlockInfo previousBlock; ///< The previous block's information. BlockInfo currentBlock; ///< The current block's information. - std::set
suicides; ///< Any accounts that have suicided. + SubState sub; ///< Sub-band VM state (suicides, refund counter, logs). unsigned depth; ///< Depth of the present call. }; diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index d29b9fef9..47236b506 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -29,7 +29,9 @@ u256 const dev::eth::c_stepGas = 1; u256 const dev::eth::c_balanceGas = 20; u256 const dev::eth::c_sha3Gas = 20; u256 const dev::eth::c_sloadGas = 20; -u256 const dev::eth::c_sstoreGas = 100; +u256 const dev::eth::c_sstoreSetGas = 300; +u256 const dev::eth::c_sstoreResetGas = 100; +u256 const dev::eth::c_sstoreRefundGas = 100; u256 const dev::eth::c_createGas = 100; u256 const dev::eth::c_callGas = 20; u256 const dev::eth::c_memoryGas = 1; diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 76be9a398..84a2551d9 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -32,7 +32,9 @@ extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD extern u256 const c_balanceGas; ///< Once per BALANCE operation. extern u256 const c_sha3Gas; ///< Once per SHA3 operation. extern u256 const c_sloadGas; ///< Once per SLOAD operation. -extern u256 const c_sstoreGas; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). +extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero. +extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change. +extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero. extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction. extern u256 const c_callGas; ///< Once per CALL operation & message call transaction. extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. diff --git a/libevm/VM.cpp b/libevm/VM.cpp index e47237d5a..bded9a10d 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -29,4 +29,5 @@ void VM::reset(u256 _gas) { m_gas = _gas; m_curPC = 0; + m_jumpDests.clear(); } diff --git a/libevm/VM.h b/libevm/VM.h index 625963e34..774667902 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -41,7 +41,7 @@ struct BreakPointHit: virtual VMException {}; struct BadInstruction: virtual VMException {}; struct BadJumpDestination: virtual VMException {}; struct OutOfGas: virtual VMException {}; -class StackTooSmall: virtual public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; +struct StackTooSmall: virtual public VMException { StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; // 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. @@ -84,6 +84,7 @@ private: u256 m_curPC = 0; bytes m_temp; u256s m_stack; + std::set m_jumpDests; }; } @@ -93,6 +94,16 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con { auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? _offset + _size : 0; }; + if (m_jumpDests.empty()) + { + m_jumpDests.insert(0); + for (unsigned i = 1; i < _ext.code.size(); ++i) + if (_ext.code[i] == (byte)Instruction::JUMPDEST) + m_jumpDests.insert(i + 1); + else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32) + i += _ext.code[i] - (int)Instruction::PUSH1 + 1; + } + u256 nextPC = m_curPC + 1; auto osteps = _steps; for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) @@ -117,11 +128,14 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::SSTORE: require(2); if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) - runGas = c_sstoreGas * 2; + runGas = c_sstoreSetGas; else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) + { runGas = 0; + _ext.sub.refunds += c_sstoreRefundGas; + } else - runGas = c_sstoreGas; + runGas = c_sstoreResetGas; break; case Instruction::SLOAD: @@ -169,6 +183,18 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con runGas = c_balanceGas; break; + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + unsigned n = (unsigned)inst - (unsigned)Instruction::LOG0; + require(n + 2); + newTempSize = memNeed(m_stack[m_stack.size() - 1 - n], m_stack[m_stack.size() - 2 - n]); + break; + } + case Instruction::CALL: case Instruction::CALLCODE: require(7); @@ -423,18 +449,13 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::SIGNEXTEND: { - unsigned k = (unsigned)m_stack[m_stack.size() - 2]; - if (k > 31) - m_stack[m_stack.size() - 2] = m_stack.back(); - else - { - u256 b = m_stack.back(); + unsigned k = m_stack.back(); + m_stack.pop_back(); + auto& b = m_stack.back(); + if (k <= 31) if ((b >> (k * 8)) & 0x80) for (int i = 31; i > k; --i) b |= (u256(0xff) << i); - m_stack[m_stack.size() - 2] = b; - } - m_stack.pop_back(); break; } case Instruction::SHA3: @@ -663,7 +684,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::JUMP: nextPC = m_stack.back(); - if (nextPC && (Instruction)_ext.getCode(nextPC - 1) != Instruction::JUMPDEST) + if (!m_jumpDests.count((unsigned)nextPC)) BOOST_THROW_EXCEPTION(BadJumpDestination()); m_stack.pop_back(); break; @@ -671,7 +692,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con if (m_stack[m_stack.size() - 2]) { nextPC = m_stack.back(); - if (nextPC && (Instruction)_ext.getCode(nextPC - 1) != Instruction::JUMPDEST) + if (!m_jumpDests.count((unsigned)nextPC)) BOOST_THROW_EXCEPTION(BadJumpDestination()); } m_stack.pop_back(); @@ -688,6 +709,36 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::JUMPDEST: break; +/* case Instruction::LOG0: + _ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + break; + case Instruction::LOG1: + _ext.log({m_stack[m_stack.size() - 1]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 2], (unsigned)m_stack[m_stack.size() - 3])); + break; + case Instruction::LOG2: + _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 3], (unsigned)m_stack[m_stack.size() - 4])); + break; + case Instruction::LOG3: + _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 4], (unsigned)m_stack[m_stack.size() - 5])); + break; + case Instruction::LOG4: + _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 5], (unsigned)m_stack[m_stack.size() - 6])); + break;*/ + case Instruction::LOG0: + _ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + break; + case Instruction::LOG1: + _ext.log({m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + break; + case Instruction::LOG2: + _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + break; + case Instruction::LOG3: + _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + break; + case Instruction::LOG4: + _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5], m_stack[m_stack.size() - 6]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2])); + break; case Instruction::CREATE: { u256 endowment = m_stack.back(); diff --git a/libevmface/Instruction.cpp b/libevmface/Instruction.cpp index c9b6ea2ce..2c2af8eb0 100644 --- a/libevmface/Instruction.cpp +++ b/libevmface/Instruction.cpp @@ -149,6 +149,11 @@ const std::map dev::eth::c_instructions = { "SWAP14", Instruction::SWAP14 }, { "SWAP15", Instruction::SWAP15 }, { "SWAP16", Instruction::SWAP16 }, + { "LOG0", Instruction::LOG0 }, + { "LOG1", Instruction::LOG1 }, + { "LOG2", Instruction::LOG2 }, + { "LOG3", Instruction::LOG3 }, + { "LOG4", Instruction::LOG4 }, { "CREATE", Instruction::CREATE }, { "CALL", Instruction::CALL }, { "CALLCODE", Instruction::CALLCODE }, @@ -277,6 +282,11 @@ static const std::map c_instructionInfo = { Instruction::SWAP14, { "SWAP14", 0, 15, 15 } }, { Instruction::SWAP15, { "SWAP15", 0, 16, 16 } }, { Instruction::SWAP16, { "SWAP16", 0, 17, 17 } }, + { Instruction::LOG0, { "LOG0", 0, 1, 0 } }, + { Instruction::LOG1, { "LOG1", 0, 2, 0 } }, + { Instruction::LOG2, { "LOG2", 0, 3, 0 } }, + { Instruction::LOG3, { "LOG3", 0, 4, 0 } }, + { Instruction::LOG4, { "LOG4", 0, 5, 0 } }, { Instruction::CREATE, { "CREATE", 0, 3, 1 } }, { Instruction::CALL, { "CALL", 0, 7, 1 } }, { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1 } }, diff --git a/libevmface/Instruction.h b/libevmface/Instruction.h index faad50fb2..ea355fab1 100644 --- a/libevmface/Instruction.h +++ b/libevmface/Instruction.h @@ -162,6 +162,12 @@ enum class Instruction: uint8_t SWAP15, ///< swaps the highest and 16th highest value on the stack SWAP16, ///< swaps the highest and 17th highest value on the stack + LOG0 = 0xa0, ///< Makes a log entry; no topics. + LOG1, ///< Makes a log entry; 1 topic. + LOG2, ///< Makes a log entry; 2 topics. + LOG3, ///< Makes a log entry; 3 topics. + LOG4, ///< Makes a log entry; 4 topics. + CREATE = 0xf0, ///< create a new account with associated code CALL, ///< message-call into an account RETURN, ///< halt execution returning output data diff --git a/liblll/Assembly.cpp b/liblll/Assembly.cpp index c26a9a98e..5b10138d1 100644 --- a/liblll/Assembly.cpp +++ b/liblll/Assembly.cpp @@ -147,7 +147,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) return _out; } -ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const +ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const { _out << _prefix << ".code:" << endl; for (AssemblyItem const& i: m_items) @@ -189,7 +189,7 @@ ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const for (auto const& i: m_subs) { _out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << endl; - i.second.streamOut(_out, _prefix + " "); + i.second.streamRLP(_out, _prefix + " "); } } return _out; diff --git a/liblll/Assembly.h b/liblll/Assembly.h index b7feaf4f4..8ab3062dc 100644 --- a/liblll/Assembly.h +++ b/liblll/Assembly.h @@ -104,11 +104,11 @@ public: void injectStart(AssemblyItem const& _i); - std::string out() const { std::stringstream ret; streamOut(ret); return ret.str(); } + std::string out() const { std::stringstream ret; streamRLP(ret); return ret.str(); } int deposit() const { return m_deposit; } bytes assemble() const; Assembly& optimise(bool _enable); - std::ostream& streamOut(std::ostream& _out, std::string const& _prefix = "") const; + std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "") const; private: void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); } @@ -127,7 +127,7 @@ private: inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a) { - _a.streamOut(_out); + _a.streamRLP(_out); return _out; } diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 2af4d2808..020c7e420 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -252,17 +252,27 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); else { - m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p); + bi::address adr = adr = bi::address::from_string(eip); + try + { + adr = bi::address::from_string(_publicAddress); + } + catch (...) {} + m_public = bi::tcp::endpoint(adr, (unsigned short)p); m_addresses.push_back(m_public.address()); } } else { // No UPnP - fallback on given public address or, if empty, the assumed peer address. - m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress) - : m_peerAddresses.size() ? m_peerAddresses[0] - : bi::address(), m_listenPort); - m_addresses.push_back(m_public.address()); + bi::address adr = m_peerAddresses.size() ? m_peerAddresses[0] : bi::address(); + try + { + adr = bi::address::from_string(_publicAddress); + } + catch (...) {} + m_public = bi::tcp::endpoint(adr, m_listenPort); + m_addresses.push_back(adr); } } diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index 11908d181..15b07e433 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -36,7 +36,7 @@ BuildTopic& BuildTopic::shiftBytes(bytes const& _b) h256 TopicFilter::sha3() const { RLPStream s; - fillStream(s); + streamRLP(s); return dev::sha3(s.out()); } diff --git a/libwhisper/Common.h b/libwhisper/Common.h index a85caafe1..21296e193 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -92,7 +92,7 @@ public: TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {} TopicFilter(RLP const& _r): m_topicMasks((TopicMasks)_r) {} - void fillStream(RLPStream& _s) const { _s << m_topicMasks; } + void streamRLP(RLPStream& _s) const { _s << m_topicMasks; } h256 sha3() const; bool matches(Envelope const& _m) const; diff --git a/libwhisper/Message.h b/libwhisper/Message.h index 84214fd18..6b28073b7 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -55,9 +55,9 @@ public: operator bool() const { return !!m_expiry; } - void streamOut(RLPStream& _s, bool _withNonce) const { _s.appendList(_withNonce ? 5 : 4) << m_expiry << m_ttl << m_topic << m_data; if (_withNonce) _s << m_nonce; } - h256 sha3() const { RLPStream s; streamOut(s, true); return dev::sha3(s.out()); } - h256 sha3NoNonce() const { RLPStream s; streamOut(s, false); return dev::sha3(s.out()); } + void streamRLP(RLPStream& _s, bool _withNonce) const { _s.appendList(_withNonce ? 5 : 4) << m_expiry << m_ttl << m_topic << m_data; if (_withNonce) _s << m_nonce; } + h256 sha3() const { RLPStream s; streamRLP(s, true); return dev::sha3(s.out()); } + h256 sha3NoNonce() const { RLPStream s; streamRLP(s, false); return dev::sha3(s.out()); } unsigned sent() const { return m_expiry - m_ttl; } unsigned expiry() const { return m_expiry; } diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 591574bf0..71030aae2 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -48,8 +48,8 @@ void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const { UpgradeGuard ll(l); auto const& m = m_messages.at(_m); - cnote << "streamOut: " << m.expiry() << m.ttl() << m.topic() << toHex(m.data()); - m.streamOut(_s, true); + cnote << "streamRLP: " << m.expiry() << m.ttl() << m.topic() << toHex(m.data()); + m.streamRLP(_s, true); } } diff --git a/test/genesis.cpp b/test/genesis.cpp index 5f17d2762..16ffb1859 100644 --- a/test/genesis.cpp +++ b/test/genesis.cpp @@ -35,9 +35,20 @@ namespace js = json_spirit; BOOST_AUTO_TEST_CASE(genesis_tests) { + const char* ptestPath = getenv("ETHEREUM_TEST_PATH"); + string testPath; + + if (ptestPath == NULL) + { + cnote << " could not find environment variable ETHEREUM_TEST_PATH \n"; + testPath = "../../../tests"; + } + else + testPath = ptestPath; + cnote << "Testing Genesis block..."; js::mValue v; - string s = asString(contents("../../../tests/genesishashestest.json")); + string s = asString(contents(testPath + "/genesishashestest.json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'genesishashestest.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); diff --git a/test/hexPrefix.cpp b/test/hexPrefix.cpp index fb2fbc826..99207ab97 100644 --- a/test/hexPrefix.cpp +++ b/test/hexPrefix.cpp @@ -33,9 +33,20 @@ namespace js = json_spirit; BOOST_AUTO_TEST_CASE(hexPrefix_test) { + const char* ptestPath = getenv("ETHEREUM_TEST_PATH"); + string testPath; + + if (ptestPath == NULL) + { + cnote << " could not find environment variable ETHEREUM_TEST_PATH \n"; + testPath = "../../../tests"; + } + else + testPath = ptestPath; + cnote << "Testing Hex-Prefix-Encode..."; js::mValue v; - string s = asString(contents("../../../tests/hexencodetest.json")); + string s = asString(contents(testPath + "/hexencodetest.json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content from 'hexencodetest.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) diff --git a/test/main.cpp b/test/main.cpp index ef009e299..3f8860d7a 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -44,7 +44,7 @@ using namespace dev::eth; BOOST_AUTO_TEST_CASE(basic_tests) { /* RLPStream s; - BlockInfo::genesis().fillStream(s, false); + BlockInfo::genesis().streamRLP(s, false); std::cout << RLP(s.out()) << std::endl; std::cout << toHex(s.out()) << std::endl; std::cout << sha3(s.out()) << std::endl;*/ diff --git a/test/rlp.cpp b/test/rlp.cpp index 69360ad66..608a8499e 100644 --- a/test/rlp.cpp +++ b/test/rlp.cpp @@ -61,7 +61,18 @@ namespace dev static void getRLPTestCases(js::mValue& v) { - string s = asString(contents("../../../tests/rlptest.json")); + const char* ptestPath = getenv("ETHEREUM_TEST_PATH"); + string testPath; + + if (ptestPath == NULL) + { + cnote << " could not find environment variable ETHEREUM_TEST_PATH \n"; + testPath = "../../../tests"; + } + else + testPath = ptestPath; + + string s = asString(contents(testPath + "/rlptest.json")); BOOST_REQUIRE_MESSAGE( s.length() > 0, "Contents of 'rlptest.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); diff --git a/test/trie.cpp b/test/trie.cpp index 899eb1f60..f8ebd10c8 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -49,9 +49,20 @@ static unsigned fac(unsigned _i) BOOST_AUTO_TEST_CASE(trie_tests) { + const char* ptestPath = getenv("ETHEREUM_TEST_PATH"); + string testPath; + + if (ptestPath == NULL) + { + cnote << " could not find environment variable ETHEREUM_TEST_PATH \n"; + testPath = "../../../tests"; + } + else + testPath = ptestPath; + cnote << "Testing Trie..."; js::mValue v; - string s = asString(contents("../../../tests/trietest.json")); + string s = asString(contents(testPath + "/trietest.json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trietest.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) diff --git a/test/vm.cpp b/test/vm.cpp index b49c5cd27..40a0a862b 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -20,9 +20,8 @@ * vm test functions. */ -#include "vm.h" -#include #include +#include "vm.h" //#define FILL_TESTS @@ -45,7 +44,7 @@ h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFun m_s.noteSending(myAddress); m_ms.internal.resize(m_ms.internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1); + auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); @@ -91,7 +90,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, if (!m_s.addresses().count(myAddress)) { m_ms.internal.resize(m_ms.internal.size() + 1); - auto na = m_s.createNewAddress(myAddress, myAddress, balance(myAddress), gasPrice, &contractgas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1); + auto na = m_s.createNewAddress(myAddress, myAddress, balance(myAddress), gasPrice, &contractgas, init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); if (na != myAddress) @@ -116,7 +115,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, { m_s.noteSending(myAddress); m_ms.internal.resize(m_ms.internal.size() + 1); - auto na = m_s.createNewAddress(_codeAddressOverride ? _codeAddressOverride : _receiveAddress, myAddress, balance(_codeAddressOverride ? _codeAddressOverride : _receiveAddress), gasPrice, &contractgas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); + auto na = m_s.createNewAddress(_codeAddressOverride ? _codeAddressOverride : _receiveAddress, myAddress, balance(_codeAddressOverride ? _codeAddressOverride : _receiveAddress), gasPrice, &contractgas, init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); @@ -131,7 +130,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, m_ms.internal.resize(m_ms.internal.size() + 1); - auto ret = m_s.call(_receiveAddress,_codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _value, gasPrice, _data, _gas, _out, origin, &suicides, &(m_ms.internal.back()), OnOpFunc(), 1); + auto ret = m_s.call(_receiveAddress,_codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _value, gasPrice, _data, _gas, _out, origin, &sub, &(m_ms.internal.back()), simpleTrace(), 1); if (!m_ms.internal.back().from) m_ms.internal.pop_back(); @@ -146,12 +145,15 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data, if (!ret) return false; + // TODO: @CJentzsch refund SSTORE stuff. + // TODO: @CJentzsch test logs. + // do suicides - for (auto const& f: suicides) + for (auto const& f: sub.suicides) addresses.erase(f); // get storage - if ((get<0>(addresses[myAddress]) >= _value) && (suicides.find(_receiveAddress) == suicides.end())) + if ((get<0>(addresses[myAddress]) >= _value) && (sub.suicides.find(_receiveAddress) == sub.suicides.end())) { for (auto const& j: m_s.storage(_receiveAddress)) { @@ -419,8 +421,11 @@ void FakeExtVM::importCallCreates(mArray& _callcreates) } } -h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) +// THIS IS BROKEN AND NEEDS TO BE REMOVED. +h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { + (void)o_sub; + if (!_origin) _origin = _sender; @@ -446,9 +451,7 @@ h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _end out = vm.go(evm, _onOp); if (o_ms) o_ms->output = out.toBytes(); - if (o_suicides) - for (auto i: evm.suicides) - o_suicides->insert(i); + // TODO: deal with evm.sub } catch (OutOfGas const& /*_e*/) { @@ -483,8 +486,6 @@ h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _end return _newAddress; } - - namespace dev { namespace test { void doTests(json_spirit::mValue& v, bool _fillin) @@ -516,7 +517,7 @@ void doTests(json_spirit::mValue& v, bool _fillin) VM vm(fev.gas); try { - output = vm.go(fev).toVector(); + output = vm.go(fev, fev.simpleTrace()).toVector(); } catch (Exception const& _e) { @@ -752,3 +753,32 @@ BOOST_AUTO_TEST_CASE(vmSystemOperationsTest) { dev::test::executeTests("vmSystemOperationsTest"); } + +BOOST_AUTO_TEST_CASE(userDefinedFile) +{ + + if (boost::unit_test::framework::master_test_suite().argc == 2) + { + string filename = boost::unit_test::framework::master_test_suite().argv[1]; + int currentVerbosity = g_logVerbosity; + g_logVerbosity = 12; + try + { + cnote << "Testing VM..." << "user defined test"; + json_spirit::mValue v; + string s = asString(contents(filename)); + BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. "); + json_spirit::read_string(s, v); + dev::test::doTests(v, false); + } + catch (Exception const& _e) + { + BOOST_ERROR("Failed VM Test with Exception: " << diagnostic_information(_e)); + } + catch (std::exception const& _e) + { + BOOST_ERROR("Failed VM Test with Exception: " << _e.what()); + } + g_logVerbosity = currentVerbosity; + } +} diff --git a/test/vm.h b/test/vm.h index d9dca1d7a..f3aae694a 100644 --- a/test/vm.h +++ b/test/vm.h @@ -28,6 +28,7 @@ along with cpp-ethereum. If not, see . #include #include "JsonSpiritHeaders.h" #include +#include #include #include #include @@ -44,7 +45,7 @@ class FakeState: public eth::State { public: /// Execute a contract-creation transaction. - h160 createNewAddress(Address _newAddress, Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = {}, std::set
* o_suicides = nullptr, eth::Manifest* o_ms = nullptr, eth::OnOpFunc const& _onOp = {}, unsigned _level = 0); + h160 createNewAddress(Address _newAddress, Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = {}, eth::SubState* o_sub = nullptr, eth::Manifest* o_ms = nullptr, eth::OnOpFunc const& _onOp = {}, unsigned _level = 0); }; class FakeExtVM: public eth::ExtVMFace @@ -80,6 +81,11 @@ public: json_spirit::mArray exportCallCreates(); void importCallCreates(json_spirit::mArray& _callcreates); + template + eth::OnOpFunc simpleTrace(); + + FakeState state() const { return m_s; } + std::map, bytes>> addresses; eth::Transactions callcreates; bytes thisTxData; @@ -91,4 +97,32 @@ private: eth::Manifest m_ms; }; +template +eth::OnOpFunc FakeExtVM::simpleTrace() +{ + return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt) + { + ExtVMType const& ext = *(ExtVMType const*)voidExt; + eth::VM& vm = *(eth::VM*)voidVM; + + std::ostringstream o; + o << std::endl << " STACK" << std::endl; + for (auto i: vm.stack()) + o << (h256)i << std::endl; + o << " MEMORY" << std::endl << memDump(vm.memory()); + o << " STORAGE" << std::endl; + for (auto const& i: ext.state().storage(ext.myAddress)) + o << std::showbase << std::hex << i.first << ": " << i.second << std::endl; + dev::LogOutputStream(true) << o.str(); + dev::LogOutputStream(false) << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32" << " ]"; + + if (eth::VMTraceChannel::verbosity <= g_logVerbosity) + { + std::ofstream f; + f.open("./vmtrace.log", std::ofstream::app); + f << o.str(); + f << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32"; + } + }; +} } } // Namespace Close