Browse Source

Merge branch 'develop' into p2p

cl-refactor
subtly 10 years ago
parent
commit
6b30cc54ce
  1. 6
      alethzero/Main.ui
  2. 6
      alethzero/MainWin.cpp
  3. 2
      alethzero/Transact.cpp
  4. 52
      eth/main.cpp
  5. 2
      libdevcore/Common.cpp
  6. 28
      libdevcore/CommonData.h
  7. 6
      libdevcrypto/TrieDB.h
  8. 30
      libethcore/BlockInfo.cpp
  9. 4
      libethcore/BlockInfo.h
  10. 17
      libethcore/CommonJS.cpp
  11. 3
      libethcore/CommonJS.h
  12. 1
      libethcore/Exceptions.h
  13. 1
      libethcore/Params.cpp
  14. 3
      libethcore/Params.h
  15. 77
      libethereum/BlockChain.cpp
  16. 26
      libethereum/BlockChain.h
  17. 8
      libethereum/BlockQueue.cpp
  18. 6
      libethereum/Client.cpp
  19. 25
      libethereum/ClientBase.cpp
  20. 5
      libethereum/ClientBase.h
  21. 5
      libethereum/EthereumHost.cpp
  22. 4
      libethereum/EthereumPeer.cpp
  23. 13
      libethereum/Executive.cpp
  24. 2
      libethereum/Executive.h
  25. 19
      libethereum/Interface.h
  26. 41
      libethereum/State.cpp
  27. 4
      libethereum/State.h
  28. 16
      libethereum/Transaction.cpp
  29. 48
      libethereum/Transaction.h
  30. 8
      libethereum/TransactionQueue.cpp
  31. 3
      libethereum/TransactionQueue.h
  32. 2
      libevm/VM.cpp
  33. 2
      libp2p/Session.cpp
  34. 1
      libsolidity/CompilerContext.h
  35. 8
      libsolidity/CompilerUtils.cpp
  36. 2
      libsolidity/CompilerUtils.h
  37. 63
      libsolidity/ExpressionCompiler.cpp
  38. 25
      libsolidity/Types.cpp
  39. 3
      libtestutils/BlockChainLoader.cpp
  40. 3
      libtestutils/StateLoader.cpp
  41. 4
      libtestutils/StateLoader.h
  42. 36
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  43. 31
      mix/ClientModel.cpp
  44. 21
      mix/ClientModel.h
  45. 10
      mix/CodeModel.cpp
  46. 2
      mix/CodeModel.h
  47. 1
      mix/MachineStates.h
  48. 7
      mix/MixApplication.cpp
  49. 80
      mix/MixClient.cpp
  50. 7
      mix/MixClient.h
  51. 10
      mix/qml/Application.qml
  52. 32
      mix/qml/CodeEditorView.qml
  53. 8
      mix/qml/DeploymentDialog.qml
  54. 125
      mix/qml/NewProjectDialog.qml
  55. 2
      mix/qml/StateListModel.qml
  56. 76
      mix/qml/StatusPane.qml
  57. 11
      mix/qml/TransactionDialog.qml
  58. 5
      mix/qml/TransactionLog.qml
  59. 12
      mix/qml/WebCodeEditor.qml
  60. 5
      mix/qml/html/codeeditor.js
  61. BIN
      mix/qml/img/signerroricon32.png
  62. 1
      mix/qml/js/TransactionHelper.js
  63. 1
      mix/res.qrc
  64. 1
      mix/test/TestService.cpp
  65. 13
      neth/main.cpp
  66. 54
      test/SolidityEndToEndTest.cpp
  67. 2
      test/TestHelper.cpp
  68. 4
      test/blockchain.cpp
  69. 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())

