Browse Source

Merge branch 'develop' into client_ref

cl-refactor
Marek Kotewicz 10 years ago
parent
commit
69a46405e2
  1. 1
      CMakeLists.txt
  2. 15
      alethzero/Transact.cpp
  3. 3
      alethzero/Transact.h
  4. 173
      alethzero/Transact.ui
  5. 95
      eth/main.cpp
  6. 31
      ethconsole/CMakeLists.txt
  7. 41
      ethconsole/main.cpp
  8. 2
      ethvm/main.cpp
  9. 4
      exp/main.cpp
  10. 6
      libdevcore/Common.cpp
  11. 25
      libdevcore/Common.h
  12. 3
      libdevcore/FixedHash.h
  13. 18
      libdevcore/RLP.h
  14. 2
      libdevcore/TrieCommon.cpp
  15. 8
      libdevcore/Worker.cpp
  16. 93
      libethash-cl/ethash_cl_miner.cpp
  17. 29
      libethash-cl/ethash_cl_miner.h
  18. 6
      libethcore/Common.h
  19. 60
      libethcore/Ethash.cpp
  20. 8
      libethcore/EthashAux.cpp
  21. 1
      libethcore/Exceptions.h
  22. 4
      libethcore/Miner.h
  23. 30
      libethereum/BlockChain.cpp
  24. 6
      libethereum/BlockChain.h
  25. 31
      libethereum/Client.cpp
  26. 8
      libethereum/Client.h
  27. 13
      libethereum/ClientBase.cpp
  28. 1
      libethereum/ClientBase.h
  29. 21
      libethereum/EthereumHost.cpp
  30. 2
      libethereum/Executive.cpp
  31. 36
      libethereum/State.cpp
  32. 2
      libethereum/State.h
  33. 7
      libethereum/Transaction.cpp
  34. 1
      libethereum/Transaction.h
  35. 26
      libethereum/TransactionQueue.cpp
  36. 6
      libevmasm/Assembly.cpp
  37. 4
      libevmasm/AssemblyItem.cpp
  38. 8
      libjsconsole/CMakeLists.txt
  39. 66
      libjsconsole/CURLRequest.cpp
  40. 58
      libjsconsole/CURLRequest.h
  41. 62
      libjsconsole/JSConsole.cpp
  42. 64
      libjsconsole/JSConsole.h
  43. 34
      libjsconsole/JSLocalConsole.cpp
  44. 50
      libjsconsole/JSLocalConsole.h
  45. 23
      libjsconsole/JSRemoteConsole.cpp
  46. 48
      libjsconsole/JSRemoteConsole.h
  47. 2
      libjsconsole/JSV8Connector.h
  48. 20
      libjsconsole/JSV8RemoteConnector.cpp
  49. 32
      libjsconsole/JSV8RemoteConnector.h
  50. 3
      libjsengine/JSResources.cmake
  51. 2
      libjsengine/JSV8Engine.cpp
  52. 3
      libjsengine/JSV8Engine.h
  53. 177
      libsolidity/ArrayUtils.cpp
  54. 4
      libsolidity/ArrayUtils.h
  55. 13
      libsolidity/Compiler.cpp
  56. 8
      libsolidity/Compiler.h
  57. 9
      libsolidity/CompilerContext.cpp
  58. 1
      libsolidity/CompilerContext.h
  59. 166
      libsolidity/CompilerUtils.cpp
  60. 23
      libsolidity/CompilerUtils.h
  61. 56
      libsolidity/ExpressionCompiler.cpp
  62. 7
      libsolidity/ExpressionCompiler.h
  63. 32
      libsolidity/Types.cpp
  64. 1
      libsolidity/Types.h
  65. 1
      libweb3jsonrpc/CMakeLists.txt
  66. 2
      libwebthree/WebThree.cpp
  67. 39
      mix/CodeModel.cpp
  68. 19
      mix/CodeModel.h
  69. 1
      mix/MixClient.cpp
  70. 18
      mix/qml/Block.qml
  71. 1
      mix/qml/Ether.qml
  72. 24
      mix/qml/QAddressView.qml
  73. 21
      mix/qml/QBoolTypeView.qml
  74. 23
      mix/qml/QHashTypeView.qml
  75. 33
      mix/qml/QIntTypeView.qml
  76. 28
      mix/qml/QStringTypeView.qml
  77. 58
      mix/qml/StructView.qml
  78. 714
      mix/qml/TransactionDialog.qml
  79. 6
      mix/qml/js/NetworkDeployment.js
  80. 4
      mix/qml/js/TransactionHelper.js
  81. 2
      test/libdevcrypto/trie.cpp
  82. 29
      test/libethcore/keymanager.cpp
  83. 121
      test/libsolidity/SolidityEndToEndTest.cpp
  84. 8
      test/libsolidity/solidityExecutionFramework.h

1
CMakeLists.txt

@ -394,6 +394,7 @@ endif ()
if (JSCONSOLE)
add_subdirectory(libjsengine)
add_subdirectory(libjsconsole)
add_subdirectory(ethconsole)
endif ()
add_subdirectory(secp256k1)

15
alethzero/Transact.cpp

@ -139,7 +139,7 @@ void Transact::updateDestination()
void Transact::updateFee()
{
ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str()));
// ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str()));
auto totalReq = total();
ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str()));
@ -435,9 +435,18 @@ Address Transact::fromAccount()
return *it;
}
void Transact::updateNonce()
{
u256 n = ethereum()->countAt(fromAccount(), PendingBlock);
ui->nonce->setMaximum((unsigned)n);
ui->nonce->setMinimum(0);
ui->nonce->setValue((unsigned)n);
}
void Transact::on_send_clicked()
{
// Secret s = findSecret(value() + fee());
u256 nonce = ui->autoNonce->isChecked() ? ethereum()->countAt(fromAccount(), PendingBlock) : ui->nonce->value();
auto a = fromAccount();
auto b = ethereum()->balanceAt(a, PendingBlock);
@ -455,7 +464,7 @@ void Transact::on_send_clicked()
{
// If execution is a contract creation, add Natspec to
// a local Natspec LEVELDB
ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice());
ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice(), nonce);
#if ETH_SOLIDITY
string src = ui->data->toPlainText().toStdString();
if (sourceIsSolidity(src))
@ -474,7 +483,7 @@ void Transact::on_send_clicked()
}
else
// TODO: cache like m_data.
ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, ui->gas->value(), gasPrice());
ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, ui->gas->value(), gasPrice(), nonce);
close();
}

3
alethzero/Transact.h

@ -45,7 +45,7 @@ public:
void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots:
void on_from_currentIndexChanged(int) { rejigData(); rejigData(); }
void on_from_currentIndexChanged(int) { updateNonce(); rejigData(); }
void on_destination_currentTextChanged(QString);
void on_value_valueChanged(int) { updateFee(); rejigData(); }
void on_gas_valueChanged(int) { updateFee(); rejigData(); }
@ -61,6 +61,7 @@ private slots:
private:
dev::eth::Client* ethereum() const { return m_ethereum; }
void rejigData();
void updateNonce();
dev::Address fromAccount();
void updateDestination();

173
alethzero/Transact.ui

@ -14,35 +14,6 @@
<string>Transact</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>D&amp;ata</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>data</cstring>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QCheckBox" name="optimize">
<property name="text">
<string>&amp;Optimise</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="gas">
<property name="suffix">
@ -92,6 +63,9 @@
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QComboBox" name="gasPriceUnits"/>
</item>
<item row="7" column="0" colspan="4">
<widget class="QLabel" name="total">
<property name="sizePolicy">
@ -105,22 +79,6 @@
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QSpinBox" name="value">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QComboBox" name="gasPriceUnits"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label5_2">
<property name="text">
@ -166,6 +124,54 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>D&amp;ata</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>data</cstring>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QComboBox" name="valueUnits"/>
</item>
<item row="3" column="1" colspan="2">
<widget class="QSpinBox" name="value">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QLineEdit" name="calculatedName">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string/>
</property>
</widget>
</item>
<item row="6" column="0" colspan="4">
<widget class="QSplitter" name="splitter_5">
<property name="orientation">
@ -205,53 +211,68 @@
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QLineEdit" name="calculatedName">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;From</string>
</property>
<property name="placeholderText">
<string/>
<property name="buddy">
<cstring>from</cstring>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QComboBox" name="valueUnits"/>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="from"/>
</item>
<item row="5" column="1" colspan="2">
<widget class="QLabel" name="fee">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="5" column="1">
<widget class="QCheckBox" name="optimize">
<property name="text">
<string/>
<string>&amp;Optimise</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<item row="5" column="3">
<widget class="QSpinBox" name="nonce">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QCheckBox" name="autoNonce">
<property name="text">
<string>&amp;From</string>
<string>Auto Nonce</string>
</property>
<property name="buddy">
<cstring>from</cstring>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoRepeat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="from"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
<connections>
<connection>
<sender>autoNonce</sender>
<signal>toggled(bool)</signal>
<receiver>nonce</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>374</x>
<y>196</y>
</hint>
<hint type="destinationlabel">
<x>451</x>
<y>190</y>
</hint>
</hints>
</connection>
</connections>
</ui>

95
eth/main.cpp

@ -41,7 +41,7 @@
#include <libwebthree/WebThree.h>
#if ETH_JSCONSOLE || !ETH_TRUE
#include <libjsconsole/JSConsole.h>
#include <libjsconsole/JSLocalConsole.h>
#endif
#if ETH_READLINE || !ETH_TRUE
#include <readline/readline.h>
@ -141,6 +141,7 @@ void help()
<< " --master <password> Give the master password for the key store." << endl
<< " --password <password> Give a password for a private key." << endl
<< " --sentinel <server> Set the sentinel for reporting bad blocks or chain issues." << endl
<< " --prime <n> Specify n as the 6 digit prime number to start Frontier." << endl
<< endl
<< "Client transacting:" << endl
/*<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl
@ -187,6 +188,7 @@ void help()
<< " --from <n> Export only from block n; n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl
<< " --to <n> Export only to block n (inclusive); n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl
<< " --only <n> Equivalent to --export-from n --export-to n." << endl
<< " --dont-check Avoids checking some of the aspects of blocks. Faster importing, but only do if you know the data is valid." << endl
<< endl
<< "General Options:" << endl
<< " -d,--db-path <path> Load database from path (default: " << getDataDir() << ")" << endl
@ -240,6 +242,16 @@ string pretty(h160 _a, dev::eth::State const& _st)
bool g_exit = false;
inline bool isPrime(unsigned _number)
{
if (((!(_number & 1)) && _number != 2 ) || (_number < 2) || (_number % 3 == 0 && _number != 3))
return false;
for(unsigned k = 1; 36 * k * k - 12 * k < _number; ++k)
if ((_number % (6 * k + 1) == 0) || (_number % (6 * k - 1) == 0))
return false;
return true;
}
void sighandler(int)
{
g_exit = true;
@ -280,9 +292,12 @@ int main(int argc, char** argv)
/// Operating mode.
OperationMode mode = OperationMode::Node;
string dbPath;
unsigned prime = 0;
bool yesIReallyKnowWhatImDoing = false;
/// File name for import/export.
string filename;
bool safeImport = false;
/// Hashes/numbers for export range.
string exportFrom = "1";
@ -395,11 +410,25 @@ int main(int argc, char** argv)
mode = OperationMode::Import;
filename = argv[++i];
}
else if (arg == "--dont-check")
safeImport = true;
else if ((arg == "-E" || arg == "--export") && i + 1 < argc)
{
mode = OperationMode::Export;
filename = argv[++i];
}
else if (arg == "--prime" && i + 1 < argc)
try
{
prime = stoi(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--yes-i-really-know-what-im-doing")
yesIReallyKnowWhatImDoing = true;
else if (arg == "--sentinel" && i + 1 < argc)
sentinel = argv[++i];
else if (arg == "--mine-on-wrong-chain")
@ -753,13 +782,18 @@ int main(int argc, char** argv)
unsigned futureTime = 0;
unsigned unknownParent = 0;
unsigned bad = 0;
chrono::steady_clock::time_point t = chrono::steady_clock::now();
double last = 0;
unsigned lastImported = 0;
unsigned imported = 0;
while (in.peek() != -1)
{
bytes block(8);
in.read((char*)block.data(), 8);
block.resize(RLP(block, RLP::LaisezFaire).actualSize());
in.read((char*)block.data() + 8, block.size() - 8);
switch (web3.ethereum()->injectBlock(block))
switch (web3.ethereum()->queueBlock(block, safeImport))
{
case ImportResult::Success: good++; break;
case ImportResult::AlreadyKnown: alreadyHave++; break;
@ -768,11 +802,52 @@ int main(int argc, char** argv)
case ImportResult::FutureTimeKnown: futureTime++; break;
default: bad++; break;
}
// sync chain with queue
tuple<ImportRoute, bool, unsigned> r = web3.ethereum()->syncQueue(10);
imported += get<2>(r);
double e = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - t).count() / 1000.0;
if ((unsigned)e >= last + 10)
{
auto i = imported - lastImported;
auto d = e - last;
cout << i << " more imported at " << (round(i * 10 / d) / 10) << " blocks/s. " << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << endl;
last = (unsigned)e;
lastImported = imported;
}
}
cout << (good + bad + futureTime + unknownParent + alreadyHave) << " total: " << good << " ok, " << alreadyHave << " got, " << futureTime << " future, " << unknownParent << " unknown parent, " << bad << " malformed." << endl;
while (web3.ethereum()->blockQueue().items().first + web3.ethereum()->blockQueue().items().second > 0)
{
sleep(1);
web3.ethereum()->syncQueue(100000);
}
double e = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - t).count() / 1000.0;
cout << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << endl;
return 0;
}
if (c_network == eth::Network::Frontier && !yesIReallyKnowWhatImDoing)
{
auto pd = contents(getDataDir() + "primes");
unordered_set<unsigned> primes = RLP(pd).toUnorderedSet<unsigned>();
while (true)
{
if (!prime)
try
{
prime = stoi(getPassword("To enter the Frontier, enter a 6 digit prime that you have not entered before: "));
}
catch (...) {}
if (isPrime(prime) && !primes.count(prime))
break;
prime = 0;
}
primes.insert(prime);
writeFile(getDataDir() + "primes", rlp(primes));
}
if (keyManager.exists())
{
if (masterPassword.empty() || !keyManager.load(masterPassword))
@ -1742,12 +1817,24 @@ int main(int argc, char** argv)
if (useConsole)
{
#if ETH_JSCONSOLE
JSConsole console(web3, make_shared<SimpleAccountHolder>([&](){return web3.ethereum();}, getAccountPassword, keyManager));
JSLocalConsole console;
jsonrpcServer = shared_ptr<dev::WebThreeStubServer>(new dev::WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager, *gasPricer));
jsonrpcServer->setMiningBenefactorChanger([&](Address const& a) { beneficiary = a; });
jsonrpcServer->StartListening();
if (jsonAdmin.empty())
jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}});
else
jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}});
cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl;
while (!g_exit)
{
console.readExpression();
stopMiningAfterXBlocks(c, n, mining);
}
jsonrpcServer->StopListening();
#endif
}
else

31
ethconsole/CMakeLists.txt

@ -0,0 +1,31 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
include_directories(${CURL_INCLUDE_DIRS})
include_directories(${V8_INCLUDE_DIRS})
set(EXECUTABLE ethconsole)
file(GLOB HEADERS "*.h")
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
endif()
target_link_libraries(${EXECUTABLE} jsconsole)
if (APPLE)
install(TARGETS ${EXECUTABLE} DESTINATION bin)
else()
eth_install_executable(${EXECUTABLE})
endif()

41
ethconsole/main.cpp

@ -0,0 +1,41 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Marek
* @date 2014
*/
#include <string>
#include <libjsconsole/JSRemoteConsole.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
int main(int argc, char** argv)
{
string remote;
if (argc == 1)
remote = "http://localhost:8545";
else if (argc == 2)
remote = argv[1];
JSRemoteConsole console(remote);
while (true)
console.readExpression();
return 0;
}

2
ethvm/main.cpp

@ -165,7 +165,7 @@ int main(int argc, char** argv)
executive.initialize(t);
executive.create(sender, value, gasPrice, gas, &data, origin);
boost::timer timer;
Timer timer;
executive.go(onOp);
double execTime = timer.elapsed();
executive.finalize();

4
exp/main.cpp

