Browse Source

Merge remote-tracking branch 'up/develop' into ui_macos

cl-refactor
yann300 10 years ago
parent
commit
2f5577240a
  1. 6
      alethzero/Main.ui
  2. 6
      alethzero/MainWin.cpp
  3. 2
      alethzero/Transact.cpp
  4. 19
      eth/main.cpp
  5. 2
      libdevcore/Common.cpp
  6. 28
      libdevcore/CommonData.h
  7. 17
      libethcore/CommonJS.cpp
  8. 3
      libethcore/CommonJS.h
  9. 1
      libethcore/Exceptions.h
  10. 1
      libethcore/Params.cpp
  11. 3
      libethcore/Params.h
  12. 44
      libethereum/BlockChain.cpp
  13. 25
      libethereum/BlockChain.h
  14. 8
      libethereum/BlockQueue.cpp
  15. 6
      libethereum/Client.cpp
  16. 25
      libethereum/ClientBase.cpp
  17. 5
      libethereum/ClientBase.h
  18. 5
      libethereum/EthereumHost.cpp
  19. 4
      libethereum/EthereumPeer.cpp
  20. 13
      libethereum/Executive.cpp
  21. 2
      libethereum/Executive.h
  22. 19
      libethereum/Interface.h
  23. 2
      libethereum/State.cpp
  24. 3
      libethereum/State.h
  25. 16
      libethereum/Transaction.cpp
  26. 48
      libethereum/Transaction.h
  27. 8
      libethereum/TransactionQueue.cpp
  28. 3
      libethereum/TransactionQueue.h
  29. 2
      libevm/VM.cpp
  30. 1
      libevmcore/CMakeLists.txt
  31. 140
      libevmcore/CommonSubexpressionEliminator.cpp
  32. 61
      libevmcore/CommonSubexpressionEliminator.h
  33. 31
      libevmcore/ExpressionClasses.cpp
  34. 11
      libevmcore/ExpressionClasses.h
  35. 42
      libevmcore/Instruction.cpp
  36. 4
      libevmcore/Instruction.h
  37. 2
      libevmcore/SemanticInformation.cpp
  38. 2
      libp2p/Session.cpp
  39. 1
      libsolidity/CMakeLists.txt
  40. 36
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  41. 31
      mix/ClientModel.cpp
  42. 21
      mix/ClientModel.h
  43. 1
      mix/MachineStates.h
  44. 7
      mix/MixApplication.cpp
  45. 80
      mix/MixClient.cpp
  46. 7
      mix/MixClient.h
  47. 8
      mix/qml/DeploymentDialog.qml
  48. 2
      mix/qml/StateListModel.qml
  49. 11
      mix/qml/TransactionDialog.qml
  50. 5
      mix/qml/TransactionLog.qml
  51. 1
      mix/qml/js/TransactionHelper.js
  52. 21
      neth/main.cpp
  53. 168
      test/SolidityOptimizer.cpp
  54. 2
      test/TestHelper.cpp
  55. 4
      test/blockchain.cpp
  56. 6
      test/transaction.cpp

6
alethzero/Main.ui

@ -570,7 +570,7 @@
<item>
<widget class="QListWidget" name="ourAccounts">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
<enum>Qt::WheelFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@ -637,7 +637,7 @@
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
<enum>Qt::WheelFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@ -715,7 +715,7 @@
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
<enum>Qt::WheelFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>

6
alethzero/MainWin.cpp

@ -1156,7 +1156,7 @@ void Main::refreshBlockChain()
auto b = bc.block(h);
for (auto const& i: RLP(b)[1])
{
Transaction t(i.data(), CheckSignature::Sender);
Transaction t(i.data(), CheckTransaction::Everything);
QString s = t.receiveAddress() ?
QString(" %2 %5> %3: %1 [%4]")
.arg(formatBalance(t.value()).c_str())
@ -1536,7 +1536,7 @@ void Main::on_blocks_currentItemChanged()
else
{
unsigned txi = item->data(Qt::UserRole + 1).toInt();
Transaction tx(block[1][txi].data(), CheckSignature::Sender);
Transaction tx(block[1][txi].data(), CheckTransaction::Everything);
auto ss = tx.safeSender();
h256 th = sha3(rlpList(ss, tx.nonce()));
TransactionReceipt receipt = ethereum()->blockChain().receipts(h).receipts[txi];
@ -1597,7 +1597,7 @@ void Main::on_debugCurrent_triggered()
State s(ethereum()->state(txi, h));
Executive e(s, ethereum()->blockChain());
Debugger dw(this, this);
dw.populate(e, Transaction(t, CheckSignature::Sender));
dw.populate(e, Transaction(t, CheckTransaction::Everything));
dw.exec();
}
}

2
alethzero/Transact.cpp

@ -306,7 +306,7 @@ void Transact::rejigData()
htmlInfo += "<h4>Hex</h4>" + QString(Div(Mono)) + QString::fromStdString(toHex(m_data)) + "</div>";
// Determine the minimum amount of gas we need to play...
qint64 baseGas = (qint64)Interface::txGas(m_data, 0);
qint64 baseGas = (qint64)Transaction::gasRequired(m_data, 0);
qint64 gasNeeded = 0;
if (b < value() + baseGas * gasPrice())

19
eth/main.cpp

@ -44,6 +44,7 @@
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include <libethcore/Ethasher.h>
#include "BuildInfo.h"
using namespace std;
using namespace dev;
@ -112,6 +113,7 @@ void help()
<< " -c,--client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/Ethereum)." << endl
<< " -D,--initdag Initialize DAG for mining and exit." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl
<< " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl
<< " -h,--help Show this help message and exit." << endl
@ -200,6 +202,7 @@ enum class NodeMode
int main(int argc, char** argv)
{
bool initDAG = false;
string listenIP;
unsigned short listenPort = 30303;
string publicIP;
@ -304,6 +307,8 @@ int main(int argc, char** argv)
structuredLogging = true;
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if (arg == "-D" || arg == "--initdag")
initDAG = true;
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
@ -436,6 +441,14 @@ int main(int argc, char** argv)
&nodesState,
miners
);
if (initDAG)
{
cout << "Initializing DAG. (This will take awhile)" << endl;
Ethasher::get()->full(web3.ethereum()->blockChain().info());
return 0;
}
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
@ -654,7 +667,7 @@ int main(int argc, char** argv)
cnote << ssbd.str();
int ssize = sechex.length();
int size = hexAddr.length();
u256 minGas = (u256)Client::txGas(data, 0);
u256 minGas = (u256)Transaction::gasRequired(data, 0);
if (size < 40)
{
if (size > 0)
@ -733,7 +746,7 @@ int main(int argc, char** argv)
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(bytes(), 0);
u256 minGas = (u256)Transaction::gasRequired(bytes(), 0);
try
{
Address dest = h160(fromHex(hexAddr, WhenError::Throw));
@ -798,7 +811,7 @@ int main(int argc, char** argv)
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init, 0);
u256 minGas = (u256)Transaction::gasRequired(init, 0);
if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using namespace dev;
namespace dev
{
char const* Version = "0.9.5";
char const* Version = "0.9.6";
}

28
libdevcore/CommonData.h

@ -236,6 +236,34 @@ inline std::vector<_T>& operator+=(std::vector<typename std::enable_if<!std::is_
return _a;
}
/// Insert the contents of a container into a set
template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b)
{
for (auto const& i: _b)
_a.insert(i);
return _a;
}
/// Concatenate the contents of a container onto a vector
template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U const& _b)
{
for (auto const& i: _b)
_a.push_back(i);
return _a;
}
/// Insert the contents of a container into a set
template <class T, class U> std::set<T> operator+(std::set<T> _a, U const& _b)
{
return _a += _b;
}
/// Concatenate the contents of a container onto a vector
template <class T, class U> std::vector<T> operator+(std::vector<T> _a, U const& _b)
{
return _a += _b;
}
/// Concatenate two vectors of elements.
template <class _T>
inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b)

17
libethcore/CommonJS.cpp

@ -65,5 +65,22 @@ std::string prettyU256(u256 _n, bool _abridged)
return s.str();
}
namespace eth
{
BlockNumber jsToBlockNumber(std::string const& _js)
{
if (_js == "latest")
return LatestBlock;
else if (_js == "earliest")
return 0;
else if (_js == "pending")
return PendingBlock;
else
return (unsigned)jsToInt(_js);
}
}
}

3
libethcore/CommonJS.h

@ -67,5 +67,8 @@ struct TransactionSkeleton
u256 gasPrice;
};
/// Convert to a block number, a bit like jsToInt, except that it correctly recognises "pending" and "latest".
BlockNumber jsToBlockNumber(std::string const& _js);
}
}

1
libethcore/Exceptions.h

@ -38,6 +38,7 @@ using errinfo_difficulty = boost::error_info<struct tag_difficulty, u256>;
using BadFieldError = boost::tuple<errinfo_field, errinfo_data>;
struct DatabaseAlreadyOpen: virtual dev::Exception {};
struct OutOfGasBase: virtual dev::Exception {};
struct NotEnoughAvailableSpace: virtual dev::Exception {};
struct NotEnoughCash: virtual dev::Exception {};
struct GasPriceTooLow: virtual dev::Exception {};

1
libethcore/Params.cpp

@ -46,7 +46,6 @@ u256 const c_sha3WordGas = 6;
u256 const c_sloadGas = 50;
u256 const c_sstoreSetGas = 20000;
u256 const c_sstoreResetGas = 5000;
u256 const c_sstoreClearGas = 5000;
u256 const c_sstoreRefundGas = 15000;
u256 const c_jumpdestGas = 1;
u256 const c_logGas = 375;

3
libethcore/Params.h

@ -48,8 +48,7 @@ extern u256 const c_sha3WordGas; ///< Once per word of the SHA3 operation's da
extern u256 const c_copyGas; ///< Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
extern u256 const c_sloadGas; ///< Once per SLOAD operation.
extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero.
extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change.
extern u256 const c_sstoreClearGas; ///< Once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness does not change from zero. NOTE: when c_sstoreSetGas does not apply.
extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_jumpdestGas; ///< Once per JUMPDEST operation.
extern u256 const c_logGas; ///< Per LOG* operation.

