// Aleth: Ethereum C++ client, tools and libraries. // Copyright 2015-2019 Aleth Authors. // Licensed under the GNU General Public License, Version 3. #include "ClientBase.h" #include #include "BlockChain.h" #include "Executive.h" #include "State.h" using namespace std; using namespace dev; using namespace dev::eth; static const int64_t c_maxGasEstimate = 50000000; std::pair ClientBase::estimateGas(Address const& _from, u256 _value, Address _dest, bytes const& _data, int64_t _maxGas, u256 _gasPrice, BlockNumber _blockNumber, GasEstimationCallback const& _callback) { try { int64_t upperBound = _maxGas; if (upperBound == Invalid256 || upperBound > c_maxGasEstimate) upperBound = c_maxGasEstimate; int64_t lowerBound = Transaction::baseGasRequired(!_dest, &_data, EVMSchedule()); Block bk = blockByNumber(_blockNumber); u256 gasPrice = _gasPrice == Invalid256 ? gasBidPrice() : _gasPrice; ExecutionResult er; ExecutionResult lastGood; bool good = false; while (upperBound != lowerBound) { int64_t mid = (lowerBound + upperBound) / 2; u256 n = bk.transactionsFrom(_from); Transaction t; if (_dest) t = Transaction(_value, gasPrice, mid, _dest, _data, n); else t = Transaction(_value, gasPrice, mid, _data, n); t.forceSender(_from); EnvInfo const env(bk.info(), bc().lastBlockHashes(), 0, mid); State tempState(bk.state()); tempState.addBalance(_from, (u256)(t.gas() * t.gasPrice() + t.value())); er = tempState.execute(env, *bc().sealEngine(), t, Permanence::Reverted).first; if (er.excepted == TransactionException::OutOfGas || er.excepted == TransactionException::OutOfGasBase || er.excepted == TransactionException::OutOfGasIntrinsic || er.codeDeposit == CodeDeposit::Failed || er.excepted == TransactionException::BadJumpDestination) lowerBound = lowerBound == mid ? upperBound : mid; else { lastGood = er; upperBound = upperBound == mid ? lowerBound : mid; good = true; } if (_callback) _callback(GasEstimationProgress { lowerBound, upperBound }); } if (_callback) _callback(GasEstimationProgress { lowerBound, upperBound }); return make_pair(upperBound, good ? lastGood : er); } catch (...) { // TODO: Some sort of notification of failure. return make_pair(u256(), ExecutionResult()); } } ImportResult ClientBase::injectBlock(bytes const& _block) { return bc().attemptImport(_block, preSeal().db()).first; } u256 ClientBase::balanceAt(Address _a, BlockNumber _block) const { return blockByNumber(_block).balance(_a); } u256 ClientBase::countAt(Address _a, BlockNumber _block) const { return blockByNumber(_block).transactionsFrom(_a); } u256 ClientBase::stateAt(Address _a, u256 _l, BlockNumber _block) const { return blockByNumber(_block).storage(_a, _l); } h256 ClientBase::stateRootAt(Address _a, BlockNumber _block) const { return blockByNumber(_block).storageRoot(_a); } bytes ClientBase::codeAt(Address _a, BlockNumber _block) const { return blockByNumber(_block).code(_a); } h256 ClientBase::codeHashAt(Address _a, BlockNumber _block) const { return blockByNumber(_block).codeHash(_a); } map> ClientBase::storageAt(Address _a, BlockNumber _block) const { return blockByNumber(_block).storage(_a); } // TODO: remove try/catch, allow exceptions LocalisedLogEntries ClientBase::logs(unsigned _watchId) const { LogFilter f; try { Guard l(x_filtersWatches); f = m_filters.at(m_watches.at(_watchId).id).filter; } catch (...) { return LocalisedLogEntries(); } return logs(f); } LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const { LocalisedLogEntries ret; unsigned begin = min(bc().number() + 1, (unsigned)numberFromHash(_f.latest())); unsigned end = min(bc().number(), min(begin, (unsigned)numberFromHash(_f.earliest()))); // Handle pending transactions differently as they're not on the block chain. if (begin > bc().number()) { Block temp = postSeal(); for (unsigned i = 0; i < temp.pending().size(); ++i) { // Might have a transaction that contains a matching log. TransactionReceipt const& tr = temp.receipt(i); LogEntries le = _f.matches(tr); for (unsigned j = 0; j < le.size(); ++j) ret.insert(ret.begin(), LocalisedLogEntry(le[j])); } begin = bc().number(); } // Handle reverted blocks // There are not so many, so let's iterate over them h256s blocks; h256 ancestor; unsigned ancestorIndex; tie(blocks, ancestor, ancestorIndex) = bc().treeRoute(_f.earliest(), _f.latest(), false); for (size_t i = 0; i < ancestorIndex; i++) prependLogsFromBlock(_f, blocks[i], BlockPolarity::Dead, ret); // cause end is our earliest block, let's compare it with our ancestor // if ancestor is smaller let's move our end to it // example: // // 3b -> 2b -> 1b // -> g // 3a -> 2a -> 1a // // if earliest is at 2a and latest is a 3b, coverting them to numbers // will give us pair (2, 3) // and we want to get all logs from 1 (ancestor + 1) to 3 // so we have to move 2a to g + 1 end = min(end, (unsigned)numberFromHash(ancestor) + 1); // Handle blocks from main chain set matchingBlocks; if (!_f.isRangeFilter()) for (auto const& i: _f.bloomPossibilities()) for (auto u: bc().withBlockBloom(i, end, begin)) matchingBlocks.insert(u); else // if it is a range filter, we want to get all logs from all blocks in given range for (unsigned i = end; i <= begin; i++) matchingBlocks.insert(i); for (auto n: matchingBlocks) prependLogsFromBlock(_f, bc().numberHash(n), BlockPolarity::Live, ret); reverse(ret.begin(), ret.end()); return ret; } void ClientBase::prependLogsFromBlock(LogFilter const& _f, h256 const& _blockHash, BlockPolarity _polarity, LocalisedLogEntries& io_logs) const { auto receipts = bc().receipts(_blockHash).receipts; for (size_t i = 0; i < receipts.size(); i++) { TransactionReceipt receipt = receipts[i]; auto th = transaction(_blockHash, i).sha3(); LogEntries le = _f.matches(receipt); for (unsigned j = 0; j < le.size(); ++j) io_logs.insert(io_logs.begin(), LocalisedLogEntry(le[j], _blockHash, (BlockNumber)bc().number(_blockHash), th, i, 0, _polarity)); } } unsigned ClientBase::installWatch(LogFilter const& _f, Reaping _r) { h256 h = _f.sha3(); { Guard l(x_filtersWatches); if (!m_filters.count(h)) { LOG(m_loggerWatch) << "FFF" << _f << h; m_filters.insert(make_pair(h, _f)); } } return installWatch(h, _r); } unsigned ClientBase::installWatch(h256 _h, Reaping _r) { unsigned ret; { Guard l(x_filtersWatches); ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; m_watches[ret] = ClientWatch(_h, _r); LOG(m_loggerWatch) << "+++" << ret << _h; } #if INITIAL_STATE_AS_CHANGES auto ch = logs(ret); if (ch.empty()) ch.push_back(InitialChange); { Guard l(x_filtersWatches); swap(m_watches[ret].changes, ch); } #endif return ret; } bool ClientBase::uninstallWatch(unsigned _i) { LOG(m_loggerWatch) << "XXX" << _i; Guard l(x_filtersWatches); auto it = m_watches.find(_i); if (it == m_watches.end()) return false; auto id = it->second.id; m_watches.erase(it); auto fit = m_filters.find(id); if (fit != m_filters.end()) if (!--fit->second.refCount) { LOG(m_loggerWatch) << "*X*" << fit->first << ":" << fit->second.filter; m_filters.erase(fit); } return true; } LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const { Guard l(x_filtersWatches); // LOG(m_loggerWatch) << "peekWatch" << _watchId; auto& w = m_watches.at(_watchId); // LOG(m_loggerWatch) << "lastPoll updated to " << //chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); if (w.lastPoll != chrono::system_clock::time_point::max()) w.lastPoll = chrono::system_clock::now(); return w.changes; } LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId) { Guard l(x_filtersWatches); LocalisedLogEntries ret; // LOG(m_loggerWatch) << "checkWatch" << _watchId; auto& w = m_watches.at(_watchId); // LOG(m_loggerWatch) << "lastPoll updated to " << //chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); std::swap(ret, w.changes); if (w.lastPoll != chrono::system_clock::time_point::max()) w.lastPoll = chrono::system_clock::now(); return ret; } BlockHeader ClientBase::blockInfo(h256 _hash) const { if (_hash == PendingBlockHash) return preSeal().info(); return BlockHeader(bc().block(_hash)); } BlockDetails ClientBase::blockDetails(h256 _hash) const { return bc().details(_hash); } Transaction ClientBase::transaction(h256 _transactionHash) const { return Transaction(bc().transaction(_transactionHash), CheckTransaction::Cheap); } LocalisedTransaction ClientBase::localisedTransaction(h256 const& _transactionHash) const { std::pair tl = bc().transactionLocation(_transactionHash); return localisedTransaction(tl.first, tl.second); } Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const { auto bl = bc().block(_blockHash); RLP b(bl); if (_i < b[1].itemCount()) return Transaction(b[1][_i].data(), CheckTransaction::Cheap); else return Transaction(); } LocalisedTransaction ClientBase::localisedTransaction(h256 const& _blockHash, unsigned _i) const { Transaction t = Transaction(bc().transaction(_blockHash, _i), CheckTransaction::Cheap); return LocalisedTransaction(t, _blockHash, _i, numberFromHash(_blockHash)); } TransactionReceipt ClientBase::transactionReceipt(h256 const& _transactionHash) const { return bc().transactionReceipt(_transactionHash); } LocalisedTransactionReceipt ClientBase::localisedTransactionReceipt(h256 const& _transactionHash) const { std::pair tl = bc().transactionLocation(_transactionHash); Transaction t = Transaction(bc().transaction(tl.first, tl.second), CheckTransaction::Cheap); TransactionReceipt tr = bc().transactionReceipt(tl.first, tl.second); u256 gasUsed = tr.cumulativeGasUsed(); if (tl.second > 0) gasUsed -= bc().transactionReceipt(tl.first, tl.second - 1).cumulativeGasUsed(); return LocalisedTransactionReceipt( tr, t.sha3(), tl.first, numberFromHash(tl.first), t.from(), t.to(), tl.second, gasUsed, toAddress(t.from(), t.nonce())); } pair ClientBase::transactionLocation(h256 const& _transactionHash) const { return bc().transactionLocation(_transactionHash); } Transactions ClientBase::transactions(h256 _blockHash) const { auto bl = bc().block(_blockHash); RLP b(bl); Transactions res; for (unsigned i = 0; i < b[1].itemCount(); i++) res.emplace_back(b[1][i].data(), CheckTransaction::Cheap); return res; } TransactionHashes ClientBase::transactionHashes(h256 _blockHash) const { return bc().transactionHashes(_blockHash); } BlockHeader ClientBase::uncle(h256 _blockHash, unsigned _i) const { auto bl = bc().block(_blockHash); RLP b(bl); if (_i < b[2].itemCount()) return BlockHeader(b[2][_i].data(), HeaderData); else return BlockHeader(); } UncleHashes ClientBase::uncleHashes(h256 _blockHash) const { return bc().uncleHashes(_blockHash); } unsigned ClientBase::transactionCount(h256 _blockHash) const { auto bl = bc().block(_blockHash); RLP b(bl); return b[1].itemCount(); } unsigned ClientBase::uncleCount(h256 _blockHash) const { auto bl = bc().block(_blockHash); RLP b(bl); return b[2].itemCount(); } unsigned ClientBase::number() const { return bc().number(); } h256s ClientBase::pendingHashes() const { return h256s() + postSeal().pendingHashes(); } BlockHeader ClientBase::pendingInfo() const { return postSeal().info(); } BlockDetails ClientBase::pendingDetails() const { auto const pendingHeader = postSeal().info(); auto const latestDetails = Interface::blockDetails(LatestBlock); return BlockDetails{static_cast(pendingHeader.number()), latestDetails.totalDifficulty + pendingHeader.difficulty(), pendingHeader.parentHash(), h256s{} /* children */, postSeal().blockData().size()}; } Addresses ClientBase::addresses(BlockNumber _block) const { Addresses ret; for (auto const& i : blockByNumber(_block).addresses()) ret.push_back(i.first); return ret; } u256 ClientBase::gasLimitRemaining() const { return postSeal().gasLimitRemaining(); } Address ClientBase::author() const { return preSeal().author(); } h256 ClientBase::hashFromNumber(BlockNumber _number) const { if (_number == PendingBlock) return h256(); if (_number == LatestBlock) return bc().currentHash(); return bc().numberHash(_number); } BlockNumber ClientBase::numberFromHash(h256 _blockHash) const { if (_blockHash == PendingBlockHash) return bc().number() + 1; else if (_blockHash == LatestBlockHash) return bc().number(); else if (_blockHash == EarliestBlockHash) return 0; return bc().number(_blockHash); } int ClientBase::compareBlockHashes(h256 _h1, h256 _h2) const { BlockNumber n1 = numberFromHash(_h1); BlockNumber n2 = numberFromHash(_h2); if (n1 > n2) return 1; else if (n1 == n2) return 0; return -1; } bool ClientBase::isKnown(h256 const& _hash) const { return _hash == PendingBlockHash || _hash == LatestBlockHash || _hash == EarliestBlockHash || bc().isKnown(_hash); } bool ClientBase::isKnown(BlockNumber _block) const { return _block == PendingBlock || _block == LatestBlock || bc().numberHash(_block) != h256(); } bool ClientBase::isKnownTransaction(h256 const& _transactionHash) const { return bc().isKnownTransaction(_transactionHash); } bool ClientBase::isKnownTransaction(h256 const& _blockHash, unsigned _i) const { return isKnown(_blockHash) && block(_blockHash).pending().size() > _i; } Block ClientBase::blockByNumber(BlockNumber _h) const { if (_h == PendingBlock) return postSeal(); else if (_h == LatestBlock) return block(bc().currentHash()); return block(bc().numberHash(_h)); } int ClientBase::chainId() const { return bc().chainParams().chainID; }