@ -88,7 +88,7 @@ int main()
data.push_back(rlp(i));
h256 ret;
DEV_TIMED(triedb)
DEV_TIMED("triedb")
{
MemoryDB mdb;
GenericTrieDB<MemoryDB> t(&mdb);
@ -99,7 +99,7 @@ int main()
ret = t.root();
}
cdebug << ret;
DEV_TIMED(hash256)
DEV_TIMED("hash256")
ret = orderedTrieRoot(data);
cdebug << ret;
}

6
libdevcore/Common.cpp

@ -48,9 +48,9 @@ const char* TimerChannel::name() { return EthRed " ⚡ "; }
TimerHelper::~TimerHelper()
{
auto e = m_t.elapsed();
if (!m_ms || e * 1000 > m_ms)
clog(TimerChannel) << m_id << e << "s";
auto e = std::chrono::high_resolution_clock::now() - m_t;
if (!m_ms || e > chrono::milliseconds(m_ms))
clog(TimerChannel) << m_id << chrono::duration_cast<chrono::milliseconds>(e).count() << "ms";
}
}

25
libdevcore/Common.h

@ -40,7 +40,7 @@
#include <unordered_set>
#include <functional>
#include <string>
#include <boost/timer.hpp>
#include <chrono>
#include <boost/functional/hash.hpp>
#pragma warning(push)
#pragma GCC diagnostic push
@ -193,16 +193,29 @@ private:
class TimerHelper
{
public:
TimerHelper(char const* _id, unsigned _msReportWhenGreater = 0): m_id(_id), m_ms(_msReportWhenGreater) {}
TimerHelper(std::string const& _id, unsigned _msReportWhenGreater = 0): m_t(std::chrono::high_resolution_clock::now()), m_id(_id), m_ms(_msReportWhenGreater) {}
~TimerHelper();
private:
boost::timer m_t;
char const* m_id;
std::chrono::high_resolution_clock::time_point m_t;
std::string m_id;
unsigned m_ms;
};
#define DEV_TIMED(S) for (::std::pair<::dev::TimerHelper, bool> __eth_t(#S, true); __eth_t.second; __eth_t.second = false)
class Timer
{
public:
Timer() { restart(); }
std::chrono::high_resolution_clock::duration duration() const { return std::chrono::high_resolution_clock::now() - m_t; }
double elapsed() const { return std::chrono::duration_cast<std::chrono::microseconds>(duration()).count() / 1000000.0; }
void restart() { m_t = std::chrono::high_resolution_clock::now(); }
private:
std::chrono::high_resolution_clock::time_point m_t;
};
#define DEV_TIMED(S) for (::std::pair<::dev::TimerHelper, bool> __eth_t(S, true); __eth_t.second; __eth_t.second = false)
#define DEV_TIMED_SCOPE(S) ::dev::TimerHelper __eth_t(S)
#if WIN32
#define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__FUNCSIG__)
@ -210,7 +223,7 @@ private:
#define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__PRETTY_FUNCTION__)
#endif
#define DEV_TIMED_ABOVE(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(#S, MS), true); __eth_t.second; __eth_t.second = false)
#define DEV_TIMED_ABOVE(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(S, MS), true); __eth_t.second; __eth_t.second = false)
#define DEV_TIMED_SCOPE_ABOVE(S, MS) ::dev::TimerHelper __eth_t(S, MS)
#if WIN32
#define DEV_TIMED_FUNCTION_ABOVE(MS) DEV_TIMED_SCOPE_ABOVE(__FUNCSIG__, MS)

3
libdevcore/FixedHash.h

@ -113,6 +113,9 @@ public:
/// @returns an abridged version of the hash as a user-readable hex string.
std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; }
/// @returns a version of the hash as a user-readable hex string that leaves out the middle part.
std::string abridgedMiddle() const { return toHex(ref().cropped(0, 4)) + "\342\200\246" + toHex(ref().cropped(N - 4)); }
/// @returns the hash as a user-readable hex string.
std::string hex() const { return toHex(ref()); }

18
libdevcore/RLP.h

@ -204,9 +204,7 @@ public:
{
ret.reserve(itemCount());
for (auto const& i: *this)
{
ret.push_back((T)i);
}
}
return ret;
}
@ -216,15 +214,21 @@ public:
{
std::set<T> ret;
if (isList())
{
for (auto const& i: *this)
{
ret.insert((T)i);
}
}
return ret;
}
template <class T>
std::unordered_set<T> toUnorderedSet() const
{
std::unordered_set<T> ret;
if (isList())
for (auto const& i: *this)
ret.insert((T)i);
return ret;
}
template <class T, class U>
std::pair<T, U> toPair() const
{

2
libdevcore/TrieCommon.cpp

@ -60,7 +60,7 @@ std::string hexPrefixEncode(bytes const& _hexVector, bool _leaf, int _begin, int
std::string hexPrefixEncode(bytesConstRef _data, bool _leaf, int _beginNibble, int _endNibble, unsigned _offset)
{
unsigned begin = _beginNibble + _offset;
unsigned end = (_endNibble < 0 ? (_data.size() * 2 - _offset) + 1 + _endNibble : _endNibble) + _offset;
unsigned end = (_endNibble < 0 ? ((int)(_data.size() * 2 - _offset) + 1) + _endNibble : _endNibble) + _offset;
bool odd = (end - begin) & 1;
std::string ret(1, ((_leaf ? 2 : 0) | (odd ? 1 : 0)) * 16);

8
libdevcore/Worker.cpp

@ -65,14 +65,14 @@ void Worker::startWorking()
m_state.exchange(ex);
// cnote << "Waiting until not Stopped...";
DEV_TIMED_ABOVE(Worker stopping, 100)
DEV_TIMED_ABOVE("Worker stopping", 100)
while (m_state == WorkerState::Stopped)
this_thread::sleep_for(chrono::milliseconds(20));
}
}));
// cnote << "Spawning" << m_name;
}
DEV_TIMED_ABOVE(Start worker, 100)
DEV_TIMED_ABOVE("Start worker", 100)
while (m_state == WorkerState::Starting)
this_thread::sleep_for(chrono::microseconds(20));
}
@ -85,7 +85,7 @@ void Worker::stopWorking()
WorkerState ex = WorkerState::Started;
m_state.compare_exchange_strong(ex, WorkerState::Stopping);
DEV_TIMED_ABOVE(Stop worker, 100)
DEV_TIMED_ABOVE("Stop worker", 100)
while (m_state != WorkerState::Stopped)
this_thread::sleep_for(chrono::microseconds(20));
}
@ -99,7 +99,7 @@ void Worker::terminate()
{
m_state.exchange(WorkerState::Killing);
DEV_TIMED_ABOVE(Terminate worker, 100)
DEV_TIMED_ABOVE("Terminate worker", 100)
m_work->join();
m_work.reset();

93
libethash-cl/ethash_cl_miner.cpp

@ -30,6 +30,7 @@
#include <queue>
#include <random>
#include <vector>
#include <boost/timer.hpp>
#include <libethash/util.h>
#include <libethash/ethash.h>
#include <libethash/internal.h>
@ -64,7 +65,7 @@ static void addDefinition(string& _source, char const* _id, unsigned _value)
ethash_cl_miner::search_hook::~search_hook() {}
ethash_cl_miner::ethash_cl_miner()
: m_opencl_1_1()
: m_openclOnePointOne()
{
}
@ -252,7 +253,7 @@ void ethash_cl_miner::finish()
bool ethash_cl_miner::init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned workgroup_size,
unsigned _workgroupSize,
unsigned _platformId,
unsigned _deviceId
)
@ -291,23 +292,23 @@ bool ethash_cl_miner::init(
return false;
}
if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
m_opencl_1_1 = true;
m_openclOnePointOne = true;
// create context
m_context = cl::Context(vector<cl::Device>(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8
m_workgroup_size = ((workgroup_size + 7) / 8) * 8;
m_workgroupSize = ((_workgroupSize + 7) / 8) * 8;
// patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled
// into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime
string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
addDefinition(code, "GROUP_SIZE", m_workgroup_size);
addDefinition(code, "GROUP_SIZE", m_workgroupSize);
addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
addDefinition(code, "MAX_OUTPUTS", c_max_search_results);
addDefinition(code, "MAX_OUTPUTS", c_maxSearchResults);
//debugf("%s", code.c_str());
// create miner OpenCL program
@ -330,7 +331,7 @@ bool ethash_cl_miner::init(
// create buffer for dag
try
{
m_dagChunksNum = 1;
m_dagChunksCount = 1;
m_dagChunks.push_back(cl::Buffer(m_context, CL_MEM_READ_ONLY, _dagSize));
ETHCL_LOG("Created one big buffer for the DAG");
}
@ -346,8 +347,8 @@ bool ethash_cl_miner::init(
<< result << ". Trying to allocate 4 chunks."
);
// The OpenCL kernel has a hard coded number of 4 chunks at the moment
m_dagChunksNum = 4;
for (unsigned i = 0; i < m_dagChunksNum; i++)
m_dagChunksCount = 4;
for (unsigned i = 0; i < m_dagChunksCount; i++)
{
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
ETHCL_LOG("Creating buffer for chunk " << i);
@ -359,24 +360,24 @@ bool ethash_cl_miner::init(
}
}
if (m_dagChunksNum == 1)
if (m_dagChunksCount == 1)
{
ETHCL_LOG("Loading single big chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash");
m_search_kernel = cl::Kernel(program, "ethash_search");
m_hashKernel = cl::Kernel(program, "ethash_hash");
m_searchKernel = cl::Kernel(program, "ethash_search");
}
else
{
ETHCL_LOG("Loading chunk kernels");
m_hash_kernel = cl::Kernel(program, "ethash_hash_chunks");
m_search_kernel = cl::Kernel(program, "ethash_search_chunks");
m_hashKernel = cl::Kernel(program, "ethash_hash_chunks");
m_searchKernel = cl::Kernel(program, "ethash_search_chunks");
}
// create buffer for header
ETHCL_LOG("Creating buffer for header.");
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
if (m_dagChunksNum == 1)
if (m_dagChunksCount == 1)
{
ETHCL_LOG("Mapping one big chunk.");
m_queue.enqueueWriteBuffer(m_dagChunks[0], CL_TRUE, 0, _dagSize, _dag);
@ -385,12 +386,12 @@ bool ethash_cl_miner::init(
{
// TODO Note: If we ever change to _dagChunksNum other than 4, then the size would need recalculation
void* dag_ptr[4];
for (unsigned i = 0; i < m_dagChunksNum; i++)
for (unsigned i = 0; i < m_dagChunksCount; i++)
{
ETHCL_LOG("Mapping chunk " << i);
dag_ptr[i] = m_queue.enqueueMapBuffer(m_dagChunks[i], true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
dag_ptr[i] = m_queue.enqueueMapBuffer(m_dagChunks[i], true, m_openclOnePointOne ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
}
for (unsigned i = 0; i < m_dagChunksNum; i++)
for (unsigned i = 0; i < m_dagChunksCount; i++)
{
memcpy(dag_ptr[i], (char *)_dag + i*((_dagSize >> 9) << 7), (i == 3) ? (_dagSize - 3 * ((_dagSize >> 9) << 7)) : (_dagSize >> 9) << 7);
m_queue.enqueueUnmapMemObject(m_dagChunks[i], dag_ptr[i]);
@ -398,11 +399,11 @@ bool ethash_cl_miner::init(
}
// create mining buffers
for (unsigned i = 0; i != c_num_buffers; ++i)
for (unsigned i = 0; i != c_bufferCount; ++i)
{
ETHCL_LOG("Creating mining buffer " << i);
m_hash_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_opencl_1_1 ? CL_MEM_HOST_READ_ONLY : 0), 32 * c_hash_batch_size);
m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t));
m_hashBuffer[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_openclOnePointOne ? CL_MEM_HOST_READ_ONLY : 0), 32 * c_hashBatchSize);
m_searchBuffer[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_maxSearchResults + 1) * sizeof(uint32_t));
}
}
catch (cl::Error const& err)
@ -413,7 +414,7 @@ bool ethash_cl_miner::init(
return true;
}
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook, unsigned _msPerBatch)
{
try
{
@ -429,8 +430,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
// update header constant buffer
m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header);
for (unsigned i = 0; i != c_num_buffers; ++i)
m_queue.enqueueWriteBuffer(m_search_buf[i], false, 0, 4, &c_zero);
for (unsigned i = 0; i != c_bufferCount; ++i)
m_queue.enqueueWriteBuffer(m_searchBuffer[i], false, 0, 4, &c_zero);
#if CL_VERSION_1_2 && 0
cl::Event pre_return_event;
@ -441,53 +442,59 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_queue.finish();
unsigned argPos = 2;
m_search_kernel.setArg(1, m_header);
for (unsigned i = 0; i < m_dagChunksNum; ++i, ++argPos)
m_search_kernel.setArg(argPos, m_dagChunks[i]);
m_searchKernel.setArg(1, m_header);
for (unsigned i = 0; i < m_dagChunksCount; ++i, ++argPos)
m_searchKernel.setArg(argPos, m_dagChunks[i]);
// pass these to stop the compiler unrolling the loops
m_search_kernel.setArg(argPos + 1, target);
m_search_kernel.setArg(argPos + 2, ~0u);
m_searchKernel.setArg(argPos + 1, target);
m_searchKernel.setArg(argPos + 2, ~0u);
unsigned buf = 0;
random_device engine;
uint64_t start_nonce = uniform_int_distribution<uint64_t>()(engine);
for (;; start_nonce += c_search_batch_size)
for (;; start_nonce += m_batchSize)
{
// supply output buffer to kernel
m_search_kernel.setArg(0, m_search_buf[buf]);
if (m_dagChunksNum == 1)
m_search_kernel.setArg(3, start_nonce);
m_searchKernel.setArg(0, m_searchBuffer[buf]);
if (m_dagChunksCount == 1)
m_searchKernel.setArg(3, start_nonce);
else
m_search_kernel.setArg(6, start_nonce);
m_searchKernel.setArg(6, start_nonce);
// execute it!
m_queue.enqueueNDRangeKernel(m_search_kernel, cl::NullRange, c_search_batch_size, m_workgroup_size);
boost::timer t;
m_queue.enqueueNDRangeKernel(m_searchKernel, cl::NullRange, m_batchSize, m_workgroupSize);
unsigned ms = t.elapsed() * 1000;
if (ms > _msPerBatch * 1.1)
m_batchSize = max<unsigned>(128, m_batchSize * 9 / 10);
else if (ms < _msPerBatch * 0.9)
m_batchSize = m_batchSize * 10 / 9;
pending.push({ start_nonce, buf });
buf = (buf + 1) % c_num_buffers;
buf = (buf + 1) % c_bufferCount;
// read results
if (pending.size() == c_num_buffers)
if (pending.size() == c_bufferCount)
{
pending_batch const& batch = pending.front();
// could use pinned host pointer instead
uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_search_buf[batch.buf], true, CL_MAP_READ, 0, (1 + c_max_search_results) * sizeof(uint32_t));
unsigned num_found = min<unsigned>(results[0], c_max_search_results);
uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_searchBuffer[batch.buf], true, CL_MAP_READ, 0, (1 + c_maxSearchResults) * sizeof(uint32_t));
unsigned num_found = min<unsigned>(results[0], c_maxSearchResults);
uint64_t nonces[c_max_search_results];
uint64_t nonces[c_maxSearchResults];
for (unsigned i = 0; i != num_found; ++i)
nonces[i] = batch.start_nonce + results[i + 1];
m_queue.enqueueUnmapMemObject(m_search_buf[batch.buf], results);
m_queue.enqueueUnmapMemObject(m_searchBuffer[batch.buf], results);
bool exit = num_found && hook.found(nonces, num_found);
exit |= hook.searched(batch.start_nonce, c_search_batch_size); // always report searched before exit
exit |= hook.searched(batch.start_nonce, m_batchSize); // always report searched before exit
if (exit)
break;
// reset search buffer if we're still going
if (num_found)
m_queue.enqueueWriteBuffer(m_search_buf[batch.buf], true, 0, 4, &c_zero);
m_queue.enqueueWriteBuffer(m_searchBuffer[batch.buf], true, 0, 4, &c_zero);
pending.pop();
}

29
libethash-cl/ethash_cl_miner.h

@ -19,6 +19,9 @@
class ethash_cl_miner
{
private:
enum { c_maxSearchResults = 63, c_bufferCount = 2, c_hashBatchSize = 1024, c_searchBatchSize = 1024 * 16 };
public:
struct search_hook
{
@ -29,7 +32,6 @@ public:
virtual bool searched(uint64_t start_nonce, uint32_t count) = 0;
};
public:
ethash_cl_miner();
~ethash_cl_miner();
@ -50,33 +52,32 @@ public:
bool init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned workgroup_size = 64,
unsigned _workgroupSize = 64,
unsigned _platformId = 0,
unsigned _deviceId = 0
);
void finish();
void search(uint8_t const* header, uint64_t target, search_hook& hook);
void search(uint8_t const* _header, uint64_t _target, search_hook& _hook, unsigned _msPerBatch = 100);
void hash_chunk(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);
void search_chunk(uint8_t const* header, uint64_t target, search_hook& hook);
void hash_chunk(uint8_t* _ret, uint8_t const* _header, uint64_t _nonce, unsigned _count);
void search_chunk(uint8_t const*_header, uint64_t _target, search_hook& _hook);
private:
static std::vector<cl::Device> getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId);
enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 };
cl::Context m_context;
cl::CommandQueue m_queue;
cl::Kernel m_hash_kernel;
cl::Kernel m_search_kernel;
unsigned int m_dagChunksNum;
cl::Kernel m_hashKernel;
cl::Kernel m_searchKernel;
unsigned int m_dagChunksCount;
std::vector<cl::Buffer> m_dagChunks;
cl::Buffer m_header;
cl::Buffer m_hash_buf[c_num_buffers];
cl::Buffer m_search_buf[c_num_buffers];
unsigned m_workgroup_size;
bool m_opencl_1_1;
cl::Buffer m_hashBuffer[c_bufferCount];
cl::Buffer m_searchBuffer[c_bufferCount];
unsigned m_workgroupSize;
unsigned m_batchSize = c_searchBatchSize;
bool m_openclOnePointOne;
/// Allow CPU to appear as an OpenCL device or not. Default is false
static bool s_allowCPU;