52
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,--create-dag <this/next/number> Create the DAG in preparation for mining on given block 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
@ -198,8 +200,20 @@ enum class NodeMode
Full
};
void doInitDAG(unsigned _n)
{
BlockInfo bi;
bi.number = _n;
cout << "Initializing DAG for epoch beginning #" << (bi.number / 30000 * 30000) << " (seedhash " << bi.seedHash().abridged() << "). This will take a while." << endl;
Ethasher::get()->full(bi);
exit(0);
}
static const unsigned NoDAGInit = (unsigned)-3;
int main(int argc, char** argv)
{
unsigned initDAG = NoDAGInit;
string listenIP;
unsigned short listenPort = 30303;
string publicIP;
@ -304,6 +318,24 @@ 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 == "--create-dag") && i + 1 < argc)
{
string m = boost::to_lower_copy(string(argv[++i]));
if (m == "next")
initDAG = PendingBlock;
else if (m == "this")
initDAG = LatestBlock;
else
try
{
initDAG = stol(m);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
@ -312,7 +344,7 @@ int main(int argc, char** argv)
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[++i] << endl;
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
@ -324,7 +356,7 @@ int main(int argc, char** argv)
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[++i] << endl;
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
}
@ -417,6 +449,12 @@ int main(int argc, char** argv)
}
}
// Two codepaths is necessary since named block require database, but numbered
// blocks are superuseful to have when database is already open in another process.
if (initDAG < NoDAGInit)
doInitDAG(initDAG);
if (!clientName.empty())
clientName += "/";
@ -436,6 +474,10 @@ int main(int argc, char** argv)
&nodesState,
miners
);
if (initDAG == LatestBlock || initDAG == PendingBlock)
doInitDAG(web3.ethereum()->blockChain().number() + (initDAG == PendingBlock ? 30000 : 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 +696,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 +775,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 +840,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)

6
libdevcrypto/TrieDB.h

@ -161,9 +161,15 @@ public:
}
}
std::string at(bytes const& _key) const { return at(&_key); }
std::string at(bytesConstRef _key) const;
void insert(bytes const& _key, bytes const& _value) { insert(&_key, &_value); }
void insert(bytesConstRef _key, bytes const& _value) { insert(_key, &_value); }
void insert(bytes const& _key, bytesConstRef _value) { insert(&_key, _value); }
void insert(bytesConstRef _key, bytesConstRef _value);
void remove(bytes const& _key) { remove(&_key); }
void remove(bytesConstRef _key);
bool contains(bytes const& _key) { return contains(&_key); }
bool contains(bytesConstRef _key) { return !at(_key).empty(); }
class iterator

30
libethcore/BlockInfo.cpp

@ -40,7 +40,7 @@ BlockInfo::BlockInfo(bytesConstRef _block, Strictness _s, h256 const& _h)
populate(_block, _s, _h);
}
void BlockInfo::setEmpty()
void BlockInfo::clear()
{
parentHash = h256();
sha3Uncles = EmptyListSHA3;
@ -105,8 +105,9 @@ h256 BlockInfo::headerHash(bytesConstRef _block)
void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const& _h)
{
// m_hash = dev::sha3(_header.data());
m_hash = _h;
if (_h)
assert(_h == dev::sha3(_header.data()));
m_seedHash = h256();
int field = 0;
@ -172,13 +173,21 @@ void BlockInfo::populate(bytesConstRef _block, Strictness _s, h256 const& _h)
BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block uncles need to be a list") << BadFieldError(2, root[2].data().toString()));
}
template <class T, class U> h256 trieRootOver(unsigned _itemCount, T const& _getKey, U const& _getValue)
{
MemoryDB db;
GenericTrieDB<MemoryDB> t(&db);
t.init();
for (unsigned i = 0; i < _itemCount; ++i)
t.insert(_getKey(i), _getValue(i));
return t.root();
}
void BlockInfo::verifyInternals(bytesConstRef _block) const
{
RLP root(_block);
u256 mgp = (u256)-1;
OverlayDB db;
/*OverlayDB db;
GenericTrieDB<OverlayDB> t(&db);
t.init();
unsigned i = 0;
@ -186,12 +195,13 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
{
bytes k = rlp(i);
t.insert(&k, tr.data());
u256 gasprice = tr[1].toInt<u256>();
mgp = min(mgp, gasprice); // the minimum gas price is not used for anything //TODO delete?
++i;
}
if (transactionsRoot != t.root())
BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(t.root(), transactionsRoot));
if (transactionsRoot != t.root())*/
auto txList = root[1];
auto expectedRoot = trieRootOver(txList.itemCount(), [&](unsigned i){ return rlp(i); }, [&](unsigned i){ return txList[i].data(); });
if (transactionsRoot != expectedRoot)
BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(expectedRoot, transactionsRoot));
if (sha3Uncles != sha3(root[2].data()))
BOOST_THROW_EXCEPTION(InvalidUnclesHash());
@ -199,7 +209,7 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
void BlockInfo::populateFromParent(BlockInfo const& _parent)
{
m_hash = m_seedHash = h256();
noteDirty();
stateRoot = _parent.stateRoot;
parentHash = _parent.hash();
number = _parent.number + 1;

4
libethcore/BlockInfo.h

@ -116,9 +116,9 @@ public:
}
bool operator!=(BlockInfo const& _cmp) const { return !operator==(_cmp); }
void setEmpty();
void clear();
void noteDirty() const { m_hash = m_seedHash= h256(); }
void noteDirty() const { m_hash = m_seedHash = h256(); }
void populateFromHeader(RLP const& _header, Strictness _s = IgnoreNonce, h256 const& _h = h256());
void populate(bytesConstRef _block, Strictness _s = IgnoreNonce, h256 const& _h = h256());

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.

