You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
482 lines
12 KiB
482 lines
12 KiB
/*
|
|
This file is part of cpp-ethereum.
|
|
|
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
cpp-ethereum is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/** @file ClientBase.cpp
|
|
* @author Gav Wood <i@gavwood.com>
|
|
* @author Marek Kotewicz <marek@ethdev.com>
|
|
* @date 2015
|
|
*/
|
|
|
|
#include "ClientBase.h"
|
|
|
|
#include <libdevcore/StructuredLogger.h>
|
|
#include "BlockChain.h"
|
|
#include "Executive.h"
|
|
#include "State.h"
|
|
|
|
using namespace std;
|
|
using namespace dev;
|
|
using namespace dev::eth;
|
|
|
|
const char* WatchChannel::name() { return EthBlue "ℹ" EthWhite " "; }
|
|
const char* WorkInChannel::name() { return EthOrange "⚒" EthGreen "▬▶"; }
|
|
const char* WorkOutChannel::name() { return EthOrange "⚒" EthNavy "◀▬"; }
|
|
const char* WorkChannel::name() { return EthOrange "⚒" EthWhite " "; }
|
|
|
|
State ClientBase::asOf(BlockNumber _h) const
|
|
{
|
|
if (_h == PendingBlock)
|
|
return postMine();
|
|
else if (_h == LatestBlock)
|
|
return preMine();
|
|
return asOf(bc().numberHash(_h));
|
|
}
|
|
|
|
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
|
|
{
|
|
prepareForTransaction();
|
|
|
|
Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret);
|
|
m_tq.import(t.rlp());
|
|
|
|
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
|
|
cnote << "New transaction " << t;
|
|
}
|
|
|
|
Address ClientBase::submitTransaction(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
|
|
{
|
|
prepareForTransaction();
|
|
|
|
Transaction t(_value, _gasPrice, _gas, _data, _nonce, _secret);
|
|
m_tq.import(t.rlp());
|
|
|
|
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
|
|
cnote << "New transaction " << t;
|
|
|
|
return right160(sha3(rlpList(t.sender(), t.nonce())));
|
|
}
|
|
|
|
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
|
|
{
|
|
auto a = toAddress(_secret);
|
|
submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, max<u256>(postMine().transactionsFrom(a), m_tq.maxNonce(a)));
|
|
}
|
|
|
|
Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
|
|
{
|
|
prepareForTransaction();
|
|
|
|
u256 n = postMine().transactionsFrom(toAddress(_secret));
|
|
Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
|
|
m_tq.import(t.rlp());
|
|
|
|
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
|
|
cnote << "New transaction " << t;
|
|
|
|
return right160(sha3(rlpList(t.sender(), t.nonce())));
|
|
}
|
|
|
|
// TODO: remove try/catch, allow exceptions
|
|
ExecutionResult ClientBase::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff)
|
|
{
|
|
ExecutionResult ret;
|
|
try
|
|
{
|
|
State temp = asOf(_blockNumber);
|
|
u256 n = temp.transactionsFrom(_from);
|
|
Transaction t(_value, _gasPrice, _gas, _dest, _data, n);
|
|
t.forceSender(_from);
|
|
if (_ff == FudgeFactor::Lenient)
|
|
temp.addBalance(_from, (u256)(t.gas() * t.gasPrice() + t.value()));
|
|
ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
|
|
}
|
|
catch (...)
|
|
{
|
|
// TODO: Some sort of notification of failure.
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ExecutionResult ClientBase::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff)
|
|
{
|
|
ExecutionResult ret;
|
|
try
|
|
{
|
|
State temp = asOf(_blockNumber);
|
|
u256 n = temp.transactionsFrom(_from);
|
|
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
|
|
|
|
Transaction t(_value, _gasPrice, _gas, _data, n);
|
|
t.forceSender(_from);
|
|
if (_ff == FudgeFactor::Lenient)
|
|
temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
|
|
ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
|
|
}
|
|
catch (...)
|
|
{
|
|
// TODO: Some sort of notification of failure.
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ImportResult ClientBase::injectBlock(bytes const& _block)
|
|
{
|
|
return bc().attemptImport(_block, preMine().db()).first;
|
|
}
|
|
|
|
u256 ClientBase::balanceAt(Address _a, BlockNumber _block) const
|
|
{
|
|
return asOf(_block).balance(_a);
|
|
}
|
|
|
|
u256 ClientBase::countAt(Address _a, BlockNumber _block) const
|
|
{
|
|
return asOf(_block).transactionsFrom(_a);
|
|
}
|
|
|
|
u256 ClientBase::stateAt(Address _a, u256 _l, BlockNumber _block) const
|
|
{
|
|
return asOf(_block).storage(_a, _l);
|
|
}
|
|
|
|
bytes ClientBase::codeAt(Address _a, BlockNumber _block) const
|
|
{
|
|
return asOf(_block).code(_a);
|
|
}
|
|
|
|
h256 ClientBase::codeHashAt(Address _a, BlockNumber _block) const
|
|
{
|
|
return asOf(_block).codeHash(_a);
|
|
}
|
|
|
|
unordered_map<u256, u256> ClientBase::storageAt(Address _a, BlockNumber _block) const
|
|
{
|
|
return asOf(_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())
|
|
{
|
|
State temp = postMine();
|
|
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);
|
|
if (le.size())
|
|
for (unsigned j = 0; j < le.size(); ++j)
|
|
ret.insert(ret.begin(), LocalisedLogEntry(le[j]));
|
|
}
|
|
begin = bc().number();
|
|
}
|
|
|
|
set<unsigned> matchingBlocks;
|
|
for (auto const& i: _f.bloomPossibilities())
|
|
for (auto u: bc().withBlockBloom(i, end, begin))
|
|
matchingBlocks.insert(u);
|
|
|
|
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++;
|
|
}
|
|
}
|
|
|
|
cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves";
|
|
return ret;
|
|
}
|
|
|
|
unsigned ClientBase::installWatch(LogFilter const& _f, Reaping _r)
|
|
{
|
|
h256 h = _f.sha3();
|
|
{
|
|
Guard l(x_filtersWatches);
|
|
if (!m_filters.count(h))
|
|
{
|
|
cwatch << "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);
|
|
cwatch << "+++" << 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)
|
|
{
|
|
cwatch << "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)
|
|
{
|
|
cwatch << "*X*" << fit->first << ":" << fit->second.filter;
|
|
m_filters.erase(fit);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const
|
|
{
|
|
Guard l(x_filtersWatches);
|
|
|
|
// cwatch << "peekWatch" << _watchId;
|
|
auto& w = m_watches.at(_watchId);
|
|
// cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(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;
|
|
|
|
// cwatch << "checkWatch" << _watchId;
|
|
auto& w = m_watches.at(_watchId);
|
|
// cwatch << "lastPoll updated to " << chrono::duration_cast<chrono::seconds>(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;
|
|
}
|
|
|
|
BlockInfo ClientBase::blockInfo(h256 _hash) const
|
|
{
|
|
if (_hash == PendingBlockHash)
|
|
return preMine().info();
|
|
return BlockInfo(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);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
pair<h256, unsigned> 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);
|
|
}
|
|
|
|
BlockInfo ClientBase::uncle(h256 _blockHash, unsigned _i) const
|
|
{
|
|
auto bl = bc().block(_blockHash);
|
|
RLP b(bl);
|
|
if (_i < b[2].itemCount())
|
|
return BlockInfo::fromHeader(b[2][_i].data());
|
|
else
|
|
return BlockInfo();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
Transactions ClientBase::pending() const
|
|
{
|
|
return postMine().pending();
|
|
}
|
|
|
|
h256s ClientBase::pendingHashes() const
|
|
{
|
|
return h256s() + postMine().pendingHashes();
|
|
}
|
|
|
|
StateDiff ClientBase::diff(unsigned _txi, h256 _block) const
|
|
{
|
|
State st = asOf(_block);
|
|
return st.fromPending(_txi).diff(st.fromPending(_txi + 1), true);
|
|
}
|
|
|
|
StateDiff ClientBase::diff(unsigned _txi, BlockNumber _block) const
|
|
{
|
|
State st = asOf(_block);
|
|
return st.fromPending(_txi).diff(st.fromPending(_txi + 1), true);
|
|
}
|
|
|
|
Addresses ClientBase::addresses(BlockNumber _block) const
|
|
{
|
|
Addresses ret;
|
|
for (auto const& i: asOf(_block).addresses())
|
|
ret.push_back(i.first);
|
|
return ret;
|
|
}
|
|
|
|
u256 ClientBase::gasLimitRemaining() const
|
|
{
|
|
return postMine().gasLimitRemaining();
|
|
}
|
|
|
|
Address ClientBase::address() const
|
|
{
|
|
return preMine().address();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|