44
libethereum/BlockChain.cpp

@ -68,20 +68,6 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc)
return _out;
}
ldb::Slice dev::eth::oldToSlice(h256 const& _h, unsigned _sub)
{
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT
static thread_local h256 h = _h ^ sha3(h256(u256(_sub)));
return ldb::Slice((char const*)&h, 32);
#else
static boost::thread_specific_ptr<h256> t_h;
if (!t_h.get())
t_h.reset(new h256);
*t_h = _h ^ sha3(h256(u256(_sub)));
return ldb::Slice((char const*)t_h.get(), 32);
#endif
}
ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub)
{
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT
@ -988,41 +974,11 @@ bytes BlockChain::block(h256 const& _hash) const
return bytes();
}
WriteGuard l(x_blocks);
m_blocks[_hash].resize(d.size());
memcpy(m_blocks[_hash].data(), d.data(), d.size());
noteUsed(_hash);
return m_blocks[_hash];
}
bytes BlockChain::oldBlock(h256 const& _hash) const
{
if (_hash == m_genesisHash)
return m_genesisBlock;
{
ReadGuard l(x_blocks);
auto it = m_blocks.find(_hash);
if (it != m_blocks.end())
return it->second;
}
string d;
m_blocksDB->Get(m_readOptions, oldToSlice(_hash), &d);
if (!d.size())
{
cwarn << "Couldn't find requested block:" << _hash.abridged();
return bytes();
}
WriteGuard l(x_blocks);
m_blocks[_hash].resize(d.size());
memcpy(m_blocks[_hash].data(), d.data(), d.size());
noteUsed(_hash);
return m_blocks[_hash];
}

25
libethereum/BlockChain.h

@ -64,7 +64,6 @@ struct BlockChainWarn: public LogChannel { static const char* name() { return "=
std::map<Address, Account> const& genesisState();
ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0);
ldb::Slice oldToSlice(h256 const& _h, unsigned _sub = 0);
using BlocksHash = std::map<h256, bytes>;
using TransactionHashes = h256s;
@ -274,30 +273,6 @@ private:
return ret.first->second;
}
template<class T, unsigned N> T oldQueryExtras(h256 const& _h, std::map<h256, T>& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const
{
{
ReadGuard l(_x);
auto it = _m.find(_h);
if (it != _m.end())
return it->second;
}
std::string s;
(_extrasDB ? _extrasDB : m_extrasDB)->Get(m_readOptions, oldToSlice(_h, N), &s);
if (s.empty())
{
// cout << "Not found in DB: " << _h << endl;
return _n;
}
noteUsed(_h, N);
WriteGuard l(_x);
auto ret = _m.insert(std::make_pair(_h, T(RLP(s))));
return ret.first->second;
}
void checkConsistency();
/// The caches of the disk DB and their locks.

8
libethereum/BlockQueue.cpp

@ -107,14 +107,6 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
}
}
namespace dev {
template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b)
{
for (auto const& i: _b)
_a.insert(i);
return _a;
} }
bool BlockQueue::doneDrain(h256s const& _bad)
{
WriteGuard l(m_lock);

6
libethereum/Client.cpp

@ -94,7 +94,7 @@ void BasicGasPricer::update(BlockChain const& _bc)
for (unsigned i = 0; i < r[1].size(); ++i)
{
auto gu = brs.receipts[i].gasUsed();
dist[Transaction(r[1][i].data(), CheckSignature::None).gasPrice()] += (unsigned)brs.receipts[i].gasUsed();
dist[Transaction(r[1][i].data(), CheckTransaction::None).gasPrice()] += (unsigned)brs.receipts[i].gasUsed();
total += (unsigned)gu;
}
}
@ -511,7 +511,7 @@ void Client::doWork()
clog(ClientNote) << "Dead block:" << h.abridged();
for (auto const& t: m_bc.transactions(h))
{
clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckSignature::None);
clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t);
}
}
@ -519,7 +519,7 @@ void Client::doWork()
// remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: fresh)
{
clog(ClientChat) << "Mined block:" << h.abridged();
clog(ClientChat) << "Live block:" << h.abridged();
for (auto const& th: m_bc.transactionHashes(h))
{
clog(ClientNote) << "Safely dropping transaction " << th;

25
libethereum/ClientBase.cpp

@ -285,11 +285,6 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId)
return ret;
}
h256 ClientBase::hashFromNumber(unsigned _number) const
{
return bc().numberHash(_number);
}
BlockInfo ClientBase::blockInfo(h256 _hash) const
{
return BlockInfo(bc().block(_hash));
@ -302,7 +297,7 @@ BlockDetails ClientBase::blockDetails(h256 _hash) const
Transaction ClientBase::transaction(h256 _transactionHash) const
{
return Transaction(bc().transaction(_transactionHash), CheckSignature::Range);
return Transaction(bc().transaction(_transactionHash), CheckTransaction::Cheap);
}
Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const
@ -310,7 +305,7 @@ 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(), CheckSignature::Range);
return Transaction(b[1][_i].data(), CheckTransaction::Cheap);
else
return Transaction();
}
@ -321,7 +316,7 @@ Transactions ClientBase::transactions(h256 _blockHash) const
RLP b(bl);
Transactions res;
for (unsigned i = 0; i < b[1].itemCount(); i++)
res.emplace_back(b[1][i].data(), CheckSignature::Range);
res.emplace_back(b[1][i].data(), CheckTransaction::Cheap);
return res;
}
@ -369,6 +364,11 @@ Transactions ClientBase::pending() const
return postMine().pending();
}
h256s ClientBase::pendingHashes() const
{
return h256s() + postMine().pendingHashes();
}
StateDiff ClientBase::diff(unsigned _txi, h256 _block) const
{
@ -399,3 +399,12 @@ 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);
}

5
libethereum/ClientBase.h

@ -63,7 +63,7 @@ struct WatchChannel: public LogChannel { static const char* name() { return "(o)
#define cwatch dev::LogOutputStream<dev::eth::WatchChannel, true>()
struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; };
struct WorkOutChannel: public LogChannel { static const char* name() { return "<W<"; } static const int verbosity = 16; };
struct WorkChannel: public LogChannel { static const char* name() { return "-W-"; } static const int verbosity = 16; };
struct WorkChannel: public LogChannel { static const char* name() { return "-W-"; } static const int verbosity = 21; };
#define cwork dev::LogOutputStream<dev::eth::WorkChannel, true>()
#define cworkin dev::LogOutputStream<dev::eth::WorkInChannel, true>()
#define cworkout dev::LogOutputStream<dev::eth::WorkOutChannel, true>()
@ -109,7 +109,7 @@ public:
virtual LocalisedLogEntries checkWatch(unsigned _watchId) override;
// TODO: switch all the _blockHash arguments to also accept _blockNumber
virtual h256 hashFromNumber(unsigned _number) const override;
virtual h256 hashFromNumber(BlockNumber _number) const override;
virtual eth::BlockInfo blockInfo(h256 _hash) const override;
virtual eth::BlockDetails blockDetails(h256 _hash) const override;
virtual eth::Transaction transaction(h256 _transactionHash) const override;
@ -122,6 +122,7 @@ public:
virtual unsigned uncleCount(h256 _blockHash) const override;
virtual unsigned number() const override;
virtual eth::Transactions pending() const override;
virtual h256s pendingHashes() const override;
using Interface::diff;
virtual StateDiff diff(unsigned _txi, h256 _block) const override;

5
libethereum/EthereumHost.cpp

@ -191,6 +191,8 @@ void EthereumHost::maintainTransactions()
for (auto const& p: randomSelection(25, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }))
peerTransactions[p].push_back(i.first);
}
for (auto const& t: ts)
m_transactionsSent.insert(t.first);
for (auto p: peerSessions())
if (auto ep = p.first->cap<EthereumPeer>())
{
@ -198,11 +200,10 @@ void EthereumHost::maintainTransactions()
unsigned n = 0;
for (auto const& h: peerTransactions[ep])
{
ep->m_knownTransactions.insert(h);
b += ts[h].rlp();
++n;
}
for (auto const& t: ts)
m_transactionsSent.insert(t.first);
ep->clearKnownTransactions();

4
libethereum/EthereumPeer.cpp

@ -334,7 +334,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case GetTransactionsPacket: break; // DEPRECATED.
case TransactionsPacket:
{
clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)";
clogS(NetAllDetail) << "Transactions (" << dec << _r.itemCount() << "entries)";
Guard l(x_knownTransactions);
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
@ -348,11 +348,11 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
host()->m_transactionsSent.insert(h);
addRating(0);
break;
case ImportResult::Success:
addRating(100);
host()->m_transactionsSent.insert(h);
break;
default:;
}

13
libethereum/Executive.cpp

@ -31,8 +31,6 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
#define ETH_VMTRACE 1
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
m_s(_s),
m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)),
@ -69,12 +67,11 @@ void Executive::initialize(Transaction const& _transaction)
}
// Check gas cost is enough.
m_gasRequired = Interface::txGas(m_t.data());
if (m_t.gas() < m_gasRequired)
if (!m_t.checkPayment())
{
clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_gasRequired << " Got" << m_t.gas();
clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas();
m_excepted = TransactionException::OutOfGas;
BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)m_gasRequired, (bigint)m_t.gas()));
BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_t.gasRequired(), (bigint)m_t.gas()));
}
// Avoid invalid transactions.
@ -119,9 +116,9 @@ bool Executive::execute()
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)m_gasRequired, &m_t.data(), m_t.sender());
return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.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)m_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_t.gasRequired(), m_t.sender());
}
bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)

2
libethereum/Executive.h