6
libethcore/Common.h

@ -97,6 +97,12 @@ enum class RelativeBlock: BlockNumber
Pending = PendingBlock
};
struct ImportRoute
{
h256s deadBlocks;
h256s liveBlocks;
};
enum class ImportResult
{
Success = 0,

60
libethcore/Ethash.cpp

@ -225,26 +225,58 @@ std::string Ethash::CPUMiner::platformInfo()
#if ETH_ETHASHCL || !ETH_TRUE
using UniqueGuard = std::unique_lock<std::mutex>;
template <class N>
class Notified
{
public:
Notified() {}
Notified(N const& _v): m_value(_v) {}
Notified(Notified const&) = delete;
Notified& operator=(N const& _v) { UniqueGuard l(m_mutex); m_value = _v; m_cv.notify_all(); return *this; }
operator N() const { UniqueGuard l(m_mutex); return m_value; }
void wait() const { UniqueGuard l(m_mutex); m_cv.wait(l); }
void wait(N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait(l, [&](){return m_value == _v;}); }
template <class F> void wait(F const& _f) const { UniqueGuard l(m_mutex); m_cv.wait(l, _f); }
private:
mutable Mutex m_mutex;
mutable std::condition_variable m_cv;
N m_value;
};
class EthashCLHook: public ethash_cl_miner::search_hook
{
public:
EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {}
EthashCLHook(EthashCLHook const&) = delete;
void abort()
{
Guard l(x_all);
if (m_aborted)
return;
{
UniqueGuard l(x_all);
if (m_aborted)
return;
// cdebug << "Attempting to abort";
m_abort = true;
for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout)
std::this_thread::sleep_for(chrono::milliseconds(30));
m_abort = true;
}
// m_abort is true so now searched()/found() will return true to abort the search.
// we hang around on this thread waiting for them to point out that they have aborted since
// otherwise we may end up deleting this object prior to searched()/found() being called.
m_aborted.wait(true);
// for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout)
// std::this_thread::sleep_for(chrono::milliseconds(30));
// if (!m_aborted)
// cwarn << "Couldn't abort. Abandoning OpenCL process.";
}
void reset()
{
UniqueGuard l(x_all);
m_aborted = m_abort = false;
}
@ -253,27 +285,19 @@ protected:
{
// dev::operator <<(std::cerr << "Found nonces: ", vector<uint64_t>(_nonces, _nonces + _count)) << std::endl;
for (uint32_t i = 0; i < _count; ++i)
{
if (m_owner->report(_nonces[i]))
{
m_aborted = true;
return true;
}
}
return (m_aborted = true);
return m_owner->shouldStop();
}
virtual bool searched(uint64_t _startNonce, uint32_t _count) override
{
Guard l(x_all);
UniqueGuard l(x_all);
// std::cerr << "Searched " << _count << " from " << _startNonce << std::endl;
m_owner->accumulateHashes(_count);
m_last = _startNonce + _count;
if (m_abort || m_owner->shouldStop())
{
m_aborted = true;
return true;
}
return (m_aborted = true);
return false;
}
@ -281,7 +305,7 @@ private:
Mutex x_all;
uint64_t m_last;
bool m_abort = false;
bool m_aborted = true;
Notified<bool> m_aborted = {true};
Ethash::GPUMiner* m_owner = nullptr;
};

8
libethcore/EthashAux.cpp

@ -119,9 +119,11 @@ void EthashAux::killCache(h256 const& _s)
EthashAux::LightType EthashAux::light(h256 const& _seedHash)
{
ReadGuard l(get()->x_lights);
LightType ret = get()->m_lights[_seedHash];
return ret ? ret : (get()->m_lights[_seedHash] = make_shared<LightAllocation>(_seedHash));
UpgradableGuard l(get()->x_lights);
if (get()->m_lights.count(_seedHash))
return get()->m_lights.at(_seedHash);
UpgradeGuard l2(l);
return (get()->m_lights[_seedHash] = make_shared<LightAllocation>(_seedHash));
}
EthashAux::LightAllocation::LightAllocation(h256 const& _seedHash)

1
libethcore/Exceptions.h

@ -51,6 +51,7 @@ DEV_SIMPLE_EXCEPTION(FeeTooSmall);
DEV_SIMPLE_EXCEPTION(TooMuchGasUsed);
DEV_SIMPLE_EXCEPTION(ExtraDataTooBig);
DEV_SIMPLE_EXCEPTION(InvalidSignature);
DEV_SIMPLE_EXCEPTION(InvalidTransactionFormat);
DEV_SIMPLE_EXCEPTION(InvalidBlockFormat);
DEV_SIMPLE_EXCEPTION(InvalidUnclesHash);
DEV_SIMPLE_EXCEPTION(TooManyUncles);

4
libethcore/Miner.h

@ -107,9 +107,9 @@ public:
}
if (!!_work)
{
DEV_TIMED_ABOVE(pause, 250)
DEV_TIMED_ABOVE("pause", 250)
pause();
DEV_TIMED_ABOVE(kickOff, 250)
DEV_TIMED_ABOVE("kickOff", 250)
kickOff();
}
else if (!_work && !!old)

30
libethereum/BlockChain.cpp