77
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
@ -398,19 +384,18 @@ pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db,
throw;
}
#endif
auto newHash = BlockInfo::headerHash(_block);
// Check block doesn't already exist first!
if (isKnown(newHash) && !_force)
if (isKnown(bi.hash()) && !_force)
{
clog(BlockChainNote) << newHash << ": Not new.";
clog(BlockChainNote) << bi.hash() << ": Not new.";
BOOST_THROW_EXCEPTION(AlreadyHaveBlock());
}
// Work out its number as the parent's number + 1
if (!isKnown(bi.parentHash))
{
clog(BlockChainNote) << newHash << ": Unknown parent " << bi.parentHash;
clog(BlockChainNote) << bi.hash() << ": Unknown parent " << bi.parentHash;
// We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on.
BOOST_THROW_EXCEPTION(UnknownParent());
}
@ -427,12 +412,12 @@ pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db,
// Check it's not crazy
if (bi.timestamp > (u256)time(0))
{
clog(BlockChainNote) << newHash << ": Future time " << bi.timestamp << " (now at " << time(0) << ")";
clog(BlockChainNote) << bi.hash() << ": Future time " << bi.timestamp << " (now at " << time(0) << ")";
// Block has a timestamp in the future. This is no good.
BOOST_THROW_EXCEPTION(FutureTime());
}
clog(BlockChainNote) << "Attempting import of " << newHash.abridged() << "...";
clog(BlockChainNote) << "Attempting import of " << bi.hash().abridged() << "...";
#if ETH_TIMED_IMPORTS
preliminaryChecks = t.elapsed();
@ -477,16 +462,16 @@ pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db,
details(bi.parentHash);
WriteGuard l(x_details);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash);
m_details[bi.hash()] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(bi.hash());
}
{
WriteGuard l(x_logBlooms);
m_logBlooms[newHash] = blb;
m_logBlooms[bi.hash()] = blb;
}
{
WriteGuard l(x_receipts);
m_receipts[newHash] = br;
m_receipts[bi.hash()] = br;
}
#if ETH_TIMED_IMPORTS
@ -498,11 +483,11 @@ pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db,
ReadGuard l2(x_details);
ReadGuard l4(x_receipts);
ReadGuard l5(x_logBlooms);
m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp()));
m_blocksDB->Put(m_writeOptions, toSlice(bi.hash()), (ldb::Slice)ref(_block));
m_extrasDB->Put(m_writeOptions, toSlice(bi.hash(), ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.hash()].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.hash(), ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[bi.hash()].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.hash(), ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[bi.hash()].rlp()));
}
#if ETH_TIMED_IMPORTS
@ -549,13 +534,13 @@ pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db,
if (td > details(last).totalDifficulty)
{
unsigned commonIndex;
tie(route, common, commonIndex) = treeRoute(last, newHash);
tie(route, common, commonIndex) = treeRoute(last, bi.hash());
{
WriteGuard l(x_lastBlockHash);
m_lastBlockHash = newHash;
m_lastBlockHash = bi.hash();
}
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32));
m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&(bi.hash()), 32));
// Most of the time these two will be equal - only when we're doing a chain revert will they not be
if (common != last)
@ -988,41 +973,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];
}