@ -71,7 +71,7 @@ public:
void operator=(Executive) = delete;
/// 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(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckTransaction::None)); }
void initialize(Transaction const& _transaction);
/// Finalise a transaction previously set up with initialize().
/// @warning Only valid after initialize() and execute(), and possibly go().

19
libethereum/Interface.h

@ -112,10 +112,11 @@ public:
// [BLOCK QUERY API]
virtual h256 hashFromNumber(unsigned _number) const = 0;
virtual Transaction transaction(h256 _transactionHash) const = 0;
virtual h256 hashFromNumber(BlockNumber _number) const = 0;
virtual BlockInfo blockInfo(h256 _hash) const = 0;
virtual BlockDetails blockDetails(h256 _hash) const = 0;
virtual Transaction transaction(h256 _transactionHash) const = 0;
virtual Transaction transaction(h256 _blockHash, unsigned _i) const = 0;
virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const = 0;
virtual UncleHashes uncleHashes(h256 _blockHash) const = 0;
@ -124,6 +125,16 @@ public:
virtual Transactions transactions(h256 _blockHash) const = 0;
virtual TransactionHashes transactionHashes(h256 _blockHash) const = 0;
BlockInfo blockInfo(BlockNumber _block) const { return blockInfo(hashFromNumber(_block)); }
BlockDetails blockDetails(BlockNumber _block) const { return blockDetails(hashFromNumber(_block)); }
Transaction transaction(BlockNumber _block, unsigned _i) const { if (_block == PendingBlock) { auto p = pending(); return _i < p.size() ? p[_i] : Transaction(); } return transaction(hashFromNumber(_block)); }
unsigned transactionCount(BlockNumber _block) const { if (_block == PendingBlock) { auto p = pending(); return p.size(); } return transactionCount(hashFromNumber(_block)); }
Transactions transactions(BlockNumber _block) const { if (_block == PendingBlock) return pending(); return transactions(hashFromNumber(_block)); }
TransactionHashes transactionHashes(BlockNumber _block) const { if (_block == PendingBlock) return pendingHashes(); return transactionHashes(hashFromNumber(_block)); }
BlockInfo uncle(BlockNumber _block, unsigned _i) const { return uncle(hashFromNumber(_block), _i); }
UncleHashes uncleHashes(BlockNumber _block) const { return uncleHashes(hashFromNumber(_block)); }
unsigned uncleCount(BlockNumber _block) const { return uncleCount(hashFromNumber(_block)); }
// [EXTRA API]:
/// @returns The height of the chain.
@ -132,6 +143,7 @@ public:
/// Get a map containing each of the pending transactions.
/// @TODO: Remove in favour of transactions().
virtual Transactions pending() const = 0;
virtual h256s pendingHashes() const = 0;
/// Differences between transactions.
StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); }
@ -143,9 +155,6 @@ public:
virtual Addresses addresses() const { return addresses(m_default); }
virtual Addresses addresses(BlockNumber _block) const = 0;
/// Get the fee associated for a transaction with the given data.
template <class T> static bigint txGas(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; }
/// Get the remaining gas limit in this block.
virtual u256 gasLimitRemaining() const = 0;

2
libethereum/State.cpp

@ -574,7 +574,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
k << i;
transactionsTrie.insert(&k.out(), tr.data());
execute(lh, Transaction(tr.data(), CheckSignature::Sender));
execute(lh, Transaction(tr.data(), CheckTransaction::Everything));
RLPStream receiptrlp;
m_receipts.back().streamRLP(receiptrlp);

3
libethereum/State.h

@ -274,6 +274,9 @@ public:
/// Get the list of pending transactions.
Transactions const& pending() const { return m_transactions; }
/// Get the list of hashes of pending transactions.
h256Set const& pendingHashes() const { return m_transactionSet; }
/// Get the transaction receipt for the transaction of the given index.
TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; }

16
libethereum/Transaction.cpp

@ -25,6 +25,7 @@
#include <libdevcrypto/Common.h>
#include <libethcore/Exceptions.h>
#include <libevm/VMFace.h>
#include "Interface.h"
#include "Transaction.h"
using namespace std;
using namespace dev;
@ -53,7 +54,7 @@ TransactionException dev::eth::toTransactionException(VMException const& _e)
return TransactionException::Unknown;
}
Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
{
int field = 0;
RLP rlp(_rlpData);
@ -81,9 +82,9 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("to many fields in the transaction RLP"));
m_vrs = SignatureStruct{ r, s, v };
if (_checkSig >= CheckSignature::Range && !m_vrs.isValid())
if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid())
BOOST_THROW_EXCEPTION(InvalidSignature());
if (_checkSig == CheckSignature::Sender)
if (_checkSig == CheckTransaction::Everything)
m_sender = sender();
}
catch (Exception& _e)
@ -91,6 +92,8 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
_e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes()));
throw;
}
if (_checkSig >= CheckTransaction::Cheap && !checkPayment())
BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(gasRequired(), (bigint)gas()));
}
Address const& Transaction::safeSender() const noexcept
@ -118,6 +121,13 @@ Address const& Transaction::sender() const
return m_sender;
}
bigint Transaction::gasRequired() const
{
if (!m_gasRequired)
m_gasRequired = Transaction::gasRequired(m_data);
return m_gasRequired;
}
void Transaction::sign(Secret _priv)
{
auto sig = dev::sign(_priv, sha3(WithoutSignature));

48
libethereum/Transaction.h

@ -24,7 +24,7 @@
#include <libdevcore/RLP.h>
#include <libdevcrypto/SHA3.h>
#include <libethcore/Common.h>
#include <libethcore/Params.h>
namespace dev
{
namespace eth
@ -37,11 +37,11 @@ enum IncludeSignature
WithSignature = 1, ///< Do include a signature.
};
enum class CheckSignature
enum class CheckTransaction
{
None,
Range,
Sender
Cheap,
Everything
};
enum class TransactionException
@ -119,10 +119,10 @@ public:
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs a transaction from the given RLP.
explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig);
explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig);
/// Constructs a transaction from the given RLP.
explicit Transaction(bytes const& _rlp, CheckSignature _checkSig): Transaction(&_rlp, _checkSig) {}
explicit Transaction(bytes const& _rlp, CheckTransaction _checkSig): Transaction(&_rlp, _checkSig) {}
/// Checks equality of transactions.
@ -178,27 +178,37 @@ public:
/// @returns the signature of the transaction. Encodes the sender.
SignatureStruct const& signature() const { return m_vrs; }
/// @returns true if the transaction contains enough gas for the basic payment.
bool checkPayment() const { return m_gas >= gasRequired(); }
/// @returns the gas required to run this transaction.
bigint gasRequired() const;
/// Get the fee associated for a transaction with the given data.
template <class T> static bigint gasRequired(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; }
private:
/// Type of transaction.
enum Type
{
NullTransaction, ///< Null transaction.
ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored.
MessageCall ///< Transaction to invoke a message call - receiveAddress() is used.
NullTransaction, ///< Null transaction.
ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored.
MessageCall ///< Transaction to invoke a message call - receiveAddress() is used.
};
void sign(Secret _priv); ///< Sign the transaction.
void sign(Secret _priv); ///< Sign the transaction.
Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction?
u256 m_nonce; ///< The transaction-count of the sender.
u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address m_receiveAddress; ///< The receiving address of the transaction.
u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender.
Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction?
u256 m_nonce; ///< The transaction-count of the sender.
u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address m_receiveAddress; ///< The receiving address of the transaction.
u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction.
SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender.
mutable Address m_sender; ///< Cached sender, determined from signature.
mutable Address m_sender; ///< Cached sender, determined from signature.
mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run.
};
/// Nice name for vector of Transaction.

8
libethereum/TransactionQueue.cpp

@ -44,21 +44,23 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP)
// Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender.
// If it doesn't work, the signature is bad.
// The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction).
Transaction t(_transactionRLP, CheckSignature::Sender);
Transaction t(_transactionRLP, CheckTransaction::Everything);
UpgradeGuard ul(l);
// If valid, append to blocks.
m_current[h] = t;
m_known.insert(h);
ctxq << "Queued vaguely legit-looking transaction" << h.abridged();
}
catch (Exception const& _e)
{
cwarn << "Ignoring invalid transaction: " << diagnostic_information(_e);
ctxq << "Ignoring invalid transaction: " << diagnostic_information(_e);
return ImportResult::Malformed;
}
catch (std::exception const& _e)
{
cwarn << "Ignoring invalid transaction: " << _e.what();
ctxq << "Ignoring invalid transaction: " << _e.what();
return ImportResult::Malformed;
}

3
libethereum/TransactionQueue.h