@ -149,6 +149,7 @@ void BlockChain::open(std::string const& _path, WithExisting _we)
ldb::Options o;
o.create_if_missing = true;
o.max_open_files = 256;
ldb::DB::Open(o, path + "/blocks", &m_blocksDB);
ldb::DB::Open(o, path + "/details", &m_extrasDB);
if (!m_blocksDB || !m_extrasDB)
@ -239,7 +240,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
m_extrasDB->Put(m_writeOptions, toSlice(m_lastBlockHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[m_lastBlockHash].rlp()));
h256 lastHash = m_lastBlockHash;
boost::timer t;
Timer t;
for (unsigned d = 1; d < originalNumber; ++d)
{
if (!(d % 1000))
@ -323,7 +324,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
{
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE(Block import, 500)
DEV_TIMED_ABOVE("Block import", 500)
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.liveBlocks;
dead += r.deadBlocks;
@ -397,6 +398,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
catch (Exception& ex)
{
// clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
ex << errinfo_phase(2);
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
throw;
@ -411,13 +413,13 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
//@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
#if ETH_TIMED_IMPORTS
boost::timer total;
Timer total;
double preliminaryChecks;
double enactment;
double collation;
double writing;
double checkBest;
boost::timer t;
Timer t;
#endif
// Check block doesn't already exist first!
@ -469,6 +471,9 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
h256 newLastBlockHash = currentHash();
unsigned newLastBlockNumber = number();
BlockLogBlooms blb;
BlockReceipts br;
u256 td;
#if ETH_CATCH
try
@ -479,8 +484,6 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
State s(_db);
auto tdIncrease = s.enactOn(_block, *this, _ir);
BlockLogBlooms blb;
BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i)
{
blb.blooms.push_back(s.receipt(i).bloom());
@ -674,15 +677,17 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
#if ETH_TIMED_IMPORTS
checkBest = t.elapsed();
if (total.elapsed() > 1.0)
if (total.elapsed() > 0.5)
{
cnote << "SLOW IMPORT:" << _block.info.hash();
cnote << "SLOW IMPORT:" << _block.info.hash() << " #" << _block.info.number;
cnote << " Import took:" << total.elapsed();
cnote << " preliminaryChecks:" << preliminaryChecks;
cnote << " enactment:" << enactment;
cnote << " collation:" << collation;
cnote << " writing:" << writing;
cnote << " checkBest:" << checkBest;
cnote << " " << _block.transactions.size() << " transactions";
cnote << " " << _block.info.gasUsed << " gas used";
}
#endif
@ -1080,6 +1085,7 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exce
}
catch (Exception& ex)
{
ex << errinfo_phase(1);
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
if (_onBad)
@ -1097,6 +1103,7 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exce
}
catch (Exception& ex)
{
ex << errinfo_phase(1);
ex << errinfo_uncleIndex(i);
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
@ -1107,15 +1114,18 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function<void(Exce
++i;
}
i = 0;
for (auto const& tr: r[1])
for (RLP const& tr: r[1])
{
bytesConstRef d = tr.data();
try
{
res.transactions.push_back(Transaction(tr.data(), CheckTransaction::Everything));
res.transactions.push_back(Transaction(d, CheckTransaction::Everything));
}
catch (Exception& ex)
{
ex << errinfo_phase(1);
ex << errinfo_transactionIndex(i);
ex << errinfo_transaction(d.toBytes());
ex << errinfo_block(_block);
throw;
}

6
libethereum/BlockChain.h

@ -76,12 +76,6 @@ using BlocksHash = std::unordered_map<h256, bytes>;
using TransactionHashes = h256s;
using UncleHashes = h256s;
struct ImportRoute
{
h256s deadBlocks;
h256s liveBlocks;
};
enum {
ExtraDetails = 0,
ExtraBlockHash,

31
libethereum/Client.cpp

@ -87,6 +87,18 @@ void VersionChecker::setOk()
}
}
ImportResult Client::queueBlock(bytes const& _block, bool _isSafe)
{
if (m_bq.status().verified + m_bq.status().verifying + m_bq.status().unverified > 30000)
this_thread::sleep_for(std::chrono::milliseconds(500));
return m_bq.import(&_block, bc(), _isSafe);
}
tuple<ImportRoute, bool, unsigned> Client::syncQueue(unsigned _max)
{
return m_bc.sync(m_bq, m_stateDB, _max);
}
void Client::onBadBlock(Exception& _ex) const
{
// BAD BLOCK!!!
@ -159,7 +171,9 @@ void Client::onBadBlock(Exception& _ex) const
DEV_HINT_ERRINFO(max);
DEV_HINT_ERRINFO(name);
DEV_HINT_ERRINFO(field);
DEV_HINT_ERRINFO(transaction);
DEV_HINT_ERRINFO(data);
DEV_HINT_ERRINFO(phase);
DEV_HINT_ERRINFO_HASH(nonce);
DEV_HINT_ERRINFO(difficulty);
DEV_HINT_ERRINFO(target);
@ -367,7 +381,7 @@ void Client::startedWorking()
{
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
cdebug << "startedWorking()";
clog(ClientTrace) << "startedWorking()";
DEV_WRITE_GUARDED(x_preMine)
m_preMine.sync(m_bc);
@ -563,7 +577,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
try
{
State temp;
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
// clog(ClientTrace) << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
DEV_READ_GUARDED(x_postMine)
temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas);
@ -628,12 +642,12 @@ void Client::syncBlockQueue()
cwork << "BQ ==> CHAIN ==> STATE";
ImportRoute ir;
unsigned count;
boost::timer t;
Timer t;
tie(ir, m_syncBlockQueue, count) = m_bc.sync(m_bq, m_stateDB, m_syncAmount);
double elapsed = t.elapsed();
if (count)
cnote << count << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (count / elapsed) << "blocks/s)";
clog(ClientNote) << count << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (count / elapsed) << "blocks/s)";
if (elapsed > c_targetDuration * 1.1 && count > c_syncMin)
m_syncAmount = max(c_syncMin, count * 9 / 10);
@ -666,7 +680,6 @@ void Client::syncTransactionQueue()
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
// Tell farm about new transaction (i.e. restartProofOfWork mining).
onPostStateChanged();
@ -732,7 +745,7 @@ void Client::restartMining()
if (preChanged || m_postMine.address() != m_preMine.address())
{
if (isMining())
cnote << "New block on chain.";
clog(ClientTrace) << "New block on chain.";
DEV_WRITE_GUARDED(x_preMine)
m_preMine = newPreMine;
@ -774,7 +787,7 @@ bool Client::remoteActive() const
void Client::onPostStateChanged()
{
cnote << "Post state changed.";
clog(ClientTrace) << "Post state changed.";
rejigMining();
m_remoteWorking = false;
}
@ -789,7 +802,7 @@ void Client::rejigMining()
{
if ((wouldMine() || remoteActive()) && !isMajorSyncing() && (!isChainBad() || mineOnBadChain()) /*&& (forceMining() || transactionsWaiting())*/)
{
cnote << "Rejigging mining...";
clog(ClientTrace) << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(m_bc);
DEV_READ_GUARDED(x_working)
@ -886,7 +899,7 @@ void Client::checkWatchGarbage()
if (m_watches[key].lastPoll != chrono::system_clock::time_point::max() && chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20))
{
toUninstall.push_back(key);
cnote << "GC: Uninstall" << key << "(" << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
clog(ClientTrace) << "GC: Uninstall" << key << "(" << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
}
for (auto i: toUninstall)
uninstallWatch(i);

8
libethereum/Client.h

@ -138,6 +138,9 @@ public:
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() override;
/// Queues a block for import.
ImportResult queueBlock(bytes const& _block, bool _isSafe = false);
using Interface::call; // to remove warning about hiding virtual function
/// Makes the given call. Nothing is recorded into the state. This cheats by creating a null address and endowing it with a lot of ETH.
ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether, Address const& _from = Address());
@ -160,6 +163,11 @@ public:
SyncStatus syncStatus() const;
/// Get the block queue.
BlockQueue const& blockQueue() const { return m_bq; }
/// Get the block queue.
OverlayDB const& stateDB() const { return m_stateDB; }
/// Freeze worker thread and sync some of the block queue.
std::tuple<ImportRoute, bool, unsigned> syncQueue(unsigned _max = 1);
// Mining stuff:

13
libethereum/ClientBase.cpp

@ -56,6 +56,19 @@ void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, b
cnote << "New transaction " << t;
}
Address ClientBase::submitTransaction(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
{
prepareForTransaction();
Transaction t(_value, _gasPrice, _gas, _data, _nonce, _secret);
m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t;
return right160(sha3(rlpList(t.sender(), t.nonce())));
}
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
auto a = toAddress(_secret);

1
libethereum/ClientBase.h

@ -81,6 +81,7 @@ public:
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, u256 _nonce);
virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override;
using Interface::submitTransaction;

21
libethereum/EthereumHost.cpp

@ -166,20 +166,21 @@ tuple<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>, vector
vector<shared_ptr<EthereumPeer>> allowed;
vector<shared_ptr<Session>> sessions;
auto const& ps = peerSessions();
allowed.reserve(ps.size());
for (auto const& j: ps)
size_t peerCount = 0;
foreachPeer([&](std::shared_ptr<EthereumPeer> _p)
{
auto pp = j.first->cap<EthereumPeer>();
if (_allow(pp.get()))
if (_allow(_p.get()))
{
allowed.push_back(move(pp));
sessions.push_back(move(j.first));
allowed.push_back(_p);
sessions.push_back(_p->session());
}
}
++peerCount;
return true;
});
chosen.reserve((ps.size() * _percent + 99) / 100);
for (unsigned i = (ps.size() * _percent + 99) / 100; i-- && allowed.size();)
size_t chosenSize = (peerCount * _percent + 99) / 100;
chosen.reserve(chosenSize);
for (unsigned i = chosenSize; i && allowed.size(); i--)
{
unsigned n = rand() % allowed.size();
chosen.push_back(std::move(allowed[n]));

2
libethereum/Executive.cpp

@ -316,7 +316,7 @@ bool Executive::go(OnOpFunc const& _onOp)
if (m_ext)
{
#if ETH_TIMED_EXECUTIONS
boost::timer t;
Timer t;
#endif
try
{

36
libethereum/State.cpp

@ -139,7 +139,7 @@ PopulationStatistics State::populateFromChain(BlockChain const& _bc, h256 const&
// 2. Enact the block's transactions onto this state.
m_ourAddress = bi.coinbaseAddress;
boost::timer t;
Timer t;
auto vb = BlockChain::verifyBlock(b);
ret.verify = t.elapsed();
t.restart();
@ -401,7 +401,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi, Impor
u256 State::enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
#if ETH_TIMED_ENACTMENTS
boost::timer t;
Timer t;
double populateVerify;
double populateGrand;
double syncReset;
@ -507,7 +507,7 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
{
if (i.second.gasPrice() >= _gp.ask(*this))
{
// boost::timer t;
// Timer t;
if (lh.empty())
lh = _bc.lastHashes();
execute(lh, i.second);
@ -531,8 +531,20 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
if (req > got)
{
// too old
cnote << i.first << "Dropping old transaction (nonce too low)";
_tq.drop(i.first);
for (Transaction const& t: m_transactions)
if (t.from() == i.second.from())
{
if (t.nonce() < i.second.nonce())
{
cnote << i.first << "Dropping old transaction (nonce too low)";
_tq.drop(i.first);
}
else if (t.nonce() == i.second.nonce() && t.gasPrice() <= i.second.gasPrice())
{
cnote << i.first << "Dropping old transaction (gas price lower)";
_tq.drop(i.first);
}
}
}
else if (got > req + _tq.waiting(i.second.sender()))
{
@ -634,7 +646,7 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
// cnote << m_state;
LastHashes lh;
DEV_TIMED_ABOVE(lastHashes, 500)
DEV_TIMED_ABOVE("lastHashes", 500)
lh = _bc.lastHashes((unsigned)m_previousBlock.number);
RLP rlp(_block.block);
@ -643,7 +655,7 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
// All ok with the block generally. Play back the transactions now...
unsigned i = 0;
DEV_TIMED_ABOVE(txEcec, 500)
DEV_TIMED_ABOVE("txExec", 500)
for (auto const& tr: _block.transactions)
{
try
@ -664,7 +676,7 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
}
h256 receiptsRoot;
DEV_TIMED_ABOVE(receiptsRoot, 500)
DEV_TIMED_ABOVE("receiptsRoot", 500)
receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot)
@ -698,12 +710,12 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
vector<BlockInfo> rewarded;
h256Hash excluded;
DEV_TIMED_ABOVE(allKin, 500)
DEV_TIMED_ABOVE("allKin", 500)
excluded = _bc.allKinFrom(m_currentBlock.parentHash, 6);
excluded.insert(m_currentBlock.hash());
unsigned ii = 0;
DEV_TIMED_ABOVE(uncleCheck, 500)
DEV_TIMED_ABOVE("uncleCheck", 500)
for (auto const& i: rlp[2])
{
try
@ -752,11 +764,11 @@ u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc, ImportR
}
}
DEV_TIMED_ABOVE(applyRewards, 500)
DEV_TIMED_ABOVE("applyRewards", 500)
applyRewards(rewarded);
// Commit all cached state changes to the state trie.
DEV_TIMED_ABOVE(commit, 500)
DEV_TIMED_ABOVE("commit", 500)
commit();
// Hash the state trie and check against the state_root hash in m_currentBlock.

2
libethereum/State.h

@ -58,6 +58,8 @@ using errinfo_transactionIndex = boost::error_info<struct tag_transactionIndex,
using errinfo_vmtrace = boost::error_info<struct tag_vmtrace, std::string>;
using errinfo_receipts = boost::error_info<struct tag_receipts, std::vector<bytes>>;
using errinfo_transaction = boost::error_info<struct tag_transaction, bytes>;
using errinfo_phase = boost::error_info<struct tag_phase, unsigned>;
using errinfo_required_LogBloom = boost::error_info<struct tag_required_LogBloom, LogBloom>;
using errinfo_got_LogBloom = boost::error_info<struct tag_get_LogBloom, LogBloom>;
using LogBloomRequirementError = boost::tuple<errinfo_required_LogBloom, errinfo_got_LogBloom>;

7
libethereum/Transaction.cpp

@ -77,6 +77,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionException cons
{
case TransactionException::None: _out << "None"; break;
case TransactionException::BadRLP: _out << "BadRLP"; break;
case TransactionException::InvalidFormat: _out << "InvalidFormat"; break;
case TransactionException::OutOfGasIntrinsic: _out << "OutOfGasIntrinsic"; break;
case TransactionException::InvalidSignature: _out << "InvalidSignature"; break;
case TransactionException::InvalidNonce: _out << "InvalidNonce"; break;
@ -100,7 +101,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
try
{
if (!rlp.isList())
BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("transaction RLP must be a list"));
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list"));
m_nonce = rlp[field = 0].toInt<u256>();
m_gasPrice = rlp[field = 1].toInt<u256>();
@ -110,7 +111,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
m_value = rlp[field = 4].toInt<u256>();
if (!rlp[field = 5].isData())
BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("transaction data RLP must be an array"));
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array"));
m_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt<byte>() - 27;
@ -118,7 +119,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
h256 s = rlp[field = 8].toInt<u256>();
if (rlp.itemCount() > 9)
BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("to many fields in the transaction RLP"));
BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP"));
m_vrs = SignatureStruct{ r, s, v };
if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid())

1
libethereum/Transaction.h

@ -50,6 +50,7 @@ enum class TransactionException
None = 0,
Unknown,
BadRLP,
InvalidFormat,
OutOfGasIntrinsic, ///< Too little gas to pay for the base transaction cost.
InvalidSignature,
InvalidNonce,

26
libethereum/TransactionQueue.cpp

@ -39,20 +39,22 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb
Transaction t;
ImportResult ir;
{
UpgradableGuard l(m_lock);
UpgradableGuard l(m_lock);
ir = check_WITH_LOCK(h, _ik);
if (ir != ImportResult::Success)
return ir;
ir = check_WITH_LOCK(h, _ik);
if (ir != ImportResult::Success)
return ir;
try {
t = Transaction(_transactionRLP, CheckTransaction::Everything);
UpgradeGuard ul(l);
ir = manageImport_WITH_LOCK(h, t, _cb);
}
catch (...) {
return ImportResult::Malformed;
}
try
{
t = Transaction(_transactionRLP, CheckTransaction::Everything);
UpgradeGuard ul(l);
ir = manageImport_WITH_LOCK(h, t, _cb);
}
catch (...)
{
return ImportResult::Malformed;
}
}
// cdebug << "import-END: Nonce of" << t.sender() << "now" << maxNonce(t.sender());
return ir;

6
libevmasm/Assembly.cpp

@ -41,7 +41,7 @@ void Assembly::append(Assembly const& _a)
if (i.type() == Tag || i.type() == PushTag)
i.setData(i.data() + m_usedTags);
else if (i.type() == PushSub || i.type() == PushSubSize)
i.setData(i.data() + m_usedTags);
i.setData(i.data() + m_subs.size());
append(i);
}
m_deposit = newDeposit;
@ -136,10 +136,10 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
_out << " PUSH [tag" << dec << i.data() << "]";
break;
case PushSub:
_out << " PUSH [$" << h256(i.data()).abridged() << "]";
_out << " PUSH [$" << h256(i.data()).abridgedMiddle() << "]";
break;
case PushSubSize:
_out << " PUSH #[$" << h256(i.data()).abridged() << "]";
_out << " PUSH #[$" << h256(i.data()).abridgedMiddle() << "]";
break;
case PushProgramSize:
_out << " PUSHSIZE";

4
libevmasm/AssemblyItem.cpp

@ -110,10 +110,10 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
_out << " PushData " << hex << (unsigned)_item.data();
break;
case PushSub:
_out << " PushSub " << hex << h256(_item.data()).abridged();
_out << " PushSub " << hex << h256(_item.data()).abridgedMiddle();
break;
case PushSubSize:
_out << " PushSubSize " << hex << h256(_item.data()).abridged();
_out << " PushSubSize " << hex << h256(_item.data()).abridgedMiddle();
break;
case PushProgramSize:
_out << " PushProgramSize";

8
libjsconsole/CMakeLists.txt

@ -14,6 +14,7 @@ include_directories(BEFORE ${V8_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${READLINE_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
include_directories(${CURL_INCLUDE_DIRS})
set(EXECUTABLE jsconsole)
@ -24,7 +25,12 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} jsengine)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_SERVER_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
endif()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

66
libjsconsole/CURLRequest.cpp

@ -0,0 +1,66 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CURLRequest.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include "CURLRequest.h"
using namespace std;
static size_t write_data(void *buffer, size_t elementSize, size_t numberOfElements, void *userp)
{
static_cast<stringstream *>(userp)->write((const char *)buffer, elementSize * numberOfElements);
return elementSize * numberOfElements;
}
void CURLRequest::commonCURLPreparation()
{
m_resultBuffer.str("");
curl_easy_setopt(m_curl, CURLOPT_URL, (m_url + "?").c_str());
curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &m_resultBuffer);
}
std::tuple<long, string> CURLRequest::commonCURLPerform()
{
CURLcode res = curl_easy_perform(m_curl);
if (res != CURLE_OK) {
throw runtime_error(curl_easy_strerror(res));
}
long httpCode = 0;
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &httpCode);
return make_tuple(httpCode, m_resultBuffer.str());
}
std::tuple<long, string> CURLRequest::post()
{
commonCURLPreparation();
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
struct curl_slist *headerList = NULL;
headerList = curl_slist_append(headerList, "Content-Type: application/json");
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headerList);
auto result = commonCURLPerform();
curl_slist_free_all(headerList);
return result;
}

58
libjsconsole/CURLRequest.h

@ -0,0 +1,58 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CURLRequest.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
// based on http://stackoverflow.com/questions/1011339/how-do-you-make-a-http-request-with-c/27026683#27026683
#pragma once
#include <stdio.h>
#include <sstream>
#include <unordered_map>
#include <curl/curl.h>
class CURLRequest
{
public:
CURLRequest(): m_curl(curl_easy_init()) {}
~CURLRequest()
{
if (m_curl)
curl_easy_cleanup(m_curl);
}
void setUrl(std::string _url) { m_url = _url; }
void setBody(std::string _body) { m_body = _body; }
std::tuple<long, std::string> post();
private:
std::string m_url;
std::string m_body;
CURL* m_curl;
std::stringstream m_resultBuffer;
void commonCURLPreparation();
std::tuple<long, std::string> commonCURLPerform();
};

62
libjsconsole/JSConsole.cpp

@ -20,65 +20,5 @@
* Ethereum client.
*/
#include <iostream>
#include <libdevcore/Log.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include "JSConsole.h"
#include "JSV8Connector.h"
// TODO! make readline optional!
#include <readline/readline.h>
#include <readline/history.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
JSConsole::JSConsole(WebThreeDirect& _web3, shared_ptr<AccountHolder> const& _accounts):
m_engine(),
m_printer(m_engine)
{
m_jsonrpcConnector.reset(new JSV8Connector(m_engine));
(void)_web3; (void)_accounts;
// m_jsonrpcServer.reset(new WebThreeStubServer(*m_jsonrpcConnector.get(), _web3, _accounts, vector<KeyPair>()));
}
void JSConsole::readExpression() const
{
string cmd = "";
g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << flush; rl_forced_update_display(); };
bool isEmpty = true;
int openBrackets = 0;
do {
char* buff = readline(promptForIndentionLevel(openBrackets).c_str());
isEmpty = !(buff && *buff);
if (!isEmpty)
{
cmd += string(buff);
cmd += " ";
free(buff);
int open = count(cmd.begin(), cmd.end(), '{');
open += count(cmd.begin(), cmd.end(), '(');
int closed = count(cmd.begin(), cmd.end(), '}');
closed += count(cmd.begin(), cmd.end(), ')');
openBrackets = open - closed;
}
} while (openBrackets > 0);
if (!isEmpty)
{
add_history(cmd.c_str());
auto value = m_engine.eval(cmd.c_str());
string result = m_printer.prettyPrint(value).cstr();
cout << result << endl;
}
}
std::string JSConsole::promptForIndentionLevel(int _i) const
{
if (_i == 0)
return "> ";
return string((_i + 1) * 2, ' ');
}
#include "JSConsole.h"

64
libjsconsole/JSConsole.h

@ -22,32 +22,66 @@
#pragma once
#include <libjsengine/JSV8Engine.h>
#include <libjsengine/JSV8Printer.h>
namespace dev { class WebThreeStubServer; }
namespace jsonrpc { class AbstractServerConnector; }
#include <libdevcore/Log.h>
// TODO! make readline optional!
#include <readline/readline.h>
#include <readline/history.h>
namespace dev
{
namespace eth
{
class AccountHolder;
template<typename Engine, typename Printer>
class JSConsole
{
public:
JSConsole(WebThreeDirect& _web3, std::shared_ptr<AccountHolder> const& _accounts);
void readExpression() const;
JSConsole(): m_engine(Engine()), m_printer(Printer(m_engine)) {}
~JSConsole() {}
void readExpression() const
{
std::string cmd = "";
g_logPost = [](std::string const& a, char const*) { std::cout << "\r \r" << a << std::endl << std::flush; rl_forced_update_display(); };
bool isEmpty = true;
int openBrackets = 0;
do {
char* buff = readline(promptForIndentionLevel(openBrackets).c_str());
isEmpty = !(buff && *buff);
if (!isEmpty)
{
cmd += std::string(buff);
cmd += " ";
free(buff);
int open = std::count(cmd.begin(), cmd.end(), '{');
open += std::count(cmd.begin(), cmd.end(), '(');
int closed = std::count(cmd.begin(), cmd.end(), '}');
closed += std::count(cmd.begin(), cmd.end(), ')');
openBrackets = open - closed;
}
} while (openBrackets > 0);
if (!isEmpty)
{
add_history(cmd.c_str());
auto value = m_engine.eval(cmd.c_str());
std::string result = m_printer.prettyPrint(value).cstr();
std::cout << result << std::endl;
}
}
protected:
Engine m_engine;
Printer m_printer;
private:
std::string promptForIndentionLevel(int _i) const;
virtual std::string promptForIndentionLevel(int _i) const
{
if (_i == 0)
return "> ";
JSV8Engine m_engine;
JSV8Printer m_printer;
std::unique_ptr<dev::WebThreeStubServer> m_jsonrpcServer;
std::unique_ptr<jsonrpc::AbstractServerConnector> m_jsonrpcConnector;
return std::string((_i + 1) * 2, ' ');
}
};
}

34
libjsconsole/JSLocalConsole.cpp

@ -0,0 +1,34 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file JSLocalConsole.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <iostream>
#include "JSLocalConsole.h"
#include "JSV8Connector.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
JSLocalConsole::JSLocalConsole()
{
m_jsonrpcConnector.reset(new JSV8Connector(m_engine));
}

50
libjsconsole/JSLocalConsole.h

@ -0,0 +1,50 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file JSLocalConsole.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <libjsengine/JSV8Engine.h>
#include <libjsengine/JSV8Printer.h>
#include "JSConsole.h"
class WebThreeStubServer;
namespace jsonrpc { class AbstractServerConnector; }
namespace dev
{
namespace eth
{
class JSLocalConsole: public JSConsole<JSV8Engine, JSV8Printer>
{
public:
JSLocalConsole();
virtual ~JSLocalConsole() {}
jsonrpc::AbstractServerConnector* connector() { return m_jsonrpcConnector.get(); }
private:
std::unique_ptr<jsonrpc::AbstractServerConnector> m_jsonrpcConnector;
};
}
}

23
libjsconsole/JSRemoteConsole.cpp

@ -0,0 +1,23 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file JSRemoteConsole.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include "JSRemoteConsole.h"

48
libjsconsole/JSRemoteConsole.h

@ -0,0 +1,48 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file JSRemoteConsole.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <libjsengine/JSV8Engine.h>
#include <libjsengine/JSV8Printer.h>
#include "JSV8RemoteConnector.h"
#include "JSConsole.h"
namespace dev
{
namespace eth
{
class JSRemoteConsole: public JSConsole<JSV8Engine, JSV8Printer>
{
public:
JSRemoteConsole(std::string _url): m_connector(m_engine, _url) {}
virtual ~JSRemoteConsole() {}
private:
JSV8RemoteConnector m_connector;
};
}
}

2
libjsconsole/JSV8Connector.h

@ -43,7 +43,7 @@ public:
bool SendResponse(std::string const& _response, void* _addInfo = nullptr);
// implement JSV8RPC interface
void onSend(char const* payload);
void onSend(char const* _payload);
};
}

20
libjsconsole/JSV8RemoteConnector.cpp

@ -0,0 +1,20 @@
//
// Created by Marek Kotewicz on 15/06/15.
//
#include "JSV8RemoteConnector.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
void JSV8RemoteConnector::onSend(char const* _payload)
{
m_request.setUrl(m_url);
m_request.setBody(_payload);
long code;
string response;
tie(code, response) = m_request.post();
(void)code;
m_lastResponse = response.c_str();
}

32
libjsconsole/JSV8RemoteConnector.h

@ -0,0 +1,32 @@
//
// Created by Marek Kotewicz on 15/06/15.
//
#pragma once
#include <string>
#include <libjsengine/JSV8RPC.h>
#include "CURLRequest.h"
namespace dev
{
namespace eth
{
class JSV8RemoteConnector : public JSV8RPC
{
public:
JSV8RemoteConnector(JSV8Engine const& _engine, std::string _url): JSV8RPC(_engine), m_url(_url) {}
virtual ~JSV8RemoteConnector() {}
// implement JSV8RPC interface
void onSend(char const* _payload);
private:
std::string m_url;
CURLRequest m_request;
};
}
}

3
libjsengine/JSResources.cmake

@ -1,8 +1,9 @@
set(web3 "${CMAKE_CURRENT_LIST_DIR}/../libjsqrc/ethereumjs/dist/web3.js")
set(admin "${CMAKE_CURRENT_LIST_DIR}/../libjsqrc/admin.js")
set(pretty_print "${CMAKE_CURRENT_LIST_DIR}/PrettyPrint.js")
set(common "${CMAKE_CURRENT_LIST_DIR}/Common.js")
set(ETH_RESOURCE_NAME "JSEngineResources")
set(ETH_RESOURCE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}")
set(ETH_RESOURCES "web3" "pretty_print" "common")
set(ETH_RESOURCES "web3" "pretty_print" "common" "admin")

2
libjsengine/JSV8Engine.cpp

@ -143,9 +143,11 @@ JSV8Engine::JSV8Engine(): m_scope(new JSV8Scope())
JSEngineResources resources;
string common = resources.loadResourceAsString("common");
string web3 = resources.loadResourceAsString("web3");
string admin = resources.loadResourceAsString("admin");
eval(common.c_str());
eval(web3.c_str());
eval("web3 = require('web3');");
eval(admin.c_str());
}
JSV8Engine::~JSV8Engine()

3
libjsengine/JSV8Engine.h

@ -22,7 +22,10 @@
#pragma once
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#include <v8.h>
#pragma clang diagnostic pop
#include "JSEngine.h"
namespace dev

177
libsolidity/ArrayUtils.cpp

@ -231,6 +231,181 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context << u256(0);
}
void ArrayUtils::copyArrayToMemory(const ArrayType& _sourceType, bool _padToWordBoundaries) const
{
solAssert(
_sourceType.getBaseType()->getCalldataEncodedSize() > 0,
"Nested arrays not yet implemented here."
);
unsigned baseSize = 1;
if (!_sourceType.isByteArray())
// We always pad the elements, regardless of _padToWordBoundaries.
baseSize = _sourceType.getBaseType()->getCalldataEncodedSize();
if (_sourceType.location() == DataLocation::CallData)
{
if (!_sourceType.isDynamicallySized())
m_context << _sourceType.getLength();
if (_sourceType.getBaseType()->getCalldataEncodedSize() > 1)
m_context << u256(baseSize) << eth::Instruction::MUL;
// stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5;
// stack: target source_offset source_len source_len source_offset target
m_context << eth::Instruction::CALLDATACOPY;
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP;
}
else if (_sourceType.location() == DataLocation::Memory)
{
// memcpy using the built-in contract
retrieveLength(_sourceType);
if (_sourceType.isDynamicallySized())
{
// change pointer to data part
m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP1;
}
// convert length to size
if (baseSize > 1)
m_context << u256(baseSize) << eth::Instruction::MUL;
// stack: <target> <source> <size>
//@TODO do not use ::CALL if less than 32 bytes?
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::DUP4;
CompilerUtils(m_context).memoryCopy();
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
// stack: <target> <size>
bool paddingNeeded = false;
if (_sourceType.isDynamicallySized())
paddingNeeded = _padToWordBoundaries && ((baseSize % 32) != 0);
else
paddingNeeded = _padToWordBoundaries && (((_sourceType.getLength() * baseSize) % 32) != 0);
if (paddingNeeded)
{
// stack: <target> <size>
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD;
// stack: <length> <target + size>
m_context << eth::Instruction::SWAP1 << u256(31) << eth::Instruction::AND;
// stack: <target + size> <remainder = size % 32>
eth::AssemblyItem skip = m_context.newTag();
if (_sourceType.isDynamicallySized())
{
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(skip);
}
// round off, load from there.
// stack <target + size> <remainder = size % 32>
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3;
m_context << eth::Instruction::SUB;
// stack: target+size remainder <target + size - remainder>
m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
// Now we AND it with ~(2**(8 * (32 - remainder)) - 1)
m_context << u256(1);
m_context << eth::Instruction::DUP4 << u256(32) << eth::Instruction::SUB;
// stack: ...<v> 1 <32 - remainder>
m_context << u256(0x100) << eth::Instruction::EXP << eth::Instruction::SUB;
m_context << eth::Instruction::NOT << eth::Instruction::AND;
// stack: target+size remainder target+size-remainder <v & ...>
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
// stack: target+size remainder target+size-remainder
m_context << u256(32) << eth::Instruction::ADD;
// stack: target+size remainder <new_padded_end>
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP;
if (_sourceType.isDynamicallySized())
m_context << skip.tag();
// stack <target + "size"> <remainder = size % 32>
m_context << eth::Instruction::POP;
}
else
// stack: <target> <size>
m_context << eth::Instruction::ADD;
}
else
{
solAssert(_sourceType.location() == DataLocation::Storage, "");
unsigned storageBytes = _sourceType.getBaseType()->getStorageBytes();
u256 storageSize = _sourceType.getBaseType()->getStorageSize();
solAssert(storageSize > 1 || (storageSize == 1 && storageBytes > 0), "");
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
retrieveLength(_sourceType);
// stack here: memory_offset storage_offset length
// jump to end if length is zero
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
eth::AssemblyItem loopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(loopEnd);
// compute memory end offset
if (baseSize > 1)
// convert length to memory size
m_context << u256(baseSize) << eth::Instruction::MUL;
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
if (_sourceType.isDynamicallySized())
{
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
m_context << eth::Instruction::SWAP1;
}
// stack here: memory_end_offset storage_data_offset memory_offset
bool haveByteOffset = !_sourceType.isByteArray() && storageBytes <= 16;
if (haveByteOffset)
m_context << u256(0) << eth::Instruction::SWAP1;
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart;
// load and store
if (_sourceType.isByteArray())
{
// Packed both in storage and memory.
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
// increment storage_data_offset by 1
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD;
// increment memory offset by 32
m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
}
else
{
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
if (haveByteOffset)
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3;
else
m_context << eth::Instruction::DUP2 << u256(0);
StorageItem(m_context, *_sourceType.getBaseType()).retrieveValue(SourceLocation(), true);
CompilerUtils(m_context).storeInMemoryDynamic(*_sourceType.getBaseType());
// increment storage_data_offset and byte offset
if (haveByteOffset)
incrementByteOffset(storageBytes, 2, 3);
else
{
m_context << eth::Instruction::SWAP1;
m_context << storageSize << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP1;
}
}
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::dupInstruction(haveByteOffset ? 5 : 4) << eth::Instruction::GT;
m_context.appendConditionalJumpTo(loopStart);
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
if (haveByteOffset)
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
if (_padToWordBoundaries && baseSize % 32 != 0)
{
// memory_end_offset - start is the actual length (we want to compute the ceil of).
// memory_offset - start is its next multiple of 32, but it might be off by 32.
// so we compute: memory_end_offset += (memory_offset - memory_end_offest) & 31
m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1 << eth::Instruction::SUB;
m_context << u256(31) << eth::Instruction::AND;
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP2;
}
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
}
}
void ArrayUtils::clearArray(ArrayType const& _type) const
{
unsigned stackHeightStart = m_context.getStackHeight();
@ -499,6 +674,8 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << _arrayType.getBaseType()->getCalldataEncodedSize() << eth::Instruction::MUL;
}
m_context << eth::Instruction::ADD;
//@todo we should also load if it is a reference type of dynamic length
// but we should apply special logic if we load from calldata.
if (_arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(
*_arrayType.getBaseType(),

4
libsolidity/ArrayUtils.h

@ -44,6 +44,10 @@ public:
/// Stack pre: source_reference [source_byte_offset/source_length] target_reference target_byte_offset
/// Stack post: target_reference target_byte_offset
void copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
/// Copies an array (which cannot be dynamically nested) from anywhere to memory.
/// Stack pre: memory_offset source_item
/// Stack post: memory_offest + length(padded)
void copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries = true) const;
/// Clears the given dynamic or static array.
/// Stack pre: storage_ref storage_byte_offset
/// Stack post:

13
libsolidity/Compiler.cpp

@ -392,9 +392,9 @@ bool Compiler::visit(FunctionDefinition const& _function)
}
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
m_context.addAndInitializeVariable(*variable);
appendStackVariableInitialisation(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addAndInitializeVariable(*localVariable);
appendStackVariableInitialisation(*localVariable);
if (_function.isConstructor())
if (auto c = m_context.getNextConstructor(dynamic_cast<ContractDefinition const&>(*_function.getScope())))
@ -639,7 +639,7 @@ void Compiler::appendModifierOrFunctionCode()
modifier.getParameters()[i]->getType());
}
for (VariableDeclaration const* localVariable: modifier.getLocalVariables())
m_context.addAndInitializeVariable(*localVariable);
appendStackVariableInitialisation(*localVariable);
unsigned const c_stackSurplus = CompilerUtils::getSizeOnStack(modifier.getParameters()) +
CompilerUtils::getSizeOnStack(modifier.getLocalVariables());
@ -653,6 +653,13 @@ void Compiler::appendModifierOrFunctionCode()
}
}
void Compiler::appendStackVariableInitialisation(VariableDeclaration const& _variable)
{
CompilerContext::LocationSetter location(m_context, _variable);
m_context.addVariable(_variable);
ExpressionCompiler(m_context).appendStackVariableInitialisation(*_variable.getType());
}
void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
{
ExpressionCompiler expressionCompiler(m_context, m_optimize);

8
libsolidity/Compiler.h

@ -84,6 +84,13 @@ private:
void registerStateVariables(ContractDefinition const& _contract);
void initializeStateVariables(ContractDefinition const& _contract);
/// Initialises all memory arrays in the local variables to point to an empty location.
void initialiseMemoryArrays(std::vector<VariableDeclaration const*> _variables);
/// Pushes the initialised value of the given type to the stack. If the type is a memory
/// reference type, allocates memory and pushes the memory pointer.
/// Not to be used for storage references.
void initialiseInMemory(Type const& _type);
virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement const& _ifStatement) override;
@ -100,6 +107,7 @@ private:
/// body itself if the last modifier was reached.
void appendModifierOrFunctionCode();
void appendStackVariableInitialisation(VariableDeclaration const& _variable);
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
bool const m_optimize;