26
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;
@ -84,7 +83,6 @@ using ProgressCallback = std::function<void(unsigned, unsigned)>;
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
* @threadsafe
* @todo Make not memory hog (should actually act as a cache and deallocate old entries).
*/
class BlockChain
{
@ -274,30 +272,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;

41
libethereum/State.cpp

@ -99,7 +99,7 @@ State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress):
m_previousBlock = CanonBlockChain::genesis();
}
else
m_previousBlock.setEmpty();
m_previousBlock.clear();
resetCurrent();
@ -190,6 +190,8 @@ State& State::operator=(State const& _s)
m_blockReward = _s.m_blockReward;
m_lastTx = _s.m_lastTx;
paranoia("after state cloning (assignment op)", true);
m_committedToMine = false;
return *this;
}
@ -276,8 +278,8 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi)
{
bool ret = false;
// BLOCK
BlockInfo bi = _bi;
if (!bi)
BlockInfo bi = _bi ? _bi : _bc.info(_block);
/* if (!bi)
while (1)
{
try
@ -299,7 +301,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << _e.what() << endl;
}
}
}*/
if (bi == m_currentBlock)
{
// We mined the last block.
@ -364,7 +366,7 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const
#endif
// Check family:
BlockInfo biParent(_bc.block(_bi.parentHash));
BlockInfo biParent = _bc.info(_bi.parentHash);
_bi.verifyParent(biParent);
#if ETH_TIMED_ENACTMENTS
@ -374,7 +376,7 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const
BlockInfo biGrandParent;
if (biParent.number)
biGrandParent.populate(_bc.block(biParent.parentHash));
biGrandParent = _bc.info(biParent.parentHash);
#if ETH_TIMED_ENACTMENTS
populateGrand = t.elapsed();
@ -434,6 +436,8 @@ void State::resetCurrent()
m_lastTx = m_db;
m_state.setRoot(m_previousBlock.stateRoot);
m_committedToMine = false;
paranoia("begin resetCurrent", true);
}
@ -539,6 +543,9 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
// m_currentBlock is assumed to be prepopulated and reset.
BlockInfo bi(_block, _checkNonce ? CheckEverything : IgnoreNonce);
cdebug << "enacting" << BlockInfo::headerHash(_block).abridged() << "==" << bi.hash().abridged() << "on" << m_previousBlock.hash().abridged();
cdebug << m_currentBlock;
#if !ETH_RELEASE
assert(m_previousBlock.hash() == bi.parentHash);
assert(m_currentBlock.parentHash == bi.parentHash);
@ -551,6 +558,10 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
// Populate m_currentBlock with the correct values.
m_currentBlock = bi;
m_currentBlock.verifyInternals(_block);
m_currentBlock.noteDirty();
cdebug << "populated and verified. incoming block hash is" << m_currentBlock.hash().abridged();
cdebug << m_currentBlock;
// cnote << "playback begins:" << m_state.root();
// cnote << m_state;
@ -574,7 +585,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);
@ -620,6 +631,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
// Initialise total difficulty calculation.
u256 tdIncrease = m_currentBlock.difficulty;
// Check uncles & apply their rewards to state.
if (rlp[2].itemCount() > 2)
BOOST_THROW_EXCEPTION(TooManyUncles());
@ -646,6 +658,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
tdIncrease += uncle.difficulty;
rewarded.push_back(uncle);
}
applyRewards(rewarded);
// Commit all cached state changes to the state trie.
@ -673,6 +686,11 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
BOOST_THROW_EXCEPTION(InvalidGasUsed() << RequirementError(bigint(gasUsed()), bigint(m_currentBlock.gasUsed)));
}
cdebug << m_currentBlock;
auto hh = m_currentBlock.hash();
m_currentBlock.noteDirty();
cdebug << "done enacting. new stateroot is" << m_currentBlock.stateRoot.abridged() << ", hash is" << m_currentBlock.hash().abridged() << " = " << hh;
return tdIncrease;
}
@ -687,6 +705,9 @@ void State::cleanup(bool _fullCommit)
paranoia("immediately after database commit", true);
m_previousBlock = m_currentBlock;
m_currentBlock.populateFromParent(m_previousBlock);
cdebug << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash().abridged();
}
else
m_db.rollback();
@ -696,7 +717,7 @@ void State::cleanup(bool _fullCommit)
void State::uncommitToMine()
{
if (m_currentBlock.sha3Uncles)
if (m_committedToMine)
{
m_cache.clear();
if (!m_transactions.size())
@ -705,7 +726,7 @@ void State::uncommitToMine()
m_state.setRoot(m_receipts.back().stateRoot());
m_db = m_lastTx;
paranoia("Uncommited to mine", true);
m_currentBlock.sha3Uncles = h256();
m_committedToMine = false;
}
}
@ -842,6 +863,8 @@ void State::commitToMine(BlockChain const& _bc)
m_currentBlock.gasUsed = gasUsed();
m_currentBlock.stateRoot = m_state.root();
m_currentBlock.parentHash = m_previousBlock.hash();
m_committedToMine = true;
}
MineInfo State::mine(unsigned _msTimeout, bool _turbo)

4
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]; }
@ -360,6 +363,7 @@ private:
BlockInfo m_previousBlock; ///< The previous block's information.
BlockInfo m_currentBlock; ///< The current block's information.
bytes m_currentBytes; ///< The current block.
bool m_committedToMine = false; ///< Have we committed to mine on the present m_currentBlock?
bytes m_currentTxs; ///< The RLP-encoded block of transactions.
bytes m_currentUncles; ///< The RLP-encoded block of uncles.

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

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/CompilerContext.h

@ -83,6 +83,7 @@ public:
/// Converts an offset relative to the current stack height to a value that can be used later
/// with baseToCurrentStackOffset to point to the same stack element.
unsigned currentToBaseStackOffset(unsigned _offset) const;
/// @returns pair of slot and byte offset of the value inside this slot.
std::pair<u256, unsigned> getStorageLocationOfVariable(Declaration const& _declaration) const;
/// Appends a JUMPI instruction to a new tag and @returns the tag

8
libsolidity/CompilerUtils.cpp

@ -157,8 +157,12 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
void CompilerUtils::popStackElement(Type const& _type)
{
unsigned const size = _type.getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
popStackSlots(_type.getSizeOnStack());
}
void CompilerUtils::popStackSlots(size_t _amount)
{
for (size_t i = 0; i < _amount; ++i)
m_context << eth::Instruction::POP;
}

2
libsolidity/CompilerUtils.h

@ -79,6 +79,8 @@ public:
void copyToStackTop(unsigned _stackDepth, unsigned _itemSize);
/// Removes the current value from the top of the stack.
void popStackElement(Type const& _type);
/// Removes element from the top of the stack _amount times.
void popStackSlots(size_t _amount);
template <class T>
static unsigned getSizeOnStack(std::vector<T> const& _variables);

