diff --git a/alethzero/Debugger.cpp b/alethzero/Debugger.cpp index 93d6b1f6a..371630456 100644 --- a/alethzero/Debugger.cpp +++ b/alethzero/Debugger.cpp @@ -67,7 +67,8 @@ void Debugger::populate(dev::eth::Executive& _executive, dev::eth::Transaction c bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction) { try { - if (_executive.setup(_transaction)) + _executive.initialize(_transaction); + if (_executive.execute()) return false; } catch (...) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 712318442..1784fbf96 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1122,7 +1122,7 @@ void Main::refreshBlockChain() blocks.insert(bc.numberHash(b)); } else if (f.toLongLong() <= bc.number()) - blocks.insert(bc.numberHash(u256(f.toLongLong()))); + blocks.insert(bc.numberHash((unsigned)f.toLongLong())); else if (f.size() == 40) { Address h(f.toStdString()); @@ -1385,7 +1385,7 @@ void Main::on_transactionQueue_currentItemChanged() if (!!receipt.bloom()) s << "
Log Bloom: " << receipt.bloom() << "
"; else - s << "
Log Bloom: Uneventful
"; + s << "
Log Bloom: Uneventful
"; auto r = receipt.rlp(); s << "
Receipt: " << toString(RLP(r)) << "
"; s << "
Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "
"; @@ -1484,7 +1484,7 @@ void Main::on_blocks_currentItemChanged() if (!!info.logBloom) s << "
Log Bloom: " << info.logBloom << "
"; else - s << "
Log Bloom: Uneventful
"; + s << "
Log Bloom: Uneventful
"; s << "
Transactions: " << block[1].itemCount() << " @" << info.transactionsRoot << "" << "
"; s << "
Uncles: " << block[2].itemCount() << " @" << info.sha3Uncles << "" << "
"; for (auto u: block[2]) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 534f18a69..6f52548c7 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -316,7 +316,7 @@ void Transact::rejigData() return; } else - gasNeeded = min((qint64)ethereum()->gasLimitRemaining(), (qint64)((b - value()) / gasPrice())); + gasNeeded = (qint64)min(ethereum()->gasLimitRemaining(), ((b - value()) / gasPrice())); // Dry-run execution to determine gas requirement and any execution errors Address to; @@ -326,7 +326,7 @@ void Transact::rejigData() else { to = m_context->fromString(ui->destination->currentText()); - er = ethereum()->call(s, value(), to, m_data, ethereum()->gasLimitRemaining(), gasPrice()); + er = ethereum()->call(s, value(), to, m_data, gasNeeded, gasPrice()); } gasNeeded = (qint64)(er.gasUsed + er.gasRefunded); htmlInfo = QString("
INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo; diff --git a/eth/main.cpp b/eth/main.cpp index 206a6df57..2a065cc8c 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -835,11 +835,8 @@ int main(int argc, char** argv) Executive e(state, c->blockChain(), 0); Transaction t = state.pending()[index]; state = state.fromPending(index); - bytes r = t.rlp(); try { - e.setup(&r); - OnOpFunc oof; if (format == "pretty") oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) @@ -872,7 +869,9 @@ int main(int argc, char** argv) f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl; }; - e.go(oof); + e.initialize(t); + if (!e.execute()) + e.go(oof); e.finalize(); } catch(Exception const& _e) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index f80680f38..688a0c326 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -63,7 +63,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) return _out; } -ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub) +ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) { #if ALL_COMPILERS_ARE_CPP11_COMPLIANT static thread_local h256 h = _h ^ sha3(h256(u256(_sub))); @@ -184,6 +184,19 @@ inline string toString(h256s const& _bs) return out.str(); } +LastHashes BlockChain::lastHashes(unsigned _n) const +{ + Guard l(x_lastLastHashes); + if (m_lastLastHashesNumber != _n || m_lastLastHashes.empty()) + { + m_lastLastHashes.resize(256); + for (unsigned i = 0; i < 256; ++i) + m_lastLastHashes[i] = _n >= i ? numberHash(_n - i) : h256(); + m_lastLastHashesNumber = _n; + } + return m_lastLastHashes; +} + h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) { _bq.tick(*this); @@ -412,6 +425,9 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) WriteGuard l(x_lastBlockHash); m_lastBlockHash = newHash; } + + noteCanonChanged(); + m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret); StructuredLogger::chainNewHead( @@ -428,7 +444,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) return ret; } -h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, bool _post) const +h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const { // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); if (!_from || !_to) @@ -438,38 +454,40 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo unsigned fn = details(_from).number; unsigned tn = details(_to).number; // cdebug << "treeRoute" << fn << "..." << tn; + h256 from = _from; while (fn > tn) { if (_pre) - ret.push_back(_from); - _from = details(_from).parent; + ret.push_back(from); + from = details(from).parent; fn--; // cdebug << "from:" << fn << _from.abridged(); } + h256 to = _to; while (fn < tn) { if (_post) - back.push_back(_to); - _to = details(_to).parent; + back.push_back(to); + to = details(to).parent; tn--; // cdebug << "to:" << tn << _to.abridged(); } - while (_from != _to) + while (from != to) { - assert(_from); - assert(_to); - _from = details(_from).parent; - _to = details(_to).parent; + assert(from); + assert(to); + from = details(from).parent; + to = details(to).parent; if (_pre) - ret.push_back(_from); + ret.push_back(from); if (_post) - back.push_back(_to); + back.push_back(to); fn--; tn--; // cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); } if (o_common) - *o_common = _from; + *o_common = from; ret.reserve(ret.size() + back.size()); for (auto it = back.cbegin(); it != back.cend(); ++it) ret.push_back(*it); @@ -677,7 +695,7 @@ vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earlie return ret; } -h256Set BlockChain::allUnclesFrom(h256 _parent) const +h256Set BlockChain::allUnclesFrom(h256 const& _parent) const { // Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). h256Set ret; @@ -692,7 +710,7 @@ h256Set BlockChain::allUnclesFrom(h256 _parent) const return ret; } -bool BlockChain::isKnown(h256 _hash) const +bool BlockChain::isKnown(h256 const& _hash) const { if (_hash == m_genesisHash) return true; @@ -706,7 +724,7 @@ bool BlockChain::isKnown(h256 _hash) const return !!d.size(); } -bytes BlockChain::block(h256 _hash) const +bytes BlockChain::block(h256 const& _hash) const { if (_hash == m_genesisHash) return m_genesisBlock; diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 235a39267..03c0fdcfd 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -30,9 +30,10 @@ #include #include #include +#include #include #include -#include +#include #include "BlockDetails.h" #include "Account.h" #include "Transaction.h" @@ -61,7 +62,7 @@ struct BlockChainNote: public LogChannel { static const char* name() { return "= // TODO: Move all this Genesis stuff into Genesis.h/.cpp std::map const& genesisState(); -ldb::Slice toSlice(h256 _h, unsigned _sub = 0); +ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; @@ -105,37 +106,42 @@ public: h256s import(bytes const& _block, OverlayDB const& _stateDB); /// Returns true if the given block is known (though not necessarily a part of the canon chain). - bool isKnown(h256 _hash) const; + bool isKnown(h256 const& _hash) const; /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. - BlockInfo info(h256 _hash) const { return BlockInfo(block(_hash)); } + BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash)); } BlockInfo info() const { return BlockInfo(block()); } /// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe. - bytes block(h256 _hash) const; + bytes block(h256 const& _hash) const; bytes block() const { return block(currentHash()); } /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. - BlockDetails details(h256 _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } + BlockDetails details(h256 const& _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } BlockDetails details() const { return details(currentHash()); } /// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe. - BlockLogBlooms logBlooms(h256 _hash) const { return queryExtras(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); } + BlockLogBlooms logBlooms(h256 const& _hash) const { return queryExtras(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); } BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); } /// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe. - BlockReceipts receipts(h256 _hash) const { return queryExtras(_hash, m_receipts, x_receipts, NullBlockReceipts); } + BlockReceipts receipts(h256 const& _hash) const { return queryExtras(_hash, m_receipts, x_receipts, NullBlockReceipts); } BlockReceipts receipts() const { return receipts(currentHash()); } /// Get a list of transaction hashes for a given block. Thread-safe. - TransactionHashes transactionHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } + TransactionHashes transactionHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); } /// Get a list of uncle hashes for a given block. Thread-safe. - UncleHashes uncleHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; } + UncleHashes uncleHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; } UncleHashes uncleHashes() const { return uncleHashes(currentHash()); } - h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; } + /// Get the hash for a given block's number. + h256 numberHash(unsigned _i) const { if (!_i) return genesisHash(); return queryExtras(h256(u256(_i)), m_blockHashes, x_blockHashes, NullBlockHash).value; } + + /// Get the last N hashes for a given block. (N is determined by the LastHashes type.) + LastHashes lastHashes() const { return lastHashes(number()); } + LastHashes lastHashes(unsigned _i) const; /** Get the block blooms for a number of blocks. Thread-safe. * @returns the object pertaining to the blocks: @@ -158,15 +164,15 @@ public: std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const; /// Get a transaction from its hash. Thread-safe. - bytes transaction(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } - std::pair transactionLocation(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair(h256(), 0); return std::make_pair(ta.blockHash, ta.index); } + bytes transaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } + std::pair transactionLocation(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair(h256(), 0); return std::make_pair(ta.blockHash, ta.index); } /// Get a block's transaction (RLP format) for the given block hash (or the most recent mined if none given) & index. Thread-safe. - bytes transaction(h256 _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); } + bytes transaction(h256 const& _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); } bytes transaction(unsigned _i) const { return transaction(currentHash(), _i); } /// Get a number for the given hash (or the most recent mined if none given). Thread-safe. - unsigned number(h256 _hash) const { return details(_hash).number; } + unsigned number(h256 const& _hash) const { return details(_hash).number; } unsigned number() const { return number(currentHash()); } /// Get a given block (RLP format). Thread-safe. @@ -178,7 +184,7 @@ public: /// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). /// @returns set including the header-hash of every parent (including @a _parent) up to and including generation +5 /// togther with all their quoted uncles. - h256Set allUnclesFrom(h256 _parent) const; + h256Set allUnclesFrom(h256 const& _parent) const; /** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent. @@ -194,7 +200,7 @@ public: * treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g * @endcode */ - h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; + h256s treeRoute(h256 const& _from, h256 const& _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; struct Statistics { @@ -219,7 +225,7 @@ private: void open(std::string _path, bool _killExisting = false); void close(); - template T queryExtras(h256 _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const + template T queryExtras(h256 const& _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const { { ReadGuard l(_x); @@ -268,6 +274,11 @@ private: void noteUsed(h256 const& _h, unsigned _extra = (unsigned)-1) const; std::chrono::system_clock::time_point m_lastCollection; + void noteCanonChanged() const { Guard l(x_lastLastHashes); m_lastLastHashes.clear(); } + mutable Mutex x_lastLastHashes; + mutable LastHashes m_lastLastHashes; + mutable unsigned m_lastLastHashesNumber = (unsigned)-1; + void updateStats() const; mutable Statistics m_lastStats; diff --git a/libethereum/Client.h b/libethereum/Client.h index 5fb30b8f0..1091bba58 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -110,7 +110,7 @@ public: private: u256 m_weiPerRef; u256 m_refsPerBlock; - u256 m_gasPerBlock = 1000000; + u256 m_gasPerBlock = 3141592; std::array m_octiles; }; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index df30595b6..b45b9cf27 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -73,7 +73,7 @@ ExecutionResult ClientBase::call(Secret _secret, u256 _value, Address _dest, byt State temp = asOf(_blockNumber); u256 n = temp.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted); } catch (...) { @@ -92,7 +92,7 @@ ExecutionResult ClientBase::create(Secret _secret, u256 _value, bytes const& _da // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _data, n, _secret); - ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted); } catch (...) { diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 7dfc51b47..5bc42540f 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -178,7 +178,7 @@ void EthereumHost::maintainTransactions() for (auto const& i: m_tq.transactions()) if (ep->m_requireTransactions || (!m_transactionsSent.count(i.first) && !ep->m_knownTransactions.count(i.first))) { - b += i.second; + b += i.second.rlp(); ++n; m_transactionsSent.insert(i.first); } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index c574fa650..dfd526bef 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -35,7 +35,7 @@ using namespace dev::eth; Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level): m_s(_s), - m_lastHashes(_s.getLastHashes(_bc, (unsigned)_s.info().number - 1)), + m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)), m_depth(_level) {} @@ -55,12 +55,33 @@ void Executive::accrueSubState(SubState& _parentContext) _parentContext += m_ext->sub; } -bool Executive::setup(bytesConstRef _rlp) +void Executive::initialize(Transaction const& _transaction) { - // Entry point for a user-executed transaction. + m_t = _transaction; + + // Avoid transactions that would take us beyond the block gas limit. + u256 startGasUsed = m_s.gasUsed(); + if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit) + { + clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas(); + m_excepted = TransactionException::BlockGasLimitReached; + BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas())); + } + + // Check gas cost is enough. + m_gasRequired = Interface::txGas(m_t.data()); + if (m_t.gas() < m_gasRequired) + { + clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_gasRequired << " Got" << m_t.gas(); + m_excepted = TransactionException::OutOfGas; + BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)m_gasRequired, (bigint)m_t.gas())); + } + + // Avoid invalid transactions. + u256 nonceReq; try { - m_t = Transaction(_rlp, CheckSignature::Sender); + nonceReq = m_s.transactionsFrom(m_t.sender()); } catch (...) { @@ -68,15 +89,6 @@ bool Executive::setup(bytesConstRef _rlp) m_excepted = TransactionException::InvalidSignature; throw; } - return setup(); -} - -bool Executive::setup() -{ - // Entry point for a user-executed transaction. - - // Avoid invalid transactions. - auto nonceReq = m_s.transactionsFrom(m_t.sender()); if (m_t.nonce() != nonceReq) { clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce(); @@ -84,46 +96,32 @@ bool Executive::setup() BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce())); } - // Check gas cost is enough. - auto gasRequired = Interface::txGas(m_t.data()); - - if (m_t.gas() < gasRequired) - { - clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasRequired << " Got" << m_t.gas(); - m_excepted = TransactionException::OutOfGas; - BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)gasRequired, (bigint)m_t.gas())); - } - - bigint gasCost = (bigint)m_t.gas() * m_t.gasPrice(); - bigint totalCost = m_t.value() + gasCost; - // Avoid unaffordable transactions. - if (m_s.balance(m_t.sender()) < totalCost) + m_gasCost = (bigint)m_t.gas() * m_t.gasPrice(); + m_totalCost = m_t.value() + m_gasCost; + if (m_s.balance(m_t.sender()) < m_totalCost) { - clog(StateDetail) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender()); + clog(StateDetail) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender()); m_excepted = TransactionException::NotEnoughCash; - BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender()))); + BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender()))); } +} - u256 startGasUsed = m_s.gasUsed(); - if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit) - { - clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas(); - m_excepted = TransactionException::BlockGasLimitReached; - BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas())); - } +bool Executive::execute() +{ + // Entry point for a user-executed transaction. // Increment associated nonce for sender. m_s.noteSending(m_t.sender()); // Pay... - clog(StateDetail) << "Paying" << formatBalance(u256(gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; - m_s.subBalance(m_t.sender(), gasCost); + clog(StateDetail) << "Paying" << formatBalance(u256(m_gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; + m_s.subBalance(m_t.sender(), m_gasCost); if (m_t.isCreation()) - return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasRequired, &m_t.data(), m_t.sender()); + return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_gasRequired, &m_t.data(), m_t.sender()); else - return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasRequired, m_t.sender()); + return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_gasRequired, m_t.sender()); } bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index eb0c27ad2..3445ad407 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -41,10 +41,21 @@ struct VMTraceChannel: public LogChannel { static const char* name() { return "E * @brief Message-call/contract-creation executor; useful for executing transactions. * * Two ways of using this class - either as a transaction executive or a CALL/CREATE executive. - * In the first use, after construction, begin with setup() and end with finalize(). Call go() - * after setup() only if it returns false. + * + * In the first use, after construction, begin with initialize(), then execute() and end with finalize(). Call go() + * after execute() only if it returns false. + * * In the second use, after construction, begin with call() or create() and end with * accrueSubState(). Call go() after call()/create() only if it returns false. + * + * Example: + * @code + * Executive e(state, blockchain, 0); + * e.initialize(transaction); + * if (!e.execute()) + * e.go(); + * e.finalize(); + * @endcode */ class Executive { @@ -59,17 +70,17 @@ public: Executive(Executive const&) = delete; void operator=(Executive) = delete; - /// Set up the executive for evaluating a transaction. You must call finalize() following this. - /// @returns true iff go() must be called (and thus a VM execution in required). - bool setup(bytesConstRef _transaction); - /// Set up the executive for evaluating a transaction. You must call finalize() following this. - /// @returns true iff go() must be called (and thus a VM execution in required). - bool setup(Transaction const& _transaction) { m_t = _transaction; return setup(); } - /// Finalise a transaction previously set up with setup(). - /// @warning Only valid after setup(), and possibly go(). + /// Initializes the executive for evaluating a transaction. You must call finalize() at some point following this. + void initialize(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckSignature::None)); } + void initialize(Transaction const& _transaction); + /// Finalise a transaction previously set up with initialize(). + /// @warning Only valid after initialize() and execute(), and possibly go(). void finalize(); - /// @returns the transaction from setup(). - /// @warning Only valid after setup(). + /// Begins execution of a transaction. You must call finalize() following this. + /// @returns true if the transaction is done, false if go() must be called. + bool execute(); + /// @returns the transaction from initialize(). + /// @warning Only valid after initialize(). Transaction const& t() const { return m_t; } /// @returns the log entries created by this operation. /// @warning Only valid after finalise(). @@ -107,8 +118,6 @@ public: ExecutionResult executionResult() const; private: - bool setup(); - State& m_s; ///< The state to which this operation/transaction is applied. LastHashes m_lastHashes; std::shared_ptr m_ext; ///< The VM externality object for the VM execution or null if no VM is required. @@ -125,6 +134,10 @@ private: Transaction m_t; ///< The original transaction. Set by setup(). LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). + + bigint m_gasRequired; + bigint m_gasCost; + bigint m_totalCost; }; } diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 47fdb7250..529ddc093 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -71,11 +71,13 @@ public: virtual void flushTransactions() = 0; /// Makes the given call. Nothing is recorded into the state. - virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = 0) = 0; + virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) = 0; + ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) { return call(_secret, _value, _dest, _data, _gas, _gasPrice, m_default); } /// Does the given creation. Nothing is recorded into the state. /// @returns the pair of the Address of the created contract together with its code. - virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = 0) = 0; + virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) = 0; + ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) { return create(_secret, _value, _data, _gas, _gasPrice, m_default); } // [STATE-QUERY API] diff --git a/libethereum/State.cpp b/libethereum/State.cpp index af3a4a223..9817e2bad 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -388,8 +388,7 @@ bool State::cull(TransactionQueue& _tq) const { try { - Transaction t(i.second, CheckSignature::Sender); - if (t.nonce() <= transactionsFrom(t.sender())) + if (i.second.nonce() <= transactionsFrom(i.second.sender())) { _tq.drop(i.first); ret = true; @@ -411,7 +410,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga TransactionReceipts ret; auto ts = _tq.transactions(); - auto lh = getLastHashes(_bc, _bc.number()); + LastHashes lh; for (int goodTxs = 1; goodTxs;) { @@ -421,12 +420,11 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga { try { - if (Transaction(i.second, CheckSignature::Range).gasPrice() >= _gp.ask(*this)) + if (i.second.gasPrice() >= _gp.ask(*this)) { - Transaction t(i.second, CheckSignature::Sender); - // don't have it yet! Execute it now. - uncommitToMine(); // boost::timer t; + if (lh.empty()) + lh = _bc.lastHashes(); execute(lh, i.second); ret.push_back(m_receipts.back()); _tq.noteGood(i); @@ -434,6 +432,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga // cnote << "TX took:" << t.elapsed() * 1000; } } +#if ETH_DEBUG catch (InvalidNonce const& in) { bigint const* req = boost::get_error_info(in); @@ -449,13 +448,19 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga else _tq.setFuture(i); } + catch (BlockGasLimitReached const& e) + { + _tq.setFuture(i); + } +#endif catch (Exception const& _e) { // Something else went wrong - drop it. _tq.drop(i.first); if (o_transactionQueueChanged) *o_transactionQueueChanged = true; - cwarn << "Sync went wrong\n" << diagnostic_information(_e); + cnote << "Dropping invalid transaction:"; + cnote << diagnostic_information(_e); } catch (std::exception const&) { @@ -463,6 +468,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga _tq.drop(i.first); if (o_transactionQueueChanged) *o_transactionQueueChanged = true; + cnote << "Transaction caused low-level exception :("; } } } @@ -498,7 +504,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) GenericTrieDB receiptsTrie(&rm); receiptsTrie.init(); - LastHashes lh = getLastHashes(_bc, (unsigned)m_previousBlock.number); + LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number); RLP rlp(_block); // All ok with the block generally. Play back the transactions now... @@ -509,7 +515,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) k << i; transactionsTrie.insert(&k.out(), tr.data()); - execute(lh, tr.data()); + execute(lh, Transaction(tr.data(), CheckSignature::Sender)); RLPStream receiptrlp; m_receipts.back().streamRLP(receiptrlp); @@ -1040,56 +1046,34 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const return true; } -LastHashes State::getLastHashes(BlockChain const& _bc, unsigned _n) const -{ - LastHashes ret; - ret.resize(256); - if (eth::c_protocolVersion > 49) - { - ret[0] = _bc.numberHash(_n); - for (unsigned i = 1; i < 256; ++i) - ret[i] = ret[i - 1] ? _bc.details(ret[i - 1]).parent : h256(); - } - return ret; -} - -ExecutionResult State::execute(BlockChain const& _bc, bytes const& _rlp, Permanence _p) -{ - return execute(getLastHashes(_bc, _bc.number()), &_rlp, _p); -} - -ExecutionResult State::execute(BlockChain const& _bc, bytesConstRef _rlp, Permanence _p) +ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p) { - return execute(getLastHashes(_bc, _bc.number()), _rlp, _p); -} - -// TODO: maintain node overlay revisions for stateroots -> each commit gives a stateroot + OverlayDB; allow overlay copying for rewind operations. -ExecutionResult State::execute(LastHashes const& _lh, bytesConstRef _rlp, Permanence _p) -{ -#ifndef ETH_RELEASE - commit(); // get an updated hash -#endif - +#if ETH_PARANOIA paranoia("start of execution.", true); - State old(*this); -#if ETH_PARANOIA auto h = rootHash(); #endif + // Create and initialize the executive. This will throw fairly cheaply and quickly if the + // transaction is bad in any way. Executive e(*this, _lh, 0); - e.setup(_rlp); + e.initialize(_t); - u256 startGasUsed = gasUsed(); + // Uncommitting is a non-trivial operation - only do it once we've verified as much of the + // transaction as possible. + uncommitToMine(); + // OK - transaction looks valid - execute. + u256 startGasUsed = gasUsed(); #if ETH_PARANOIA ctrace << "Executing" << e.t() << "on" << h; ctrace << toHex(e.t().rlp()); #endif + if (!e.execute()) #if ETH_VMTRACE - e.go(e.simpleTrace()); + e.go(e.simpleTrace()); #else - e.go(); + e.go(); #endif e.finalize(); diff --git a/libethereum/State.h b/libethereum/State.h index ee88f443e..5ed76cc27 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -188,15 +188,9 @@ public: /// Like sync but only operate on _tq, killing the invalid/old ones. bool cull(TransactionQueue& _tq) const; - /// Returns the last few block hashes of the current chain. - LastHashes getLastHashes(BlockChain const& _bc, unsigned _n) const; - /// Execute a given transaction. /// This will append @a _t to the transaction list and change the state accordingly. - ExecutionResult execute(BlockChain const& _bc, bytes const& _rlp, Permanence _p = Permanence::Committed); - ExecutionResult execute(BlockChain const& _bc, bytesConstRef _rlp, Permanence _p = Permanence::Committed); - ExecutionResult execute(LastHashes const& _lh, bytes const& _rlp, Permanence _p = Permanence::Committed) { return execute(_lh, &_rlp, _p); } - ExecutionResult execute(LastHashes const& _lh, bytesConstRef _rlp, Permanence _p = Permanence::Committed); + ExecutionResult execute(LastHashes const& _lh, Transaction const& _t, Permanence _p = Permanence::Committed); /// Get the remaining gas limit in this block. u256 gasLimitRemaining() const { return m_currentBlock.gasLimit - gasUsed(); } diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 5701fc4a5..803d320ee 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -46,7 +46,7 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP) UpgradeGuard ul(l); // If valid, append to blocks. - m_current[h] = _transactionRLP.toBytes(); + m_current[h] = t; m_known.insert(h); } catch (Exception const& _e) @@ -63,20 +63,20 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP) return true; } -void TransactionQueue::setFuture(std::pair const& _t) +void TransactionQueue::setFuture(std::pair const& _t) { WriteGuard l(m_lock); if (m_current.count(_t.first)) { m_current.erase(_t.first); - m_unknown.insert(make_pair(Transaction(_t.second, CheckSignature::Sender).sender(), _t)); + m_unknown.insert(make_pair(_t.second.sender(), _t)); } } -void TransactionQueue::noteGood(std::pair const& _t) +void TransactionQueue::noteGood(std::pair const& _t) { WriteGuard l(m_lock); - auto r = m_unknown.equal_range(Transaction(_t.second, CheckSignature::Sender).sender()); + auto r = m_unknown.equal_range(_t.second.sender()); for (auto it = r.first; it != r.second; ++it) m_current.insert(it->second); m_unknown.erase(r.first, r.second); diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index b58944e4f..b104b98ca 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -25,6 +25,7 @@ #include #include "libethcore/Common.h" #include +#include "Transaction.h" namespace dev { @@ -46,19 +47,19 @@ public: void drop(h256 _txHash); - std::map transactions() const { ReadGuard l(m_lock); return m_current; } + std::map transactions() const { ReadGuard l(m_lock); return m_current; } std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); } - void setFuture(std::pair const& _t); - void noteGood(std::pair const& _t); + void setFuture(std::pair const& _t); + void noteGood(std::pair const& _t); void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } private: mutable boost::shared_mutex m_lock; ///< General lock. std::set m_known; ///< Hashes of transactions in both sets. - std::map m_current; ///< Map of SHA3(tx) to tx. - std::multimap> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. + std::map m_current; ///< Map of SHA3(tx) to tx. + std::multimap> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. }; } diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 61028d458..5db2d8219 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -165,7 +165,7 @@ int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, NetworkPreferences const& return retport; } -bi::tcp::endpoint Network::traverseNAT(std::set const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) +bi::tcp::endpoint Network::traverseNAT(std::set const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpInterfaceAddr) { asserts(_listenPort != 0); @@ -177,26 +177,26 @@ bi::tcp::endpoint Network::traverseNAT(std::set const& _ifAddresses // let m_upnp continue as null - we handle it properly. catch (...) {} - bi::tcp::endpoint upnpep; + bi::tcp::endpoint upnpEP; if (upnp && upnp->isValid()) { - bi::address paddr; + bi::address pAddr; int extPort = 0; for (auto const& addr: _ifAddresses) if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) { - paddr = addr; + pAddr = addr; break; } - auto eip = upnp->externalIP(); - bi::address eipaddr(bi::address::from_string(eip)); - if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) + auto eIP = upnp->externalIP(); + bi::address eIPAddr(bi::address::from_string(eIP)); + if (extPort && eIP != string("0.0.0.0") && !isPrivateAddress(eIPAddr)) { clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << "."; - clog(NetNote) << "External addr:" << eip; - o_upnpifaddr = paddr; - upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort); + clog(NetNote) << "External addr:" << eIP; + o_upnpInterfaceAddr = pAddr; + upnpEP = bi::tcp::endpoint(eIPAddr, (unsigned short)extPort); } else clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place)."; @@ -205,7 +205,7 @@ bi::tcp::endpoint Network::traverseNAT(std::set const& _ifAddresses delete upnp; } - return upnpep; + return upnpEP; } bi::tcp::endpoint Network::resolveHost(string const& _addr) diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 19d946d01..7729c0ffe 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -112,7 +112,8 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c State execState = _state; Executive execution(execState, lastHashes, 0); - execution.setup(&rlp); + execution.initialize(&rlp); + execution.execute(); std::vector machineStates; std::vector levels; std::vector codes; @@ -184,7 +185,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c // execute on a state if (!_call) { - _state.execute(lastHashes, rlp); + _state.execute(lastHashes, _t); if (_t.isCreation() && _state.code(d.contractAddress).empty()) BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); // collect watches diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 50ca22c54..988859fe5 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -182,18 +182,17 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) Transactions txList; for (auto const& txi: txs.transactions()) { - Transaction tx(txi.second, CheckSignature::Sender); - txList.push_back(tx); + txList.push_back(txi.second); mObject txObject; - txObject["nonce"] = toString(tx.nonce()); - txObject["data"] = "0x" + toHex(tx.data()); - txObject["gasLimit"] = toString(tx.gas()); - txObject["gasPrice"] = toString(tx.gasPrice()); - txObject["r"] = "0x" + toString(tx.signature().r); - txObject["s"] = "0x" + toString(tx.signature().s); - txObject["v"] = to_string(tx.signature().v + 27); - txObject["to"] = tx.isCreation() ? "" : toString(tx.receiveAddress()); - txObject["value"] = toString(tx.value()); + txObject["nonce"] = toString(txi.second.nonce()); + txObject["data"] = "0x" + toHex(txi.second.data()); + txObject["gasLimit"] = toString(txi.second.gas()); + txObject["gasPrice"] = toString(txi.second.gasPrice()); + txObject["r"] = "0x" + toString(txi.second.signature().r); + txObject["s"] = "0x" + toString(txi.second.signature().s); + txObject["v"] = to_string(txi.second.signature().v + 27); + txObject["to"] = txi.second.isCreation() ? "" : toString(txi.second.receiveAddress()); + txObject["value"] = toString(txi.second.value()); txArray.push_back(txObject); } diff --git a/test/checkRandomStateTest.cpp b/test/checkRandomStateTest.cpp index a4d390b16..49aca852f 100644 --- a/test/checkRandomStateTest.cpp +++ b/test/checkRandomStateTest.cpp @@ -83,12 +83,11 @@ bool doStateTest(mValue& _v) ImportTest importer(o, false); eth::State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { - output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/createRandomStateTest.cpp b/test/createRandomStateTest.cpp index f422d1717..5758598b9 100644 --- a/test/createRandomStateTest.cpp +++ b/test/createRandomStateTest.cpp @@ -183,12 +183,11 @@ void doStateTests(json_spirit::mValue& _v) test::ImportTest importer(o, true); eth::State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { - output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 86062a90b..2451aa381 100644 --- a/test/solidityExecutionFramework.h +++ b/test/solidityExecutionFramework.h @@ -142,7 +142,8 @@ protected: try { // this will throw since the transaction is invalid, but it should nevertheless store the transaction - executive.setup(&transactionRLP); + executive.initialize(&transactionRLP); + executive.execute(); } catch (...) {} if (_isCreation) diff --git a/test/state.cpp b/test/state.cpp index 4ab59f7a1..7c586ec7d 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -57,13 +57,12 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) ImportTest importer(o, _fillin); State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { Listener::ExecTimeGuard guard{i.first}; - output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp index 5b7b0415e..384d85344 100644 --- a/test/stateOriginal.cpp +++ b/test/stateOriginal.cpp @@ -79,13 +79,9 @@ BOOST_AUTO_TEST_CASE(Complex) cout << s; // Inject a transaction to transfer funds from miner to me. - bytes tx; - { - Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); - assert(t.sender() == myMiner.address()); - tx = t.rlp(); - } - s.execute(bc, tx); + Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); + assert(t.sender() == myMiner.address()); + s.execute(bc.lastHashes(), t); cout << s;