9
libsolidity/CompilerContext.cpp

@ -65,15 +65,6 @@ void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
m_localVariables.erase(&_declaration);
}
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
{
LocationSetter locationSetter(*this, _declaration);
addVariable(_declaration);
int const size = _declaration.getType()->getSizeOnStack();
for (int i = 0; i < size; ++i)
*this << u256(0);
}
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
{
auto ret = m_compiledContracts.find(&_contract);

1
libsolidity/CompilerContext.h

@ -46,7 +46,6 @@ public:
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
void removeVariable(VariableDeclaration const& _declaration);
void addAndInitializeVariable(VariableDeclaration const& _declaration);
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
bytes const& getCompiledContract(ContractDefinition const& _contract) const;

166
libsolidity/CompilerUtils.cpp

@ -25,6 +25,7 @@
#include <libevmcore/Instruction.h>
#include <libevmcore/Params.h>
#include <libsolidity/ArrayUtils.h>
#include <libsolidity/LValue.h>
using namespace std;
@ -91,142 +92,20 @@ void CompilerUtils::loadFromMemoryDynamic(
}
}
unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
void CompilerUtils::storeInMemory(unsigned _offset)
{
solAssert(_type.getCategory() != Type::Category::Array, "Unable to statically store dynamic type.");
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
unsigned numBytes = prepareMemoryStore(IntegerType(256), true);
if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE;
return numBytes;
}
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{
if (_type.getCategory() == Type::Category::Array)
{
auto const& type = dynamic_cast<ArrayType const&>(_type);
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
if (type.location() == DataLocation::CallData)
{
if (!type.isDynamicallySized())
m_context << type.getLength();
// stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5;
// stack: target source_offset source_len source_len source_offset target
m_context << eth::Instruction::CALLDATACOPY;
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP;
}
else if (type.location() == DataLocation::Memory)
{
// memcpy using the built-in contract
ArrayUtils(m_context).retrieveLength(type);
if (type.isDynamicallySized())
{
// change pointer to data part
m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP1;
}
// stack: <target> <source> <length>
// stack for call: outsize target size source value contract gas
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4;
m_context << eth::Instruction::DUP2 << eth::Instruction::DUP5;
m_context << u256(0) << u256(identityContractAddress);
//@TODO do not use ::CALL if less than 32 bytes?
//@todo in production, we should not have to pair c_callNewAccountGas.
m_context << u256(eth::c_callGas + 15 + eth::c_callNewAccountGas) << eth::Instruction::GAS;
m_context << eth::Instruction::SUB << eth::Instruction::CALL;
m_context << eth::Instruction::POP; // ignore return value
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
// stack: <target> <length>
if (_padToWordBoundaries && (type.isDynamicallySized() || (type.getLength()) % 32 != 0))
{
// stack: <target> <length>
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD;
// stack: <length> <target + length>
m_context << eth::Instruction::SWAP1 << u256(31) << eth::Instruction::AND;
// stack: <target + length> <remainder = length % 32>
eth::AssemblyItem skip = m_context.newTag();
if (type.isDynamicallySized())
{
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(skip);
}
// round off, load from there.
// stack <target + length> <remainder = length % 32>
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3;
m_context << eth::Instruction::SUB;
// stack: target+length remainder <target + length - remainder>
m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
// Now we AND it with ~(2**(8 * (32 - remainder)) - 1)
m_context << u256(1);
m_context << eth::Instruction::DUP4 << u256(32) << eth::Instruction::SUB;
// stack: ...<v> 1 <32 - remainder>
m_context << u256(0x100) << eth::Instruction::EXP << eth::Instruction::SUB;
m_context << eth::Instruction::NOT << eth::Instruction::AND;
// stack: target+length remainder target+length-remainder <v & ...>
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
// stack: target+length remainder target+length-remainder
m_context << u256(32) << eth::Instruction::ADD;
// stack: target+length remainder <new_padded_end>
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP;
if (type.isDynamicallySized())
m_context << skip.tag();
// stack <target + "length"> <remainder = length % 32>
m_context << eth::Instruction::POP;
}
else
// stack: <target> <length>
m_context << eth::Instruction::ADD;
}
else
{
solAssert(type.location() == DataLocation::Storage, "");
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes
// jump to end if length is zero
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
eth::AssemblyItem loopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(loopEnd);
// compute memory end offset
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
m_context << eth::Instruction::SWAP1;
// stack here: memory_end_offset storage_data_offset memory_offset
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart;
// load and store
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
// increment storage_data_offset by 1
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD;
// increment memory offset by 32
m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT;
m_context.appendConditionalJumpTo(loopStart);
// stack here: memory_end_offset storage_data_offset memory_offset
if (_padToWordBoundaries)
{
// memory_end_offset - start is the actual length (we want to compute the ceil of).
// memory_offset - start is its next multiple of 32, but it might be off by 32.
// so we compute: memory_end_offset += (memory_offset - memory_end_offest) & 31
m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1 << eth::Instruction::SUB;
m_context << u256(31) << eth::Instruction::AND;
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP2;
}
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
}
}
ArrayUtils(m_context).copyArrayToMemory(
dynamic_cast<ArrayType const&>(_type),
_padToWordBoundaries
);
else
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
@ -341,6 +220,21 @@ void CompilerUtils::encodeToMemory(
popStackSlots(argSize + dynPointers + 1);
}
void CompilerUtils::memoryCopy()
{
// Stack here: size target source
// stack for call: outsize target size source value contract gas
//@TODO do not use ::CALL if less than 32 bytes?
m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1;
m_context << u256(0) << u256(identityContractAddress);
// compute gas costs
m_context << u256(32) << eth::Instruction::DUP5 << u256(31) << eth::Instruction::ADD;
m_context << eth::Instruction::DIV << u256(eth::c_identityWordGas) << eth::Instruction::MUL;
m_context << u256(eth::c_identityGas) << eth::Instruction::ADD;
m_context << eth::Instruction::CALL;
m_context << eth::Instruction::POP; // ignore return value
}
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
{
// For a type extension, we need to remove all higher-order bits that we might have ignored in
@ -515,7 +409,14 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
break;
}
default:
solAssert(false, "Invalid type conversion requested.");
solAssert(
false,
"Invalid type conversion " +
_typeOnStack.toString(false) +
" to " +
_targetType.toString(false) +
" requested."
);
}
break;
}
@ -599,11 +500,10 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
return size;
}
void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundaries)
void CompilerUtils::computeHashStatic()
{
unsigned length = storeInMemory(0, _type, _padToWordBoundaries);
solAssert(length <= CompilerUtils::freeMemoryPointer, "");
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
}
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)

