diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 7b89d3f99..d60be548a 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -153,8 +153,11 @@ public: BlockReceipts receipts(h256 const& _hash) const { return queryExtras(_hash, m_receipts, x_receipts, NullBlockReceipts); } BlockReceipts receipts() const { return receipts(currentHash()); } + /// Get the transaction by block hash and index; + TransactionReceipt transactionReceipt(h256 const& _blockHash, unsigned _i) const { return receipts(_blockHash).receipts[_i]; } + /// Get the transaction receipt by transaction hash. Thread-safe. - TransactionReceipt transactionReceipt(h256 const& _transactionHash) const {TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytesConstRef(); return receipts(ta.blockHash).receipts[ta.index]; } + TransactionReceipt transactionReceipt(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytesConstRef(); return transactionReceipt(ta.blockHash, ta.index); } /// Get a list of transaction hashes for a given block. Thread-safe. 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; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index d43f240b7..f99bcea83 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -407,10 +407,9 @@ void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& } } -void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed) +void Client::appendFromBlock(h256 const& _block, BlockPolarity _polarity, h256Hash& io_changed) { // TODO: more precise check on whether the txs match. - auto d = bc().info(_block); auto receipts = bc().receipts(_block).receipts; Guard l(x_filtersWatches); @@ -419,18 +418,16 @@ void Client::appendFromNewBlock(h256 const& _block, h256Hash& io_changed) for (pair& i: m_filters) { // acceptable number & looks like block may contain a matching log entry. - unsigned logIndex = 0; for (size_t j = 0; j < receipts.size(); j++) { - logIndex++; auto tr = receipts[j]; auto m = i.second.filter.matches(tr); if (m.size()) { - auto transactionHash = transaction(d.hash(), j).sha3(); + auto transactionHash = transaction(_block, j).sha3(); // filter catches them for (LogEntry const& l: m) - i.second.changes.push_back(LocalisedLogEntry(l, d, transactionHash, j, logIndex)); + i.second.changes.push_back(LocalisedLogEntry(l, _block, (BlockNumber)bc().number(_block), transactionHash, j, 0, _polarity)); io_changed.insert(i.first); } } @@ -562,10 +559,10 @@ void Client::syncTransactionQueue() h->noteNewTransactions(); } -void Client::onChainChanged(ImportRoute const& _ir) +void Client::onDeadBlocks(h256s const& _blocks, h256Hash& io_changed) { // insert transactions that we are declaring the dead part of the chain - for (auto const& h: _ir.deadBlocks) + for (auto const& h: _blocks) { clog(ClientTrace) << "Dead block:" << h; for (auto const& t: bc().transactions(h)) @@ -575,23 +572,25 @@ void Client::onChainChanged(ImportRoute const& _ir) } } + for (auto const& h: _blocks) + appendFromBlock(h, BlockPolarity::Dead, io_changed); +} + +void Client::onNewBlocks(h256s const& _blocks, h256Hash& io_changed) +{ // remove transactions from m_tq nicely rather than relying on out of date nonce later on. - for (auto const& h: _ir.liveBlocks) + for (auto const& h: _blocks) clog(ClientTrace) << "Live block:" << h; - for (auto const& t: _ir.goodTranactions) - { - clog(ClientTrace) << "Safely dropping transaction " << t.sha3(); - m_tq.dropGood(t); - } - if (auto h = m_host.lock()) h->noteNewBlocks(); - h256Hash changeds; - for (auto const& h: _ir.liveBlocks) - appendFromNewBlock(h, changeds); + for (auto const& h: _blocks) + appendFromBlock(h, BlockPolarity::Live, io_changed); +} +void Client::restartMining() +{ // RESTART MINING if (!isMajorSyncing()) @@ -624,8 +623,6 @@ void Client::onChainChanged(ImportRoute const& _ir) DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postMine) m_postMine = m_working; - changeds.insert(PendingChangedFilter); - onPostStateChanged(); } @@ -633,7 +630,19 @@ void Client::onChainChanged(ImportRoute const& _ir) // we should resync with it manually until we are stricter about what constitutes "knowing". onTransactionQueueReady(); } +} +void Client::onChainChanged(ImportRoute const& _ir) +{ + h256Hash changeds; + onDeadBlocks(_ir.deadBlocks, changeds); + for (auto const& t: _ir.goodTranactions) + { + clog(ClientTrace) << "Safely dropping transaction " << t.sha3(); + m_tq.dropGood(t); + } + onNewBlocks(_ir.liveBlocks, changeds); + restartMining(); noteChanged(changeds); } diff --git a/libethereum/Client.h b/libethereum/Client.h index 18ac1c648..896444176 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -220,7 +220,7 @@ protected: /// Collate the changed filters for the hash of the given block. /// Insert any filters that are activated into @a o_changed. - void appendFromNewBlock(h256 const& _blockHash, h256Hash& io_changed); + void appendFromBlock(h256 const& _blockHash, BlockPolarity _polarity, h256Hash& io_changed); /// Record that the set of filters @a _filters have changed. /// This doesn't actually make any callbacks, but incrememnts some counters in m_watches. @@ -242,6 +242,15 @@ protected: /// Called when wouldMine(), turboMining(), isChainBad(), forceMining(), pendingTransactions() have changed. void rejigMining(); + /// Called on chain changes + void onDeadBlocks(h256s const& _blocks, h256Hash& io_changed); + + /// Called on chain changes + void onNewBlocks(h256s const& _blocks, h256Hash& io_changed); + + /// Called after processing blocks by onChainChanged(_ir) + void restartMining(); + /// Magically called when the chain has changed. An import route is provided. /// Called by either submitWork() or in our main thread through syncBlockQueue(). void onChainChanged(ImportRoute const& _ir); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 769880269..900768a42 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -171,51 +171,67 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const // Might have a transaction that contains a matching log. TransactionReceipt const& tr = temp.receipt(i); LogEntries le = _f.matches(tr); - if (le.size()) - for (unsigned j = 0; j < le.size(); ++j) - ret.insert(ret.begin(), LocalisedLogEntry(le[j])); + 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; - for (auto const& i: _f.bloomPossibilities()) - for (auto u: bc().withBlockBloom(i, end, begin)) - matchingBlocks.insert(u); + 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); - unsigned falsePos = 0; for (auto n: matchingBlocks) - { - int total = 0; - auto h = bc().numberHash(n); - auto info = bc().info(h); - auto receipts = bc().receipts(h).receipts; - unsigned logIndex = 0; - for (size_t i = 0; i < receipts.size(); i++) - { - logIndex++; - TransactionReceipt receipt = receipts[i]; - if (_f.matches(receipt.bloom())) - { - auto th = transaction(info.hash(), i).sha3(); - LogEntries le = _f.matches(receipt); - if (le.size()) - { - total += le.size(); - for (unsigned j = 0; j < le.size(); ++j) - ret.insert(ret.begin(), LocalisedLogEntry(le[j], info, th, i, logIndex)); - } - } - - if (!total) - falsePos++; - } - } + prependLogsFromBlock(_f, bc().numberHash(n), BlockPolarity::Live, ret); - cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves"; + 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(); @@ -317,6 +333,12 @@ 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); @@ -327,11 +349,31 @@ Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const 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); + return LocalisedTransactionReceipt( + tr, + t.sha3(), + tl.first, + numberFromHash(tl.first), + tl.second, + toAddress(t.from(), t.nonce())); +} + pair ClientBase::transactionLocation(h256 const& _transactionHash) const { return bc().transactionLocation(_transactionHash); @@ -479,3 +521,7 @@ bool ClientBase::isKnownTransaction(h256 const& _transactionHash) const return bc().isKnownTransaction(_transactionHash); } +bool ClientBase::isKnownTransaction(h256 const& _blockHash, unsigned _i) const +{ + return isKnown(_blockHash) && bc().transactions().size() > _i; +} diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 874f10548..56e042f7b 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -104,6 +104,7 @@ public: virtual LocalisedLogEntries logs(unsigned _watchId) const override; virtual LocalisedLogEntries logs(LogFilter const& _filter) const override; + virtual void prependLogsFromBlock(LogFilter const& _filter, h256 const& _blockHash, BlockPolarity _polarity, LocalisedLogEntries& io_logs) const; /// Install, uninstall and query watches. virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) override; @@ -118,8 +119,11 @@ public: virtual BlockInfo blockInfo(h256 _hash) const override; virtual BlockDetails blockDetails(h256 _hash) const override; virtual Transaction transaction(h256 _transactionHash) const override; + virtual LocalisedTransaction localisedTransaction(h256 const& _transactionHash) const override; virtual Transaction transaction(h256 _blockHash, unsigned _i) const override; + virtual LocalisedTransaction localisedTransaction(h256 const& _blockHash, unsigned _i) const override; virtual TransactionReceipt transactionReceipt(h256 const& _transactionHash) const override; + virtual LocalisedTransactionReceipt localisedTransactionReceipt(h256 const& _transactionHash) const override; virtual std::pair transactionLocation(h256 const& _transactionHash) const override; virtual Transactions transactions(h256 _blockHash) const override; virtual TransactionHashes transactionHashes(h256 _blockHash) const override; @@ -148,6 +152,7 @@ public: virtual bool isKnown(h256 const& _hash) const override; virtual bool isKnown(BlockNumber _block) const override; virtual bool isKnownTransaction(h256 const& _transactionHash) const override; + virtual bool isKnownTransaction(h256 const& _blockHash, unsigned _i) const override; /// TODO: consider moving it to a separate interface diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 213e7fb26..b21ebfdb2 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -134,8 +134,11 @@ public: // [BLOCK QUERY API] virtual bool isKnownTransaction(h256 const& _transactionHash) const = 0; + virtual bool isKnownTransaction(h256 const& _blockHash, unsigned _i) const = 0; virtual Transaction transaction(h256 _transactionHash) const = 0; + virtual LocalisedTransaction localisedTransaction(h256 const& _transactionHash) const = 0; virtual TransactionReceipt transactionReceipt(h256 const& _transactionHash) const = 0; + virtual LocalisedTransactionReceipt localisedTransactionReceipt(h256 const& _transactionHash) const = 0; virtual std::pair transactionLocation(h256 const& _transactionHash) const = 0; virtual h256 hashFromNumber(BlockNumber _number) const = 0; virtual BlockNumber numberFromHash(h256 _blockHash) const = 0; @@ -146,6 +149,7 @@ public: virtual BlockInfo blockInfo(h256 _hash) const = 0; virtual BlockDetails blockDetails(h256 _hash) const = 0; virtual Transaction transaction(h256 _blockHash, unsigned _i) const = 0; + virtual LocalisedTransaction localisedTransaction(h256 const& _blockHash, unsigned _i) const = 0; virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const = 0; virtual UncleHashes uncleHashes(h256 _blockHash) const = 0; virtual unsigned transactionCount(h256 _blockHash) const = 0; diff --git a/libethereum/LogFilter.cpp b/libethereum/LogFilter.cpp index 6e0fbd709..95c07f2a3 100644 --- a/libethereum/LogFilter.cpp +++ b/libethereum/LogFilter.cpp @@ -46,6 +46,18 @@ h256 LogFilter::sha3() const return dev::sha3(s.out()); } +bool LogFilter::isRangeFilter() const +{ + if (m_addresses.size()) + return false; + + for (auto const& t: m_topics) + if (t.size()) + return false; + + return true; +} + bool LogFilter::matches(LogBloom _bloom) const { if (m_addresses.size()) @@ -77,14 +89,67 @@ vector LogFilter::bloomPossibilities() const { // return combination of each of the addresses/topics vector ret; - // TODO proper combinatorics. - for (auto i: m_addresses) - ret.push_back(LogBloom().shiftBloom<3>(dev::sha3(i))); + + // | every address with every topic + for (auto const& i: m_addresses) + { + // 1st case, there are addresses and topics + // + // m_addresses = [a0, a1]; + // m_topics = [[t0], [t1a, t1b], [], []]; + // + // blooms = [ + // a0 | t0, a0 | t1a | t1b, + // a1 | t0, a1 | t1a | t1b + // ] + // + for (auto const& t: m_topics) + if (t.size()) + { + LogBloom b = LogBloom().shiftBloom<3>(dev::sha3(i)); + for (auto const &j: t) + b = b.shiftBloom<3>(dev::sha3(j)); + ret.push_back(b); + } + } + + // 2nd case, there are no topics + // + // m_addresses = [a0, a1]; + // m_topics = [[t0], [t1a, t1b], [], []]; + // + // blooms = [a0, a1]; + // + if (!ret.size()) + for (auto const& i: m_addresses) + ret.push_back(LogBloom().shiftBloom<3>(dev::sha3(i))); + + // 3rd case, there are no addresses, at least create blooms from topics + // + // m_addresses = []; + // m_topics = [[t0], [t1a, t1b], [], []]; + // + // blooms = [t0, t1a | t1b]; + // + if (!m_addresses.size()) + for (auto const& t: m_topics) + if (t.size()) + { + LogBloom b; + for (auto const &j: t) + b = b.shiftBloom<3>(dev::sha3(j)); + ret.push_back(b); + } + return ret; } LogEntries LogFilter::matches(TransactionReceipt const& _m) const { + // there are no addresses or topics to filter + if (isRangeFilter()) + return _m.log(); + LogEntries ret; if (matches(_m.bloom())) for (LogEntry const& e: _m.log()) diff --git a/libethereum/LogFilter.h b/libethereum/LogFilter.h index ff33346f8..092f173ef 100644 --- a/libethereum/LogFilter.h +++ b/libethereum/LogFilter.h @@ -50,10 +50,20 @@ public: void streamRLP(RLPStream& _s) const; h256 sha3() const; + /// hash of earliest block which should be filtered h256 earliest() const { return m_earliest; } + + /// hash of latest block which should be filtered h256 latest() const { return m_latest; } + /// Range filter is a filter which doesn't care about addresses or topics + /// Matches are all entries from earliest to latest + /// @returns true if addresses and topics are unspecified + bool isRangeFilter() const; + + /// @returns bloom possibilities for all addresses and topics std::vector bloomPossibilities() const; + bool matches(LogBloom _bloom) const; bool matches(State const& _s, unsigned _i) const; LogEntries matches(TransactionReceipt const& _r) const; diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 8ed0043c9..02373fe9e 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -130,5 +130,30 @@ private: /// Nice name for vector of Transaction. using Transactions = std::vector; +class LocalisedTransaction: public Transaction +{ +public: + LocalisedTransaction( + Transaction const& _t, + h256 const& _blockHash, + unsigned _transactionIndex, + BlockNumber _blockNumber = 0 + ): + Transaction(_t), + m_blockHash(_blockHash), + m_transactionIndex(_transactionIndex), + m_blockNumber(_blockNumber) + {} + + h256 const& blockHash() const { return m_blockHash; } + unsigned transactionIndex() const { return m_transactionIndex; } + BlockNumber blockNumber() const { return m_blockNumber; } + +private: + h256 m_blockHash; + unsigned m_transactionIndex; + BlockNumber m_blockNumber; +}; + } } diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index 0a0b154f4..6043cc5ee 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -58,5 +58,51 @@ using TransactionReceipts = std::vector; std::ostream& operator<<(std::ostream& _out, eth::TransactionReceipt const& _r); +class LocalisedTransactionReceipt: public TransactionReceipt +{ +public: + LocalisedTransactionReceipt( + TransactionReceipt const& _t, + h256 const& _hash, + h256 const& _blockHash, + BlockNumber _blockNumber, + unsigned _transactionIndex, + Address const& _contractAddress = Address() + ): + TransactionReceipt(_t), + m_hash(_hash), + m_blockHash(_blockHash), + m_blockNumber(_blockNumber), + m_transactionIndex(_transactionIndex), + m_contractAddress(_contractAddress) + { + LogEntries entries = log(); + for (unsigned i = 0; i < entries.size(); i++) + m_localisedLogs.push_back(LocalisedLogEntry( + entries[i], + m_blockHash, + m_blockNumber, + m_hash, + m_transactionIndex, + i + )); + } + + h256 const& hash() const { return m_hash; } + h256 const& blockHash() const { return m_blockHash; } + BlockNumber blockNumber() const { return m_blockNumber; } + unsigned transactionIndex() const { return m_transactionIndex; } + Address const& contractAddress() const { return m_contractAddress; } + LocalisedLogEntries const& localisedLogs() const { return m_localisedLogs; }; + +private: + h256 m_hash; + h256 m_blockHash; + BlockNumber m_blockNumber; + unsigned m_transactionIndex = 0; + Address m_contractAddress; + LocalisedLogEntries m_localisedLogs; +}; + } } diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index a5c45af26..651a9be15 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -36,6 +36,13 @@ namespace dev namespace eth { +enum class BlockPolarity +{ + Unknown, + Dead, + Live +}; + struct LogEntry { LogEntry() {} @@ -76,17 +83,20 @@ struct LocalisedLogEntry: public LogEntry explicit LocalisedLogEntry( LogEntry const& _le, - BlockInfo const& _bi, - h256 _th, - unsigned _ti, - unsigned _li + h256 const& _blockHash, + BlockNumber _blockNumber, + h256 const& _transactionHash, + unsigned _transactionIndex, + unsigned _logIndex, + BlockPolarity _polarity = BlockPolarity::Unknown ): LogEntry(_le), - blockHash(_bi.hash()), - blockNumber((BlockNumber)_bi.number()), - transactionHash(_th), - transactionIndex(_ti), - logIndex(_li), + blockHash(_blockHash), + blockNumber(_blockNumber), + transactionHash(_transactionHash), + transactionIndex(_transactionIndex), + logIndex(_logIndex), + polarity(_polarity), mined(true) {} @@ -95,6 +105,7 @@ struct LocalisedLogEntry: public LogEntry h256 transactionHash; unsigned transactionIndex = 0; unsigned logIndex = 0; + BlockPolarity polarity = BlockPolarity::Unknown; bool mined = false; bool isSpecial = false; h256 special; diff --git a/libweb3jsonrpc/JsonHelper.cpp b/libweb3jsonrpc/JsonHelper.cpp index 54fde52e8..c074525b9 100644 --- a/libweb3jsonrpc/JsonHelper.cpp +++ b/libweb3jsonrpc/JsonHelper.cpp @@ -183,30 +183,17 @@ Json::Value toJson(dev::eth::TransactionReceipt const& _t) return res; } -Json::Value toJson(dev::eth::TransactionReceipt const& _tr, std::pair _location, BlockNumber _blockNumber, Transaction const& _t) +Json::Value toJson(dev::eth::LocalisedTransactionReceipt const& _t) { Json::Value res; - h256 h = _t.sha3(); - res["transactionHash"] = toJS(h); - res["transactionIndex"] = _location.second; - res["blockHash"] = toJS(_location.first); - res["blockNumber"] = _blockNumber; - res["cumulativeGasUsed"] = toJS(_tr.gasUsed()); // TODO: check if this is fine - res["gasUsed"] = toJS(_tr.gasUsed()); - res["contractAddress"] = toJS(toAddress(_t.from(), _t.nonce())); - res["logs"] = Json::Value(Json::arrayValue); - for (unsigned i = 0; i < _tr.log().size(); i++) - { - LogEntry e = _tr.log()[i]; - Json::Value l = toJson(e); - l["type"] = "mined"; - l["blockNumber"] = _blockNumber; - l["blockHash"] = toJS(_location.first); - l["logIndex"] = i; - l["transactionHash"] = toJS(h); - l["transactionIndex"] = _location.second; - res["logs"].append(l); - } + res["transactionHash"] = toJS(_t.hash()); + res["transactionIndex"] = _t.transactionIndex(); + res["blockHash"] = toJS(_t.blockHash()); + res["blockNumber"] = _t.blockNumber(); + res["cumulativeGasUsed"] = toJS(_t.gasUsed()); // TODO: check if this is fine + res["gasUsed"] = toJS(_t.gasUsed()); + res["contractAddress"] = toJS(_t.contractAddress()); + res["logs"] = dev::toJson(_t.localisedLogs()); return res; } @@ -228,6 +215,26 @@ Json::Value toJson(dev::eth::Transaction const& _t) return res; } +Json::Value toJson(dev::eth::LocalisedTransaction const& _t) +{ + Json::Value res; + if (_t) + { + res["hash"] = toJS(_t.sha3()); + res["input"] = toJS(_t.data()); + res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress()); + res["from"] = toJS(_t.safeSender()); + res["gas"] = toJS(_t.gas()); + res["gasPrice"] = toJS(_t.gasPrice()); + res["nonce"] = toJS(_t.nonce()); + res["value"] = toJS(_t.value()); + res["blockHash"] = toJS(_t.blockHash()); + res["transactionIndex"] = toJS(_t.transactionIndex()); + res["blockNumber"] = toJS(_t.blockNumber()); + } + return res; +} + Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) { Json::Value res; @@ -237,6 +244,7 @@ Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) else { res = toJson(static_cast(_e)); + res["polarity"] = _e.polarity == BlockPolarity::Live ? true : false; if (_e.mined) { res["type"] = "mined"; @@ -270,6 +278,70 @@ Json::Value toJson(dev::eth::LogEntry const& _e) return res; } +Json::Value toJson(std::unordered_map const& _entriesByBlock, vector const& _order) +{ + Json::Value res(Json::arrayValue); + for (auto const& i: _order) + { + auto entries = _entriesByBlock.at(i); + Json::Value currentBlock(Json::objectValue); + LocalisedLogEntry entry = entries[0]; + if (entry.mined) + { + + currentBlock["blockNumber"] = entry.blockNumber; + currentBlock["blockHash"] = toJS(entry.blockHash); + currentBlock["type"] = "mined"; + } + else + currentBlock["type"] = "pending"; + + currentBlock["polarity"] = entry.polarity == BlockPolarity::Live ? true : false; + currentBlock["logs"] = Json::Value(Json::arrayValue); + + for (LocalisedLogEntry const& e: entries) + { + Json::Value log(Json::objectValue); + log["logIndex"] = e.logIndex; + log["transactionIndex"] = e.transactionIndex; + log["transactionHash"] = toJS(e.transactionHash); + log["address"] = toJS(e.address); + log["data"] = toJS(e.data); + log["topics"] = Json::Value(Json::arrayValue); + for (auto const& t: e.topics) + log["topics"].append(toJS(t)); + + currentBlock["logs"].append(log); + } + + res.append(currentBlock); + } + + return res; +} + +Json::Value toJsonByBlock(LocalisedLogEntries const& _entries) +{ + vector order; + unordered_map entriesByBlock; + + for (dev::eth::LocalisedLogEntry const& e: _entries) + { + if (e.isSpecial) // skip special log + continue; + + if (entriesByBlock.count(e.blockHash) == 0) + { + entriesByBlock[e.blockHash] = LocalisedLogEntries(); + order.push_back(e.blockHash); + } + + entriesByBlock[e.blockHash].push_back(e); + } + + return toJson(entriesByBlock, order); +} + TransactionSkeleton toTransactionSkeleton(Json::Value const& _json) { TransactionSkeleton ret; diff --git a/libweb3jsonrpc/JsonHelper.h b/libweb3jsonrpc/JsonHelper.h index 624a73e54..11219fc55 100644 --- a/libweb3jsonrpc/JsonHelper.h +++ b/libweb3jsonrpc/JsonHelper.h @@ -44,6 +44,7 @@ namespace eth { class Transaction; +class LocalisedTransaction; struct BlockDetails; class Interface; using Transactions = std::vector; @@ -57,11 +58,13 @@ Json::Value toJson(BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes co Json::Value toJson(BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts); Json::Value toJson(TransactionSkeleton const& _t); Json::Value toJson(Transaction const& _t); +Json::Value toJson(LocalisedTransaction const& _t); Json::Value toJson(TransactionReceipt const& _t); -//TODO: wrap these params into one structure eg. "LocalisedTransactionReceipt" -Json::Value toJson(TransactionReceipt const& _tr, std::pair _location, BlockNumber _blockNumber, Transaction const& _t); +Json::Value toJson(LocalisedTransactionReceipt const& _t); Json::Value toJson(LocalisedLogEntry const& _e); Json::Value toJson(LogEntry const& _e); +Json::Value toJson(std::unordered_map const& _entriesByBlock); +Json::Value toJsonByBlock(LocalisedLogEntries const& _entries); TransactionSkeleton toTransactionSkeleton(Json::Value const& _json); LogFilter toLogFilter(Json::Value const& _json); LogFilter toLogFilter(Json::Value const& _json, Interface const& _client); // commented to avoid warning. Uncomment once in use @ PoC-7. diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index f2a95f785..df2006cc5 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -380,8 +380,7 @@ Json::Value WebThreeStubServerBase::eth_getTransactionByHash(string const& _tran if (!client()->isKnownTransaction(h)) return Json::Value(Json::nullValue); - auto l = client()->transactionLocation(h); - return toJson(client()->transaction(h), l, client()->numberFromHash(l.first)); + return toJson(client()->localisedTransaction(h)); } catch (...) { @@ -395,8 +394,10 @@ Json::Value WebThreeStubServerBase::eth_getTransactionByBlockHashAndIndex(string { h256 bh = jsToFixed<32>(_blockHash); unsigned ti = jsToInt(_transactionIndex); - Transaction t = client()->transaction(bh, ti); - return toJson(t, make_pair(bh, ti), client()->numberFromHash(bh)); + if (!client()->isKnownTransaction(bh, ti)) + return Json::Value(Json::nullValue); + + return toJson(client()->localisedTransaction(bh, ti)); } catch (...) { @@ -409,9 +410,12 @@ Json::Value WebThreeStubServerBase::eth_getTransactionByBlockNumberAndIndex(stri try { BlockNumber bn = jsToBlockNumber(_blockNumber); + h256 bh = client()->hashFromNumber(bn); unsigned ti = jsToInt(_transactionIndex); - Transaction t = client()->transaction(bn, ti); - return toJson(t, make_pair(client()->hashFromNumber(bn), ti), bn); + if (!client()->isKnownTransaction(bh, ti)) + return Json::Value(Json::nullValue); + + return toJson(client()->localisedTransaction(bh, ti)); } catch (...) { @@ -427,8 +431,7 @@ Json::Value WebThreeStubServerBase::eth_getTransactionReceipt(string const& _tra if (!client()->isKnownTransaction(h)) return Json::Value(Json::nullValue); - auto l = client()->transactionLocation(h); - return toJson(client()->transactionReceipt(h), l, client()->numberFromHash(l.first), client()->transaction(h)); + return toJson(client()->localisedTransactionReceipt(h)); } catch (...) { @@ -686,7 +689,7 @@ Json::Value WebThreeStubServerBase::eth_getFilterChangesEx(string const& _filter auto entries = client()->checkWatch(id); if (entries.size()) cnote << "FIRING WATCH" << id << entries.size(); - return toJson(entries); + return toJsonByBlock(entries); } catch (...) { @@ -710,7 +713,7 @@ Json::Value WebThreeStubServerBase::eth_getFilterLogsEx(string const& _filterId) { try { - return toJson(client()->logs(jsToInt(_filterId))); + return toJsonByBlock(client()->logs(jsToInt(_filterId))); } catch (...) { @@ -722,7 +725,19 @@ Json::Value WebThreeStubServerBase::eth_getLogs(Json::Value const& _json) { try { - return toJson(client()->logs(toLogFilter(_json))); + return toJson(client()->logs(toLogFilter(_json, *client()))); + } + catch (...) + { + BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); + } +} + +Json::Value WebThreeStubServerBase::eth_getLogsEx(Json::Value const& _json) +{ + try + { + return toJsonByBlock(client()->logs(toLogFilter(_json))); } catch (...) { diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index d90015aec..62209a5b4 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -136,6 +136,7 @@ public: virtual Json::Value eth_getFilterLogs(std::string const& _filterId); virtual Json::Value eth_getFilterLogsEx(std::string const& _filterId); virtual Json::Value eth_getLogs(Json::Value const& _json); + virtual Json::Value eth_getLogsEx(Json::Value const& _json); virtual Json::Value eth_getWork(); virtual bool eth_submitWork(std::string const& _nonce, std::string const&, std::string const& _mixHash); virtual std::string eth_register(std::string const& _address); diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 988271c95..48421b940 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -57,6 +57,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("eth_getFilterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getFilterLogsI); this->bindAndAddMethod(jsonrpc::Procedure("eth_getFilterLogsEx", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getFilterLogsExI); this->bindAndAddMethod(jsonrpc::Procedure("eth_getLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_getLogsI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_getLogsEx", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_getLogsExI); this->bindAndAddMethod(jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI); this->bindAndAddMethod(jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI); this->bindAndAddMethod(jsonrpc::Procedure("eth_register", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_registerI); @@ -296,6 +297,10 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_getLogs(request[0u]); } + inline virtual void eth_getLogsExI(const Json::Value &request, Json::Value &response) + { + response = this->eth_getLogsEx(request[0u]); + } inline virtual void eth_getWorkI(const Json::Value &request, Json::Value &response) { (void)request; @@ -511,6 +516,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerCallMethod("eth_getLogsEx",p); + if (result.isArray()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } Json::Value eth_getWork() throw (jsonrpc::JsonRpcException) { Json::Value p;