@ -24,6 +24,7 @@
#include <boost/thread.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
#include "libethcore/Common.h"
#include "Transaction.h"
@ -34,6 +35,8 @@ namespace eth
class BlockChain;
struct TransactionQueueChannel: public LogChannel { static const char* name() { return "->Q"; } static const int verbosity = 4; };
#define ctxq dev::LogOutputStream<dev::eth::TransactionQueueChannel, true>()
/**
* @brief A queue of Transactions, each stored as RLP.

2
libevm/VM.cpp

@ -108,7 +108,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
runGas = c_sstoreSetGas;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
{
runGas = c_sstoreClearGas;
runGas = c_sstoreResetGas;
_ext.sub.refunds += c_sstoreRefundGas;
}
else

1
libevmcore/CMakeLists.txt

@ -25,6 +25,7 @@ else()
endif()
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} devcrypto)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

140
libevmcore/CommonSubexpressionEliminator.cpp

@ -23,6 +23,7 @@
#include <functional>
#include <boost/range/adaptor/reversed.hpp>
#include <libdevcrypto/SHA3.h>
#include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/AssemblyItem.h>
@ -34,8 +35,8 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{
optimizeBreakingItem();
map<int, ExpressionClasses::Id> initialStackContents;
map<int, ExpressionClasses::Id> targetStackContents;
map<int, Id> initialStackContents;
map<int, Id> targetStackContents;
int minHeight = m_stackHeight + 1;
if (!m_stackElements.empty())
minHeight = min(minHeight, m_stackElements.begin()->first);
@ -58,18 +59,18 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
ostream& CommonSubexpressionEliminator::stream(
ostream& _out,
map<int, ExpressionClasses::Id> _initialStack,
map<int, ExpressionClasses::Id> _targetStack
map<int, Id> _initialStack,
map<int, Id> _targetStack
) const
{
auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id)
auto streamExpressionClass = [this](ostream& _out, Id _id)
{
auto const& expr = m_expressionClasses.representative(_id);
_out << " " << dec << _id << ": " << *expr.item;
if (expr.sequenceNumber)
_out << "@" << dec << expr.sequenceNumber;
_out << "(";
for (ExpressionClasses::Id arg: expr.arguments)
for (Id arg: expr.arguments)
_out << dec << arg << ",";
_out << ")" << endl;
};
@ -77,7 +78,7 @@ ostream& CommonSubexpressionEliminator::stream(
_out << "Optimizer analysis:" << endl;
_out << "Final stack height: " << dec << m_stackHeight << endl;
_out << "Equivalence classes: " << endl;
for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
for (Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
streamExpressionClass(_out, eqClass);
_out << "Initial stack: " << endl;
@ -119,7 +120,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
);
else if (instruction != Instruction::POP)
{
vector<ExpressionClasses::Id> arguments(info.args);
vector<Id> arguments(info.args);
for (int i = 0; i < info.args; ++i)
arguments[i] = stackElement(m_stackHeight - i);
if (_item.instruction() == Instruction::SSTORE)
@ -130,6 +131,8 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
storeInMemory(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::MLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0]));
else if (_item.instruction() == Instruction::SHA3)
setStackElement(m_stackHeight + _item.deposit(), applySha3(arguments.at(0), arguments.at(1)));
else
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem));
}
@ -142,7 +145,6 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI))
return;
using Id = ExpressionClasses::Id;
static AssemblyItem s_jump = Instruction::JUMP;
Id condition = stackElement(m_stackHeight - 1);
@ -163,7 +165,7 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
}
}
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class)
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Id _class)
{
m_stackElements[_stackHeight] = _class;
}
@ -194,26 +196,28 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight)));
}
void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
void CommonSubexpressionEliminator::storeInStorage(Id _slot, Id _value)
{
if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value)
// do not execute the storage if we know that the value is already there
return;
m_sequenceNumber++;
decltype(m_storageContent) storageContents;
// copy over values at points where we know that they are different from _slot
// Copy over all values (i.e. retain knowledge about them) where we know that this store
// operation will not destroy the knowledge. Specifically, we copy storage locations we know
// are different from _slot or locations where we know that the stored value is equal to _value.
for (auto const& storageItem: m_storageContent)
if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot))
if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value)
storageContents.insert(storageItem);
m_storageContent = move(storageContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber);
Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id));
m_storageContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot)
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(Id _slot)
{
if (m_storageContent.count(_slot))
return m_storageContent.at(_slot);
@ -221,7 +225,7 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionC
return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber);
}
void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
void CommonSubexpressionEliminator::storeInMemory(Id _slot, Id _value)
{
if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value)
// do not execute the store if we know that the value is already there
@ -233,14 +237,14 @@ void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, E
if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot))
memoryContents.insert(memoryItem);
m_memoryContent = move(memoryContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber);
Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
m_memoryContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot)
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(Id _slot)
{
if (m_memoryContent.count(_slot))
return m_memoryContent.at(_slot);
@ -248,6 +252,37 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionCl
return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber);
}
CommonSubexpressionEliminator::Id CommonSubexpressionEliminator::applySha3(Id _start, Id _length)
{
// Special logic if length is a short constant, otherwise we cannot tell.
u256 const* l = m_expressionClasses.knownConstant(_length);
// unknown or too large length
if (!l || *l > 128)
return m_expressionClasses.find(Instruction::SHA3, {_start, _length}, true, m_sequenceNumber);
vector<Id> arguments;
for (u256 i = 0; i < *l; i += 32)
{
Id slot = m_expressionClasses.find(Instruction::ADD, {_start, m_expressionClasses.find(i)});
arguments.push_back(loadFromMemory(slot));
}
if (m_knownSha3Hashes.count(arguments))
return m_knownSha3Hashes.at(arguments);
Id v;
// If all arguments are known constants, compute the sha3 here
if (all_of(arguments.begin(), arguments.end(), [this](Id _a) { return !!m_expressionClasses.knownConstant(_a); }))
{
bytes data;
for (Id a: arguments)
data += toBigEndian(*m_expressionClasses.knownConstant(a));
data.resize(size_t(*l));
v = m_expressionClasses.find(u256(sha3(data)));
}
else
v = m_expressionClasses.find(Instruction::SHA3, {_start, _length}, true, m_sequenceNumber);
return m_knownSha3Hashes[arguments] = v;
}
CSECodeGenerator::CSECodeGenerator(
ExpressionClasses& _expressionClasses,
vector<CSECodeGenerator::StoreOperation> const& _storeOperations
@ -259,8 +294,8 @@ CSECodeGenerator::CSECodeGenerator(
}
AssemblyItems CSECodeGenerator::generateCode(
map<int, ExpressionClasses::Id> const& _initialStack,
map<int, ExpressionClasses::Id> const& _targetStackContents
map<int, Id> const& _initialStack,
map<int, Id> const& _targetStackContents
)
{
m_stack = _initialStack;
@ -280,7 +315,7 @@ AssemblyItems CSECodeGenerator::generateCode(
}
// store all needed sequenced expressions
set<pair<unsigned, ExpressionClasses::Id>> sequencedExpressions;
set<pair<unsigned, Id>> sequencedExpressions;
for (auto const& p: m_neededBy)
for (auto id: {p.first, p.second})
if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
@ -327,19 +362,20 @@ AssemblyItems CSECodeGenerator::generateCode(
return m_generatedItems;
}
void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
void CSECodeGenerator::addDependencies(Id _c)
{
if (m_neededBy.count(_c))
return; // we already computed the dependencies for _c
ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
for (ExpressionClasses::Id argument: expr.arguments)
for (Id argument: expr.arguments)
{
addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c));
}
if (expr.item->type() == Operation && (
expr.item->instruction() == Instruction::SLOAD ||
expr.item->instruction() == Instruction::MLOAD
expr.item->instruction() == Instruction::MLOAD ||
expr.item->instruction() == Instruction::SHA3
))
{
// this loads an unknown value from storage or memory and thus, in addition to its
@ -347,22 +383,52 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
// they are different that occur before this load
StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ?
StoreOperation::Storage : StoreOperation::Memory;
ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0);
Id slotToLoadFrom = expr.arguments.at(0);
for (auto const& p: m_storeOperations)
{
if (p.first.first != target)
continue;
ExpressionClasses::Id slot = p.first.second;
Id slot = p.first.second;
StoreOperations const& storeOps = p.second;
if (storeOps.front().sequenceNumber > expr.sequenceNumber)
continue;
if (
(target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) ||
(target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom))
)
bool knownToBeIndependent = false;
switch (expr.item->instruction())
{
case Instruction::SLOAD:
knownToBeIndependent = m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom);
break;
case Instruction::MLOAD:
knownToBeIndependent = m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom);
break;
case Instruction::SHA3:
{
Id length = expr.arguments.at(1);
Id offsetToStart = m_expressionClasses.find(Instruction::SUB, {slot, slotToLoadFrom});
u256 const* o = m_expressionClasses.knownConstant(offsetToStart);
u256 const* l = m_expressionClasses.knownConstant(length);
if (l && *l == 0)
knownToBeIndependent = true;
else if (o)
{
// We could get problems here if both *o and *l are larger than 2**254
// but it is probably ok for the optimizer to produce wrong code for such cases
// which cannot be executed anyway because of the non-payable price.
if (u2s(*o) <= -32)
knownToBeIndependent = true;
else if (l && u2s(*o) >= 0 && *o >= *l)
knownToBeIndependent = true;
}
break;
}
default:
break;
}
if (knownToBeIndependent)
continue;
// note that store and load never have the same sequence number
ExpressionClasses::Id latestStore = storeOps.front().expression;
Id latestStore = storeOps.front().expression;
for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it)
if (it->sequenceNumber < expr.sequenceNumber)
latestStore = it->expression;
@ -372,7 +438,7 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
}
}
int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced)
int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
{
// do some cleanup
removeStackTopIfPossible();
@ -392,8 +458,8 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow
OptimizerException,
"Sequence constrained operation requested out of sequence."
);
ExpressionClasses::Ids const& arguments = expr.arguments;
for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments))
vector<Id> const& arguments = expr.arguments;
for (Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg);
// The arguments are somewhere on the stack now, so it remains to move them at the correct place.
@ -478,7 +544,7 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow
}
}
int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
int CSECodeGenerator::classElementPosition(Id _id) const
{
assertThrow(
m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition,
@ -488,7 +554,7 @@ int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
return m_classPositions.at(_id);
}
bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result)
bool CSECodeGenerator::canBeRemoved(Id _element, Id _result)
{
// Returns false if _element is finally needed or is needed by a class that has not been
// computed yet. Note that m_classPositions also includes classes that were deleted in the meantime.
@ -507,7 +573,7 @@ bool CSECodeGenerator::removeStackTopIfPossible()
if (m_stack.empty())
return false;
assertThrow(m_stack.count(m_stackHeight) > 0, OptimizerException, "");
ExpressionClasses::Id top = m_stack[m_stackHeight];
Id top = m_stack[m_stackHeight];
if (!canBeRemoved(top))
return false;
m_generatedItems.push_back(AssemblyItem(Instruction::POP));

61
libevmcore/CommonSubexpressionEliminator.h

@ -57,19 +57,20 @@ using AssemblyItems = std::vector<AssemblyItem>;
class CommonSubexpressionEliminator
{
public:
using Id = ExpressionClasses::Id;
struct StoreOperation
{
enum Target { Memory, Storage };
StoreOperation(
Target _target,
ExpressionClasses::Id _slot,
Id _slot,
unsigned _sequenceNumber,
ExpressionClasses::Id _expression
Id _expression
): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {}
Target target;
ExpressionClasses::Id slot;
Id slot;
unsigned sequenceNumber;
ExpressionClasses::Id expression;
Id expression;
};
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
@ -83,8 +84,8 @@ public:
/// Streams debugging information to @a _out.
std::ostream& stream(
std::ostream& _out,
std::map<int, ExpressionClasses::Id> _initialStack = std::map<int, ExpressionClasses::Id>(),
std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>()
std::map<int, Id> _initialStack = std::map<int, Id>(),
std::map<int, Id> _targetStack = std::map<int, Id>()
) const;
private:
@ -96,39 +97,42 @@ private:
/// Simplifies the given item using
/// Assigns a new equivalence class to the next sequence number of the given stack element.
void setStackElement(int _stackHeight, ExpressionClasses::Id _class);
void setStackElement(int _stackHeight, Id _class);
/// Swaps the given stack elements in their next sequence number.
void swapStackElements(int _stackHeightA, int _stackHeightB);
/// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet).
ExpressionClasses::Id stackElement(int _stackHeight);
Id stackElement(int _stackHeight);
/// @returns the equivalence class id of the special initial stack element at the given height
/// (must not be positive).
ExpressionClasses::Id initialStackElement(int _stackHeight);
Id initialStackElement(int _stackHeight);
/// Increments the sequence number, deletes all storage information that might be overwritten
/// and stores the new value at the given slot.
void storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
void storeInStorage(Id _slot, Id _value);
/// Retrieves the current value at the given slot in storage or creates a new special sload class.
ExpressionClasses::Id loadFromStorage(ExpressionClasses::Id _slot);
Id loadFromStorage(Id _slot);
/// Increments the sequence number, deletes all memory information that might be overwritten
/// and stores the new value at the given slot.
void storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
void storeInMemory(Id _slot, Id _value);
/// Retrieves the current value at the given slot in memory or creates a new special mload class.
ExpressionClasses::Id loadFromMemory(ExpressionClasses::Id _slot);
Id loadFromMemory(Id _slot);
/// Finds or creates a new expression that applies the sha3 hash function to the contents in memory.
Id applySha3(Id _start, Id _length);
/// Current stack height, can be negative.
int m_stackHeight = 0;
/// Current stack layout, mapping stack height -> equivalence class
std::map<int, ExpressionClasses::Id> m_stackElements;
std::map<int, Id> m_stackElements;
/// Current sequence number, this is incremented with each modification to storage or memory.
unsigned m_sequenceNumber = 1;
/// Knowledge about storage content.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_storageContent;
std::map<Id, Id> m_storageContent;
/// Knowledge about memory content. Keys are memory addresses, note that the values overlap
/// and are not contained here if they are not completely known.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_memoryContent;
std::map<Id, Id> m_memoryContent;
/// Keeps record of all sha3 hashes that are computed.
std::map<std::vector<Id>, Id> m_knownSha3Hashes;
/// Keeps information about which storage or memory slots were written to at which sequence
/// number with what instruction.
std::vector<StoreOperation> m_storeOperations;
@ -149,6 +153,7 @@ class CSECodeGenerator
public:
using StoreOperation = CommonSubexpressionEliminator::StoreOperation;
using StoreOperations = std::vector<StoreOperation>;
using Id = ExpressionClasses::Id;
/// Initializes the code generator with the given classes and store operations.
/// The store operations have to be sorted by sequence number in ascending order.
@ -159,25 +164,25 @@ public:
/// @param _targetStackContents final contents of the stack, by stack height relative to initial
/// @note should only be called once on each object.
AssemblyItems generateCode(
std::map<int, ExpressionClasses::Id> const& _initialStack,
std::map<int, ExpressionClasses::Id> const& _targetStackContents
std::map<int, Id> const& _initialStack,
std::map<int, Id> const& _targetStackContents
);
private:
/// Recursively discovers all dependencies to @a m_requests.
void addDependencies(ExpressionClasses::Id _c);
void addDependencies(Id _c);
/// Produce code that generates the given element if it is not yet present.
/// @returns the stack position of the element or c_invalidPosition if it does not actually
/// generate a value on the stack.
/// @param _allowSequenced indicates that sequence-constrained operations are allowed
int generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced = false);
int generateClassElement(Id _c, bool _allowSequenced = false);
/// @returns the position of the representative of the given id on the stack.
/// @note throws an exception if it is not on the stack.
int classElementPosition(ExpressionClasses::Id _id) const;
int classElementPosition(Id _id) const;
/// @returns true if @a _element can be removed - in general or, if given, while computing @a _result.
bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1));
bool canBeRemoved(Id _element, Id _result = Id(-1));
/// Appends code to remove the topmost stack element if it can be removed.
bool removeStackTopIfPossible();
@ -196,19 +201,19 @@ private:
/// Current height of the stack relative to the start.
int m_stackHeight = 0;
/// If (b, a) is in m_requests then b is needed to compute a.
std::multimap<ExpressionClasses::Id, ExpressionClasses::Id> m_neededBy;
std::multimap<Id, Id> m_neededBy;
/// Current content of the stack.
std::map<int, ExpressionClasses::Id> m_stack;
std::map<int, Id> m_stack;
/// Current positions of equivalence classes, equal to c_invalidPosition if already deleted.
std::map<ExpressionClasses::Id, int> m_classPositions;
std::map<Id, int> m_classPositions;
/// The actual eqivalence class items and how to compute them.
ExpressionClasses& m_expressionClasses;
/// Keeps information about which storage or memory slots were written to by which operations.
/// The operations are sorted ascendingly by sequence number.
std::map<std::pair<StoreOperation::Target, ExpressionClasses::Id>, StoreOperations> m_storeOperations;
std::map<std::pair<StoreOperation::Target, Id>, StoreOperations> m_storeOperations;
/// The set of equivalence classes that should be present on the stack at the end.
std::set<ExpressionClasses::Id> m_finalClasses;
std::set<Id> m_finalClasses;
};
template <class _AssemblyItemIterator>

31
libevmcore/ExpressionClasses.cpp

@ -84,24 +84,35 @@ ExpressionClasses::Id ExpressionClasses::find(
bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is a non-zero constant.
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
return constant.matches(representative(difference), *this) && constant.d() != u256(0);
return knownNonZero(find(Instruction::SUB, {_a, _b}));
}
bool ExpressionClasses::knownToBeDifferentBy32(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is at least 32 away from zero.
u256 const* v = knownConstant(find(Instruction::SUB, {_a, _b}));
// forbidden interval is ["-31", 31]
return v && *v + 31 > u256(62);
}
bool ExpressionClasses::knownZero(Id _c)
{
return Pattern(u256(0)).matches(representative(_c), *this);
}
bool ExpressionClasses::knownNonZero(Id _c)
{
return Pattern(u256(0)).matches(representative(find(Instruction::ISZERO, {_c})), *this);
}
u256 const* ExpressionClasses::knownConstant(Id _c)
{
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
if (!constant.matches(representative(difference), *this))
return false;
// forbidden interval is ["-31", 31]
return constant.d() + 31 > u256(62);
if (!constant.matches(representative(_c), *this))
return nullptr;
return &constant.d();
}
string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const

11
libevmcore/ExpressionClasses.h

@ -78,6 +78,15 @@ public:
bool knownToBeDifferent(Id _a, Id _b);
/// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32.
bool knownToBeDifferentBy32(Id _a, Id _b);
/// @returns true if the value of the given class is known to be zero.
/// @note that this is not the negation of knownNonZero
bool knownZero(Id _c);
/// @returns true if the value of the given class is known to be nonzero.
/// @note that this is not the negation of knownZero
bool knownNonZero(Id _c);
/// @returns a pointer to the value if the given class is known to be a constant,
/// and a nullptr otherwise.
u256 const* knownConstant(Id _c);
std::string fullDAGToString(Id _id) const;
@ -131,7 +140,7 @@ public:
/// @returns the id of the matched expression if this pattern is part of a match group.
Id id() const { return matchGroupValue().id; }
/// @returns the data of the matched expression if this pattern is part of a match group.
u256 d() const { return matchGroupValue().item->data(); }
u256 const& d() const { return matchGroupValue().item->data(); }
std::string toString() const;

42
libevmcore/Instruction.cpp

@ -21,6 +21,7 @@
#include "Instruction.h"
#include <functional>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h>
@ -294,27 +295,42 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, ZeroTier } }
};
string dev::eth::disassemble(bytes const& _mem)
void dev::eth::eachInstruction(
bytes const& _mem,
function<void(Instruction,u256 const&)> const& _onInstruction
)
{
stringstream ret;
unsigned numerics = 0;
for (auto it = _mem.begin(); it != _mem.end(); ++it)
{
byte n = *it;
auto iit = c_instructionInfo.find((Instruction)n);
if (numerics || iit == c_instructionInfo.end() || (byte)iit->first != n) // not an instruction or expecting an argument...
Instruction instr = Instruction(*it);
size_t additional = 0;
if (isValidInstruction(instr))
additional = instructionInfo(instr).additional;
u256 data;
for (size_t i = 0; i < additional; ++i)
{
if (numerics)
numerics--;
ret << "0x" << hex << (int)n << " ";
data <<= 8;
if (it != _mem.end() && ++it != _mem.end())
data |= *it;
}
_onInstruction(instr, data);
}
}
string dev::eth::disassemble(bytes const& _mem)
{
stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
if (!isValidInstruction(_instr))
ret << "0x" << hex << int(_instr) << " ";
else
{
auto const& ii = iit->second;
ret << ii.name << " ";
numerics = ii.additional;
InstructionInfo info = instructionInfo(_instr);
ret << info.name << " ";
if (info.additional)
ret << "0x" << hex << _data << " ";
}
}
});
return ret.str();
}

4
libevmcore/Instruction.h

@ -21,6 +21,7 @@
#pragma once
#include <functional>
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libevmcore/Exceptions.h>
@ -253,6 +254,9 @@ bool isValidInstruction(Instruction _inst);
/// Convert from string mnemonic to Instruction type.
extern const std::map<std::string, Instruction> c_instructions;
/// Iterate through EVM code and call a function on each instruction.
void eachInstruction(bytes const& _mem, std::function<void(Instruction,u256 const&)> const& _onInstruction);
/// Convert from EVM code to simple EVM assembly language.
std::string disassemble(bytes const& _mem);

2
libevmcore/SemanticInformation.cpp

@ -52,8 +52,6 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
return true; // GAS and PC assume a specific order of opcodes
if (_item.instruction() == Instruction::MSIZE)
return true; // msize is modified already by memory access, avoid that for now
if (_item.instruction() == Instruction::SHA3)
return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content.
InstructionInfo info = instructionInfo(_item.instruction());
if (_item.instruction() == Instruction::SSTORE)
return false;

2
libp2p/Session.cpp

@ -245,7 +245,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
break;
default:
for (auto const& i: m_capabilities)
if (_t >= i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount())
if (_t >= (int)i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount())
{
if (i.second->m_enabled)
return i.second->interpret(_t - i.second->m_idOffset, _r);

1
libsolidity/CMakeLists.txt

@ -27,7 +27,6 @@ endif()
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} devcrypto)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )

36
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -170,18 +170,6 @@ static Json::Value toJson(map<u256, u256> const& _storage)
return res;
}
static unsigned toBlockNumber(std::string const& _js)
{
if (_js == "latest")
return LatestBlock;
else if (_js == "earliest")
return 0;
else if (_js == "pending")
return PendingBlock;
else
return (unsigned)jsToInt(_js);
}
static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7.
{
dev::eth::LogFilter filter;
@ -190,9 +178,9 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to
// check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty())
filter.withEarliest(toBlockNumber(_json["fromBlock"].asString()));
filter.withEarliest(jsToBlockNumber(_json["fromBlock"].asString()));
if (!_json["toBlock"].empty())
filter.withLatest(toBlockNumber(_json["toBlock"].asString()));
filter.withLatest(jsToBlockNumber(_json["toBlock"].asString()));
if (!_json["address"].empty())
{
if (_json["address"].isArray())
@ -336,7 +324,7 @@ string WebThreeStubServerBase::eth_getBalance(string const& _address, string con
{
try
{
return toJS(client()->balanceAt(jsToAddress(_address), toBlockNumber(_blockNumber)));
return toJS(client()->balanceAt(jsToAddress(_address), jsToBlockNumber(_blockNumber)));
}
catch (...)
{
@ -348,7 +336,7 @@ string WebThreeStubServerBase::eth_getStorageAt(string const& _address, string c
{
try
{
return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_position), toBlockNumber(_blockNumber)));
return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_position), jsToBlockNumber(_blockNumber)));
}
catch (...)
{
@ -360,7 +348,7 @@ string WebThreeStubServerBase::eth_getTransactionCount(string const& _address, s
{
try
{
return toJS(client()->countAt(jsToAddress(_address), toBlockNumber(_blockNumber)));
return toJS(client()->countAt(jsToAddress(_address), jsToBlockNumber(_blockNumber)));
}
catch (...)
{
@ -385,7 +373,7 @@ string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const
{
try
{
return toJS(_blockNumber == "pending" ? client()->pending().size() : client()->transactionCount(client()->hashFromNumber(toBlockNumber(_blockNumber))));
return toJS(client()->transactionCount(jsToBlockNumber(_blockNumber)));
}
catch (...)
{
@ -409,7 +397,7 @@ string WebThreeStubServerBase::eth_getUncleCountByBlockNumber(string const& _blo
{
try
{
return toJS(client()->uncleCount(client()->hashFromNumber(toBlockNumber(_blockNumber))));
return toJS(client()->uncleCount(jsToBlockNumber(_blockNumber)));
}
catch (...)
{
@ -421,7 +409,7 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const&
{
try
{
return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)));
return toJS(client()->codeAt(jsToAddress(_address), jsToBlockNumber(_blockNumber)));
}
catch (...)
{
@ -499,7 +487,7 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const&
try
{
t = toTransaction(_json);
number = toBlockNumber(_blockNumber);
number = jsToBlockNumber(_blockNumber);
}
catch (...)
{
@ -546,7 +534,7 @@ Json::Value WebThreeStubServerBase::eth_getBlockByNumber(string const& _blockNum
{
try
{
auto h = client()->hashFromNumber(jsToInt(_blockNumber));
auto h = jsToBlockNumber(_blockNumber);
if (_includeTransactions)
return toJson(client()->blockInfo(h), client()->uncleHashes(h), client()->transactions(h));
else
@ -586,7 +574,7 @@ Json::Value WebThreeStubServerBase::eth_getTransactionByBlockNumberAndIndex(stri
{
try
{
return toJson(client()->transaction(client()->hashFromNumber(jsToInt(_blockNumber)), jsToInt(_transactionIndex)));
return toJson(client()->transaction(jsToBlockNumber(_blockNumber), jsToInt(_transactionIndex)));
}
catch (...)
{
@ -610,7 +598,7 @@ Json::Value WebThreeStubServerBase::eth_getUncleByBlockNumberAndIndex(string con
{
try
{
return toJson(client()->uncle(client()->hashFromNumber(toBlockNumber(_blockNumber)), jsToInt(_uncleIndex)));
return toJson(client()->uncle(jsToBlockNumber(_blockNumber), jsToInt(_uncleIndex)));
}
catch (...)
{

31
mix/ClientModel.cpp

@ -154,6 +154,14 @@ QVariantMap ClientModel::contractAddresses() const
return res;
}
QVariantMap ClientModel::gasCosts() const
{
QVariantMap res;
for (auto const& c: m_gasCosts)
res.insert(c.first, QVariant::fromValue(static_cast<int>(c.second)));
return res;
}
void ClientModel::setupState(QVariantMap _state)
{
QVariantList balances = _state.value("accounts").toList();
@ -173,6 +181,7 @@ void ClientModel::setupState(QVariantMap _state)
QString contractId = transaction.value("contractId").toString();
QString functionId = transaction.value("functionId").toString();
u256 gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue());
bool gasAuto = transaction.value("gasAuto").toBool();
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QString sender = transaction.value("sender").toString();
@ -183,7 +192,7 @@ void ClientModel::setupState(QVariantMap _state)
contractId = functionId;
TransactionSettings transactionSettings(contractId, transaction.value("url").toString());
transactionSettings.gasPrice = 10000000000000;
transactionSettings.gas = 125000;
transactionSettings.gasAuto = true;
transactionSettings.value = 0;
transactionSettings.sender = Secret(sender.toStdString());
transactionSequence.push_back(transactionSettings);
@ -192,7 +201,7 @@ void ClientModel::setupState(QVariantMap _state)
{
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_codeModel->contracts().keys()[0];
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasPrice, Secret(sender.toStdString()));
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()));
transactionSettings.parameterValues = transaction.value("parameters").toMap();
if (contractId == functionId || functionId == "Constructor")
@ -228,7 +237,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
//std contract
bytes const& stdContractCode = m_codeModel->getStdContractCode(transaction.contractId, transaction.stdContractUrl);
TransactionSettings stdTransaction = transaction;
stdTransaction.gas = 500000;// TODO: get this from std contracts library
stdTransaction.gasAuto = true;
Address address = deployContract(stdContractCode, stdTransaction);
m_stdContractAddresses[stdTransaction.contractId] = address;
m_stdContractNames[address] = stdTransaction.contractId;
@ -277,6 +286,8 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
m_contractNames[newAddress] = transaction.contractId;
contractAddressesChanged();
}
gasCostsChanged();
m_gasCosts[transaction.contractId] = m_client->lastExecution().gasUsed;
}
else
{
@ -507,13 +518,13 @@ void ClientModel::debugRecord(unsigned _index)
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
{
Address newAddress = m_client->submitTransaction(_ctrTransaction.sender, _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice);
Address newAddress = m_client->submitTransaction(_ctrTransaction.sender, _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice, _ctrTransaction.gasAuto);
return newAddress;
}
void ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{
m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice);
m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice, _tr.gasAuto);
}
RecordLogEntry* ClientModel::lastBlock() const
@ -523,7 +534,7 @@ RecordLogEntry* ClientModel::lastBlock() const
strGas << blockInfo.gasUsed;
stringstream strNumber;
strNumber << blockInfo.number;
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash().ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block);
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()));
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
return record;
}
@ -544,13 +555,17 @@ void ClientModel::onNewTransaction()
unsigned recordIndex = tr.executonIndex;
QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex);
QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(toString(tr.value));
QString value = QString::fromStdString(toString(tr.value));
QString contract = address;
QString function;
QString returned;
QString gasUsed;
bool creation = (bool)tr.contractAddress;
if (!tr.isCall())
gasUsed = QString::fromStdString(toString(tr.gasUsed));
//TODO: handle value transfer
FixedHash<4> functionHash;
bool abi = false;
@ -605,7 +620,7 @@ void ClientModel::onNewTransaction()
}
}
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction);
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, gasUsed);
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newRecord(log);
}

21
mix/ClientModel.h

@ -48,10 +48,10 @@ struct SolidityType;
struct TransactionSettings
{
TransactionSettings() {}
TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice, Secret _sender):
contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice), sender(_sender) {}
TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender):
contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender) {}
TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl):
contractId(_stdContractName), stdContractUrl(_stdContractUrl) {}
contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl) {}
/// Contract name
QString contractId;
@ -61,6 +61,8 @@ struct TransactionSettings
u256 value;
/// Gas
u256 gas;
/// Calculate gas automatically
bool gasAuto = true;
/// Gas price
u256 gasPrice;
/// Mapping from contract function parameter name to value
@ -95,6 +97,8 @@ class RecordLogEntry: public QObject
Q_PROPERTY(bool call MEMBER m_call CONSTANT)
/// @returns record type
Q_PROPERTY(RecordType type MEMBER m_type CONSTANT)
/// Gas used
Q_PROPERTY(QString gasUsed MEMBER m_gasUsed CONSTANT)
public:
enum RecordType
@ -105,8 +109,8 @@ public:
RecordLogEntry():
m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {}
RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type):
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type) {}
RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed):
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed) {}
private:
unsigned m_recordIndex;
@ -118,6 +122,7 @@ private:
QString m_returned;
bool m_call;
RecordType m_type;
QString m_gasUsed;
};
/**
@ -136,6 +141,8 @@ public:
Q_PROPERTY(bool mining MEMBER m_mining NOTIFY miningStateChanged)
/// @returns deployed contracts addresses
Q_PROPERTY(QVariantMap contractAddresses READ contractAddresses NOTIFY contractAddressesChanged)
/// @returns deployed contracts gas costs
Q_PROPERTY(QVariantMap gasCosts READ gasCosts NOTIFY gasCostsChanged)
// @returns the last block
Q_PROPERTY(RecordLogEntry* lastBlock READ lastBlock CONSTANT)
/// ethereum.js RPC request entry point
@ -180,6 +187,8 @@ signals:
void runFailed(QString const& _message);
/// Contract address changed
void contractAddressesChanged();
/// Gas costs updated
void gasCostsChanged();
/// Execution state changed
void newBlock();
/// Execution state changed
@ -197,6 +206,7 @@ signals:
private:
RecordLogEntry* lastBlock() const;
QVariantMap contractAddresses() const;
QVariantMap gasCosts() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, std::map<Secret, u256> const& _balances);
dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
@ -212,6 +222,7 @@ private:
std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server;
std::map<QString, u256> m_gasCosts;
std::map<QString, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses;

1
mix/MachineStates.h

@ -80,6 +80,7 @@ namespace mix
dev::Address sender;
dev::Address contractAddress;
dev::u256 value;
dev::u256 gasUsed;
unsigned transactionIndex;
unsigned executonIndex = 0;

7
mix/MixApplication.cpp

@ -52,13 +52,8 @@ ApplicationService::ApplicationService()
MixApplication::MixApplication(int& _argc, char* _argv[]):
QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine())
{
setWindowIcon(QIcon(":/res/mix_256x256x32.png"));
m_engine->load(QUrl("qrc:/qml/Application.qml"));
if (!m_engine->rootObjects().empty())
{
QWindow *window = qobject_cast<QWindow*>(m_engine->rootObjects().at(0));
if (window)
window->setIcon(QIcon(":/res/mix_256x256x32.png"));
}
}
void MixApplication::initialize()

80
mix/MixClient.cpp

@ -40,10 +40,8 @@ namespace dev
namespace mix
{
// TODO: merge as much as possible with the Client.cpp into a mutually inherited base class.
const Secret c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
const u256 c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow
Secret const c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
u256 const c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow
bytes MixBlockChain::createGenesisBlock(h256 _stateRoot)
{
@ -100,9 +98,18 @@ void MixClient::resetState(std::map<Secret, u256> _accounts)
m_executions.clear();
}
void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call)
Transaction MixClient::replaceGas(Transaction const& _t, Secret const& _secret, u256 const& _gas)
{
if (_t.isCreation())
return Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret);
else
return Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret);
}
void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret)
{
bytes rlp = _t.rlp();
Transaction t = _gasAuto ? replaceGas(_t, _secret, m_state.gasLimitRemaining()) : _t;
bytes rlp = t.rlp();
// do debugging run first
LastHashes lastHashes(256);
@ -167,6 +174,31 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
execution.go(onOp);
execution.finalize();
dev::eth::ExecutionResult er = execution.executionResult();
switch (er.excepted)
{
case TransactionException::None:
break;
case TransactionException::NotEnoughCash:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Insufficient balance for contract deployment"));
case TransactionException::OutOfGasBase:
case TransactionException::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas"));
case TransactionException::BlockGasLimitReached:
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached"));
case TransactionException::OutOfStack:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack"));
case TransactionException::StackUnderflow:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow"));
//these should not happen in mix
case TransactionException::Unknown:
case TransactionException::BadInstruction:
case TransactionException::BadJumpDestination:
case TransactionException::InvalidSignature:
case TransactionException::InvalidNonce:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error"));
};
ExecutionResult d;
d.result = execution.executionResult();
@ -176,6 +208,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
d.address = _t.receiveAddress();
d.sender = _t.sender();
d.value = _t.value();
d.gasUsed = er.gasUsed + er.gasRefunded;
if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
if (!_call)
@ -185,9 +218,11 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
// execute on a state
if (!_call)
{
dev::eth::ExecutionResult er =_state.execute(lastHashes, _t);
if (_t.isCreation() && _state.code(d.contractAddress).empty())
t = _gasAuto ? replaceGas(_t, _secret, d.gasUsed) : _t;
er =_state.execute(lastHashes, t);
if (t.isCreation() && _state.code(d.contractAddress).empty())
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment"));
d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit;
// collect watches
h256Set changed;
Guard l(x_filtersWatches);
@ -242,25 +277,25 @@ State MixClient::asOf(h256 const& _block) const
return State(m_stateDB, bc(), _block);
}
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
executeTransaction(t, m_state, false);
executeTransaction(t, m_state, false, _gasAuto, _secret);
}
Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
executeTransaction(t, m_state, false);
executeTransaction(t, m_state, false, _gasAuto, _secret);
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address;
}
dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto)
{
(void)_blockNumber;
State temp = asOf(eth::PendingBlock);
@ -268,10 +303,25 @@ dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp, true);
executeTransaction(t, temp, true, _gasAuto, _secret);
return lastExecution().result;
}
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, false);
}
Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
{
return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false);
}
dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
{
return call(_secret, _value, _dest, _data, _gas, _gasPrice, _blockNumber,false);
}
dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
{
(void)_blockNumber;
@ -285,7 +335,7 @@ dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes c
Transaction t(_value, _gasPrice, _gas, _data, n, _secret);
bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp, true);
executeTransaction(t, temp, true, false, _secret);
return lastExecution().result;
}

7
mix/MixClient.h

@ -58,6 +58,10 @@ public:
dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock) override;
dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock) override;
void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto);
Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto);
dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto);
void setAddress(Address _us) override;
void setMiningThreads(unsigned _threads) override;
unsigned miningThreads() const override;
@ -86,8 +90,9 @@ protected:
virtual void prepareForTransaction() override {}
private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call);
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret);
void noteChanged(h256Set const& _filters);
dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::Secret const& _secret, dev::u256 const& _gas);
std::vector<KeyPair> m_userAccounts;
eth::State m_state;

8
mix/qml/DeploymentDialog.qml

@ -73,6 +73,12 @@ Dialog {
balance.text = comboAccounts.balances[0];
});
});
var gas = 0;
var gasCosts = clientModel.gasCosts;
for (var g in gasCosts)
gas += gasCosts[g];
gasToUse = gas;
}
function stopForInputError(inError)
@ -421,7 +427,7 @@ Dialog {
Layout.preferredWidth: 350
id: localPackageUrl
readOnly: true
enabled: rowRegister.isOkToRegister()
}
DefaultLabel

2
mix/qml/StateListModel.qml

@ -44,6 +44,7 @@ Item {
value: QEtherHelper.createEther(t.value.value, t.value.unit),
gas: QEtherHelper.createBigInt(t.gas.value),
gasPrice: QEtherHelper.createEther(t.gasPrice.value, t.gasPrice.unit),
gasAuto: t.gasAuto,
stdContract: t.stdContract ? true : false,
parameters: {},
sender: t.sender
@ -91,6 +92,7 @@ Item {
url: t.url,
value: { value: t.value.value, unit: t.value.unit },
gas: { value: t.gas.value() },
gasAuto: t.gasAuto,
gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit },
stdContract: t.stdContract,
parameters: {}

11
mix/qml/TransactionDialog.qml

@ -17,6 +17,7 @@ Dialog {
title: qsTr("Edit Transaction")
property int transactionIndex
property alias gas: gasValueEdit.gasValue;
property alias gasAuto: gasAutoCheck.checked;
property alias gasPrice: gasPriceField.value;
property alias transactionValue: valueField.value;
property string contractId: contractComboBox.currentValue();
@ -39,6 +40,7 @@ Dialog {
transactionIndex = index;
gasValueEdit.gasValue = item.gas;
gasAutoCheck.checked = item.gasAuto ? true : false;
gasPriceField.value = item.gasPrice;
valueField.value = item.value;
var contractId = item.contractId;
@ -164,6 +166,7 @@ Dialog {
contractId: transactionDialog.contractId,
functionId: transactionDialog.functionId,
gas: transactionDialog.gas,
gasAuto: transactionDialog.gasAuto,
gasPrice: transactionDialog.gasPrice,
value: transactionDialog.transactionValue,
parameters: {},
@ -314,8 +317,16 @@ Dialog {
onGasValueChanged: text = gasValue.value();
onTextChanged: gasValue.setValue(text);
implicitWidth: 200
enabled: !gasAutoCheck.checked
id: gasValueEdit;
}
CheckBox
{
id: gasAutoCheck
checked: true
text: qsTr("Auto");
}
}
CommonSeparator

5
mix/qml/TransactionLog.qml

@ -127,6 +127,11 @@ Item {
title: qsTr("Returned")
width: 120
}
TableViewColumn {
role: "gasUsed"
title: qsTr("Gas Used")
width: 120
}
onActivated: {
var item = logTable.model.get(row);
if (item.type === RecordLogEntry.Transaction)

1
mix/qml/js/TransactionHelper.js

@ -6,6 +6,7 @@ function defaultTransaction()
value: createEther("0", QEther.Wei),
functionId: "",
gas: createBigInt("250000"),
gasAuto: true,
gasPrice: createEther("100000", QEther.Wei),
parameters: {},
stdContract: false

21
neth/main.cpp

@ -40,6 +40,7 @@
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include <libethcore/Ethasher.h>
#include "BuildInfo.h"
#undef KEY_EVENT // from windows.h
@ -76,6 +77,7 @@ void help()
<< " -c,--client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/Ethereum)." << endl
<< " -D,--initdag Initialize DAG for mining and exit." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl
<< " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl
<< " -h,--help Show this help message and exit." << endl
@ -321,6 +323,7 @@ enum class NodeMode
int main(int argc, char** argv)
{
bool initDAG = true;
string listenIP;
unsigned short listenPort = 30303;
string publicIP;
@ -424,6 +427,8 @@ int main(int argc, char** argv)
structuredLogging = true;
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if (arg == "-D" || arg == "--initdag")
initDAG = true;
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
@ -556,6 +561,14 @@ int main(int argc, char** argv)
&nodesState,
miners
);
if (initDAG)
{
cout << "Initialize DAG. (This will take awhile)" << endl;
Ethasher::get()->full(web3.ethereum()->blockChain().info());
return 0;
}
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
@ -873,7 +886,7 @@ int main(int argc, char** argv)
ssbd << bbd;
cnote << ssbd.str();
int ssize = fields[4].length();
u256 minGas = (u256)Client::txGas(data, 0);
u256 minGas = (u256)Transaction::gasRequired(data, 0);
if (size < 40)
{
if (size > 0)
@ -939,7 +952,7 @@ int main(int argc, char** argv)
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(bytes(), 0);
u256 minGas = (u256)Transaction::gasRequired(bytes(), 0);
try
{
Address dest = h160(fromHex(fields[0], WhenError::Throw));
@ -1027,7 +1040,7 @@ int main(int argc, char** argv)
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init, 0);
u256 minGas = (u256)Transaction::gasRequired(init, 0);
if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)
@ -1113,7 +1126,7 @@ int main(int argc, char** argv)
auto b = bc.block(h);
for (auto const& i: RLP(b)[1])
{
Transaction t(i.data(), CheckSignature::Sender);
Transaction t(i.data(), CheckTransaction::Everything);
auto s = t.receiveAddress() ?
boost::format(" %1% %2%> %3%: %4% [%5%]") %
toString(t.safeSender()) %

168
test/SolidityOptimizer.cpp

@ -56,8 +56,16 @@ public:
m_nonOptimizedContract = m_contractAddress;
m_optimize = true;
bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
size_t nonOptimizedSize = 0;
eth::eachInstruction(nonOptimizedBytecode, [&](Instruction, u256 const&) {
nonOptimizedSize++;
});
size_t optimizedSize = 0;
eth::eachInstruction(optimizedBytecode, [&](Instruction, u256 const&) {
optimizedSize++;
});
BOOST_CHECK_MESSAGE(
nonOptimizedBytecode.size() > optimizedBytecode.size(),
nonOptimizedSize > optimizedSize,
"Optimizer did not reduce bytecode size."
);
m_optimizedContract = m_contractAddress;
@ -75,11 +83,16 @@ public:
"\nOptimized: " + toHex(optimizedOutput));
}
void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation)
AssemblyItems getCSE(AssemblyItems const& _input)
{
eth::CommonSubexpressionEliminator cse;
BOOST_REQUIRE(cse.feedItems(_input.begin(), _input.end()) == _input.end());
AssemblyItems output = cse.getOptimizedItems();
return cse.getOptimizedItems();
}
void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation)
{
AssemblyItems output = getCSE(_input);
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
@ -569,6 +582,155 @@ BOOST_AUTO_TEST_CASE(cse_jumpi_jump)
});
}
BOOST_AUTO_TEST_CASE(cse_empty_sha3)
{
AssemblyItems input{
u256(0),
Instruction::DUP2,
Instruction::SHA3
};
checkCSE(input, {
u256(sha3(bytesConstRef()))
});
}
BOOST_AUTO_TEST_CASE(cse_partial_sha3)
{
AssemblyItems input{
u256(0xabcd) << (256 - 16),
u256(0),
Instruction::MSTORE,
u256(2),
u256(0),
Instruction::SHA3
};
checkCSE(input, {
u256(0xabcd) << (256 - 16),
u256(0),
Instruction::MSTORE,
u256(sha3(bytes{0xab, 0xcd}))
});
}
BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_location)
{
// sha3 twice from same dynamic location
AssemblyItems input{
Instruction::DUP2,
Instruction::DUP1,
Instruction::MSTORE,
u256(64),
Instruction::DUP2,
Instruction::SHA3,
u256(64),
Instruction::DUP3,
Instruction::SHA3
};
checkCSE(input, {
Instruction::DUP2,
Instruction::DUP1,
Instruction::MSTORE,
u256(64),
Instruction::DUP2,
Instruction::SHA3,
Instruction::DUP1
});
}
BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content)
{
// sha3 twice from different dynamic location but with same content
AssemblyItems input{
Instruction::DUP1,
u256(0x80),
Instruction::MSTORE, // m[128] = DUP1
u256(0x20),
u256(0x80),
Instruction::SHA3, // sha3(m[128..(128+32)])
Instruction::DUP2,
u256(12),
Instruction::MSTORE, // m[12] = DUP1
u256(0x20),
u256(12),
Instruction::SHA3 // sha3(m[12..(12+32)])
};
checkCSE(input, {
u256(0x80),
Instruction::DUP2,
Instruction::DUP2,
Instruction::MSTORE,
u256(0x20),
Instruction::SWAP1,
Instruction::SHA3,
u256(12),
Instruction::DUP3,
Instruction::SWAP1,
Instruction::MSTORE,
Instruction::DUP1
});
}
BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_dynamic_store_in_between)
{
// sha3 twice from different dynamic location but with same content,
// dynamic mstore in between, which forces us to re-calculate the sha3
AssemblyItems input{
u256(0x80),
Instruction::DUP2,
Instruction::DUP2,
Instruction::MSTORE, // m[128] = DUP1
u256(0x20),
Instruction::DUP1,
Instruction::DUP3,
Instruction::SHA3, // sha3(m[128..(128+32)])
u256(12),
Instruction::DUP5,
Instruction::DUP2,
Instruction::MSTORE, // m[12] = DUP1
Instruction::DUP12,
Instruction::DUP14,
Instruction::MSTORE, // destroys memory knowledge
Instruction::SWAP2,
Instruction::SWAP1,
Instruction::SWAP2,
Instruction::SHA3 // sha3(m[12..(12+32)])
};
checkCSE(input, input);
}
BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between)
{
// sha3 twice from different dynamic location but with same content,
// dynamic mstore in between, but does not force us to re-calculate the sha3
AssemblyItems input{
u256(0x80),
Instruction::DUP2,
Instruction::DUP2,
Instruction::MSTORE, // m[128] = DUP1
u256(0x20),
Instruction::DUP1,
Instruction::DUP3,
Instruction::SHA3, // sha3(m[128..(128+32)])
u256(12),
Instruction::DUP5,
Instruction::DUP2,
Instruction::MSTORE, // m[12] = DUP1
Instruction::DUP12,
u256(12 + 32),
Instruction::MSTORE, // does not destoy memory knowledge
Instruction::DUP13,
u256(128 - 32),
Instruction::MSTORE, // does not destoy memory knowledge
u256(0x20),
u256(12),
Instruction::SHA3 // sha3(m[12..(12+32)])
};
// if this changes too often, only count the number of SHA3 and MSTORE instructions
AssemblyItems output = getCSE(input);
BOOST_CHECK_EQUAL(4, count(output.begin(), output.end(), AssemblyItem(Instruction::MSTORE)));
BOOST_CHECK_EQUAL(1, count(output.begin(), output.end(), AssemblyItem(Instruction::SHA3)));
}
BOOST_AUTO_TEST_SUITE_END()
}

2
test/TestHelper.cpp

@ -172,7 +172,7 @@ void ImportTest::importTransaction(json_spirit::mObject& _o)
{
RLPStream transactionRLPStream = createRLPStreamFromTransactionFields(_o);
RLP transactionRLP(transactionRLPStream.out());
m_transaction = Transaction(transactionRLP.data(), CheckSignature::Sender);
m_transaction = Transaction(transactionRLP.data(), CheckTransaction::Everything);
}
}

4
test/blockchain.cpp

@ -383,7 +383,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
try
{
Transaction t(createRLPStreamFromTransactionFields(tx).out(), CheckSignature::Sender);
Transaction t(createRLPStreamFromTransactionFields(tx).out(), CheckTransaction::Everything);
txsFromField.push_back(t);
}
catch (Exception const& _e)
@ -400,7 +400,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
RLP root(blockRLP);
for (auto const& tr: root[1])
{
Transaction tx(tr.data(), CheckSignature::Sender);
Transaction tx(tr.data(), CheckTransaction::Everything);
txsFromRlp.push_back(tx);
}

6
test/transaction.cpp

@ -44,7 +44,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
{
bytes stream = importByteArray(o["rlp"].get_str());
RLP rlp(stream);
txFromRlp = Transaction(rlp.data(), CheckSignature::Sender);
txFromRlp = Transaction(rlp.data(), CheckTransaction::Everything);
if (!txFromRlp.signature().isValid())
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") );
}
@ -64,7 +64,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
BOOST_REQUIRE(o.count("transaction") > 0);
mObject tObj = o["transaction"].get_obj();
Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckSignature::Sender);
Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckTransaction::Everything);
//Check the fields restored from RLP to original fields
BOOST_CHECK_MESSAGE(txFromFields.data() == txFromRlp.data(), "Data in given RLP not matching the Transaction data!");
@ -91,7 +91,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
try
{
Transaction txFromFields(rlpStream.out(), CheckSignature::Sender);
Transaction txFromFields(rlpStream.out(), CheckTransaction::Everything);
if (!txFromFields.signature().isValid())
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") );

Loading…
Cancel
Save