23
libsolidity/CompilerUtils.h

@ -65,18 +65,14 @@ public:
bool _padToWordBoundaries = true,
bool _keepUpdatedMemoryOffset = true
);
/// Stores data from stack in memory.
/// Stores a 256 bit integer from stack in memory.
/// @param _offset offset in memory
/// @param _type type of the data on the stack
/// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
/// @returns the number of bytes written to memory (can be different from _bytes if
/// _padToWordBoundaries is true)
unsigned storeInMemory(unsigned _offset,
Type const& _type = IntegerType(256),
bool _padToWordBoundaries = false
);
void storeInMemory(unsigned _offset);
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
/// and also updates that. For arrays, only copies the data part.
/// @param _padToWordBoundaries if true, adds zeros to pad to multiple of 32 bytes. Array elements
/// are always padded (except for byte arrays), regardless of this parameter.
/// Stack pre: memory_offset value...
/// Stack post: (memory_offset+length)
void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
@ -99,6 +95,11 @@ public:
bool _copyDynamicDataInPlace = false
);
/// Uses a CALL to the identity contract to perform a memory-to-memory copy.
/// Stack pre: <size> <target> <source>
/// Stack post:
void memoryCopy();
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
/// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
/// if a reference type is converted from calldata or storage to memory.
@ -124,10 +125,8 @@ public:
static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
/// Appends code that computes tha SHA3 hash of the topmost stack element of type @a _type.
/// If @a _pad is set, padds the type to muliples of 32 bytes.
/// @note Only works for types of fixed size.
void computeHashStatic(Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
/// Appends code that computes tha SHA3 hash of the topmost stack element of 32 byte type.
void computeHashStatic();
/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.

56
libsolidity/ExpressionCompiler.cpp

@ -56,6 +56,62 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
StorageItem(m_context, _varDecl).storeValue(*_varDecl.getType(), _varDecl.getLocation(), true);
}
void ExpressionCompiler::appendStackVariableInitialisation(Type const& _type, bool _toMemory)
{
CompilerUtils utils(m_context);
auto const* referenceType = dynamic_cast<ReferenceType const*>(&_type);
if (!referenceType || referenceType->location() == DataLocation::Storage)
{
for (size_t i = 0; i < _type.getSizeOnStack(); ++i)
m_context << u256(0);
if (_toMemory)
utils.storeInMemoryDynamic(_type);
return;
}
solAssert(referenceType->location() == DataLocation::Memory, "");
if (!_toMemory)
{
// allocate memory
utils.fetchFreeMemoryPointer();
m_context << eth::Instruction::DUP1 << u256(max(32u, _type.getCalldataEncodedSize()));
m_context << eth::Instruction::ADD;
utils.storeFreeMemoryPointer();
m_context << eth::Instruction::DUP1;
}
if (auto structType = dynamic_cast<StructType const*>(&_type))
for (auto const& member: structType->getMembers())
appendStackVariableInitialisation(*member.type, true);
else if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
{
if (arrayType->isDynamicallySized())
{
// zero length
m_context << u256(0);
CompilerUtils(m_context).storeInMemoryDynamic(IntegerType(256));
}
else if (arrayType->getLength() > 0)
{
m_context << arrayType->getLength() << eth::Instruction::SWAP1;
// stack: items_to_do memory_pos
auto repeat = m_context.newTag();
m_context << repeat;
appendStackVariableInitialisation(*arrayType->getBaseType(), true);
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::SWAP1;
m_context << eth::Instruction::SUB << eth::Instruction::SWAP1;
m_context << eth::Instruction::DUP2;
m_context.appendConditionalJumpTo(repeat);
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
}
}
else
solAssert(false, "Requested initialisation for unknown type: " + _type.toString());
if (!_toMemory)
// remove the updated memory pointer
m_context << eth::Instruction::POP;
}
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
{
CompilerContext::LocationSetter locationSetter(m_context, _varDecl);

7
libsolidity/ExpressionCompiler.h

@ -64,6 +64,13 @@ public:
/// Appends code to set a state variable to its initial value/expression.
void appendStateVariableInitialization(VariableDeclaration const& _varDecl);
/// Appends code to initialise a local variable.
/// If @a _toMemory is false, leaves the value on the stack. For memory references, this
/// allocates new memory.
/// If @a _toMemory is true, directly stores the data in the memory pos on the stack and
/// updates it.
void appendStackVariableInitialisation(Type const& _type, bool _toMemory = false);
/// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl);

32
libsolidity/Types.cpp

@ -721,9 +721,13 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
}
else
{
// Require that the base type is the same, not only convertible.
// This disallows assignment of nested arrays from storage to memory for now.
if (*getBaseType() != *convertTo.getBaseType())
// Conversion to storage pointer or to memory, we de not copy element-for-element here, so
// require that the base type is the same, not only convertible.
// This disallows assignment of nested dynamic arrays from storage to memory for now.
if (
*copyForLocationIfReference(location(), getBaseType()) !=
*copyForLocationIfReference(location(), convertTo.getBaseType())
)
return false;
if (isDynamicallySized() != convertTo.isDynamicallySized())
return false;
@ -822,16 +826,16 @@ string ArrayType::toString(bool _short) const
TypePointer ArrayType::externalType() const
{
if (m_arrayKind != ArrayKind::Ordinary)
return this->copyForLocation(DataLocation::CallData, true);
return this->copyForLocation(DataLocation::Memory, true);
if (!m_baseType->externalType())
return TypePointer();
if (m_baseType->getCategory() == Category::Array && m_baseType->isDynamicallySized())
return TypePointer();
if (isDynamicallySized())
return std::make_shared<ArrayType>(DataLocation::CallData, m_baseType->externalType());
return std::make_shared<ArrayType>(DataLocation::Memory, m_baseType->externalType());
else
return std::make_shared<ArrayType>(DataLocation::CallData, m_baseType->externalType(), m_length);
return std::make_shared<ArrayType>(DataLocation::Memory, m_baseType->externalType(), m_length);
}
TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) const
@ -970,6 +974,22 @@ bool StructType::operator==(Type const& _other) const
return ReferenceType::operator==(other) && other.m_struct == m_struct;
}
unsigned StructType::getCalldataEncodedSize(bool _padded) const
{
unsigned size = 0;
for (auto const& member: getMembers())
if (!member.type->canLiveOutsideStorage())
return 0;
else
{
unsigned memberSize = member.type->getCalldataEncodedSize(_padded);
if (memberSize == 0)
return 0;
size += memberSize;
}
return size;
}
u256 StructType::getStorageSize() const
{
return max<u256>(1, getMembers().getStorageSize());

1
libsolidity/Types.h

@ -542,6 +542,7 @@ public:
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize(bool _padded) const override;
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override;
virtual unsigned getSizeOnStack() const override { return 2; }

1
libweb3jsonrpc/CMakeLists.txt

@ -54,4 +54,3 @@ install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib L
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
add_custom_target(aux_json SOURCES "spec.json")

2
libwebthree/WebThree.cpp

@ -80,7 +80,7 @@ std::string WebThreeDirect::composeClientVersion(std::string const& _client, std
#else
char const* jit = "";
#endif
return _client + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" + _clientName + "/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM) + jit;
return _client + "-" + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" + _clientName + "/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM) + jit;
}
p2p::NetworkPreferences const& WebThreeDirect::networkPreferences() const

39
mix/CodeModel.cpp

@ -392,6 +392,7 @@ void CodeModel::gasEstimation(solidity::CompilerStack const& _cs)
}
eth::AssemblyItems const& runtimeAssembly = *_cs.getRuntimeAssemblyItems(n);
QString contractName = QString::fromStdString(contractDefinition.getName());
// Functional gas costs (per function, but also for accessors)
for (auto it: contractDefinition.getInterfaceFunctions())
{
@ -399,13 +400,15 @@ void CodeModel::gasEstimation(solidity::CompilerStack const& _cs)
continue;
SourceLocation loc = it.second->getDeclaration().getLocation();
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(runtimeAssembly, it.second->externalSignature());
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function);
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function,
contractName, QString::fromStdString(it.second->getDeclaration().getName()));
}
if (auto const* fallback = contractDefinition.getFallbackFunction())
{
SourceLocation loc = fallback->getLocation();
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(runtimeAssembly, "INVALID");
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function);
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function,
contractName, "fallback");
}
for (auto const& it: contractDefinition.getDefinedFunctions())
{
@ -416,13 +419,15 @@ void CodeModel::gasEstimation(solidity::CompilerStack const& _cs)
GasEstimator::GasConsumption cost = GasEstimator::GasConsumption::infinite();
if (entry > 0)
cost = GasEstimator::functionalEstimation(runtimeAssembly, entry, *it);
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function);
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function,
contractName, QString::fromStdString(it->getName()));
}
if (auto const* constructor = contractDefinition.getConstructor())
{
SourceLocation loc = constructor->getLocation();
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(*_cs.getAssemblyItems(n));
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Constructor);
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Constructor,
contractName, contractName);
}
}
}
@ -435,6 +440,14 @@ QVariantList CodeModel::gasCostByDocumentId(QString const& _documentId) const
return QVariantList();
}
QVariantList CodeModel::gasCostBy(QString const& _contractName, QString const& _functionName) const
{
if (m_gasCostsMaps)
return m_gasCostsMaps->gasCostsBy(_contractName, _functionName);
else
return QVariantList();
}
void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames)
{
Guard pl(x_pendingContracts);
@ -643,9 +656,9 @@ void CodeModel::setOptimizeCode(bool _value)
emit scheduleCompilationJob(++m_backgroundJobId);
}
void GasMapWrapper::push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type)
void GasMapWrapper::push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type, QString _contractName, QString _functionName)
{
GasMap* gas = new GasMap(_start, _end, _value, _isInfinite, _type, this);
GasMap* gas = new GasMap(_start, _end, _value, _isInfinite, _type, _contractName, _functionName, this);
m_gasMaps.find(_source).value().push_back(QVariant::fromValue(gas));
}
@ -668,3 +681,17 @@ QVariantList GasMapWrapper::gasCostsByDocId(QString _source)
return QVariantList();
}
QVariantList GasMapWrapper::gasCostsBy(QString _contractName, QString _functionName)
{
QVariantList gasMap;
for (auto const& map: m_gasMaps)
{
for (auto const& gas: map)
{
if (gas.value<GasMap*>()->contractName() == _contractName && (_functionName.isEmpty() || gas.value<GasMap*>()->functionName() == _functionName))
gasMap.push_back(gas);
}
}
return gasMap;
}

19
mix/CodeModel.h

@ -31,6 +31,7 @@
#include <QMetaEnum>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libevmcore/Params.h>
#include <libevmasm/Assembly.h>
#include "SolidityType.h"
#include "QBigInt.h"
@ -140,6 +141,8 @@ class GasMap: public QObject
Q_PROPERTY(QString gas MEMBER m_gas CONSTANT)
Q_PROPERTY(bool isInfinite MEMBER m_isInfinite CONSTANT)
Q_PROPERTY(QString codeBlockType READ codeBlockType CONSTANT)
Q_PROPERTY(QString contractName MEMBER m_contractName CONSTANT)
Q_PROPERTY(QString functionName MEMBER m_functionName CONSTANT)
public:
@ -150,13 +153,19 @@ public:
Constructor
};
GasMap(int _start, int _end, QString _gas, bool _isInfinite, type _type, QObject* _parent): QObject(_parent), m_start(_start), m_end(_end), m_gas(_gas), m_isInfinite(_isInfinite), m_type(_type) {}
GasMap(int _start, int _end, QString _gas, bool _isInfinite, type _type, QString _contractName, QString _functionName, QObject* _parent): QObject(_parent),
m_start(_start), m_end(_end), m_gas(_gas), m_isInfinite(_isInfinite), m_type(_type), m_contractName(_contractName), m_functionName(_functionName) {}
QString contractName() { return m_contractName; }
QString functionName() { return m_functionName; }
private:
int m_start;
int m_end;
QString m_gas;
bool m_isInfinite;
type m_type;
QString m_contractName;
QString m_functionName;
QString codeBlockType() const
{
@ -178,10 +187,11 @@ class GasMapWrapper: public QObject
public:
GasMapWrapper(QObject* _parent = nullptr): QObject(_parent){}
void push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type);
void push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type, QString _contractName = "", QString _functionName = "");
bool contains(QString _key);
void insert(QString _source, QVariantList _variantList);
QVariantList gasCostsByDocId(QString _source);
QVariantList gasCostsBy(QString _contractName, QString _functionName = "");
private:
GasCostsMaps m_gasMaps;
@ -200,6 +210,8 @@ public:
Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged)
Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged)
Q_PROPERTY(bool optimizeCode MEMBER m_optimizeCode WRITE setOptimizeCode)
Q_PROPERTY(int callStipend READ callStipend)
Q_PROPERTY(int txGas READ txGas)
/// @returns latest compilation results for contracts
QVariantMap contracts() const;
@ -234,7 +246,10 @@ public:
void gasEstimation(solidity::CompilerStack const& _cs);
/// Gas cost by doc id
Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const;
Q_INVOKABLE QVariantList gasCostBy(QString const& _contractName, QString const& _functionName) const;
Q_INVOKABLE void setOptimizeCode(bool _value);
int txGas() { return static_cast<int>(dev::eth::c_txGas); }
int callStipend() { return static_cast<int>(dev::eth::c_callStipend); }
signals:
/// Emited on compilation state change

1
mix/MixClient.cpp

@ -229,6 +229,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
case TransactionException::BadInstruction:
case TransactionException::InvalidSignature:
case TransactionException::InvalidNonce:
case TransactionException::InvalidFormat:
case TransactionException::BadRLP:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error"));
}

18
mix/qml/Block.qml

@ -243,7 +243,6 @@ ColumnLayout
}
}
Rectangle
{
Layout.preferredWidth: toWidth
@ -266,8 +265,6 @@ ColumnLayout
}
}
function userFrienldyToken(value)
{
if (value && value.indexOf("<") === 0)
@ -293,7 +290,7 @@ ColumnLayout
color: labelColor
font.bold: true
font.pointSize: dbgStyle.absoluteSize(1)
width: parent.width -30
width: parent.width - 30
text: {
if (index >= 0 && transactions.get(index).returned)
return transactions.get(index).returned
@ -402,18 +399,5 @@ ColumnLayout
}
}
}
Rectangle
{
id: right
Layout.preferredWidth: blockWidth
height: 10
anchors.top: parent.bottom
anchors.topMargin: 5
color: "#DEDCDC"
radius: 15
anchors.left: parent.left
anchors.leftMargin: statusWidth
}
}

1
mix/qml/Ether.qml