63
libsolidity/ExpressionCompiler.cpp

@ -60,57 +60,82 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
FunctionType accessorType(_varDecl);
unsigned length = 0;
TypePointers const& paramTypes = accessorType.getParameterTypes();
// move arguments to memory
for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes))
length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
// retrieve the position of the variable
auto const& location = m_context.getStorageLocationOfVariable(_varDecl);
m_context << location.first;
m_context << location.first << u256(location.second);
TypePointer returnType = _varDecl.getType();
for (TypePointer const& paramType: paramTypes)
{
// move offset to memory
CompilerUtils(m_context).storeInMemory(length);
unsigned argLen = paramType->getCalldataEncodedSize();
length -= argLen;
m_context << u256(argLen + 32) << u256(length) << eth::Instruction::SHA3;
returnType = dynamic_cast<MappingType const&>(*returnType).getValueType();
for (size_t i = 0; i < paramTypes.size(); ++i)
{
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
{
// pop offset
m_context << eth::Instruction::POP;
// move storage offset to memory.
CompilerUtils(m_context).storeInMemory(32);
// move key to memory.
CompilerUtils(m_context).copyToStackTop(paramTypes.size() - i, 1);
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
// push offset
m_context << u256(0);
returnType = mappingType->getValueType();
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
{
// pop offset
m_context << eth::Instruction::POP;
CompilerUtils(m_context).copyToStackTop(paramTypes.size() - i + 1, 1);
ArrayUtils(m_context).accessIndex(*arrayType);
returnType = arrayType->getBaseType();
}
else
solAssert(false, "Index access is allowed only for \"mapping\" and \"array\" types.");
}
// remove index arguments.
if (paramTypes.size() == 1)
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::SWAP1;
else if (paramTypes.size() >= 2)
{
m_context << eth::swapInstruction(paramTypes.size());
m_context << eth::Instruction::POP;
m_context << eth::swapInstruction(paramTypes.size());
CompilerUtils(m_context).popStackSlots(paramTypes.size() - 1);
}
unsigned retSizeOnStack = 0;
solAssert(accessorType.getReturnParameterTypes().size() >= 1, "");
if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
{
// remove offset
m_context << eth::Instruction::POP;
auto const& names = accessorType.getReturnParameterNames();
auto const& types = accessorType.getReturnParameterTypes();
// struct
for (size_t i = 0; i < names.size(); ++i)
{
if (types[i]->getCategory() == Type::Category::Mapping)
if (types[i]->getCategory() == Type::Category::Mapping || types[i]->getCategory() == Type::Category::Array)
continue;
pair<u256, unsigned> const& offsets = structType->getStorageOffsetsOfMember(names[i]);
m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second);
StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true);
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 is not yet implemented.");
m_context << eth::Instruction::SWAP1;
retSizeOnStack += types[i]->getSizeOnStack();
}
// remove slot
m_context << eth::Instruction::POP;
}
else
{
// simple value
solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
m_context << u256(location.second);
StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
retSizeOnStack = returnType->getSizeOnStack();
}
solAssert(retSizeOnStack <= 15, "Stack too deep.");
solAssert(retSizeOnStack <= 15, "Stack is too deep.");
m_context << eth::dupInstruction(retSizeOnStack + 1);
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
}
@ -749,7 +774,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
appendTypeMoveToMemory(IntegerType(256));
m_context << u256(0) << eth::Instruction::SHA3;
m_context << u256(0);
setLValueToStorageItem( _indexAccess);
setLValueToStorageItem(_indexAccess);
}
else if (baseType.getCategory() == Type::Category::Array)
{

25
libsolidity/Types.cpp

@ -981,15 +981,26 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
m_location(Location::External), m_isConstant(true), m_declaration(&_varDecl)
{
TypePointers params;
TypePointers paramTypes;
vector<string> paramNames;
auto returnType = _varDecl.getType();
while (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
while (true)
{
params.push_back(mappingType->getKeyType());
paramNames.push_back("");
returnType = mappingType->getValueType();
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
{
paramTypes.push_back(mappingType->getKeyType());
paramNames.push_back("");
returnType = mappingType->getValueType();
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
{
returnType = arrayType->getBaseType();
paramNames.push_back("");
paramTypes.push_back(make_shared<IntegerType>(256));
}
else
break;
}
TypePointers retParams;
@ -997,7 +1008,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
{
for (pair<string, TypePointer> const& member: structType->getMembers())
if (member.second->canLiveOutsideStorage())
if (member.second->getCategory() != Category::Mapping && member.second->getCategory() != Category::Array)
{
retParamNames.push_back(member.first);
retParams.push_back(member.second);
@ -1009,7 +1020,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
retParamNames.push_back("");
}
swap(params, m_parameterTypes);
swap(paramTypes, m_parameterTypes);
swap(paramNames, m_parameterNames);
swap(retParams, m_returnParameterTypes);
swap(retParamNames, m_returnParameterNames);

3
libtestutils/BlockChainLoader.cpp

@ -31,11 +31,12 @@ using namespace dev::eth;
BlockChainLoader::BlockChainLoader(Json::Value const& _json)
{
// load pre state
StateLoader sl(_json["pre"]);
StateLoader sl(_json["pre"], m_dir.path());
m_state = sl.state();
// load genesisBlock
m_bc.reset(new BlockChain(fromHex(_json["genesisRLP"].asString()), m_dir.path(), WithExisting::Kill));
assert(m_state.rootHash() == m_bc->info().stateRoot);
// load blocks
for (auto const& block: _json["blocks"])

3
libtestutils/StateLoader.cpp

@ -26,7 +26,8 @@ using namespace dev;
using namespace dev::eth;
using namespace dev::test;
StateLoader::StateLoader(Json::Value const& _json)
StateLoader::StateLoader(Json::Value const& _json, std::string const& _dbPath):
m_state(State::openDB(_dbPath, WithExisting::Kill), BaseState::Empty)
{
for (string const& name: _json.getMemberNames())
{

4
libtestutils/StateLoader.h

@ -23,6 +23,7 @@
#include <json/json.h>
#include <libethereum/State.h>
#include "TransientDirectory.h"
namespace dev
{
@ -35,11 +36,12 @@ namespace test
class StateLoader
{
public:
StateLoader(Json::Value const& _json);
StateLoader(Json::Value const& _json, std::string const& _dbPath);
eth::State const& state() const { return m_state; }
private:
eth::State m_state;
};
}
}

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;

10
mix/CodeModel.cpp

@ -193,13 +193,6 @@ void CodeModel::reset(QVariantMap const& _documents)
void CodeModel::registerCodeChange(QString const& _documentId, QString const& _code)
{
CompiledContract* contract = contractByDocumentId(_documentId);
if (contract != nullptr && contract->m_sourceHash == qHash(_code))
{
emit compilationComplete();
return;
}
{
Guard pl(x_pendingContracts);
m_pendingContracts[_documentId] = _code;
@ -256,7 +249,6 @@ void CodeModel::runCompilationJob(int _jobId)
{
if (_jobId != m_backgroundJobId)
return; //obsolete job
ContractMap result;
solidity::CompilerStack cs(true);
try
@ -309,7 +301,7 @@ void CodeModel::runCompilationJob(int _jobId)
CompiledContract* contract = nullptr;
if (location && location->sourceName.get() && (contract = contractByDocumentId(QString::fromStdString(*location->sourceName))))
message = message.replace(QString::fromStdString(*location->sourceName), contract->contract()->name()); //substitute the location to match our contract names
compilationError(message);
compilationError(message, QString::fromStdString(*location->sourceName));
}
m_compiling = false;
emit stateChanged();

2
mix/CodeModel.h

@ -160,7 +160,7 @@ signals:
/// Emitted on compilation complete
void compilationComplete();
/// Emitted on compilation error
void compilationError(QString _error);
void compilationError(QString _error, QString _sourceName);
/// Internal signal used to transfer compilation job to background thread
void scheduleCompilationJob(int _jobId);
/// Emitted if there are any changes in the code model

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;

10
mix/qml/Application.qml

@ -398,4 +398,14 @@ ApplicationWindow {
enabled: !projectModel.isEmpty && codeModel.hasContract
onTriggered: projectModel.deployProject();
}
Action {
id: goToCompilationError
text: qsTr("Go to compilation error")
shortcut: "F4"
onTriggered:
{
mainContent.codeEditor.goToCompilationError();
}
}
}

32
mix/qml/CodeEditorView.qml

@ -7,13 +7,13 @@ import QtQuick.Dialogs 1.1
Item {
id: codeEditorView
property string currentDocumentId: ""
property string sourceInError
property int openDocCount: 0
signal documentEdit(string documentId)
signal breakpointsChanged(string documentId)
signal isCleanChanged(var isClean, string documentId)
signal loadComplete
function getDocumentText(documentId) {
for (var i = 0; i < openDocCount; i++) {
if (editorListModel.get(i).documentId === documentId) {
@ -50,7 +50,6 @@ Item {
doLoadDocument(editors.itemAt(openDocCount).item, editorListModel.get(openDocCount))
}
openDocCount++;
}
function doLoadDocument(editor, document) {
@ -71,6 +70,7 @@ Item {
editor.onIsCleanChanged.connect(function() {
isCleanChanged(editor.isClean, document.documentId);
});
editor.sourceName = document.documentId;
}
function getEditor(documentId) {
@ -135,8 +135,36 @@ Item {
editor.changeGeneration();
}
function goToCompilationError() {
if (sourceInError === "")
return;
if (currentDocumentId !== sourceInError)
projectModel.openDocument(sourceInError);
for (var i = 0; i < openDocCount; i++)
{
var doc = editorListModel.get(i);
if (doc.isContract && doc.documentId === sourceInError)
{
var editor = editors.itemAt(i).item;
if (editor)
editor.goToCompilationError();
break;
}
}
}
Component.onCompleted: projectModel.codeEditor = codeEditorView;
Connections {
target: codeModel
onCompilationError: {
sourceInError = _sourceName;
}
onCompilationComplete: {
sourceInError = "";
}
}
Connections {
target: projectModel
onDocumentOpened: {

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

125
mix/qml/NewProjectDialog.qml

@ -5,96 +5,103 @@ import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
Dialog {
id: newProjectWin
modality: Qt.ApplicationModal
width: 640
height: 120
visible: false
Item
{
property alias projectTitle: titleField.text
readonly property string projectPath: "file://" + pathField.text
property alias pathFieldText: pathField.text
signal accepted
function open() {
newProjectWin.setX((Screen.width - width) / 2);
newProjectWin.setY((Screen.height - height) / 2);
visible = true;
newProjectWin.visible = true;
titleField.focus = true;
}
function close() {
visible = false;
newProjectWin.visible = false;
}
function acceptAndClose() {
close();
accepted();
}
contentItem: Rectangle {
anchors.fill: parent
GridLayout
{
id: dialogContent
columns: 2
Dialog {
id: newProjectWin
modality: Qt.ApplicationModal
width: 640
height: 120
visible: false
contentItem: Rectangle {
anchors.fill: parent
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
GridLayout
{
id: dialogContent
columns: 2
anchors.fill: parent
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
Label {
text: qsTr("Title")
}
TextField {
id: titleField
focus: true
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
Label {
text: qsTr("Title")
}
}
Label {
text: qsTr("Path")
}
RowLayout {
TextField {
id: pathField
id: titleField
focus: true
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
}
}
Button {
text: qsTr("Browse")
onClicked: createProjectFileDialog.open()
Label {
text: qsTr("Path")
}
RowLayout {
TextField {
id: pathField
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
}
}
Button {
text: qsTr("Browse")
onClicked:
{
newProjectWin.close()
createProjectFileDialog.open()
}
}
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
id: okButton;
enabled: titleField.text != "" && pathField.text != ""
text: qsTr("OK");
onClicked: {
acceptAndClose();
Button {
id: okButton;
enabled: titleField.text != "" && pathField.text != ""
text: qsTr("OK");
onClicked: {
acceptAndClose();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
}
Component.onCompleted: pathField.text = fileIo.homePath
}
FileDialog {
@ -109,7 +116,7 @@ Dialog {
if (Qt.platform.os == "windows" && u.indexOf("/") == 0)
u = u.substring(1, u.length);
pathField.text = u;
newProjectWin.open()
}
}
Component.onCompleted: pathField.text = fileIo.homePath
}

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: {}

76
mix/qml/StatusPane.qml

@ -112,8 +112,17 @@ Rectangle {
}
Connections {
target: codeModel
onCompilationComplete: updateStatus();
onCompilationError: updateStatus(_error);
onCompilationComplete:
{
goToLine.visible = false;
updateStatus();
}
onCompilationError:
{
goToLine.visible = true
updateStatus(_error);
}
}
color: "transparent"
@ -176,24 +185,57 @@ Rectangle {
else
width = undefined
}
}
Button
{
anchors.fill: parent
id: toolTip
action: toolTipInfo
text: ""
style:
ButtonStyle {
background:Rectangle {
color: "transparent"
Button
{
anchors.fill: parent
id: toolTip
action: toolTipInfo
text: ""
z: 3;
style:
ButtonStyle {
background:Rectangle {
color: "transparent"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
logsContainer.toggle();
}
}
}
MouseArea {
}
Rectangle
{
visible: false
color: "transparent"
width: 40
height: parent.height
anchors.top: parent.top
anchors.left: status.right
anchors.leftMargin: 15
id: goToLine
RowLayout
{
anchors.fill: parent
onClicked: {
logsContainer.toggle();
Rectangle
{
color: "transparent"
anchors.fill: parent
Button
{
z: 4
anchors.right: parent.right
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
id: goToLineBtn
text: ""
iconSource: "qrc:/qml/img/signerroricon32.png"
action: goToCompilationError
}
}
}
}
@ -235,6 +277,8 @@ Rectangle {
var coordinates = logsContainer.mapToItem(top, 0, 0);
logsContainer.parent = top;
logsContainer.x = status.x + statusContainer.x - logStyle.generic.layout.dateWidth - logStyle.generic.layout.typeWidth + 70
if (Qt.platform.os === "osx")
logsContainer.y = statusContainer.y;
}
LogsPaneStyle {

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)

12
mix/qml/WebCodeEditor.qml

@ -16,7 +16,8 @@ Item {
property string currentMode: ""
property bool initialized: false
property bool unloaded: false
property var currentBreakpoints: [];
property var currentBreakpoints: []
property string sourceName
function setText(text, mode) {
currentText = text;
@ -69,6 +70,11 @@ Item {
editorBrowser.runJavaScript("changeGeneration()", function(result) {});
}
function goToCompilationError() {
if (initialized && editorBrowser)
editorBrowser.runJavaScript("goToCompilationError()", function(result) {});
}
Clipboard
{
id: clipboard
@ -121,8 +127,10 @@ Item {
editorBrowser.runJavaScript("compilationComplete()", function(result) { });
}
function compilationError(error)
function compilationError(error, sourceName)
{
if (sourceName !== parent.sourceName)
return;
if (!editorBrowser || !error)
return;
var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false);

5
mix/qml/html/codeeditor.js

@ -189,4 +189,9 @@ compilationComplete = function()
compilationCompleteBool = true;
}
goToCompilationError = function()
{
editor.setCursor(annotation.line, annotation.column)
}
editor.setOption("extraKeys", extraKeys);

BIN
mix/qml/img/signerroricon32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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

1
mix/res.qrc

@ -63,5 +63,6 @@
<file>qml/img/copyiconactive.png</file>
<file>qml/img/searchicon.png</file>
<file>qml/img/stop_button2x.png</file>
<file>qml/img/signerroricon32.png</file>
</qresource>
</RCC>

1
mix/test/TestService.cpp

@ -163,7 +163,6 @@ bool TestService::keyClickChar(QObject* _item, QString const& _character, int _m
bool TestService::mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay)
{
QWindow* window = qobject_cast<QWindow*>(_item);
QMetaObject const* mo = _item->metaObject();
if (!window)
window = eventWindow(_item);
mouseEvent(MouseClick, window, _item, Qt::MouseButton(_button), Qt::KeyboardModifiers(_modifiers), QPointF(_x, _y), _delay);

13
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
@ -535,8 +537,6 @@ int main(int argc, char** argv)
}
}
if (!clientName.empty())
clientName += "/";
@ -556,6 +556,7 @@ int main(int argc, char** argv)
&nodesState,
miners
);
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 +874,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 +940,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 +1028,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 +1114,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()) %

54
test/SolidityEndToEndTest.cpp

@ -966,6 +966,60 @@ BOOST_AUTO_TEST_CASE(simple_accessor)
BOOST_CHECK(callContractFunction("data()") == encodeArgs(8));
}
BOOST_AUTO_TEST_CASE(array_accessor)
{
char const* sourceCode = R"(
contract test {
uint[8] public data;
uint[] public dynamicData;
uint24[] public smallTypeData;
struct st { uint a; uint[] finalArray; }
mapping(uint256 => mapping(uint256 => st[5])) public multiple_map;
function test() {
data[0] = 8;
dynamicData.length = 3;
dynamicData[2] = 8;
smallTypeData.length = 128;
smallTypeData[1] = 22;
smallTypeData[127] = 2;
multiple_map[2][1][2].a = 3;
multiple_map[2][1][2].finalArray.length = 4;
multiple_map[2][1][2].finalArray[3] = 5;
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("data(uint256)", 0) == encodeArgs(8));
BOOST_CHECK(callContractFunction("data(uint256)", 8) == encodeArgs());
BOOST_CHECK(callContractFunction("dynamicData(uint256)", 2) == encodeArgs(8));
BOOST_CHECK(callContractFunction("dynamicData(uint256)", 8) == encodeArgs());
BOOST_CHECK(callContractFunction("smallTypeData(uint256)", 1) == encodeArgs(22));
BOOST_CHECK(callContractFunction("smallTypeData(uint256)", 127) == encodeArgs(2));
BOOST_CHECK(callContractFunction("smallTypeData(uint256)", 128) == encodeArgs());
BOOST_CHECK(callContractFunction("multiple_map(uint256,uint256,uint256)", 2, 1, 2) == encodeArgs(3));
}
BOOST_AUTO_TEST_CASE(accessors_mapping_for_array)
{
char const* sourceCode = R"(
contract test {
mapping(uint => uint[8]) public data;
mapping(uint => uint[]) public dynamicData;
function test() {
data[2][2] = 8;
dynamicData[2].length = 3;
dynamicData[2][2] = 8;
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("data(uint256,uint256)", 2, 2) == encodeArgs(8));
BOOST_CHECK(callContractFunction("data(uint256, 256)", 2, 8) == encodeArgs());
BOOST_CHECK(callContractFunction("dynamicData(uint256,uint256)", 2, 2) == encodeArgs(8));
BOOST_CHECK(callContractFunction("dynamicData(uint256,uint256)", 2, 8) == encodeArgs());
}
BOOST_AUTO_TEST_CASE(multiple_elementary_accessors)
{
char const* sourceCode = "contract test {\n"

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