@ -54,6 +54,7 @@ RowLayout {
{
id: units
visible: displayUnitSelection;
implicitWidth: 145
onCurrentTextChanged:
{
if (value)

24
mix/qml/QAddressView.qml

@ -2,14 +2,14 @@ import QtQuick 2.0
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
Item
Row
{
property alias value: textinput.text
property alias accountRef: ctrModel
property string subType
property bool readOnly
property alias currentIndex: trCombobox.currentIndex
property alias currentText: textinput.text
property alias displayInput: textInputRect.visible
property variant accounts
signal indexChanged()
id: editRoot
@ -22,7 +22,7 @@ Item
}
function currentValue() {
return currentText;
return value;
}
function currentType()
@ -38,7 +38,6 @@ Item
function load()
{
accountRef.clear();
accountRef.append({"itemid": " - "});
if (subType === "contract" || subType === "address")
{
var trCr = 0;
@ -52,7 +51,7 @@ Item
if (i > transactionIndex)
break;
var tr = blockChainPanel.model.blocks[k].transactions[i]
if (tr.functionId === tr.contractId /*&& (dec[1] === tr.contractId || item.subType === "address")*/)
if (tr.functionId === tr.contractId)
{
accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" });
trCr++;
@ -87,6 +86,7 @@ Item
}
trCombobox.currentIndex = 0;
}
trCombobox.update()
}
function select(address)
@ -102,10 +102,10 @@ Item
}
Rectangle {
anchors.fill: parent
radius: 4
anchors.verticalCenter: parent.verticalCenter
height: 20
id: textInputRect
TextInput {
id: textinput
text: value
@ -141,12 +141,12 @@ Item
property bool selected: false
id: trCombobox
model: ctrModel
width: 350
textRole: "itemid"
height: 20
anchors.verticalCenter: parent.verticalCenter
anchors.left: textinput.parent.right
anchors.leftMargin: 3
onCurrentIndexChanged: {
function update()
{
trCombobox.selected = false;
if (currentText === "")
return;
@ -164,5 +164,9 @@ Item
}
indexChanged();
}
onCurrentIndexChanged: {
update()
}
}
}

21
mix/qml/QBoolTypeView.qml

@ -21,16 +21,19 @@ Item
value = value === "true" ? "1" : value
value = value === "false" ? "0" : value;
var setValue = "1"
if (value === "")
boolCombo.currentIndex = parseInt(defaultValue);
setValue = parseInt(defaultValue);
else
boolCombo.currentIndex = parseInt(value);
setValue = parseInt(value);
boolCombo.checked = setValue === "1" ? true: false
boolCombo.enabled = !readOnly;
}
Rectangle {
color: "transparent"
anchors.fill: parent
ComboBox
CheckBox
{
property bool inited;
Component.onCompleted:
@ -41,17 +44,13 @@ Item
id: boolCombo
anchors.fill: parent
onCurrentIndexChanged:
onCheckedChanged:
{
if (inited)
value = comboModel.get(currentIndex).value;
}
model: ListModel
{
id: comboModel
ListElement { text: qsTr("False"); value: "0" }
ListElement { text: qsTr("True"); value: "1" }
value = checked ? "1" : "0"
}
text: qsTr("True")
}
}
}

23
mix/qml/QHashTypeView.qml

@ -13,22 +13,15 @@ Item
id: boldFont
}
Rectangle {
anchors.fill: parent
radius: 4
TextInput {
id: textinput
text: value
TextInput {
id: textinput
text: value
wrapMode: Text.WrapAnywhere
MouseArea {
id: mouseArea
anchors.fill: parent
wrapMode: Text.WrapAnywhere
clip: true
font.family: boldFont.name
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
}
}

33
mix/qml/QIntTypeView.qml

@ -1,35 +1,26 @@
import QtQuick 2.0
import QtQuick.Controls 1.1
Item
{
property alias value: textinput.text
property alias readOnly: textinput.readOnly
id: editRoot
width: readOnly ? textinput.implicitWidth : 150
width: 200
DebuggerPaneStyle {
id: dbgStyle
}
Rectangle {
anchors.fill: parent
radius: 4
TextInput {
anchors.verticalCenter: parent.verticalCenter
id: textinput
font.family: dbgStyle.general.basicFont
clip: true
selectByMouse: true
text: value
TextField {
anchors.verticalCenter: parent.verticalCenter
id: textinput
selectByMouse: true
text: value
implicitWidth: 200
MouseArea {
id: mouseArea
anchors.fill: parent
font.pointSize: dbgStyle.general.basicFontSize
color: dbgStyle.general.basicColor
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
}
}

28
mix/qml/QStringTypeView.qml

@ -1,4 +1,5 @@
import QtQuick 2.0
import QtQuick.Controls 1.1
Item
{
@ -11,25 +12,16 @@ Item
id: dbgStyle
}
Rectangle {
anchors.fill: parent
radius: 4
TextInput {
anchors.verticalCenter: parent.verticalCenter
id: textinput
font.family: dbgStyle.general.basicFont
clip: true
selectByMouse: true
text: value
TextField {
anchors.verticalCenter: parent.verticalCenter
id: textinput
selectByMouse: true
text: value
MouseArea {
id: mouseArea
anchors.fill: parent
font.pointSize: dbgStyle.general.basicFontSize
color: dbgStyle.general.basicColor
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
}
}

58
mix/qml/StructView.qml

@ -13,58 +13,40 @@ Column
property int transactionIndex
property string context
Layout.fillWidth: true
spacing: 0
DebuggerPaneStyle {
id: dbgStyle
}
spacing: 5
Repeater
{
id: repeater
visible: model.length > 0
Layout.fillWidth: true
RowLayout
{
id: row
height: 20 + (members[index].type.category === QSolidityType.Struct ? (20 * members[index].type.members.length) : 0)
height: 30 + (members[index].type.category === QSolidityType.Struct ? (30 * members[index].type.members.length) : 0)
Layout.fillWidth: true
DefaultLabel {
height: 20
id: typeLabel
text: modelData.type.name
anchors.verticalCenter: parent.verticalCenter
font.family: dbgStyle.general.basicFont
color: dbgStyle.general.basicColor
font.pointSize: dbgStyle.general.basicFontSize
}
DefaultLabel {
height: 20
id: nameLabel
text: modelData.name
anchors.verticalCenter: parent.verticalCenter
font.family: dbgStyle.general.basicFont
color: dbgStyle.general.basicColor
font.pointSize: dbgStyle.general.basicFontSize
}
Rectangle
{
Layout.preferredWidth: 150
Row
{
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
Label {
id: nameLabel
text: modelData.name
}
DefaultLabel {
height: 20
id: equalLabel
text: "="
anchors.verticalCenter: parent.verticalCenter
font.family: dbgStyle.general.basicFont
color: dbgStyle.general.basicColor
font.pointSize: dbgStyle.general.basicFontSize
Label {
id: typeLabel
text: " (" + modelData.type.name + ")"
font.italic: true
font.weight: Font.Light
}
}
}
Loader
{
id: typeLoader
height: 20
anchors.verticalCenter: parent.verticalCenter
sourceComponent:
{
var t = modelData.type.category;

714
mix/qml/TransactionDialog.qml

@ -7,12 +7,13 @@ import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/InputValidator.js" as InputValidator
import "js/NetworkDeployment.js" as NetworkDeployment
import "."
Dialog {
id: modalTransactionDialog
modality: Qt.ApplicationModal
width: 570
width: 580
height: 500
visible: false
title: qsTr("Edit Transaction")
@ -22,7 +23,7 @@ Dialog {
property alias gasAuto: gasAutoCheck.checked;
property alias gasPrice: gasPriceField.value;
property alias transactionValue: valueField.value;
property string contractId: contractComboBox.currentValue();
property string contractId: contractCreationComboBox.currentValue();
property alias functionId: functionComboBox.currentText;
property var paramValues;
property var paramsModel: [];
@ -30,21 +31,16 @@ Dialog {
property alias stateAccounts: senderComboBox.model
property bool saveStatus
signal accepted;
property int rowWidth: 500
StateDialogStyle {
id: transactionDialogStyle
}
function open(index, blockIdx, item) {
rowFunction.visible = !useTransactionDefaultValue;
rowValue.visible = !useTransactionDefaultValue;
rowGas.visible = !useTransactionDefaultValue;
rowGasPrice.visible = !useTransactionDefaultValue;
transactionIndex = index
blockIndex = blockIdx
typeLoader.transactionIndex = index
typeLoader.blockIndex = blockIdx
paramScroll.transactionIndex = index
paramScroll.blockIndex = blockIdx
saveStatus = item.saveStatus
gasValueEdit.gasValue = item.gas;
gasAutoCheck.checked = item.gasAuto ? true : false;
@ -52,48 +48,20 @@ Dialog {
valueField.value = item.value;
var contractId = item.contractId;
var functionId = item.functionId;
rowFunction.visible = true;
paramValues = item.parameters !== undefined ? item.parameters : {};
if (item.sender)
senderComboBox.select(item.sender);
contractsModel.clear();
var contractIndex = -1;
var contracts = codeModel.contracts;
for (var c in contracts) {
contractsModel.append({ cid: c, text: contracts[c].contract.name });
if (contracts[c].contract.name === contractId)
contractIndex = contractsModel.count - 1;
}
if (contractIndex == -1 && contractsModel.count > 0)
contractIndex = 0; //@todo suggest unused contract
contractComboBox.currentIndex = contractIndex;
trTypeCreate.checked = item.isContractCreation
trTypeSend.checked = !item.isFunctionCall
trTypeExecute.checked = item.isFunctionCall && !item.isContractCreation
recipients.accounts = senderComboBox.model;
recipients.subType = "address";
recipients.load();
recipients.init();
recipients.select(contractId);
if (item.isContractCreation)
loadFunctions(contractComboBox.currentValue());
else
loadFunctions(contractFromToken(recipients.currentValue()))
selectFunction(functionId);
trType.checked = item.isContractCreation
trType.init();
paramsModel = [];
if (item.isContractCreation)
loadCtorParameters();
else
loadParameters();
load(item.isContractCreation, item.isFunctionCall, functionId, contractId)
estimatedGas.updateView()
visible = true;
valueField.focus = true;
}
function loadCtorParameters(contractId)
@ -111,12 +79,12 @@ Dialog {
function loadFunctions(contractId)
{
functionsModel.clear();
functionsModel.append({ text: " - " });
var contract = codeModel.contracts[contractId];
if (contract) {
var functions = codeModel.contracts[contractId].contract.functions;
for (var f = 0; f < functions.length; f++) {
functionsModel.append({ text: functions[f].name });
if (functions[f].name !== contractId)
functionsModel.append({ text: functions[f].name });
}
}
}
@ -156,9 +124,9 @@ Dialog {
function loadParameters() {
paramsModel = []
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var contract = codeModel.contracts[contractFromToken(recipients.currentValue())];
var contract = codeModel.contracts[contractFromToken(contractCreationComboBox.currentValue())];
if (contract) {
var func = contract.contract.functions[functionComboBox.currentIndex - 1];
var func = contract.contract.functions[functionComboBox.currentIndex + 1];
if (func) {
var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++)
@ -171,13 +139,11 @@ Dialog {
function initTypeLoader()
{
typeLoader.value = {}
typeLoader.members = []
typeLoader.value = paramValues;
typeLoader.members = paramsModel;
paramLabel.visible = paramsModel.length > 0;
paramScroll.visible = paramsModel.length > 0;
modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300);
paramScroll.value = {}
paramScroll.members = []
paramScroll.value = paramValues;
paramScroll.members = paramsModel;
paramScroll.updateView()
}
function acceptAndClose()
@ -213,16 +179,16 @@ Dialog {
item.functionId = transactionDialog.functionId;
}
item.isContractCreation = trType.checked;
item.isContractCreation = trTypeCreate.checked;
if (item.isContractCreation)
item.functionId = item.contractId;
item.isFunctionCall = item.functionId !== " - ";
item.isFunctionCall = trTypeExecute.checked
if (!item.isContractCreation)
{
item.contractId = recipients.currentText;
item.label = item.contractId + " " + item.functionId;
if (recipients.current().type === "address")
item.contractId = recipientsAccount.currentValue();
item.label = contractFromToken(item.contractId) + "." + item.functionId + "()";
if (recipientsAccount.current().type === "address")
{
item.functionId = "";
item.isFunctionCall = false;
@ -230,8 +196,9 @@ Dialog {
}
else
{
item.isFunctionCall = true
item.functionId = item.contractId;
item.label = qsTr("Deploy") + " " + item.contractId;
item.label = item.contractId + "." + item.contractId + "()";
}
item.saveStatus = saveStatus
item.sender = senderComboBox.model[senderComboBox.currentIndex].secret;
@ -246,187 +213,360 @@ Dialog {
return token;
}
function load(isContractCreation, isFunctionCall, functionId, contractId)
{
if (!isContractCreation)
{
contractCreationComboBox.visible = false
recipientsAccount.visible = true
recipientsAccount.accounts = senderComboBox.model;
amountLabel.text = qsTr("Amount")
if (!isFunctionCall)
recipientsAccount.subType = "address"
else
recipientsAccount.subType = "contract";
recipientsAccount.load();
recipientsAccount.init();
if (contractId)
recipientsAccount.select(contractId);
if (functionId)
selectFunction(functionId);
if (isFunctionCall)
{
labelRecipient.text = qsTr("Recipient Contract")
functionRect.show()
loadFunctions(contractFromToken(recipientsAccount.currentValue()))
loadParameters();
paramScroll.updateView()
}
else
{
paramsModel = []
paramScroll.updateView()
labelRecipient.text = qsTr("Recipient Account")
functionRect.hide()
}
}
else
{
//contract creation
contractsModel.clear();
var contractIndex = -1;
var contracts = codeModel.contracts;
for (var c in contracts) {
contractsModel.append({ cid: c, text: contracts[c].contract.name });
if (contracts[c].contract.name === contractId)
contractIndex = contractsModel.count - 1;
}
if (contractIndex == -1 && contractsModel.count > 0)
contractIndex = 0; //@todo suggest unused contract
contractCreationComboBox.currentIndex = contractIndex;
contractCreationComboBox.visible = true
labelRecipient.text = qsTr("Contract")
amountLabel.text = qsTr("Endownment")
functionRect.hide()
recipientsAccount.visible = false
loadCtorParameters(contractCreationComboBox.currentValue());
paramScroll.updateView()
}
}
contentItem: Rectangle {
id: containerRect
color: transactionDialogStyle.generic.backgroundColor
ColumnLayout {
anchors.fill: parent
ScrollView
{
anchors.top: parent.top
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ColumnLayout {
id: dialogContent
anchors.top: parent.top
spacing: 10
RowLayout
Layout.preferredWidth: rowWidth
anchors.top: parent.top
anchors.topMargin: 10
anchors.left: parent.left
width: 500
anchors.leftMargin:
{
return (containerRect.width - 530) /2
}
RowLayout
{
Rectangle
{
id: rowSender
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Sender")
Layout.preferredWidth: 150
Label {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Sender Account")
}
ComboBox {
}
function select(secret)
{
for (var i in model)
if (model[i].secret === secret)
{
currentIndex = i;
break;
}
}
ComboBox {
function select(secret)
{
for (var i in model)
if (model[i].secret === secret)
{
currentIndex = i;
break;
}
}
Layout.preferredWidth: 350
id: senderComboBox
currentIndex: 0
textRole: "name"
editable: false
}
}
id: senderComboBox
Layout.preferredWidth: 350
currentIndex: 0
textRole: "name"
editable: false
RowLayout
{
Rectangle
{
Layout.preferredWidth: 150
Layout.preferredHeight: 80
color: "transparent"
Label
{
anchors.verticalCenter: parent.verticalCenter
anchors.top: parent.top
anchors.right: parent.right
text: qsTr("Type of Transaction")
}
}
RowLayout
Column
{
id: rowIsContract
Layout.fillWidth: true
height: 150
CheckBox {
id: trType
onCheckedChanged:
{
init();
Layout.preferredWidth: 350
Layout.preferredHeight: 90
ExclusiveGroup {
id: rbbuttonList
onCurrentChanged: {
if (current)
{
if (current.objectName === "trTypeSend")
{
recipientsAccount.visible = true
contractCreationComboBox.visible = false
modalTransactionDialog.load(false, false)
}
else if (current.objectName === "trTypeCreate")
{
contractCreationComboBox.visible = true
recipientsAccount.visible = false
modalTransactionDialog.load(true, true)
}
else if (current.objectName === "trTypeExecute")
{
recipientsAccount.visible = true
contractCreationComboBox.visible = false
modalTransactionDialog.load(false, true)
}
}
}
}
function init()
{
rowFunction.visible = !checked;
rowContract.visible = checked;
rowRecipient.visible = !checked;
paramLabel.visible = checked;
paramScroll.visible = checked;
functionComboBox.enabled = !checked;
if (checked)
loadCtorParameters(contractComboBox.currentValue());
}
RadioButton {
id: trTypeSend
objectName: "trTypeSend"
exclusiveGroup: rbbuttonList
height: 30
text: qsTr("Send ether to account")
text: qsTr("is contract creation")
checked: true
}
RadioButton {
id: trTypeCreate
objectName: "trTypeCreate"
exclusiveGroup: rbbuttonList
height: 30
text: qsTr("Create Contract")
}
RadioButton {
id: trTypeExecute
objectName: "trTypeExecute"
exclusiveGroup: rbbuttonList
height: 30
text: qsTr("Execute Contract")
}
}
}
RowLayout
RowLayout
{
Rectangle
{
id: rowRecipient
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Recipient")
Layout.preferredWidth: 150
Label {
id: labelRecipient
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
text: qsTr("Recipient Account")
}
}
QAddressView
QAddressView
{
id: recipientsAccount
displayInput: false
onIndexChanged:
{
id: recipients
onIndexChanged:
{
rowFunction.visible = current().type === "contract";
paramLabel.visible = current().type === "contract";
paramScroll.visible = current().type === "contract";
if (!rowIsContract.checked)
loadFunctions(contractFromToken(recipients.currentValue()))
}
if (rbbuttonList.current.objectName === "trTypeExecute")
loadFunctions(contractFromToken(currentValue()))
}
}
RowLayout
{
id: rowContract
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Contract")
ComboBox {
id: contractCreationComboBox
function currentValue() {
return (currentIndex >=0 && currentIndex < contractsModel.count) ? contractsModel.get(currentIndex).cid : "";
}
ComboBox {
id: contractComboBox
function currentValue() {
return (currentIndex >=0 && currentIndex < contractsModel.count) ? contractsModel.get(currentIndex).cid : "";
}
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: contractsModel
}
onCurrentIndexChanged: {
loadCtorParameters(currentValue());
}
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: contractsModel
}
onCurrentIndexChanged: {
loadCtorParameters(currentValue());
}
}
}
RowLayout
RowLayout
{
Rectangle
{
id: rowFunction
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
Layout.preferredWidth: 150
id: functionRect
function hide()
{
parent.visible = false
functionRect.visible = false
functionComboBox.visible = false
}
function show()
{
parent.visible = true
functionRect.visible = true
functionComboBox.visible = true
}
Label {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
text: qsTr("Function")
}
ComboBox {
id: functionComboBox
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
}
onCurrentIndexChanged: {
loadParameters();
}
}
ComboBox {
id: functionComboBox
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
}
onCurrentIndexChanged: {
loadParameters();
}
}
}
CommonSeparator
StructView
{
id: paramScroll
members: paramsModel;
accounts: senderComboBox.model
context: "parameter"
Layout.fillWidth: true
function updateView()
{
Layout.fillWidth: true
paramScroll.visible = paramsModel.length > 0
paramScroll.Layout.preferredHeight = paramsModel.length < 6 ? paramsModel.length * 30 : 205
if (paramsModel.length === 0)
{
paramScroll.height = 0
}
}
}
RowLayout
RowLayout
{
Rectangle
{
id: rowValue
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Value")
}
Ether {
id: valueField
edit: true
displayFormattedValue: true
Layout.preferredWidth: 150
Label {
id: amountLabel
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
text: qsTr("Amount")
}
}
CommonSeparator
Ether {
Layout.preferredWidth: 350
id: valueField
edit: true
displayFormattedValue: false
displayUnitSelection: true
}
}
Rectangle
{
Layout.preferredHeight: 30
Layout.fillWidth: true
color: "transparent"
Rectangle
{
color: "#cccccc"
height: 1
width: parent.width
anchors.verticalCenter: parent.verticalCenter
}
}
Rectangle
{
height: 20
color: "transparent"
Layout.preferredWidth: 500
Rectangle
{
Layout.fillWidth: true
anchors.horizontalCenter: parent.horizontalCenter
Label {
text: qsTr("Transaction fees")
anchors.horizontalCenter: parent.horizontalCenter
}
}
RowLayout
}
RowLayout
{
Layout.preferredHeight: 45
Rectangle
{
id: rowGas
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
Layout.preferredWidth: 150
Label {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
text: qsTr("Gas")
}
}
Row
{
Layout.preferredWidth: 350
DefaultTextField
{
property variant gasValue
@ -435,6 +575,55 @@ Dialog {
implicitWidth: 200
enabled: !gasAutoCheck.checked
id: gasValueEdit;
Label
{
id: estimatedGas
anchors.top: parent.bottom
text: ""
Connections
{
target: functionComboBox
onCurrentIndexChanged:
{
estimatedGas.displayGas(contractFromToken(recipientsAccount.currentValue()), functionComboBox.currentText)
}
}
function displayGas(contractName, functionName)
{
var gasCost = codeModel.gasCostBy(contractName, functionName);
if (gasCost && gasCost.length > 0)
{
var gas = codeModel.txGas + codeModel.callStipend + parseInt(gasCost[0].gas)
estimatedGas.text = qsTr("Estimated cost: ") + gasCost[0].gas + " gas"
}
}
function updateView()
{
if (rbbuttonList.current.objectName === "trTypeExecute")
estimatedGas.displayGas(contractFromToken(recipientsAccount.currentValue()), functionComboBox.currentText)
else if (rbbuttonList.current.objectName === "trTypeCreate")
{
var contractName = contractCreationComboBox.currentValue()
estimatedGas.displayGas(contractName, contractName)
}
else if (rbbuttonList.current.objectName === "trTypeSend")
{
var gas = codeModel.txGas + codeModel.callStipend
estimatedGas.text = qsTr("Estimated cost: ") + gas + " gas"
}
}
Connections
{
target: rbbuttonList
onCurrentChanged: {
estimatedGas.updateView()
}
}
}
}
CheckBox
@ -444,101 +633,92 @@ Dialog {
text: qsTr("Auto");
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
RowLayout
{
Layout.preferredWidth: 500
Layout.preferredHeight: 45
Rectangle
{
id: rowGasPrice
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
Layout.preferredWidth: 150
Label {
id: gasPriceLabel
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
text: qsTr("Gas Price")
}
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
}
}
CommonSeparator
{
Layout.fillWidth: true
}
DefaultLabel {
id: paramLabel
text: qsTr("Parameters:")
Layout.preferredWidth: 75
}
ScrollView
{
id: paramScroll
anchors.top: paramLabel.bottom
anchors.topMargin: 10
Layout.fillWidth: true
Layout.fillHeight: true
StructView
{
id: typeLoader
Layout.preferredWidth: 150
members: paramsModel;
accounts: senderComboBox.model
context: "parameter"
Label {
id: gasPriceMarket
anchors.top: gasPriceLabel.bottom
Component.onCompleted:
{
NetworkDeployment.gasPrice(function(result)
{
gasPriceMarket.text = qsTr("Current market: ") + " " + result + " Wei";
}, function (){});
}
}
}
}
CommonSeparator
{
Layout.fillWidth: true
visible: paramsModel.length > 0
Ether {
Layout.preferredWidth: 350
id: gasPriceField
edit: true
displayFormattedValue: false
displayUnitSelection: true
}
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
RowLayout
{
text: qsTr("OK");
onClicked: {
var invalid = InputValidator.validate(paramsModel, paramValues);
if (invalid.length === 0)
{
close();
accepted();
Layout.preferredWidth: 500
Row
{
width: parent.width
anchors.right: parent.right
Button {
id: updateBtn
text: qsTr("Cancel");
onClicked: close();
}
else
{
errorDialog.text = qsTr("Some parameters are invalid:\n");
for (var k in invalid)
errorDialog.text += invalid[k].message + "\n";
errorDialog.open();
Button {
text: qsTr("Update");
onClicked: {
var invalid = InputValidator.validate(paramsModel, paramValues);
if (invalid.length === 0)
{
close();
accepted();
}
else
{
errorDialog.text = qsTr("Some parameters are invalid:\n");
for (var k in invalid)
errorDialog.text += invalid[k].message + "\n";
errorDialog.open();
}
}
}
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
MessageDialog {
id: errorDialog
standardButtons: StandardButton.Ok
icon: StandardIcon.Critical
}
}
MessageDialog {
id: errorDialog
standardButtons: StandardButton.Ok
icon: StandardIcon.Critical
RowLayout
{
Layout.preferredHeight: 30
anchors.bottom: parent.bottom
}
}
}
}
}

6
mix/qml/js/NetworkDeployment.js

@ -201,7 +201,7 @@ function executeTrNextStep(trIndex, state, ctrAddresses, callBack)
callBack();
}
function gasPrice(callBack)
function gasPrice(callBack, error)
{
var requests = [{
jsonrpc: "2.0",
@ -210,7 +210,9 @@ function gasPrice(callBack)
id: jsonRpcRequestId
}];
rpcCall(requests, function (httpCall, response){
callBack(JSON.parse(response)[0].result);
callBack(JSON.parse(response)[0].result)
}, function(message){
error(message)
});
}

4
mix/qml/js/TransactionHelper.js

@ -17,7 +17,7 @@ function defaultTransaction()
};
}
function rpcCall(requests, callBack)
function rpcCall(requests, callBack, error)
{
var jsonRpcUrl = "http://localhost:8545";
var rpcRequest = JSON.stringify(requests);
@ -33,7 +33,7 @@ function rpcCall(requests, callBack)
{
var errorText = qsTr("Unable to initiate request to the live network. Please verify your ethereum node is up.") + qsTr(" Error status: ") + httpRequest.status;
console.log(errorText);
deploymentError(errorText);
error(errorText);
}
else
{

2
test/libdevcrypto/trie.cpp

@ -585,7 +585,7 @@ template<typename Trie> void perfTestTrie(char const* _name)
d.init();
cnote << "TriePerf " << _name << p;
std::vector<h256> keys(1000);
boost::timer t;
Timer t;
size_t ki = 0;
for (size_t i = 0; i < p; ++i)
{

29
test/libethcore/keymanager.cpp

@ -57,6 +57,8 @@ BOOST_AUTO_TEST_CASE(KeyManagerConstructor)
BOOST_CHECK_EQUAL(km.keysFile(), km.defaultPath());
BOOST_CHECK_EQUAL(km.defaultPath(), getDataDir("ethereum") + "/keys.info");
BOOST_CHECK(km.store().keys() == SecretStore().keys());
for (auto a: km.accounts())
km.kill(a);
}
BOOST_AUTO_TEST_CASE(KeyManagerKeysFile)
@ -72,13 +74,15 @@ BOOST_AUTO_TEST_CASE(KeyManagerKeysFile)
BOOST_CHECK(!km.exists());
BOOST_CHECK_THROW(km.create(password), FileError);
km.setKeysFile(tmpDir.path() + "/notExistingDir/keysFile.json");
BOOST_CHECK_THROW(km.create(password), FileError);
BOOST_CHECK(!km.exists());
BOOST_CHECK_NO_THROW(km.create(password));
BOOST_CHECK(km.exists());
km.setKeysFile(tmpDir.path() + "keysFile.json");
BOOST_CHECK_NO_THROW(km.create(password));
km.save(password);
BOOST_CHECK(km.load(password));
for (auto a: km.accounts())
km.kill(a);
}
BOOST_AUTO_TEST_CASE(KeyManagerHints)
@ -96,6 +100,25 @@ BOOST_AUTO_TEST_CASE(KeyManagerHints)
BOOST_CHECK(!km.haveHint(password + "2"));
km.notePassword(password);
BOOST_CHECK(km.haveHint(password));
for (auto a: km.accounts())
km.kill(a);
}
BOOST_AUTO_TEST_CASE(KeyManagerAccounts)
{
KeyManager km;
string password = "hardPassword";
TransientDirectory tmpDir;
km.setKeysFile(tmpDir.path()+ "keysFile.json");
BOOST_CHECK_NO_THROW(km.create(password));
BOOST_CHECK(km.accounts().empty());
BOOST_CHECK(km.load(password));
for (auto a: km.accounts())
km.kill(a);
}
BOOST_AUTO_TEST_SUITE_END()

121
test/libsolidity/SolidityEndToEndTest.cpp

@ -4517,6 +4517,105 @@ BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer)
);
}
BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage)
{
char const* sourceCode = R"(
contract Test {
uint24[] public data;
function set(uint24[] _data) returns (uint) {
data = _data;
return data.length;
}
function get() returns (uint24[]) {
return data;
}
}
)";
compileAndRun(sourceCode, 0, "Test");
vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
BOOST_REQUIRE(
callContractFunction("set(uint24[])", u256(0x20), u256(data.size()), data) ==
encodeArgs(u256(data.size()))
);
BOOST_CHECK(callContractFunction("data(uint256)", u256(7)) == encodeArgs(u256(8)));
BOOST_CHECK(callContractFunction("data(uint256)", u256(15)) == encodeArgs(u256(16)));
BOOST_CHECK(callContractFunction("data(uint256)", u256(18)) == encodeArgs());
BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(0x20), u256(data.size()), data));
}
BOOST_AUTO_TEST_CASE(arrays_complex_from_and_to_storage)
{
char const* sourceCode = R"(
contract Test {
uint24[3][] public data;
function set(uint24[3][] _data) returns (uint) {
data = _data;
return data.length;
}
function get() returns (uint24[3][]) {
return data;
}
}
)";
compileAndRun(sourceCode, 0, "Test");
vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
BOOST_REQUIRE(
callContractFunction("set(uint24[3][])", u256(0x20), u256(data.size() / 3), data) ==
encodeArgs(u256(data.size() / 3))
);
BOOST_CHECK(callContractFunction("data(uint256,uint256)", u256(2), u256(2)) == encodeArgs(u256(9)));
BOOST_CHECK(callContractFunction("data(uint256,uint256)", u256(5), u256(1)) == encodeArgs(u256(17)));
BOOST_CHECK(callContractFunction("data(uint256,uint256)", u256(6), u256(0)) == encodeArgs());
BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(0x20), u256(data.size() / 3), data));
}
BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access)
{
char const* sourceCode = R"(
contract Test {
function set(uint24[3][] _data, uint a, uint b) returns (uint l, uint e) {
l = _data.length;
e = _data[a][b];
}
}
)";
compileAndRun(sourceCode, 0, "Test");
vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
BOOST_REQUIRE(callContractFunction(
"set(uint24[3][],uint256,uint256)",
u256(0x60),
u256(3),
u256(2),
u256(data.size() / 3),
data
) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2])));
}
BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
{
char const* sourceCode = R"(
contract Test {
function set(bytes _data, uint i) returns (uint l, byte c) {
l = _data.length;
c = _data[i];
}
}
)";
compileAndRun(sourceCode, 0, "Test");
string data("abcdefgh");
BOOST_REQUIRE(callContractFunction(
"set(bytes,uint256)",
u256(0x40),
u256(3),
u256(data.size()),
data
) == encodeArgs(u256(data.size()), string("d")));
}
BOOST_AUTO_TEST_CASE(storage_array_ref)
{
char const* sourceCode = R"(
@ -4570,6 +4669,28 @@ BOOST_AUTO_TEST_CASE(storage_array_ref)
BOOST_CHECK(callContractFunction("find(uint256)", u256(400)) == encodeArgs(u256(-1)));
}
BOOST_AUTO_TEST_CASE(memory_types_initialisation)
{
char const* sourceCode = R"(
contract Test {
mapping(uint=>uint) data;
function stat() returns (uint[5])
{
data[2] = 3; // make sure to use some memory
}
function dyn() returns (uint[]) { stat(); }
function nested() returns (uint[3][]) { stat(); }
function nestedStat() returns (uint[3][7]) { stat(); }
}
)";
compileAndRun(sourceCode, 0, "Test");
BOOST_CHECK(callContractFunction("stat()") == encodeArgs(vector<u256>(5)));
BOOST_CHECK(callContractFunction("dyn()") == encodeArgs(u256(0x20), u256(0)));
BOOST_CHECK(callContractFunction("nested()") == encodeArgs(u256(0x20), u256(0)));
BOOST_CHECK(callContractFunction("nestedStat()") == encodeArgs(vector<u256>(3 * 7)));
}
BOOST_AUTO_TEST_SUITE_END()
}

8
test/libsolidity/solidityExecutionFramework.h

@ -127,6 +127,14 @@ public:
return _padLeft ? padding + _value : _value + padding;
}
static bytes encode(std::string const& _value) { return encode(asBytes(_value), false); }
template <class _T>
static bytes encode(std::vector<_T> const& _value)
{
bytes ret;
for (auto const& v: _value)
ret += encode(v);
return ret;
}
template <class FirstArg, class... Args>
static bytes encodeArgs(FirstArg const& _firstArg, Args const&... _followingArgs)

Loading…
Cancel
Save