Browse Source

Merge branch 'develop' into rlpx

cl-refactor
subtly 10 years ago
parent
commit
4a1d6caae7
  1. 16
      CMakeLists.txt
  2. 3
      CodingStandards.txt
  3. 44
      alethzero/Main.ui
  4. 60
      alethzero/MainWin.cpp
  5. 9
      alethzero/MainWin.h
  6. 2
      alethzero/OurWebThreeStubServer.cpp
  7. 29
      eth/main.cpp
  8. 39
      ethminer/MinerAux.h
  9. 22
      ethvm/main.cpp
  10. 7
      evmjit/include/evmjit/JIT.h
  11. 92
      evmjit/libevmjit/JIT.cpp
  12. 21
      libdevcore/FixedHash.h
  13. 22
      libdevcore/TrieCommon.h
  14. 24
      libdevcrypto/Common.cpp
  15. 2
      libdevcrypto/Common.h
  16. 2
      libdevcrypto/CryptoPP.cpp
  17. 1
      libethash-cl/CMakeLists.txt
  18. 70
      libethash-cl/ethash_cl_miner.cpp
  19. 16
      libethash-cl/ethash_cl_miner.h
  20. 16
      libethcore/Common.h
  21. 30
      libethcore/Ethash.cpp
  22. 11
      libethcore/Ethash.h
  23. 14
      libethcore/Transaction.cpp
  24. 4
      libethcore/Transaction.h
  25. 4
      libethereum/Account.h
  26. 8
      libethereum/BlockChain.cpp
  27. 503
      libethereum/BlockChainSync.cpp
  28. 52
      libethereum/BlockChainSync.h
  29. 9
      libethereum/BlockQueue.cpp
  30. 8
      libethereum/BlockQueue.h
  31. 15
      libethereum/Client.cpp
  32. 6
      libethereum/Client.h
  33. 43
      libethereum/ClientBase.cpp
  34. 9
      libethereum/ClientBase.h
  35. 1
      libethereum/CommonNet.h
  36. 126
      libethereum/EthereumHost.cpp
  37. 4
      libethereum/EthereumHost.h
  38. 36
      libethereum/EthereumPeer.cpp
  39. 4
      libethereum/EthereumPeer.h
  40. 9
      libethereum/Executive.cpp
  41. 4
      libethereum/Executive.h
  42. 2
      libethereum/ExtVM.cpp
  43. 29
      libethereum/Interface.cpp
  44. 12
      libethereum/Interface.h
  45. 63
      libethereum/State.cpp
  46. 2
      libethereum/State.h
  47. 7
      libethereum/Transaction.h
  48. 312
      libethereum/TransactionQueue.cpp
  49. 99
      libethereum/TransactionQueue.h
  50. 4
      libethereum/VerifiedBlock.h
  51. 6
      libevm/ExtVMFace.cpp
  52. 2
      libevm/ExtVMFace.h
  53. 82
      libevm/SmartVM.cpp
  54. 1
      libjsengine/CMakeLists.txt
  55. 2
      libp2p/Common.h
  56. 7
      libp2p/Host.h
  57. 4
      libp2p/RLPXSocket.h
  58. 10
      libp2p/Session.cpp
  59. 10
      libtestutils/StateLoader.cpp
  60. 4
      libweb3jsonrpc/AccountHolder.cpp
  61. 3
      libweb3jsonrpc/JsonHelper.cpp
  62. 2
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  63. 2
      libwebthree/CMakeLists.txt
  64. 54
      libwhisper/BloomFilter.h
  65. 2
      libwhisper/Common.cpp
  66. 13
      libwhisper/Message.cpp
  67. 1
      libwhisper/Message.h
  68. 26
      libwhisper/WhisperHost.cpp
  69. 20
      libwhisper/WhisperPeer.cpp
  70. 8
      mix/DebuggingStateWrapper.h
  71. 2
      mix/HttpServer.h
  72. 28
      mix/MixClient.cpp
  73. 6
      mix/MixClient.h
  74. 26
      neth/main.cpp
  75. 2
      solc/CMakeLists.txt
  76. 1
      test/CMakeLists.txt
  77. 24
      test/TestHelper.cpp
  78. 3
      test/fuzzTesting/createRandomTest.cpp
  79. 136
      test/fuzzTesting/fuzzHelper.cpp
  80. 106
      test/libdevcore/FixedHash.cpp
  81. 1
      test/libdevcrypto/crypto.cpp
  82. 854
      test/libethereum/StateTestsFiller/stPreCompiledContractsTransactionFiller.json
  83. 43
      test/libethereum/StateTestsFiller/stSpecialTestFiller.json
  84. 5
      test/libethereum/blockchain.cpp
  85. 5
      test/libethereum/state.cpp
  86. 108
      test/libethereum/transactionqueue.cpp
  87. 2
      test/libp2p/peer.cpp
  88. 1
      test/libp2p/rlpx.cpp
  89. 248
      test/libsolidity/SolidityWallet.cpp
  90. 23
      test/libwhisper/bloomFilter.cpp
  91. 20
      test/libwhisper/whisperMessage.cpp
  92. 36
      test/libwhisper/whisperTopic.cpp

16
CMakeLists.txt

@ -203,7 +203,9 @@ eth_format_option(ROCKSDB)
eth_format_option(GUI)
eth_format_option(TESTS)
eth_format_option(NOBOOST)
eth_format_option(ROCKSDB)
eth_format_option(TOOLS)
eth_format_option(ETHKEY)
eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE)
eth_format_option_on_decent_platform(SERPENT)
@ -234,6 +236,15 @@ elseif (BUNDLE STREQUAL "full")
set(TOOLS ON)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "cli")
set(SERPENT ${DECENT_PLATFORM})
set(SOLIDITY ON)
set(USENPM ON)
set(GUI OFF)
# set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "core")
set(SERPENT OFF)
set(SOLIDITY ON)
@ -312,6 +323,7 @@ message("-- Hardware identification support ${CPUID_FO
message("-- HTTP Request support ${CURL_FOUND}")
message("-- VMTRACE VM execution tracing ${VMTRACE}")
message("-- PROFILING Profiling support ${PROFILING}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}")
@ -325,7 +337,6 @@ message("-- SERPENT Build Serpent language components ${SERPENT}
message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}")
message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
@ -362,8 +373,6 @@ else ()
set(GENERAL 0)
endif ()
message("GENERAL ${GENERAL}")
add_subdirectory(libdevcore)
if (GENERAL)
add_subdirectory(libevmcore)
@ -398,6 +407,7 @@ if (JSCONSOLE)
endif ()
if (NOT WIN32)
add_definitions(-DETH_HAVE_SECP256K1)
add_subdirectory(secp256k1)
endif ()

3
CodingStandards.txt

@ -196,7 +196,7 @@ a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector<int>; rather than
b. Generally avoid shortening a standard form that already includes all important information:
- e.g. stick to shared_ptr<X> rather than shortening to ptr<X>.
c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently.
- e.g. using Guard = boost::lock_guard<std::mutex>; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly.
- e.g. using Guard = std::lock_guard<std::mutex>; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly.
d. In general expressions should be roughly as important/semantically meaningful as the space they occupy.
@ -226,4 +226,3 @@ a. Includes should go in order of lower level (STL -> boost -> libdevcore -> lib
#include <libethereum/Defaults.h>
b. The only exception to the above rule is the top of a .cpp file where its corresponding header should be located.

44
alethzero/Main.ui

@ -132,7 +132,7 @@
<x>0</x>
<y>0</y>
<width>1617</width>
<height>24</height>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@ -192,8 +192,11 @@
<addaction name="injectBlock"/>
<addaction name="forceMining"/>
<addaction name="separator"/>
<addaction name="vmInterpreter"/>
<addaction name="vmJIT"/>
<addaction name="vmSmart"/>
<addaction name="separator"/>
<addaction name="usePrivate"/>
<addaction name="jitvm"/>
<addaction name="retryUnknown"/>
<addaction name="confirm"/>
</widget>
@ -704,7 +707,7 @@
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_8">
<widget class="QDockWidget" name="blockChainDockWidget">
<property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set>
</property>
@ -1697,17 +1700,6 @@ font-size: 14pt</string>
<string>&amp;Clear Pending</string>
</property>
</action>
<action name="jitvm">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="text">
<string>Use &amp;LLVM-EVM</string>
</property>
</action>
<action name="killAccount">
<property name="text">
<string>&amp;Kill Account</string>
@ -1783,6 +1775,30 @@ font-size: 14pt</string>
<string>&amp;Gas Prices...</string>
</property>
</action>
<action name="vmInterpreter">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Interpreter</string>
</property>
</action>
<action name="vmJIT">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>JIT</string>
</property>
</action>
<action name="vmSmart">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Smart</string>
</property>
</action>
<action name="sentinel">
<property name="text">
<string>&amp;Sentinel...</string>

60
alethzero/MainWin.cpp

@ -239,18 +239,31 @@ Main::Main(QWidget *parent) :
ethereum()->setDefault(LatestBlock);
m_vmSelectionGroup = new QActionGroup{ui->menu_Debug};
m_vmSelectionGroup->addAction(ui->vmInterpreter);
m_vmSelectionGroup->addAction(ui->vmJIT);
m_vmSelectionGroup->addAction(ui->vmSmart);
m_vmSelectionGroup->setExclusive(true);
#if ETH_EVMJIT
ui->vmSmart->setChecked(true); // Default when JIT enabled
on_vmSmart_triggered();
#else
ui->vmInterpreter->setChecked(true);
ui->vmJIT->setEnabled(false);
ui->vmSmart->setEnabled(false);
#endif
readSettings();
m_transact = new Transact(this, this);
m_transact->setWindowFlags(Qt::Dialog);
m_transact->setWindowModality(Qt::WindowModal);
connect(ui->blockChainDockWidget, &QDockWidget::visibilityChanged, [=]() { refreshBlockChain(); });
#if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts);
#endif
#if !ETH_EVMJIT
ui->jitvm->setEnabled(false);
ui->jitvm->setChecked(false);
#endif
installWatches();
startTimer(100);
@ -731,7 +744,8 @@ void Main::writeSettings()
s.setValue("url", ui->urlEdit->text());
s.setValue("privateChain", m_privateChain);
s.setValue("verbosity", ui->verbosity->value());
s.setValue("jitvm", ui->jitvm->isChecked());
if (auto vm = m_vmSelectionGroup->checkedAction())
s.setValue("vm", vm->text());
bytes d = m_webThree->saveNetwork();
if (!d.empty())
@ -822,8 +836,28 @@ void Main::readSettings(bool _skipGeometry)
m_privateChain = s.value("privateChain", "").toString();
ui->usePrivate->setChecked(m_privateChain.size());
ui->verbosity->setValue(s.value("verbosity", 1).toInt());
ui->jitvm->setChecked(s.value("jitvm", true).toBool());
on_jitvm_triggered();
#if ETH_EVMJIT // We care only if JIT is enabled. Otherwise it can cause misconfiguration.
auto vmName = s.value("vm").toString();
if (!vmName.isEmpty())
{
if (vmName == ui->vmInterpreter->text())
{
ui->vmInterpreter->setChecked(true);
on_vmInterpreter_triggered();
}
else if (vmName == ui->vmJIT->text())
{
ui->vmJIT->setChecked(true);
on_vmJIT_triggered();
}
else if (vmName == ui->vmSmart->text())
{
ui->vmSmart->setChecked(true);
on_vmSmart_triggered();
}
}
#endif
ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html
on_urlEdit_returnPressed();
@ -1000,11 +1034,9 @@ void Main::on_usePrivate_triggered()
on_killBlockchain_triggered();
}
void Main::on_jitvm_triggered()
{
bool jit = ui->jitvm->isChecked();
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
}
void Main::on_vmInterpreter_triggered() { VMFactory::setKind(VMKind::Interpreter); }
void Main::on_vmJIT_triggered() { VMFactory::setKind(VMKind::JIT); }
void Main::on_vmSmart_triggered() { VMFactory::setKind(VMKind::Smart); }
void Main::on_urlEdit_returnPressed()
{
@ -1260,7 +1292,7 @@ void Main::refreshBlockCount()
auto d = ethereum()->blockChain().details();
BlockQueueStatus b = ethereum()->blockQueueStatus();
SyncStatus sync = ethereum()->syncStatus();
QString syncStatus = EthereumHost::stateName(sync.state);
QString syncStatus = QString("PV%1 %2").arg(sync.protocolVersion).arg(EthereumHost::stateName(sync.state));
if (sync.state == SyncState::Hashes)
syncStatus += QString(": %1/%2%3").arg(sync.hashesReceived).arg(sync.hashesEstimated ? "~" : "").arg(sync.hashesTotal);
if (sync.state == SyncState::Blocks || sync.state == SyncState::NewBlocks)
@ -1277,7 +1309,7 @@ void Main::on_turboMining_triggered()
void Main::refreshBlockChain()
{
if (!ui->blocks->isVisible() && isVisible())
if (!(ui->blockChainDockWidget->isVisible() || !tabifiedDockWidgets(ui->blockChainDockWidget).isEmpty()))
return;
DEV_TIMED_FUNCTION_ABOVE(500);

9
alethzero/MainWin.h

@ -44,6 +44,7 @@
#include "Connect.h"
class QListWidgetItem;
class QActionGroup;
namespace Ui {
class Main;
@ -182,8 +183,10 @@ private slots:
void on_forceMining_triggered();
void on_usePrivate_triggered();
void on_turboMining_triggered();
void on_jitvm_triggered();
void on_retryUnknown_triggered();
void on_vmInterpreter_triggered();
void on_vmJIT_triggered();
void on_vmSmart_triggered();
// Debugger
void on_debugCurrent_triggered();
@ -272,6 +275,8 @@ private:
dev::Address m_nameReg;
dev::Address m_beneficiary;
QActionGroup* m_vmSelectionGroup = nullptr;
QList<QPair<QString, QString>> m_consoleHistory;
QMutex m_logLock;
QString m_logHistory;
@ -287,6 +292,6 @@ private:
std::unique_ptr<DappHost> m_dappHost;
DappLoader* m_dappLoader;
QWebEnginePage* m_webPage;
Connect m_connect;
};

2
alethzero/OurWebThreeStubServer.cpp

@ -130,7 +130,7 @@ void OurAccountHolder::doValidations()
else
// sign and submit.
if (Secret s = m_main->retrieveSecret(t.from))
m_main->ethereum()->submitTransaction(s, t);
m_main->ethereum()->submitTransaction(t, s);
}
}

29
eth/main.cpp

@ -37,6 +37,7 @@
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libethereum/BlockChainSync.h>
#include <libethcore/KeyManager.h>
#include <libwebthree/WebThree.h>
@ -194,7 +195,7 @@ void help()
<< "General Options:" << endl
<< " -d,--db-path <path> Load database from path (default: " << getDataDir() << ")" << endl
#if ETH_EVMJIT || !ETH_TRUE
<< " -J,--jit Enable EVM JIT (default: off)." << endl
<< " --vm <vm-kind> Select VM. Options are: interpreter, jit, smart. (default: interpreter)" << endl
#endif
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl
<< " -V,--version Show the version and exit." << endl
@ -375,6 +376,8 @@ void interactiveMode(eth::Client* c, std::shared_ptr<eth::TrivialGasPricer> gasP
cout << "Current block: " << c->blockChain().details().number << endl;
else if (c && cmd == "blockqueue")
cout << "Current blockqueue status: " << endl << c->blockQueueStatus() << endl;
else if (c && cmd == "sync")
cout << "Current sync status: " << endl << c->syncStatus() << endl;
else if (c && cmd == "hashrate")
cout << "Current hash rate: " << toString(c->hashrate()) << " hashes per second." << endl;
else if (c && cmd == "findblock")
@ -1441,8 +1444,21 @@ int main(int argc, char** argv)
}
}
#if ETH_EVMJIT
else if (arg == "-J" || arg == "--jit")
jit = true;
else if (arg == "--vm" && i + 1 < argc)
{
string vmKind = argv[++i];
if (vmKind == "interpreter")
VMFactory::setKind(VMKind::Interpreter);
else if (vmKind == "jit")
VMFactory::setKind(VMKind::JIT);
else if (vmKind == "smart")
VMFactory::setKind(VMKind::Smart);
else
{
cerr << "Unknown VM kind: " << vmKind << endl;
return -1;
}
}
#endif
else if (arg == "-h" || arg == "--help")
help();
@ -1479,7 +1495,7 @@ int main(int argc, char** argv)
g_logPost = [&](std::string const& a, char const*){
static SpinLock s_lock;
SpinGuard l(s_lock);
if (g_silence)
logbuf += a + "\n";
else
@ -1736,13 +1752,13 @@ int main(int argc, char** argv)
{
#if ETH_JSCONSOLE || !ETH_TRUE
JSLocalConsole console;
// TODO: set port properly?
shared_ptr<dev::WebThreeStubServer> rpcServer = make_shared<dev::WebThreeStubServer>(*console.connector(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager, *gasPricer);
while (!g_exit)
{
console.readExpression();
stopMiningAfterXBlocks(c, n, mining);
}
jsonrpcServer->StopListening();
rpcServer->StopListening();
#endif
}
else
@ -1764,4 +1780,3 @@ int main(int argc, char** argv)
writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData);
return 0;
}

39
ethminer/MinerAux.h

@ -128,6 +128,33 @@ public:
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--cl-global-work" && i + 1 < argc)
try {
m_globalWorkSizeMultiplier = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--cl-local-work" && i + 1 < argc)
try {
m_localWorkSize = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--cl-ms-per-batch" && i + 1 < argc)
try {
m_msPerBatch = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
BOOST_THROW_EXCEPTION(BadArgument());
}
else if (arg == "--list-devices")
m_shouldListDevices = true;
else if (arg == "--allow-opencl-cpu")
@ -266,16 +293,16 @@ public:
else if (m_minerType == MinerType::GPU)
{
if (!ProofOfWork::GPUMiner::configureGPU(
m_localWorkSize,
m_globalWorkSizeMultiplier,
m_msPerBatch,
m_openclPlatform,
m_openclDevice,
m_clAllowCPU,
m_extraGPUMemory,
m_currentBlock
))
{
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
exit(1);
}
ProofOfWork::GPUMiner::setNumInstances(m_miningThreads);
}
if (mode == OperationMode::DAGInit)
@ -318,6 +345,9 @@ public:
<< " --list-devices List the detected OpenCL devices and exit." << endl
<< " --current-block Let the miner know the current block number at configuration time. Will help determine DAG size and required GPU memory." << endl
<< " --cl-extragpu-mem Set the memory (in MB) you believe your GPU requires for stuff other than mining. Windows rendering e.t.c.." << endl
<< " --cl-local-work Set the OpenCL local work size. Default is " << toString(dev::eth::Ethash::defaultLocalWorkSize) << endl
<< " --cl-global-work Set the OpenCL global work size as a multiple of the local work size. Default is " << toString(dev::eth::Ethash::defaultGlobalWorkSizeMultiplier) << " * " << toString(dev::eth::Ethash::defaultLocalWorkSize) << endl
<< " --cl-ms-per-batch Set the OpenCL target milliseconds per batch (global workgroup size). Default is " << toString(dev::eth::Ethash::defaultMSPerBatch) << ". If 0 is given then no autoadjustment of global work size will happen" << endl
;
}
@ -506,6 +536,9 @@ private:
unsigned m_miningThreads = UINT_MAX;
bool m_shouldListDevices = false;
bool m_clAllowCPU = false;
unsigned m_globalWorkSizeMultiplier = dev::eth::Ethash::defaultGlobalWorkSizeMultiplier;
unsigned m_localWorkSize = dev::eth::Ethash::defaultLocalWorkSize;
unsigned m_msPerBatch = dev::eth::Ethash::defaultMSPerBatch;
boost::optional<uint64_t> m_currentBlock;
// default value is 350MB of GPU memory for other stuff (windows system rendering, e.t.c.)
unsigned m_extraGPUMemory = 350000000;

22
ethvm/main.cpp

@ -46,8 +46,7 @@ void help()
#if ETH_EVMJIT || !ETH_TRUE
<< endl
<< "VM options:" << endl
<< " -J,--jit Enable LLVM VM (default: off)." << endl
<< " --smart Enable smart VM (default: off)." << endl
<< " --vm <vm-kind> Select VM. Options are: interpreter, jit, smart. (default: interpreter)" << endl
#endif
<< endl
<< "Options for trace:" << endl
@ -97,10 +96,21 @@ int main(int argc, char** argv)
else if (arg == "-V" || arg == "--version")
version();
#if ETH_EVMJIT
else if (arg == "-J" || arg == "--jit")
VMFactory::setKind(VMKind::JIT);
else if (arg == "--smart")
VMFactory::setKind(VMKind::Smart);
else if (arg == "--vm" && i + 1 < argc)
{
string vmKind = argv[++i];
if (vmKind == "interpreter")
VMFactory::setKind(VMKind::Interpreter);
else if (vmKind == "jit")
VMFactory::setKind(VMKind::JIT);
else if (vmKind == "smart")
VMFactory::setKind(VMKind::Smart);
else
{
cerr << "Unknown VM kind: " << vmKind << endl;
return -1;
}
}
#endif
else if (arg == "--mnemonics")
st.setShowMnemonics();

7
evmjit/include/evmjit/JIT.h

@ -107,9 +107,7 @@ enum class ReturnCode
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMConfigError = -101,
LLVMCompileError = -102,
LLVMLinkError = -103,
LLVMError = -101,
UnexpectedException = -111,
@ -155,6 +153,9 @@ public:
/// \param _codeHash The Keccak hash of the EVM code.
EXPORT static bool isCodeReady(h256 const& _codeHash);
/// Compile the given EVM code to machine code and make available for execution.
EXPORT static void compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
EXPORT static ReturnCode exec(ExecutionContext& _context);
};

92
evmjit/libevmjit/JIT.cpp

@ -1,6 +1,7 @@
#include "evmjit/JIT.h"
#include <array>
#include <mutex>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
@ -82,6 +83,7 @@ void parseOptions()
class JITImpl
{
std::unique_ptr<llvm::ExecutionEngine> m_engine;
mutable std::mutex x_codeMap;
std::unordered_map<h256, ExecFunc> m_codeMap;
public:
@ -97,6 +99,8 @@ public:
ExecFunc getExecFunc(h256 const& _codeHash) const;
void mapExecFunc(h256 _codeHash, ExecFunc _funcAddr);
ExecFunc compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
};
JITImpl::JITImpl()
@ -134,6 +138,7 @@ JITImpl::JITImpl()
ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const
{
std::lock_guard<std::mutex> lock{x_codeMap};
auto it = m_codeMap.find(_codeHash);
if (it != m_codeMap.end())
return it->second;
@ -142,9 +147,37 @@ ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const
void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr)
{
std::lock_guard<std::mutex> lock{x_codeMap};
m_codeMap.emplace(std::move(_codeHash), _funcAddr);
}
ExecFunc JITImpl::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash)
{
auto name = hash2str(_codeHash);
auto module = Cache::getObject(name);
if (!module)
{
// TODO: Listener support must be redesigned. These should be a feature of JITImpl
//listener->stateChanged(ExecState::Compilation);
assert(_code || !_codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(_code, _code + _codeSize, name);
if (g_optimize)
{
//listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
prepare(*module);
}
if (g_dump)
module->dump();
m_engine->addModule(std::move(module));
//listener->stateChanged(ExecState::CodeGen);
return (ExecFunc)m_engine->getFunctionAddress(name);
}
} // anonymous namespace
bool JIT::isCodeReady(h256 const& _codeHash)
@ -152,62 +185,41 @@ bool JIT::isCodeReady(h256 const& _codeHash)
return JITImpl::instance().getExecFunc(_codeHash) != nullptr;
}
ReturnCode JIT::exec(ExecutionContext& _context)
void JIT::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash)
{
auto& jit = JITImpl::instance();
auto execFunc = jit.compile(_code, _codeSize, _codeHash);
if (execFunc) // FIXME: What with error?
jit.mapExecFunc(_codeHash, execFunc);
}
std::unique_ptr<ExecStats> listener{new ExecStats};
listener->stateChanged(ExecState::Started);
ReturnCode JIT::exec(ExecutionContext& _context)
{
//std::unique_ptr<ExecStats> listener{new ExecStats};
//listener->stateChanged(ExecState::Started);
//static StatsCollector statsCollector;
auto code = _context.code();
auto codeSize = _context.codeSize();
auto& jit = JITImpl::instance();
auto codeHash = _context.codeHash();
static StatsCollector statsCollector;
auto mainFuncName = hash2str(codeHash);
// TODO: Remove cast
auto execFunc = jit.getExecFunc(codeHash);
if (!execFunc)
{
auto module = Cache::getObject(mainFuncName);
if (!module)
{
listener->stateChanged(ExecState::Compilation);
assert(code || !codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(code, code + codeSize, mainFuncName);
if (g_optimize)
{
listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
prepare(*module);
}
if (g_dump)
module->dump();
jit.engine().addModule(std::move(module));
listener->stateChanged(ExecState::CodeGen);
execFunc = (ExecFunc)jit.engine().getFunctionAddress(mainFuncName);
if (!CHECK(execFunc))
return ReturnCode::LLVMLinkError;
execFunc = jit.compile(_context.code(), _context.codeSize(), codeHash);
if (!execFunc)
return ReturnCode::LLVMError;
jit.mapExecFunc(codeHash, execFunc);
}
listener->stateChanged(ExecState::Execution);
//listener->stateChanged(ExecState::Execution);
auto returnCode = execFunc(&_context);
listener->stateChanged(ExecState::Return);
//listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return)
_context.returnData = _context.getReturnData(); // Save reference to return data
listener->stateChanged(ExecState::Finished);
if (g_stats)
statsCollector.stats.push_back(std::move(listener));
//listener->stateChanged(ExecState::Finished);
// if (g_stats)
// statsCollector.stats.push_back(std::move(listener));
return returnCode;
}

21
libdevcore/FixedHash.h

@ -31,6 +31,10 @@
namespace dev
{
/// Compile-time calculation of Log2 of constant values.
template <unsigned N> struct StaticLog2 { enum { result = 1 + StaticLog2<N/2>::result }; };
template <> struct StaticLog2<1> { enum { result = 0 }; };
extern std::random_device s_fixedHashEngine;
/// Fixed-size raw-byte array container type, with an API optimised for storing hashes.
@ -102,7 +106,7 @@ public:
FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; }
FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; }
/// @returns true if all bytes in @a _c are set in this object.
/// @returns true if all one-bits in @a _c are set in this object.
bool contains(FixedHash const& _c) const { return (*this & _c) == _c; }
/// @returns a particular byte from the hash.
@ -171,18 +175,21 @@ public:
template <unsigned P, unsigned M> inline FixedHash<M> bloomPart() const
{
static_assert((M & (M - 1)) == 0, "M must be power-of-two");
static const unsigned c_bloomBits = M * 8;
unsigned mask = c_bloomBits - 1;
unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8;
unsigned const c_bloomBits = M * 8;
unsigned const c_mask = c_bloomBits - 1;
unsigned const c_bloomBytes = (StaticLog2<c_bloomBits>::result + 7) / 8;
static_assert((M & (M - 1)) == 0, "M must be power-of-two");
static_assert(P * c_bloomBytes <= N, "out of range");
FixedHash<M> ret;
byte const* p = data();
for (unsigned i = 0; i < P; ++i)
{
unsigned index = 0;
for (unsigned j = 0; j < bloomBytes; ++j, ++p)
for (unsigned j = 0; j < c_bloomBytes; ++j, ++p)
index = (index << 8) | *p;
index &= mask;
index &= c_mask;
ret[M - 1 - index / 8] |= (1 << (index % 8));
}
return ret;

22
libdevcore/TrieCommon.h

@ -32,26 +32,38 @@ inline byte nibble(bytesConstRef _data, unsigned _i)
return (_i & 1) ? (_data[_i / 2] & 15) : (_data[_i / 2] >> 4);
}
inline unsigned sharedNibbles(bytesConstRef _a, unsigned _ab, unsigned _ae, bytesConstRef _b, unsigned _bb, unsigned _be)
/// Interprets @a _first and @a _second as vectors of nibbles and returns the length of the longest common
/// prefix of _first[_beginFirst..._endFirst] and _second[_beginSecond..._endSecond].
inline unsigned sharedNibbles(bytesConstRef _first, unsigned _beginFirst, unsigned _endFirst, bytesConstRef _second, unsigned _beginSecond, unsigned _endSecond)
{
unsigned ret = 0;
for (unsigned ai = _ab, bi = _bb; ai < _ae && bi < _be && nibble(_a, ai) == nibble(_b, bi); ++ai, ++bi, ++ret) {}
while (_beginFirst < _endFirst && _beginSecond < _endSecond && nibble(_first, _beginFirst) == nibble(_second, _beginSecond))
{
++_beginFirst;
++_beginSecond;
++ret;
}
return ret;
}
/**
* Nibble-based view on a bytesConstRef.
*/
struct NibbleSlice
{
bytesConstRef data;
unsigned offset;
NibbleSlice(bytesConstRef _d = bytesConstRef(), unsigned _o = 0): data(_d), offset(_o) {}
NibbleSlice(bytesConstRef _data = bytesConstRef(), unsigned _offset = 0): data(_data), offset(_offset) {}
byte operator[](unsigned _index) const { return nibble(data, offset + _index); }
unsigned size() const { return data.size() * 2 - offset; }
bool empty() const { return !size(); }
NibbleSlice mid(unsigned _index) const { return NibbleSlice(data, offset + _index); }
void clear() { data.reset(); offset = 0; }
/// @returns true iff _k is a prefix of this.
bool contains(NibbleSlice _k) const { return shared(_k) == _k.size(); }
/// @returns the number of shared nibbles at the beginning of this and _k.
unsigned shared(NibbleSlice _k) const { return sharedNibbles(data, offset, offset + size(), _k.data, _k.offset, _k.offset + _k.size()); }
/**
* @brief Determine if we, a full key, are situated prior to a particular key-prefix.
@ -60,8 +72,8 @@ struct NibbleSlice
*/
bool isEarlierThan(NibbleSlice _k) const
{
unsigned i;
for (i = 0; i < _k.size() && i < size(); ++i)
unsigned i = 0;
for (; i < _k.size() && i < size(); ++i)
if (operator[](i) < _k[i]) // Byte is lower - we're earlier..
return true;
else if (operator[](i) > _k[i]) // Byte is higher - we're not earlier.

24
libdevcrypto/Common.cpp

@ -46,7 +46,6 @@ struct Secp256k1Context
~Secp256k1Context() { secp256k1_stop(); }
};
static Secp256k1Context s_secp256k1;
void dev::crypto::secp256k1Init() { (void)s_secp256k1; }
#endif
static Secp256k1PP s_secp256k1pp;
@ -184,17 +183,40 @@ bytes dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _ci
Public dev::recover(Signature const& _sig, h256 const& _message)
{
#ifdef ETH_HAVE_SECP256K1
bytes o(65);
int pubkeylen;
if (!secp256k1_ecdsa_recover_compact(_message.data(), h256::size, _sig.data(), o.data(), &pubkeylen, false, _sig[64]))
return Public();
return FixedHash<64>(o.data()+1, Public::ConstructFromPointer);
#else
return s_secp256k1pp.recover(_sig, _message.ref());
#endif
}
Signature dev::sign(Secret const& _k, h256 const& _hash)
{
#ifdef ETH_HAVE_SECP256K1
Signature s;
int v;
if (!secp256k1_ecdsa_sign_compact(_hash.data(), h256::size, s.data(), _k.data(), Nonce::get().data(), &v))
return Signature();
s[64] = v;
return s;
#else
return s_secp256k1pp.sign(_k, _hash);
#endif
}
bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash)
{
if (!_p)
return false;
#ifdef ETH_HAVE_SECP256K1
return _p == recover(_s, _hash);
#else
return s_secp256k1pp.verify(_p, _s, _hash.ref(), true);
#endif
}
bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen)

2
libdevcrypto/Common.h

@ -177,8 +177,6 @@ namespace crypto
{
struct InvalidState: public dev::Exception {};
void secp256k1Init();
/// Key derivation
h256 kdf(Secret const& _priv, h256 const& _hash);

2
libdevcrypto/CryptoPP.cpp

@ -103,7 +103,7 @@ bool Secp256k1PP::decryptECIES(Secret const& _k, bytes& io_text)
// interop w/go ecies implementation
// io_cipher[0] must be 2, 3, or 4, else invalidpublickey
if (io_text[0] < 2 || io_text[0] > 4)
if (io_text.empty() || io_text[0] < 2 || io_text[0] > 4)
// invalid message: publickey
return false;

1
libethash-cl/CMakeLists.txt

@ -20,6 +20,7 @@ file(GLOB OUR_HEADERS "*.h")
set(HEADERS ${OUR_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${OpenCL_INCLUDE_DIRS})
include_directories(..)
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})

70
libethash-cl/ethash_cl_miner.cpp

@ -33,6 +33,7 @@
#include <vector>
#include <libethash/util.h>
#include <libethash/ethash.h>
#include <libethcore/Ethash.h>
#include <libethash/internal.h>
#include "ethash_cl_miner.h"
#include "ethash_cl_miner_kernel.h"
@ -49,6 +50,7 @@
#undef max
using namespace std;
using namespace dev::eth;
// TODO: If at any point we can use libdevcore in here then we should switch to using a LogChannel
#define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
@ -140,11 +142,17 @@ unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
bool ethash_cl_miner::configureGPU(
unsigned _platformId,
unsigned _localWorkSize,
unsigned _globalWorkSize,
unsigned _msPerBatch,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
)
{
s_workgroupSize = _localWorkSize;
s_initialGlobalWorkSize = _globalWorkSize;
s_msPerBatch = _msPerBatch;
s_allowCPU = _allowCPU;
s_extraRequiredGPUMem = _extraGPUMemory;
// by default let's only consider the DAG of the first epoch
@ -175,6 +183,9 @@ bool ethash_cl_miner::configureGPU(
bool ethash_cl_miner::s_allowCPU = false;
unsigned ethash_cl_miner::s_extraRequiredGPUMem;
unsigned ethash_cl_miner::s_msPerBatch = Ethash::defaultMSPerBatch;
unsigned ethash_cl_miner::s_workgroupSize = Ethash::defaultLocalWorkSize;
unsigned ethash_cl_miner::s_initialGlobalWorkSize = Ethash::defaultGlobalWorkSizeMultiplier * Ethash::defaultLocalWorkSize;
bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback)
{
@ -254,7 +265,6 @@ void ethash_cl_miner::finish()
bool ethash_cl_miner::init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned _workgroupSize,
unsigned _platformId,
unsigned _deviceId
)
@ -299,14 +309,18 @@ bool ethash_cl_miner::init(
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_workgroupSize = ((_workgroupSize + 7) / 8) * 8;
// make sure that global work size is evenly divisible by the local workgroup size
m_globalWorkSize = s_initialGlobalWorkSize;
if (m_globalWorkSize % s_workgroupSize != 0)
m_globalWorkSize = ((m_globalWorkSize / s_workgroupSize) + 1) * s_workgroupSize;
// remember the device's address bits
m_deviceBits = device.getInfo<CL_DEVICE_ADDRESS_BITS>();
// 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_workgroupSize);
addDefinition(code, "GROUP_SIZE", s_workgroupSize);
addDefinition(code, "DAG_SIZE", (unsigned)(_dagSize / ETHASH_MIX_BYTES));
addDefinition(code, "ACCESSES", ETHASH_ACCESSES);
addDefinition(code, "MAX_OUTPUTS", c_maxSearchResults);
@ -323,7 +337,7 @@ bool ethash_cl_miner::init(
ETHCL_LOG("Printing program log");
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
}
catch (cl::Error const& err)
catch (cl::Error const&)
{
ETHCL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false;
@ -415,9 +429,8 @@ bool ethash_cl_miner::init(
return true;
}
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook, unsigned _msPerBatch)
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
{
(void)_msPerBatch;
try
{
struct pending_batch
@ -454,10 +467,9 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
unsigned buf = 0;
random_device engine;
uint64_t start_nonce = uniform_int_distribution<uint64_t>()(engine);
for (;; start_nonce += m_batchSize)
for (;; start_nonce += m_globalWorkSize)
{
// chrono::high_resolution_clock::time_point t = chrono::high_resolution_clock::now();
auto t = chrono::high_resolution_clock::now();
// supply output buffer to kernel
m_searchKernel.setArg(0, m_searchBuffer[buf]);
if (m_dagChunksCount == 1)
@ -466,7 +478,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_searchKernel.setArg(6, start_nonce);
// execute it!
m_queue.enqueueNDRangeKernel(m_searchKernel, cl::NullRange, m_batchSize, m_workgroupSize);
m_queue.enqueueNDRangeKernel(m_searchKernel, cl::NullRange, m_globalWorkSize, s_workgroupSize);
pending.push({ start_nonce, buf });
buf = (buf + 1) % c_bufferCount;
@ -486,7 +498,7 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
m_queue.enqueueUnmapMemObject(m_searchBuffer[batch.buf], results);
bool exit = num_found && hook.found(nonces, num_found);
exit |= hook.searched(batch.start_nonce, m_batchSize); // always report searched before exit
exit |= hook.searched(batch.start_nonce, m_globalWorkSize); // always report searched before exit
if (exit)
break;
@ -497,19 +509,31 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
pending.pop();
}
/* chrono::high_resolution_clock::duration d = chrono::high_resolution_clock::now() - t;
if (d > chrono::milliseconds(_msPerBatch * 10 / 9))
// adjust global work size depending on last search time
if (s_msPerBatch)
{
cerr << "Batch of" << m_batchSize << "took" << chrono::duration_cast<chrono::milliseconds>(d).count() << "ms, >>" << _msPerBatch << "ms.";
m_batchSize = max<unsigned>(128, m_batchSize * 9 / 10);
cerr << "New batch size" << m_batchSize;
// Global work size must be:
// - less than or equal to 2 ^ DEVICE_BITS - 1
// - divisible by lobal work size (workgroup size)
auto d = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - t);
if (d != chrono::milliseconds(0)) // if duration is zero, we did not get in the actual searh/or search not finished
{
if (d > chrono::milliseconds(s_msPerBatch * 10 / 9))
{
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, >> " << _msPerBatch << " ms." << endl;
m_globalWorkSize = max<unsigned>(128, m_globalWorkSize + s_workgroupSize);
// cerr << "New global work size" << m_globalWorkSize << endl;
}
else if (d < chrono::milliseconds(s_msPerBatch * 9 / 10))
{
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, << " << _msPerBatch << " ms." << endl;
m_globalWorkSize = min<unsigned>(pow(2, m_deviceBits) - 1, m_globalWorkSize - s_workgroupSize);
// Global work size should never be less than the workgroup size
m_globalWorkSize = max<unsigned>(s_workgroupSize, m_globalWorkSize);
// cerr << "New global work size" << m_globalWorkSize << endl;
}
}
}
else if (d < chrono::milliseconds(_msPerBatch * 9 / 10))
{
cerr << "Batch of" << m_batchSize << "took" << chrono::duration_cast<chrono::milliseconds>(d).count() << "ms, <<" << _msPerBatch << "ms.";
m_batchSize = m_batchSize * 10 / 9;
cerr << "New batch size" << m_batchSize;
}*/
}
// not safe to return until this is ready

16
libethash-cl/ethash_cl_miner.h

@ -45,6 +45,9 @@ public:
static void listDevices();
static bool configureGPU(
unsigned _platformId,
unsigned _localWorkSize,
unsigned _globalWorkSize,
unsigned _msPerBatch,
bool _allowCPU,
unsigned _extraGPUMemory,
boost::optional<uint64_t> _currentBlock
@ -53,12 +56,11 @@ public:
bool init(
uint8_t const* _dag,
uint64_t _dagSize,
unsigned _workgroupSize = 64,
unsigned _platformId = 0,
unsigned _deviceId = 0
);
void finish();
void search(uint8_t const* _header, uint64_t _target, search_hook& _hook, unsigned _msPerBatch = 100);
void search(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);
@ -76,10 +78,16 @@ private:
cl::Buffer m_header;
cl::Buffer m_hashBuffer[c_bufferCount];
cl::Buffer m_searchBuffer[c_bufferCount];
unsigned m_workgroupSize;
unsigned m_batchSize = c_searchBatchSize;
unsigned m_globalWorkSize;
bool m_openclOnePointOne;
unsigned m_deviceBits;
/// The local work size for the search
static unsigned s_workgroupSize;
/// The initial global work size for the searches
static unsigned s_initialGlobalWorkSize;
/// The target milliseconds per batch for the search. If 0, then no adjustment will happen
static unsigned s_msPerBatch;
/// Allow CPU to appear as an OpenCL device or not. Default is false
static bool s_allowCPU;
/// GPU memory required for other things, like window rendering e.t.c.

16
libethcore/Common.h

@ -97,10 +97,13 @@ enum class RelativeBlock: BlockNumber
Pending = PendingBlock
};
class Transaction;
struct ImportRoute
{
h256s deadBlocks;
h256s liveBlocks;
std::vector<Transaction> goodTranactions;
};
enum class ImportResult
@ -129,10 +132,10 @@ struct ImportRequirements
};
/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight.
class Signal
template<typename... Args> class Signal
{
public:
using Callback = std::function<void()>;
using Callback = std::function<void(Args...)>;
class HandlerAux
{
@ -141,7 +144,7 @@ public:
public:
~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; }
void reset() { m_s = nullptr; }
void fire() { m_h(); }
void fire(Args&&... _args) { m_h(std::forward<Args>(_args)...); }
private:
HandlerAux(unsigned _i, Signal* _s, Callback const& _h): m_i(_i), m_s(_s), m_h(_h) {}
@ -165,13 +168,13 @@ public:
return h;
}
void operator()() { for (auto const& f: m_fire) f.second->fire(); }
void operator()(Args&... _args) { for (auto const& f: m_fire) f.second->fire(std::forward<Args>(_args)...); }
private:
std::map<unsigned, std::shared_ptr<Signal::HandlerAux>> m_fire;
std::map<unsigned, std::shared_ptr<typename Signal::HandlerAux>> m_fire;
};
using Handler = std::shared_ptr<Signal::HandlerAux>;
template<class... Args> using Handler = std::shared_ptr<typename Signal<Args...>::HandlerAux>;
struct TransactionSkeleton
{
@ -182,6 +185,7 @@ struct TransactionSkeleton
bytes data;
u256 gas = UndefinedU256;
u256 gasPrice = UndefinedU256;
u256 nonce = UndefinedU256;
};
void badBlock(bytesConstRef _header, std::string const& _err);

30
libethcore/Ethash.cpp

@ -54,6 +54,9 @@ namespace dev
namespace eth
{
const unsigned Ethash::defaultLocalWorkSize = 64;
const unsigned Ethash::defaultGlobalWorkSizeMultiplier = 512; // * CL_DEFAULT_LOCAL_WORK_SIZE
const unsigned Ethash::defaultMSPerBatch = 100;
const Ethash::WorkPackage Ethash::NullWorkPackage = Ethash::WorkPackage();
std::string Ethash::name()
@ -373,7 +376,7 @@ void Ethash::GPUMiner::workLoop()
this_thread::sleep_for(chrono::milliseconds(500));
}
bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device);
m_miner->init(dagData.data(), dagData.size(), s_platformId, device);
}
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
@ -409,6 +412,9 @@ void Ethash::GPUMiner::listDevices()
}
bool Ethash::GPUMiner::configureGPU(
unsigned _localWorkSize,
unsigned _globalWorkSizeMultiplier,
unsigned _msPerBatch,
unsigned _platformId,
unsigned _deviceId,
bool _allowCPU,
@ -418,7 +424,27 @@ bool Ethash::GPUMiner::configureGPU(
{
s_platformId = _platformId;
s_deviceId = _deviceId;
return ethash_cl_miner::configureGPU(_platformId, _allowCPU, _extraGPUMemory, _currentBlock);
if (_localWorkSize != 32 && _localWorkSize != 64 && _localWorkSize != 128)
{
cout << "Given localWorkSize of " << toString(_localWorkSize) << "is invalid. Must be either 32,64, or 128" << endl;
return false;
}
if (!ethash_cl_miner::configureGPU(
_platformId,
_localWorkSize,
_globalWorkSizeMultiplier * _localWorkSize,
_msPerBatch,
_allowCPU,
_extraGPUMemory,
_currentBlock)
)
{
cout << "No GPU device with sufficient memory was found. Can't GPU mine. Remove the -G argument" << endl;
return false;
}
return true;
}
#endif

11
libethcore/Ethash.h

@ -88,7 +88,7 @@ public:
static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); }
static std::string platformInfo();
static void listDevices() {}
static bool configureGPU(unsigned, unsigned, bool, unsigned, boost::optional<uint64_t>) { return false; }
static bool configureGPU(unsigned, unsigned, unsigned, unsigned, unsigned, bool, unsigned, boost::optional<uint64_t>) { return false; }
static void setNumInstances(unsigned _instances) { s_numInstances = std::min<unsigned>(_instances, std::thread::hardware_concurrency()); }
protected:
void kickOff() override
@ -118,6 +118,9 @@ public:
static unsigned getNumDevices();
static void listDevices();
static bool configureGPU(
unsigned _localWorkSize,
unsigned _globalWorkSizeMultiplier,
unsigned _msPerBatch,
unsigned _platformId,
unsigned _deviceId,
bool _allowCPU,
@ -147,6 +150,12 @@ public:
#else
using GPUMiner = CPUMiner;
#endif
/// Default value of the local work size. Also known as workgroup size.
static const unsigned defaultLocalWorkSize;
/// Default value of the global work size as a multiplier of the local work size
static const unsigned defaultGlobalWorkSizeMultiplier;
/// Default value of the milliseconds per global work size (per batch)
static const unsigned defaultMSPerBatch;
};
}

14
libethcore/Transaction.cpp

@ -29,6 +29,20 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
TransactionBase::TransactionBase(TransactionSkeleton const& _ts, Secret const& _s):
m_type(_ts.creation ? ContractCreation : MessageCall),
m_nonce(_ts.nonce),
m_value(_ts.value),
m_receiveAddress(_ts.to),
m_gasPrice(_ts.gasPrice),
m_gas(_ts.gas),
m_data(_ts.data),
m_sender(_ts.from)
{
if (_s)
sign(_s);
}
TransactionBase::TransactionBase(bytesConstRef _rlpData, CheckTransaction _checkSig)
{
int field = 0;

4
libethcore/Transaction.h

@ -51,6 +51,9 @@ public:
/// Constructs a null transaction.
TransactionBase() {}
/// Constructs a transaction from a transaction skeleton & optional secret.
TransactionBase(TransactionSkeleton const& _ts, Secret const& _s = Secret());
/// Constructs a signed message-call transaction.
TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
@ -69,7 +72,6 @@ public:
/// Constructs a transaction from the given RLP.
explicit TransactionBase(bytes const& _rlp, CheckTransaction _checkSig): TransactionBase(&_rlp, _checkSig) {}
/// Checks equality of transactions.
bool operator==(TransactionBase const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
/// Checks inequality of transactions.

4
libethereum/Account.h

@ -152,8 +152,7 @@ public:
h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; }
/// Sets the code of the account. Must only be called when isFreshCode() returns true.
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); }
void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; changed(); }
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = std::move(_code); changed(); }
/// @returns true if the account's code is available through code().
bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); }
@ -206,4 +205,3 @@ private:
}
}

8
libethereum/BlockChain.cpp

@ -337,6 +337,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
h256s fresh;
h256s dead;
h256s badBlocks;
Transactions goodTransactions;
unsigned count = 0;
for (VerifiedBlock const& block: blocks)
if (!badBlocks.empty())
@ -351,6 +352,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);
fresh += r.liveBlocks;
dead += r.deadBlocks;
goodTransactions += r.goodTranactions;
++count;
}
catch (dev::eth::UnknownParent)
@ -377,7 +379,7 @@ tuple<ImportRoute, bool, unsigned> BlockChain::sync(BlockQueue& _bq, OverlayDB c
badBlocks.push_back(block.verified.info.hash());
}
}
return make_tuple(ImportRoute{dead, fresh}, _bq.doneDrain(badBlocks), count);
return make_tuple(ImportRoute{dead, fresh, goodTransactions}, _bq.doneDrain(badBlocks), count);
}
pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir) noexcept
@ -497,6 +499,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
BlockReceipts br;
u256 td;
Transactions goodTransactions;
#if ETH_CATCH
try
#endif
@ -510,6 +513,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
{
blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i));
goodTransactions.push_back(s.pending()[i]);
}
s.cleanup(true);
@ -750,7 +754,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
dead.push_back(h);
else
fresh.push_back(h);
return ImportRoute{dead, fresh};
return ImportRoute{dead, fresh, move(goodTransactions)};
}
void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end)

503
libethereum/BlockChainSync.cpp

@ -38,7 +38,19 @@ using namespace dev;
using namespace dev::eth;
using namespace p2p;
unsigned const c_chainReorgSize = 30000;
unsigned const c_chainReorgSize = 30000; /// Added to estimated hashes to account for potential chain reorganiation
unsigned const c_hashSubchainSize = 8192; /// PV61 subchain size
std::ostream& dev::eth::operator<<(std::ostream& _out, SyncStatus const& _sync)
{
_out << "protocol: " << _sync.protocolVersion << endl;
_out << "state: " << EthereumHost::stateName(_sync.state) << " ";
if (_sync.state == SyncState::Hashes)
_out << _sync.hashesReceived << "/" << (_sync.hashesEstimated ? "~" : "") << _sync.hashesTotal;
if (_sync.state == SyncState::Blocks || _sync.state == SyncState::NewBlocks)
_out << _sync.blocksReceived << "/" << _sync.blocksTotal;
return _out;
}
BlockChainSync::BlockChainSync(EthereumHost& _host):
m_host(_host)
@ -75,15 +87,18 @@ void BlockChainSync::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
std::shared_ptr<Session> session = _peer->session();
if (!session)
return; // Expired
if (_peer->m_genesisHash != host().chain().genesisHash())
_peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != host().protocolVersion() && _peer->m_protocolVersion != EthereumHost::c_oldProtocolVersion)
_peer->disable("Invalid protocol version.");
else if (_peer->m_networkId != host().networkId())
_peer->disable("Invalid network identifier.");
else if (_peer->session()->info().clientVersion.find("/v0.7.0/") != string::npos)
else if (session->info().clientVersion.find("/v0.7.0/") != string::npos)
_peer->disable("Blacklisted client version.");
else if (host().isBanned(_peer->session()->id()))
else if (host().isBanned(session->id()))
_peer->disable("Peer banned for previous bad behaviour.");
else
{
@ -91,7 +106,6 @@ void BlockChainSync::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
_peer->m_expectedHashes = hashes;
onNewPeer(_peer);
}
DEV_INVARIANT_CHECK;
}
unsigned BlockChainSync::estimatedHashes() const
@ -114,7 +128,6 @@ void BlockChainSync::requestBlocks(std::shared_ptr<EthereumPeer> _peer)
{
clog(NetAllDetail) << "Waiting for block queue before downloading blocks";
pauseSync();
_peer->setIdle();
return;
}
_peer->requestBlocks();
@ -137,13 +150,14 @@ void BlockChainSync::logNewBlock(h256 const& _h)
void BlockChainSync::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
_peer->setIdle();
if (m_state != SyncState::Blocks && m_state != SyncState::NewBlocks && m_state != SyncState::Waiting)
clog(NetWarn) << "Unexpected Blocks received!";
{
clog(NetMessageSummary) << "Ignoring unexpected blocks";
return;
}
if (m_state == SyncState::Waiting)
{
clog(NetAllDetail) << "Ignored blocks while waiting";
@ -184,6 +198,7 @@ void BlockChainSync::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const
case ImportResult::BadChain:
logNewBlock(h);
_peer->disable("Malformed block received.");
restartSync();
return;
case ImportResult::FutureTimeKnown:
@ -282,7 +297,7 @@ void BlockChainSync::onPeerNewBlock(std::shared_ptr<EthereumPeer> _peer, RLP con
case ImportResult::FutureTimeUnknown:
case ImportResult::UnknownParent:
logNewBlock(h);
clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
clog(NetMessageDetail) << "Received block with no known parent. Resyncing...";
resetSyncFor(_peer, h, _r[1].toInt<u256>());
break;
default:;
@ -291,7 +306,6 @@ void BlockChainSync::onPeerNewBlock(std::shared_ptr<EthereumPeer> _peer, RLP con
DEV_GUARDED(_peer->x_knownBlocks)
_peer->m_knownBlocks.insert(h);
}
DEV_INVARIANT_CHECK;
}
PV60Sync::PV60Sync(EthereumHost& _host):
@ -305,6 +319,7 @@ SyncStatus PV60Sync::status() const
RecursiveGuard l(x_sync);
SyncStatus res;
res.state = m_state;
res.protocolVersion = EthereumHost::c_oldProtocolVersion;
if (m_state == SyncState::Hashes)
{
res.hashesTotal = m_estimatedHashes;
@ -344,26 +359,30 @@ void PV60Sync::restartSync()
{
resetSync();
host().bq().clear();
if (isSyncing())
transition(m_syncer.lock(), SyncState::Idle);
std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
if (syncer)
transition(syncer, SyncState::Idle);
}
void PV60Sync::completeSync()
{
if (isSyncing())
transition(m_syncer.lock(), SyncState::Idle);
std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
if (syncer)
transition(syncer, SyncState::Idle);
}
void PV60Sync::pauseSync()
{
if (isSyncing())
setState(m_syncer.lock(), SyncState::Waiting, true);
std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
if (syncer)
transition(syncer, SyncState::Waiting, true);
}
void PV60Sync::continueSync()
{
if (isSyncing())
transition(m_syncer.lock(), SyncState::Blocks);
std::shared_ptr<EthereumPeer> syncer = m_syncer.lock();
if (syncer)
transition(syncer, SyncState::Blocks);
}
void PV60Sync::onNewPeer(std::shared_ptr<EthereumPeer> _peer)
@ -381,26 +400,10 @@ void PV60Sync::transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, boo
RLPStream s;
if (_s == SyncState::Hashes)
{
if (m_state == SyncState::Idle)
if (m_state == SyncState::Idle || m_state == SyncState::Hashes)
{
if (isSyncing(_peer))
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
m_syncingLatestHash = _peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
setState(_peer, _s, true);
_peer->requestHashes(m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash);
DEV_INVARIANT_CHECK;
return;
}
else if (m_state == SyncState::Hashes)
{
if (!isSyncing(_peer))
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
setState(_peer, _s, true);
_peer->requestHashes(m_syncingLastReceivedHash);
DEV_INVARIANT_CHECK;
m_estimatedHashes = _peer->m_expectedHashes - c_chainReorgSize;
syncHashes(_peer);
return;
}
}
@ -462,7 +465,6 @@ void PV60Sync::transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, boo
}
else if (_s == SyncState::Idle)
{
host().foreachPeer([this](std::shared_ptr<EthereumPeer> _p) { _p->setIdle(); return true; });
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)
{
clog(NetMessageDetail) << "Finishing blocks fetch...";
@ -473,7 +475,6 @@ void PV60Sync::transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, boo
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
_peer->m_sub.doneFetch();
_peer->setIdle();
setState(_peer, SyncState::Idle, false);
}
else if (m_state == SyncState::Hashes)
@ -502,7 +503,9 @@ void PV60Sync::setNeedsSyncing(std::shared_ptr<EthereumPeer> _peer, h256 const&
if (_peer->m_latestHash)
noteNeedsSyncing(_peer);
_peer->session()->addNote("sync", string(isSyncing(_peer) ? "ongoing" : "holding") + (needsSyncing(_peer) ? " & needed" : ""));
shared_ptr<Session> session = _peer->session();
if (session)
session->addNote("sync", string(isSyncing(_peer) ? "ongoing" : "holding") + (needsSyncing(_peer) ? " & needed" : ""));
}
bool PV60Sync::needsSyncing(std::shared_ptr<EthereumPeer> _peer) const
@ -562,7 +565,6 @@ void PV60Sync::attemptSync(std::shared_ptr<EthereumPeer> _peer)
else
{
clog(NetAllDetail) << "Yes. Their chain is better.";
m_estimatedHashes = _peer->m_expectedHashes - c_chainReorgSize;
transition(_peer, SyncState::Hashes);
}
}
@ -573,7 +575,9 @@ void PV60Sync::noteNeedsSyncing(std::shared_ptr<EthereumPeer> _peer)
if (isSyncing())
{
clog(NetAllDetail) << "Sync in progress: Just set to help out.";
if (m_state == SyncState::Blocks)
if (m_state == SyncState::Hashes && _peer->m_asking == Asking::Nothing)
requestSubchain(_peer);
else if (m_state == SyncState::Blocks)
requestBlocks(_peer);
}
else
@ -649,20 +653,45 @@ void PV60Sync::noteDoneBlocks(std::shared_ptr<EthereumPeer> _peer, bool _clemenc
}
resetSync();
downloadMan().reset();
}
_peer->m_sub.doneFetch();
}
void PV60Sync::syncHashes(std::shared_ptr<EthereumPeer> _peer)
{
if (m_state == SyncState::Idle)
{
if (isSyncing(_peer))
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
m_syncingLatestHash = _peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
setState(_peer, SyncState::Hashes, true);
_peer->requestHashes(m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash);
}
else if (m_state == SyncState::Hashes)
{
if (!isSyncing(_peer))
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
setState(_peer, SyncState::Hashes, true);
_peer->requestHashes(m_syncingLastReceivedHash);
}
}
void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
_peer->setIdle();
if (!isSyncing(_peer))
{
clog(NetMessageSummary) << "Ignoring hashes since not syncing";
return;
}
if (_peer->m_syncHash != (m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash))
{
clog(NetMessageSummary) << "Ignoring unexpected hashes";
return;
}
if (_hashes.size() == 0)
{
transition(_peer, SyncState::Blocks);
@ -684,7 +713,8 @@ void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
transition(_peer, SyncState::Idle);
_peer->disable("Bad blocks");
restartSync();
return;
}
else if (status == QueueStatus::Unknown)
@ -711,7 +741,6 @@ void PV60Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
void PV60Sync::onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
DEV_INVARIANT_CHECK;
if (isSyncing() && (m_state != SyncState::NewBlocks || isSyncing(_peer)))
{
clog(NetMessageSummary) << "Ignoring since we're already downloading.";
@ -769,8 +798,33 @@ void PV60Sync::onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const&
void PV60Sync::abortSync()
{
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
host().foreachPeer([this](std::shared_ptr<EthereumPeer> _p) { _p->setIdle(); return true; });
setState(std::shared_ptr<EthereumPeer>(), SyncState::Idle, false, true);
bool continueSync = false;
if (m_state == SyncState::Blocks)
{
// Main syncer aborted, try to find a replacement
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p)
{
if (_p->m_asking == Asking::Blocks)
{
setState(_p, SyncState::Blocks, true, true); // will kick off other peers to help if available.
continueSync = true;
return false;
}
if (_p->m_asking == Asking::Nothing && shouldGrabBlocks(_p))
{
transition(_p, SyncState::Blocks);
clog(NetMessageDetail) << "New sync peer selected";
continueSync = true;
return false;
}
return true;
});
}
if (!continueSync)
{
// Just set to idle. Hashchain is keept, Sync will be continued if there are more peers to sync with
setState(std::shared_ptr<EthereumPeer>(), SyncState::Idle, false, true);
}
DEV_INVARIANT_CHECK;
}
@ -778,37 +832,366 @@ void PV60Sync::onPeerAborting()
{
RecursiveGuard l(x_sync);
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
if (m_syncer.expired())
if (m_syncer.expired() && m_state != SyncState::Idle)
{
clog(NetWarn) << "Syncing peer disconnected, restarting sync";
m_syncer.reset();
abortSync();
}
DEV_INVARIANT_CHECK;
}
bool PV60Sync::invariants() const
{
if (m_state == SyncState::Idle && isSyncing())
return false;
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Idle while peer syncing"));
if (m_state != SyncState::Idle && !isSyncing())
return false;
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Active while peer not syncing"));
if (m_state == SyncState::Hashes)
{
if (!m_syncingLatestHash)
return false;
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("m_syncingLatestHash is not set while downloading hashes"));
if (m_syncingNeededBlocks.empty() != (!m_syncingLastReceivedHash))
return false;
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Received hashes but the hashes list is empty (or the other way around)"));
}
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)
{
if (downloadMan().isComplete())
return false;
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Block download complete but the state is still Blocks"));
}
if (m_state == SyncState::Waiting && !host().bq().isActive())
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Waiting while block queue is idle"));
return true;
}
PV61Sync::PV61Sync(EthereumHost& _host):
PV60Sync(_host)
{
}
void PV61Sync::syncHashes(std::shared_ptr<EthereumPeer> _peer)
{
if (_peer->m_protocolVersion != host().protocolVersion())
{
m_readyChainMap.clear();
m_completeChainMap.clear();
m_downloadingChainMap.clear();
m_syncingBlockNumber = 0;
m_chainSyncPeers.clear();
m_knownHashes.clear();
m_hashScanComplete = false;
PV60Sync::syncHashes(_peer);
return;
}
if (m_state == SyncState::Idle)
{
bool busy = false;
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p) { if (_p->m_asking != Asking::Nothing && _p->m_asking != Asking::State) busy = true; return !busy; });
if (busy)
return false;
if (isSyncing(_peer))
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
if (m_syncingBlockNumber == 0)
m_syncingBlockNumber = host().chain().number() + c_hashSubchainSize;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
setState(_peer, SyncState::Hashes, true);
_peer->requestHashes(m_syncingBlockNumber, 1);
}
if (m_state == SyncState::Waiting && !host().bq().isActive())
return false;
else if (m_state == SyncState::Hashes)
{
if (!isSyncing(_peer))
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
m_syncingBlockNumber += c_hashSubchainSize;
setState(_peer, SyncState::Hashes, true);
_peer->requestHashes(m_syncingBlockNumber, 1);
}
}
void PV61Sync::requestSubchain(std::shared_ptr<EthereumPeer> _peer)
{
auto syncPeer = m_chainSyncPeers.find(_peer);
if (syncPeer != m_chainSyncPeers.end())
{
// Already downoading, request next batch
h256s& d = m_downloadingChainMap.at(syncPeer->second);
_peer->requestHashes(d.back());
}
else if (needsSyncing(_peer))
{
if (!m_readyChainMap.empty())
{
clog(NetAllDetail) << "Helping with hashchin download";
h256s& d = m_readyChainMap.begin()->second;
_peer->requestHashes(d.back());
m_downloadingChainMap[m_readyChainMap.begin()->first] = move(d);
m_chainSyncPeers[_peer] = m_readyChainMap.begin()->first;
m_readyChainMap.erase(m_readyChainMap.begin());
}
else if (!m_downloadingChainMap.empty() && m_hashScanComplete)
{
// Lead syncer is done, just grab whatever we can
h256s& d = m_downloadingChainMap.begin()->second;
_peer->requestHashes(d.back());
}
}
}
void PV61Sync::requestSubchains()
{
host().foreachPeer([this](std::shared_ptr<EthereumPeer> _p)
{
if (_p->m_asking == Asking::Nothing)
requestSubchain(_p);
return true;
});
}
void PV61Sync::completeSubchain(std::shared_ptr<EthereumPeer> _peer, unsigned _n)
{
m_completeChainMap[_n] = move(m_downloadingChainMap.at(_n));
m_downloadingChainMap.erase(_n);
for (auto s = m_chainSyncPeers.begin(); s != m_chainSyncPeers.end(); ++s)
if (s->second == _n) //TODO: optimize this
{
m_chainSyncPeers.erase(s);
break;
}
_peer->m_syncHashNumber = 0;
auto syncer = m_syncer.lock();
if (!syncer)
{
restartSync();
return;
}
if (m_readyChainMap.empty() && m_downloadingChainMap.empty() && m_hashScanComplete)
{
//Done chain-get
m_syncingNeededBlocks.clear();
for (auto h = m_completeChainMap.rbegin(); h != m_completeChainMap.rend(); ++h)
m_syncingNeededBlocks.insert(m_syncingNeededBlocks.end(), h->second.begin(), h->second.end());
m_completeChainMap.clear();
m_knownHashes.clear();
m_syncingBlockNumber = 0;
transition(syncer, SyncState::Blocks);
}
else
requestSubchain(_peer);
}
void PV61Sync::restartSync()
{
m_completeChainMap.clear();
m_readyChainMap.clear();
m_downloadingChainMap.clear();
m_chainSyncPeers.clear();
m_syncingBlockNumber = 0;
m_knownHashes.clear();
m_hashScanComplete = false;
PV60Sync::restartSync();
}
void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{
RecursiveGuard l(x_sync);
if (m_syncingBlockNumber == 0 || (_peer == m_syncer.lock() && _peer->m_protocolVersion != host().protocolVersion()))
{
// Syncing in pv60 mode
PV60Sync::onPeerHashes(_peer, _hashes);
return;
}
if (_hashes.size() == 0)
{
if (isSyncing(_peer) && _peer->m_syncHashNumber == m_syncingBlockNumber)
{
// End of hash chain, add last chunk to download
m_readyChainMap.insert(make_pair(m_syncingBlockNumber, h256s { _peer->m_latestHash }));
m_hashScanComplete = true;
_peer->m_syncHashNumber = 0;
requestSubchain(_peer);
}
else
{
auto syncPeer = m_chainSyncPeers.find(_peer);
if (syncPeer == m_chainSyncPeers.end())
clog(NetMessageDetail) << "Hashes response from unexpected peer";
else
{
// Peer does not have request hashes, move back from downloading to ready
unsigned number = syncPeer->second;
m_chainSyncPeers.erase(_peer);
m_readyChainMap[number] = move(m_downloadingChainMap.at(number));
m_downloadingChainMap.erase(number);
resetNeedsSyncing(_peer);
requestSubchains();
}
}
return;
}
if (isSyncing(_peer) && _peer->m_syncHashNumber == m_syncingBlockNumber)
{
// Got new subchain marker
assert(_hashes.size() == 1);
m_knownHashes.insert(_hashes[0]);
m_readyChainMap.insert(make_pair(m_syncingBlockNumber, h256s { _hashes[0] }));
if ((m_readyChainMap.size() + m_downloadingChainMap.size() + m_completeChainMap.size()) * c_hashSubchainSize > _peer->m_expectedHashes)
{
_peer->disable("Too many hashes from lead peer");
restartSync();
return;
}
transition(_peer, SyncState::Hashes);
requestSubchains();
}
else
{
auto syncPeer = m_chainSyncPeers.find(_peer);
unsigned number = 0;
if (syncPeer == m_chainSyncPeers.end())
{
//check downlading peers
for (auto const& downloader: m_downloadingChainMap)
if (downloader.second.back() == _peer->m_syncHash)
{
number = downloader.first;
break;
}
}
else
number = syncPeer->second;
if (number == 0)
{
clog(NetAllDetail) << "Hashes response from unexpected/expired peer";
return;
}
auto downloadingPeer = m_downloadingChainMap.find(number);
if (downloadingPeer == m_downloadingChainMap.end() || downloadingPeer->second.back() != _peer->m_syncHash)
{
// Too late, other peer has already downloaded our hashes
m_chainSyncPeers.erase(_peer);
requestSubchain(_peer);
return;
}
h256s& hashes = downloadingPeer->second;
unsigned knowns = 0;
unsigned unknowns = 0;
for (unsigned i = 0; i < _hashes.size(); ++i)
{
auto h = _hashes[i];
auto status = host().bq().blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host().chain().isKnown(h) || !!m_knownHashes.count(h))
{
clog(NetMessageSummary) << "Subchain download complete";
m_chainSyncPeers.erase(_peer);
completeSubchain(_peer, number);
return;
}
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
_peer->disable("Bad hashes");
if (isSyncing(_peer))
restartSync();
else
{
//try with other peer
m_readyChainMap[number] = move(m_downloadingChainMap.at(number));
m_downloadingChainMap.erase(number);
m_chainSyncPeers.erase(_peer);
}
return;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
hashes.push_back(h);
}
else
knowns++;
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << hashes.back();
if (hashes.size() > c_hashSubchainSize)
{
_peer->disable("Too many subchain hashes");
restartSync();
return;
}
requestSubchain(_peer);
}
DEV_INVARIANT_CHECK;
}
void PV61Sync::onPeerAborting()
{
RecursiveGuard l(x_sync);
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
for (auto s = m_chainSyncPeers.begin(); s != m_chainSyncPeers.end();)
{
if (s->first.expired())
{
unsigned number = s->second;
m_readyChainMap[number] = move(m_downloadingChainMap.at(number));
m_downloadingChainMap.erase(number);
m_chainSyncPeers.erase(s++);
}
else
++s;
}
if (m_syncer.expired())
{
if (m_state == SyncState::Hashes)
{
// Main syncer aborted, other peers are probably still downloading hashes, just set one of them as syncer
host().foreachPeer([&](std::shared_ptr<EthereumPeer> _p)
{
if (_p->m_asking != Asking::Hashes)
return true;
setState(_p, SyncState::Hashes, true, true);
return false;
});
}
if (m_syncer.expired())
PV60Sync::onPeerAborting();
}
else if (isPV61Syncing() && m_state == SyncState::Hashes)
requestSubchains();
DEV_INVARIANT_CHECK;
}
SyncStatus PV61Sync::status() const
{
RecursiveGuard l(x_sync);
SyncStatus res = PV60Sync::status();
if (m_state == SyncState::Hashes && isPV61Syncing())
{
res.protocolVersion = 61;
res.hashesReceived = 0;
for (auto const& d : m_readyChainMap)
res.hashesReceived += d.second.size();
for (auto const& d : m_downloadingChainMap)
res.hashesReceived += d.second.size();
for (auto const& d : m_completeChainMap)
res.hashesReceived += d.second.size();
}
return res;
}
bool PV61Sync::isPV61Syncing() const
{
return m_syncingBlockNumber != 0;
}
bool PV61Sync::invariants() const
{
if (m_state == SyncState::Hashes)
{
if (isPV61Syncing() && !m_syncingBlockNumber)
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment("Syncing in PV61 with no block number set"));
}
else if (!PV60Sync::invariants())
return false;
return true;
}

52
libethereum/BlockChainSync.h

@ -114,7 +114,7 @@ protected:
void requestBlocks(std::shared_ptr<EthereumPeer> _peer);
protected:
Handler m_bqRoomAvailable; ///< Triggered once block queue
Handler<> m_bqRoomAvailable; ///< Triggered once block queue
mutable RecursiveMutex x_sync;
SyncState m_state = SyncState::Idle; ///< Current sync state
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
@ -131,8 +131,8 @@ private:
/**
* @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash downaload is complete
* Syncs to peers and keeps up to date
* @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash download is complete
* syncs to peers and keeps up to date
*/
/**
@ -227,7 +227,7 @@ protected:
void pauseSync() override;
void resetSyncFor(std::shared_ptr<EthereumPeer> _peer, h256 const& _latestHash, u256 const& _td) override;
private:
protected:
/// Transition sync state in a particular direction. @param _peer Peer that is responsible for state tranfer
void transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, bool _force = false, bool _needHelp = true);
@ -261,6 +261,12 @@ private:
/// Called when peer done downloading blocks
void noteDoneBlocks(std::shared_ptr<EthereumPeer> _who, bool _clemency);
/// Start chainhash sync
virtual void syncHashes(std::shared_ptr<EthereumPeer> _peer);
/// Request subchain, no-op for pv60
virtual void requestSubchain(std::shared_ptr<EthereumPeer> /*_peer*/) {}
/// Abort syncing
void abortSync();
@ -275,5 +281,43 @@ private:
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty of the peer we aresyncing to, as of the current sync.
std::weak_ptr<EthereumPeer> m_syncer; ///< Peer we are currently syncing with
};
/**
* @brief Syncrhonization over PV61. Selects a single peer and requests every c_hashSubchainSize hash, splitting the hashchain into subchains and downloading each subchain in parallel.
* Syncs to peers and keeps up to date
*/
class PV61Sync: public PV60Sync
{
public:
PV61Sync(EthereumHost& _host);
protected:
void restartSync() override;
void requestSubchain(std::shared_ptr<EthereumPeer> _peer) override;
void syncHashes(std::shared_ptr<EthereumPeer> _peer) override;
void onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes) override;
void onPeerAborting() override;
SyncStatus status() const override;
bool invariants() const override;
private:
/// Called when subchain is complete. Check if if hashchain is fully downloaded and proceed to downloading blocks
void completeSubchain(std::shared_ptr<EthereumPeer> _peer, unsigned _n);
/// Find a subchain for peers to downloading
void requestSubchains();
/// Check if downloading hashes in parallel
bool isPV61Syncing() const;
std::map<unsigned, h256s> m_completeChainMap; ///< Fully downloaded subchains
std::map<unsigned, h256s> m_readyChainMap; ///< Subchains ready for download
std::map<unsigned, h256s> m_downloadingChainMap; ///< Subchains currently being downloading. In sync with m_chainSyncPeers
std::map<std::weak_ptr<EthereumPeer>, unsigned, std::owner_less<std::weak_ptr<EthereumPeer>>> m_chainSyncPeers; ///< Peers to m_downloadingSubchain number map
h256Hash m_knownHashes; ///< Subchain start markers. Used to track suchain completion
unsigned m_syncingBlockNumber = 0; ///< Current subchain marker
bool m_hashScanComplete = false; ///< True if leading peer completed hashchain scan and we have a list of subchains ready
};
std::ostream& operator<<(std::ostream& _out, SyncStatus const& _sync);
}
}

9
libethereum/BlockQueue.cpp

@ -21,6 +21,7 @@
#include "BlockQueue.h"
#include <thread>
#include <sstream>
#include <libdevcore/Log.h>
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
@ -473,7 +474,13 @@ void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
bool BlockQueue::invariants() const
{
Guard l(m_verification);
return m_readySet.size() == m_verified.size() + m_unverified.size() + m_verifying.size();
if (!(m_readySet.size() == m_verified.size() + m_unverified.size() + m_verifying.size()))
{
std::stringstream s;
s << "Failed BlockQueue invariant: m_readySet: " << m_readySet.size() << " m_verified: " << m_verified.size() << " m_unverified: " << m_unverified.size() << " m_verifying" << m_verifying.size();
BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment(s.str()));
}
return true;
}
void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)

8
libethereum/BlockQueue.h

@ -111,8 +111,8 @@ public:
/// Get some infomration on the given block's status regarding us.
QueueStatus blockStatus(h256 const& _h) const;
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler onRoomAvailable(T const& _t) { return m_onRoomAvailable.add(_t); }
template <class T> Handler<> onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler<> onRoomAvailable(T const& _t) { return m_onRoomAvailable.add(_t); }
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
@ -145,8 +145,8 @@ private:
std::unordered_multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For blocks that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears.
h256Hash m_knownBad; ///< Set of blocks that we know will never be valid.
std::multimap<unsigned, std::pair<h256, bytes>> m_future; ///< Set of blocks that are not yet valid. Ordered by timestamp
Signal m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
Signal<> m_onReady; ///< Called when a subsequent call to import blocks will return a non-empty container. Be nice and exit fast.
Signal<> m_onRoomAvailable; ///< Called when space for new blocks becomes availabe after a drain. Be nice and exit fast.
mutable Mutex m_verification; ///< Mutex that allows writing to m_verified, m_verifying and m_unverified.
std::condition_variable m_moreToVerify; ///< Signaled when m_unverified has a new entry.

15
libethereum/Client.cpp

@ -604,19 +604,18 @@ void Client::onChainChanged(ImportRoute const& _ir)
for (auto const& t: m_bc.transactions(h))
{
clog(ClientTrace) << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
m_tq.import(t, IfDropped::Retry);
}
}
// remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: _ir.liveBlocks)
{
clog(ClientTrace) << "Live block:" << h;
for (auto const& th: m_bc.transactionHashes(h))
{
clog(ClientTrace) << "Safely dropping transaction " << th;
m_tq.drop(th);
}
for (auto const& t: _ir.goodTranactions)
{
clog(ClientTrace) << "Safely dropping transaction " << t.sha3();
m_tq.dropGood(t);
}
if (auto h = m_host.lock())
@ -651,7 +650,7 @@ void Client::onChainChanged(ImportRoute const& _ir)
for (auto const& t: m_postMine.pending())
{
clog(ClientTrace) << "Resubmitting post-mine transaction " << t;
auto ir = m_tq.import(t, TransactionQueue::ImportCallback(), IfDropped::Retry);
auto ir = m_tq.import(t, IfDropped::Retry);
if (ir != ImportResult::Success)
onTransactionQueueReady();
}

6
libethereum/Client.h

@ -310,10 +310,10 @@ private:
GenericFarm<ProofOfWork> m_farm; ///< Our mining farm.
Handler m_tqReady;
Handler m_bqReady;
Handler<> m_tqReady;
Handler<> m_bqReady;
bool m_wouldMine = false; ///< True if we /should/ be mining.
bool m_wouldMine = false; ///< True if we /should/ be mining.
bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping.
bool m_forceMining = false; ///< Mine even when there are no transactions pending?
bool m_mineOnBadChain = false; ///< Mine even when the canary says it's a bad chain.

43
libethereum/ClientBase.cpp

@ -45,48 +45,21 @@ State ClientBase::asOf(BlockNumber _h) const
return asOf(bc().numberHash(_h));
}
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
{
prepareForTransaction();
Transaction t(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret);
m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t;
}
Address ClientBase::submitTransaction(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce)
{
prepareForTransaction();
Transaction t(_value, _gasPrice, _gas, _data, _nonce, _secret);
m_tq.import(t.rlp());
StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged());
cnote << "New transaction " << t;
return right160(sha3(rlpList(t.sender(), t.nonce())));
}
void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
auto a = toAddress(_secret);
submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, max<u256>(postMine().transactionsFrom(a), m_tq.maxNonce(a)));
}
Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
Address ClientBase::submitTransaction(TransactionSkeleton const& _t, Secret const& _secret)
{
prepareForTransaction();
u256 n = postMine().transactionsFrom(toAddress(_secret));
Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
m_tq.import(t.rlp());
TransactionSkeleton ts(_t);
ts.from = toAddress(_secret);
if (_t.nonce == UndefinedU256)
ts.nonce = max<u256>(postMine().transactionsFrom(ts.from), m_tq.maxNonce(ts.from));
Transaction t(ts, _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())));
return _t.creation ? right160(sha3(rlpList(ts.from, ts.nonce))) : Address();
}
// TODO: remove try/catch, allow exceptions

9
libethereum/ClientBase.h

@ -75,14 +75,9 @@ public:
ClientBase() {}
virtual ~ClientBase() {}
/// Submits the given message-call transaction.
virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, u256 _nonce);
virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override;
/// Submits a new contract-creation transaction.
/// Submits the given 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;
virtual Address submitTransaction(TransactionSkeleton const& _t, Secret const& _secret) override;
using Interface::submitTransaction;
/// Makes the given call. Nothing is recorded into the state.

1
libethereum/CommonNet.h

@ -91,6 +91,7 @@ enum class SyncState
struct SyncStatus
{
SyncState state = SyncState::Idle;
unsigned protocolVersion = 0;
unsigned hashesTotal = 0;
unsigned hashesReceived = 0;
bool hashesEstimated = false;

126
libethereum/EthereumHost.cpp

@ -41,6 +41,7 @@ using namespace dev::eth;
using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
static unsigned const c_maxSendTransactions = 256;
char const* const EthereumHost::s_stateNames[static_cast<int>(SyncState::Size)] = {"Idle", "Waiting", "Hashes", "Blocks", "NewBlocks" };
@ -53,6 +54,7 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
m_networkId (_networkId)
{
m_latestBlockSent = _ch.currentHash();
m_tq.onImport([this](ImportResult _ir, h256 const& _h, h512 const& _nodeId) { onTransactionImported(_ir, _h, _nodeId); });
}
EthereumHost::~EthereumHost()
@ -67,8 +69,8 @@ bool EthereumHost::ensureInitialised()
m_latestBlockSent = m_chain.currentHash();
clog(NetNote) << "Initialising: latest=" << m_latestBlockSent;
for (auto const& i: m_tq.transactions())
m_transactionsSent.insert(i.first);
Guard l(x_transactions);
m_transactionsSent = m_tq.knownTransactions();
return true;
}
return false;
@ -82,6 +84,7 @@ void EthereumHost::reset()
m_sync.reset();
m_latestBlockSent = h256();
Guard tl(x_transactions);
m_transactionsSent.clear();
}
@ -114,25 +117,29 @@ void EthereumHost::doWork()
void EthereumHost::maintainTransactions()
{
// Send any new transactions.
unordered_map<std::shared_ptr<EthereumPeer>, h256s> peerTransactions;
auto ts = m_tq.transactions();
for (auto const& i: ts)
unordered_map<std::shared_ptr<EthereumPeer>, std::vector<size_t>> peerTransactions;
auto ts = m_tq.topTransactions(c_maxSendTransactions);
{
bool unsent = !m_transactionsSent.count(i.first);
auto peers = get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }));
for (auto const& p: peers)
peerTransactions[p].push_back(i.first);
Guard l(x_transactions);
for (size_t i = 0; i < ts.size(); ++i)
{
auto const& t = ts[i];
bool unsent = !m_transactionsSent.count(t.sha3());
auto peers = get<1>(randomSelection(0, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(t.sha3())); }));
for (auto const& p: peers)
peerTransactions[p].push_back(i);
}
for (auto const& t: ts)
m_transactionsSent.insert(t.sha3());
}
for (auto const& t: ts)
m_transactionsSent.insert(t.first);
foreachPeer([&](shared_ptr<EthereumPeer> _p)
{
bytes b;
unsigned n = 0;
for (auto const& h: peerTransactions[_p])
for (auto const& i: peerTransactions[_p])
{
_p->m_knownTransactions.insert(h);
b += ts[h].rlp();
_p->m_knownTransactions.insert(ts[i].sha3());
b += ts[i].rlp();
++n;
}
@ -230,10 +237,10 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
}
}
BlockChainSync& EthereumHost::sync()
BlockChainSync* EthereumHost::sync()
{
if (m_sync)
return *m_sync; // We only chose sync strategy once
return m_sync.get(); // We only chose sync strategy once
bool pv61 = false;
foreachPeer([&](std::shared_ptr<EthereumPeer> _p)
@ -242,38 +249,43 @@ BlockChainSync& EthereumHost::sync()
pv61 = true;
return !pv61;
});
m_sync.reset(pv61 ? new PV60Sync(*this) : new PV60Sync(*this));
return *m_sync;
m_sync.reset(pv61 ? new PV61Sync(*this) : new PV60Sync(*this));
return m_sync.get();
}
void EthereumHost::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
{
Guard l(x_sync);
sync().onPeerStatus(_peer);
if (sync())
sync()->onPeerStatus(_peer);
}
void EthereumHost::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{
Guard l(x_sync);
sync().onPeerHashes(_peer, _hashes);
if (sync())
sync()->onPeerHashes(_peer, _hashes);
}
void EthereumHost::onPeerBlocks(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
{
Guard l(x_sync);
sync().onPeerBlocks(_peer, _r);
if (sync())
sync()->onPeerBlocks(_peer, _r);
}
void EthereumHost::onPeerNewHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _hashes)
{
Guard l(x_sync);
sync().onPeerNewHashes(_peer, _hashes);
if (sync())
sync()->onPeerNewHashes(_peer, _hashes);
}
void EthereumHost::onPeerNewBlock(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
{
Guard l(x_sync);
sync().onPeerNewBlock(_peer, _r);
if (sync())
sync()->onPeerNewBlock(_peer, _r);
}
void EthereumHost::onPeerTransactions(std::shared_ptr<EthereumPeer> _peer, RLP const& _r)
@ -285,35 +297,21 @@ void EthereumHost::onPeerTransactions(std::shared_ptr<EthereumPeer> _peer, RLP c
}
unsigned itemCount = _r.itemCount();
clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)";
Guard l(_peer->x_knownTransactions);
for (unsigned i = 0; i < min<unsigned>(itemCount, 32); ++i) // process 256 transactions at most. TODO: much better solution.
{
auto h = sha3(_r[i].data());
_peer->m_knownTransactions.insert(h);
ImportResult ir = m_tq.import(_r[i].data());
switch (ir)
{
case ImportResult::Malformed:
_peer->addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
m_transactionsSent.insert(h);
_peer->addRating(0);
break;
case ImportResult::Success:
_peer->addRating(100);
break;
default:;
}
}
m_tq.enqueue(_r, _peer->session()->id());
}
void EthereumHost::onPeerAborting()
{
Guard l(x_sync);
if (m_sync)
m_sync->onPeerAborting();
try
{
if (m_sync)
m_sync->onPeerAborting();
}
catch (Exception&)
{
cwarn << "Exception on peer destruciton: " << boost::current_exception_diagnostic_information();
}
}
bool EthereumHost::isSyncing() const
@ -331,3 +329,37 @@ SyncStatus EthereumHost::status() const
return SyncStatus();
return m_sync->status();
}
void EthereumHost::onTransactionImported(ImportResult _ir, h256 const& _h, h512 const& _nodeId)
{
auto session = host()->peerSession(_nodeId);
if (!session)
return;
std::shared_ptr<EthereumPeer> peer = session->cap<EthereumPeer>();
if (!peer)
peer = session->cap<EthereumPeer>(c_oldProtocolVersion);
if (!peer)
return;
Guard l(peer->x_knownTransactions);
peer->m_knownTransactions.insert(_h);
switch (_ir)
{
case ImportResult::Malformed:
peer->addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
{
Guard l(x_transactions);
m_transactionsSent.insert(_h);
}
peer->addRating(0);
break;
case ImportResult::Success:
peer->addRating(100);
break;
default:;
}
}

4
libethereum/EthereumHost.h

@ -105,6 +105,7 @@ private:
void maintainTransactions();
void maintainBlocks(h256 const& _currentBlock);
void onTransactionImported(ImportResult _ir, h256 const& _h, h512 const& _nodeId);
/// Check to see if the network peer-state initialisation has happened.
bool isInitialised() const { return (bool)m_latestBlockSent; }
@ -115,7 +116,7 @@ private:
virtual void onStarting() override { startWorking(); }
virtual void onStopping() override { stopWorking(); }
BlockChainSync& sync();
BlockChainSync* sync();
BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
@ -132,6 +133,7 @@ private:
bool m_newBlocks = false;
mutable Mutex x_sync;
mutable Mutex x_transactions;
DownloadMan m_man;
std::unique_ptr<BlockChainSync> m_sync;
};

36
libethereum/EthereumPeer.cpp

@ -127,20 +127,19 @@ void EthereumPeer::requestStatus()
m_requireTransactions = true;
RLPStream s;
bool latest = m_peerCapabilityVersion == host()->protocolVersion();
prep(s, StatusPacket, latest ? 6 : 5)
prep(s, StatusPacket, 5)
<< (latest ? host()->protocolVersion() : EthereumHost::c_oldProtocolVersion)
<< host()->networkId()
<< host()->chain().details().totalDifficulty
<< host()->chain().currentHash()
<< host()->chain().genesisHash();
if (latest)
s << u256(host()->chain().number());
sealAndSend(s);
}
void EthereumPeer::requestHashes(u256 _number, unsigned _count)
{
assert(m_asking == Asking::Nothing);
assert(m_protocolVersion == host()->protocolVersion());
m_syncHashNumber = _number;
m_syncHash = h256();
setAsking(Asking::Hashes);
@ -198,7 +197,7 @@ void EthereumPeer::requestBlocks()
void EthereumPeer::setAsking(Asking _a)
{
m_asking = _a;
m_lastAsk = chrono::system_clock::now();
m_lastAsk = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
auto s = session();
if (s)
@ -211,7 +210,8 @@ void EthereumPeer::setAsking(Asking _a)
void EthereumPeer::tick()
{
auto s = session();
if (s && (chrono::system_clock::now() - m_lastAsk > chrono::seconds(10) && m_asking != Asking::Nothing))
time_t now = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
if (s && (now - m_lastAsk > 10 && m_asking != Asking::Nothing))
// timeout
s->disconnect(PingTimeout);
}
@ -228,6 +228,7 @@ bool EthereumPeer::isCriticalSyncing() const
bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
m_lastAsk = std::chrono::system_clock::to_time_t(chrono::system_clock::now());
try
{
switch (_id)
@ -240,21 +241,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
m_latestHash = _r[3].toHash<h256>();
m_genesisHash = _r[4].toHash<h256>();
if (m_peerCapabilityVersion == host()->protocolVersion())
{
if (_r.itemCount() != 6)
{
clog(NetImpolite) << "Peer does not support PV61+ status extension.";
m_protocolVersion = EthereumHost::c_oldProtocolVersion;
}
else
{
m_protocolVersion = host()->protocolVersion();
m_latestBlockNumber = _r[5].toInt<u256>();
}
}
m_protocolVersion = host()->protocolVersion();
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << m_genesisHash << "/" << m_latestBlockNumber << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
setAsking(Asking::Nothing);
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << m_genesisHash << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
setIdle();
host()->onPeerStatus(dynamic_pointer_cast<EthereumPeer>(dynamic_pointer_cast<EthereumPeer>(shared_from_this())));
break;
}
@ -311,6 +301,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
clog(NetWarn) << "Peer giving us hashes when we didn't ask for them.";
break;
}
setIdle();
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
hashes[i] = _r[i].toHash<h256>();
@ -357,7 +348,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
if (m_asking != Asking::Blocks)
clog(NetImpolite) << "Peer giving us blocks when we didn't ask for them.";
else
{
setIdle();
host()->onPeerBlocks(dynamic_pointer_cast<EthereumPeer>(shared_from_this()), _r);
}
break;
}
case NewBlockPacket:
@ -381,9 +375,9 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
return false;
}
}
catch (Exception const& _e)
catch (Exception const&)
{
clog(NetWarn) << "Peer causing an Exception:" << _e.what() << _r;
clog(NetWarn) << "Peer causing an Exception:" << boost::current_exception_diagnostic_information() << _r;
}
catch (std::exception const& _e)
{

4
libethereum/EthereumPeer.h

@ -135,14 +135,12 @@ private:
/// What, if anything, we last asked the other peer for.
Asking m_asking = Asking::Nothing;
/// When we asked for it. Allows a time out.
std::chrono::system_clock::time_point m_lastAsk;
std::atomic<time_t> m_lastAsk;
/// These are determined through either a Status message or from NewBlock.
h256 m_latestHash; ///< Peer's latest block's hash that we know about or default null value if no need to sync.
u256 m_totalDifficulty; ///< Peer's latest block's total difficulty.
h256 m_genesisHash; ///< Peer's genesis hash
u256 m_latestBlockNumber; ///< Number of the latest block this peer has
/// This is built as we ask for hashes. Once no more hashes are given, we present this to the
/// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks.

9
libethereum/Executive.cpp

@ -153,6 +153,11 @@ u256 Executive::gasUsed() const
return m_t.gas() - m_gas;
}
u256 Executive::gasUsedNoRefunds() const
{
return m_t.gas() - m_gas + m_refunded;
}
void Executive::accrueSubState(SubState& _parentContext)
{
if (m_ext)
@ -391,8 +396,8 @@ void Executive::finalize()
// SSTORE refunds...
// must be done before the miner gets the fees.
if (m_ext)
m_gas += min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds);
m_refunded = m_ext ? min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds) : 0;
m_gas += m_refunded;
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());

4
libethereum/Executive.h

@ -110,6 +110,9 @@ public:
/// @returns total gas used in the transaction/operation.
/// @warning Only valid after finalise().
u256 gasUsed() const;
/// @returns total gas used in the transaction/operation, excluding anything refunded.
/// @warning Only valid after finalise().
u256 gasUsedNoRefunds() const;
/// Set up the executive for evaluating a bare CREATE (contract-creation) operation.
/// @returns false iff go() must be called (and thus a VM execution in required).
@ -154,6 +157,7 @@ private:
bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called.
TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception.
u256 m_gas = 0; ///< The gas for EVM code execution. Initial amount before go() execution, final amount after go() execution.
u256 m_refunded = 0; ///< The amount of gas refunded.
Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().

2
libethereum/ExtVM.cpp

@ -70,9 +70,11 @@ void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp)
// Current stack is too small to handle more CALL/CREATE executions.
// It needs to be done only once as newly allocated stack space it enough to handle
// the rest of the calls up to the depth limit (c_depthLimit).
#if __GNUC__
if (_depth == c_offloadPoint)
goOnOffloadedStack(_e, _onOp);
else
#endif
_e.go(_onOp);
}
}

29
libethereum/Interface.cpp

@ -20,6 +20,31 @@
*/
#include "Interface.h"
using namespace std;
using namespace dev;
using namespace eth;
#pragma GCC diagnostic ignored "-Wunused-variable"
namespace { char dummy; }
void Interface::submitTransaction(Secret const& _secret, u256 const& _value, Address const& _dest, bytes const& _data, u256 const& _gas, u256 const& _gasPrice, u256 const& _nonce)
{
TransactionSkeleton ts;
ts.creation = false;
ts.value = _value;
ts.to = _dest;
ts.data = _data;
ts.gas = _gas;
ts.gasPrice = _gasPrice;
ts.nonce = _nonce;
submitTransaction(ts, _secret);
}
Address Interface::submitTransaction(Secret const& _secret, u256 const& _endowment, bytes const& _init, u256 const& _gas, u256 const& _gasPrice, u256 const& _nonce)
{
TransactionSkeleton ts;
ts.creation = true;
ts.value = _endowment;
ts.data = _init;
ts.gas = _gas;
ts.gasPrice = _gasPrice;
ts.nonce = _nonce;
return submitTransaction(ts, _secret);
}

12
libethereum/Interface.h

@ -65,16 +65,16 @@ public:
// [TRANSACTION API]
/// Submits the given message-call transaction.
virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0;
/// Submits a new transaction.
/// @returns the new contract's address (assuming it all goes through and it's contract creation).
virtual Address submitTransaction(TransactionSkeleton const& _t, Secret const& _secret) = 0;
/// 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 = 10000, u256 _gasPrice = 10 * szabo) = 0;
/// Submits the given message-call transaction.
void submitTransaction(Secret const& _secret, u256 const& _value, Address const& _dest, bytes const& _data = bytes(), u256 const& _gas = 10000, u256 const& _gasPrice = 10 * szabo, u256 const& _nonce = UndefinedU256);
/// Submits a new contract-creation transaction.
/// @returns the new contract's address (assuming it all goes through).
Address submitTransaction(Secret const& _secret, TransactionSkeleton const& _t) { if (_t.creation) return submitTransaction(_secret, _t.value, _t.data, _t.gas, _t.gasPrice); submitTransaction(_secret, _t.value, _t.to, _t.data, _t.gas, _t.gasPrice); return Address(); }
Address submitTransaction(Secret const& _secret, u256 const& _endowment, bytes const& _init, u256 const& _gas = 10000, u256 const& _gasPrice = 10 * szabo, u256 const& _nonce = UndefinedU256);
/// Blocks until all pending transactions have been processed.
virtual void flushTransactions() = 0;

63
libethereum/State.cpp

@ -38,6 +38,7 @@
#include "Executive.h"
#include "CachedAddressState.h"
#include "CanonBlockChain.h"
#include "TransactionQueue.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -47,6 +48,7 @@ namespace fs = boost::filesystem;
#define ETH_TIMED_ENACTMENTS 0
static const u256 c_blockReward = c_network == Network::Olympic ? (1500 * finney) : (5 * ether);
static const unsigned c_maxSyncTransactions = 256;
const char* StateSafeExceptions::name() { return EthViolet "" EthBlue ""; }
const char* StateDetail::name() { return EthViolet "" EthWhite ""; }
@ -495,7 +497,7 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
pair<TransactionReceipts, bool> ret;
ret.second = false;
auto ts = _tq.transactions();
auto ts = _tq.topTransactions(c_maxSyncTransactions);
LastHashes lh;
@ -504,27 +506,26 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
for (int goodTxs = 1; goodTxs; )
{
goodTxs = 0;
for (auto const& i: ts)
if (!m_transactionSet.count(i.first))
for (auto const& t: ts)
if (!m_transactionSet.count(t.sha3()))
{
try
{
if (i.second.gasPrice() >= _gp.ask(*this))
if (t.gasPrice() >= _gp.ask(*this))
{
// Timer t;
if (lh.empty())
lh = _bc.lastHashes();
execute(lh, i.second);
execute(lh, t);
ret.first.push_back(m_receipts.back());
_tq.noteGood(i);
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
}
else if (i.second.gasPrice() < _gp.ask(*this) * 9 / 10)
else if (t.gasPrice() < _gp.ask(*this) * 9 / 10)
{
// less than 90% of our ask price for gas. drop.
cnote << i.first << "Dropping El Cheapo transaction (<90% of ask price)";
_tq.drop(i.first);
cnote << t.sha3() << "Dropping El Cheapo transaction (<90% of ask price)";
_tq.drop(t.sha3());
}
}
catch (InvalidNonce const& in)
@ -535,57 +536,54 @@ pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQu
if (req > got)
{
// too old
for (Transaction const& t: m_transactions)
if (t.from() == i.second.from())
/* for (Transaction const& mt: m_transactions)
{
if (mt.from() == t.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);
}
if (mt.nonce() < t.nonce())
cnote << t.sha3() << "Dropping old transaction (nonce too low)";
else if (mt.nonce() == t.nonce() && mt.gasPrice() <= t.gasPrice())
cnote << t.sha3() << "Dropping old transaction (gas price lower)";
}
}*/
_tq.drop(t.sha3());
}
else if (got > req + _tq.waiting(i.second.sender()))
else if (got > req + _tq.waiting(t.sender()))
{
// too new
cnote << i.first << "Dropping new transaction (too many nonces ahead)";
_tq.drop(i.first);
cnote << t.sha3() << "Dropping new transaction (too many nonces ahead)";
_tq.drop(t.sha3());
}
else
_tq.setFuture(i);
_tq.setFuture(t.sha3());
}
catch (BlockGasLimitReached const& e)
{
bigint const& got = *boost::get_error_info<errinfo_got>(e);
if (got > m_currentBlock.gasLimit)
{
cnote << i.first << "Dropping over-gassy transaction (gas > block's gas limit)";
_tq.drop(i.first);
cnote << t.sha3() << "Dropping over-gassy transaction (gas > block's gas limit)";
_tq.drop(t.sha3());
}
else
{
// Temporarily no gas left in current block.
// OPTIMISE: could note this and then we don't evaluate until a block that does have the gas left.
// for now, just leave alone.
// _tq.setFuture(i);
// _tq.setFuture(t.sha3());
}
}
catch (Exception const& _e)
{
// Something else went wrong - drop it.
cnote << i.first << "Dropping invalid transaction:" << diagnostic_information(_e);
_tq.drop(i.first);
cnote << t.sha3() << "Dropping invalid transaction:" << diagnostic_information(_e);
_tq.drop(t.sha3());
}
catch (std::exception const&)
{
// Something else went wrong - drop it.
_tq.drop(i.first);
cnote << i.first << "Transaction caused low-level exception :(";
_tq.drop(t.sha3());
cnote << t.sha3() << "Transaction caused low-level exception :(";
}
}
if (chrono::steady_clock::now() > deadline)
@ -1271,6 +1269,7 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(e.t());
// m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsedNoRefunds(), e.logs())); // TODO: Switch in to be compliant with YP.
m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()));
m_transactionSet.insert(e.t().sha3());
}

2
libethereum/State.h

@ -32,7 +32,6 @@
#include <libethcore/ProofOfWork.h>
#include <libethcore/Miner.h>
#include <libevm/ExtVMFace.h>
#include "TransactionQueue.h"
#include "Account.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
@ -67,6 +66,7 @@ using LogBloomRequirementError = boost::tuple<errinfo_required_LogBloom, errinfo
class BlockChain;
class State;
class TransactionQueue;
struct VerifiedBlockRef;
struct StateChat: public LogChannel { static const char* name(); static const int verbosity = 4; };

7
libethereum/Transaction.h

@ -85,6 +85,9 @@ public:
/// Constructs a null transaction.
Transaction() {}
/// Constructs from a transaction skeleton & optional secret.
Transaction(TransactionSkeleton const& _ts, Secret const& _s = Secret()): TransactionBase(_ts, _s) {}
/// Constructs a signed message-call transaction.
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret):
TransactionBase(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret)
@ -96,12 +99,12 @@ public:
{}
/// Constructs an unsigned message-call transaction.
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0):
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = UndefinedU256):
TransactionBase(_value, _gasPrice, _gas, _dest, _data, _nonce)
{}
/// Constructs an unsigned contract-creation transaction.
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0):
Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = UndefinedU256):
TransactionBase(_value, _gasPrice, _gas, _data, _nonce)
{}

312
libethereum/TransactionQueue.cpp

@ -31,7 +31,28 @@ using namespace dev::eth;
const char* TransactionQueueChannel::name() { return EthCyan "┉┅▶"; }
const char* TransactionQueueTraceChannel::name() { return EthCyan " ┅▶"; }
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb, IfDropped _ik)
TransactionQueue::TransactionQueue(unsigned _limit, unsigned _futureLimit):
m_current(PriorityCompare { *this }),
m_limit(_limit),
m_futureLimit(_futureLimit)
{
unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
for (unsigned i = 0; i < verifierThreads; ++i)
m_verifiers.emplace_back([=](){
setThreadName("txcheck" + toString(i));
this->verifierBody();
});
}
TransactionQueue::~TransactionQueue()
{
m_aborting = true;
m_queueReady.notify_all();
for (auto& i: m_verifiers)
i.join();
}
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, IfDropped _ik)
{
// Check if we already know this transaction.
h256 h = sha3(_transactionRLP);
@ -49,14 +70,13 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb
{
t = Transaction(_transactionRLP, CheckTransaction::Everything);
UpgradeGuard ul(l);
ir = manageImport_WITH_LOCK(h, t, _cb);
ir = manageImport_WITH_LOCK(h, t);
}
catch (...)
{
return ImportResult::Malformed;
}
}
// cdebug << "import-END: Nonce of" << t.sender() << "now" << maxNonce(t.sender());
return ir;
}
@ -71,72 +91,91 @@ ImportResult TransactionQueue::check_WITH_LOCK(h256 const& _h, IfDropped _ik)
return ImportResult::Success;
}
ImportResult TransactionQueue::import(Transaction const& _transaction, ImportCallback const& _cb, IfDropped _ik)
ImportResult TransactionQueue::import(Transaction const& _transaction, IfDropped _ik)
{
// Check if we already know this transaction.
h256 h = _transaction.sha3(WithSignature);
// cdebug << "import-BEGIN: Nonce of sender" << maxNonce(_transaction.sender());
ImportResult ret;
{
UpgradableGuard l(m_lock);
// TODO: keep old transactions around and check in State for nonce validity
auto ir = check_WITH_LOCK(h, _ik);
if (ir != ImportResult::Success)
return ir;
{
UpgradeGuard ul(l);
ret = manageImport_WITH_LOCK(h, _transaction, _cb);
ret = manageImport_WITH_LOCK(h, _transaction);
}
}
// cdebug << "import-END: Nonce of" << _transaction.sender() << "now" << maxNonce(_transaction.sender());
return ret;
}
std::unordered_map<h256, Transaction> TransactionQueue::transactions() const
Transactions TransactionQueue::topTransactions(unsigned _limit) const
{
ReadGuard l(m_lock);
Transactions res;
unsigned n = _limit;
for (auto t = m_current.begin(); n != 0 && t != m_current.end(); ++t, --n)
res.push_back(t->transaction);
return res;
}
h256Hash TransactionQueue::knownTransactions() const
{
ReadGuard l(m_lock);
return m_current;
return m_known;
}
ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb)
ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction)
{
try
{
// Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender.
// If it doesn't work, the signature is bad.
// The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction).
assert(_h == _transaction.sha3());
// Remove any prior transaction with the same nonce but a lower gas price.
// Bomb out if there's a prior transaction with higher gas price.
auto r = m_senders.equal_range(_transaction.from());
for (auto it = r.first; it != r.second; ++it)
if (m_current.count(it->second) && m_current[it->second].nonce() == _transaction.nonce())
if (_transaction.gasPrice() < m_current[it->second].gasPrice())
auto cs = m_currentByAddressAndNonce.find(_transaction.from());
if (cs != m_currentByAddressAndNonce.end())
{
auto t = cs->second.find(_transaction.nonce());
if (t != cs->second.end())
{
if (_transaction.gasPrice() < (*t->second).transaction.gasPrice())
return ImportResult::OverbidGasPrice;
else
{
remove_WITH_LOCK(it->second);
break;
}
else if (m_future.count(it->second) && m_future[it->second].nonce() == _transaction.nonce())
if (_transaction.gasPrice() < m_future[it->second].gasPrice())
remove_WITH_LOCK((*t->second).transaction.sha3());
}
}
auto fs = m_future.find(_transaction.from());
if (fs != m_future.end())
{
auto t = fs->second.find(_transaction.nonce());
if (t != fs->second.end())
{
if (_transaction.gasPrice() < t->second.transaction.gasPrice())
return ImportResult::OverbidGasPrice;
else
{
remove_WITH_LOCK(it->second);
break;
fs->second.erase(t);
--m_futureSize;
}
else {}
// If valid, append to blocks.
}
}
// If valid, append to transactions.
insertCurrent_WITH_LOCK(make_pair(_h, _transaction));
m_known.insert(_h);
if (_cb)
m_callbacks[_h] = _cb;
clog(TransactionQueueTraceChannel) << "Queued vaguely legit-looking transaction" << _h;
while (m_current.size() > m_limit)
{
clog(TransactionQueueTraceChannel) << "Dropping out of bounds transaction" << _h;
remove_WITH_LOCK(m_current.rbegin()->transaction.sha3());
}
m_onReady();
}
catch (Exception const& _e)
@ -155,7 +194,6 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
u256 TransactionQueue::maxNonce(Address const& _a) const
{
// cdebug << "txQ::maxNonce" << _a;
ReadGuard l(m_lock);
return maxNonce_WITH_LOCK(_a);
}
@ -163,93 +201,129 @@ u256 TransactionQueue::maxNonce(Address const& _a) const
u256 TransactionQueue::maxNonce_WITH_LOCK(Address const& _a) const
{
u256 ret = 0;
auto r = m_senders.equal_range(_a);
for (auto it = r.first; it != r.second; ++it)
if (m_current.count(it->second))
{
// cdebug << it->first << "1+" << m_current.at(it->second).nonce();
ret = max(ret, m_current.at(it->second).nonce() + 1);
}
else if (m_future.count(it->second))
{
// cdebug << it->first << "1+" << m_future.at(it->second).nonce();
ret = max(ret, m_future.at(it->second).nonce() + 1);
}
else
{
cwarn << "ERRROR!!!!! m_senders references non-current transaction";
cwarn << "Sender" << it->first << "has transaction" << it->second;
cwarn << "Count of m_current for" << it->second << "is" << m_current.count(it->second);
}
return ret;
auto cs = m_currentByAddressAndNonce.find(_a);
if (cs != m_currentByAddressAndNonce.end() && !cs->second.empty())
ret = cs->second.rbegin()->first;
auto fs = m_future.find(_a);
if (fs != m_future.end() && !fs->second.empty())
ret = std::max(ret, fs->second.rbegin()->first);
return ret + 1;
}
void TransactionQueue::insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p)
{
// cdebug << "txQ::insertCurrent" << _p.first << _p.second.sender() << _p.second.nonce();
m_senders.insert(make_pair(_p.second.sender(), _p.first));
if (m_current.count(_p.first))
if (m_currentByHash.count(_p.first))
{
cwarn << "Transaction hash" << _p.first << "already in current?!";
m_current.insert(_p);
return;
}
Transaction const& t = _p.second;
// Insert into current
auto inserted = m_currentByAddressAndNonce[t.from()].insert(std::make_pair(t.nonce(), PriorityQueue::iterator()));
PriorityQueue::iterator handle = m_current.emplace(VerifiedTransaction(t));
inserted.first->second = handle;
m_currentByHash[_p.first] = handle;
// Move following transactions from future to current
makeCurrent_WITH_LOCK(t);
m_known.insert(_p.first);
}
bool TransactionQueue::remove_WITH_LOCK(h256 const& _txHash)
{
// cdebug << "txQ::remove" << _txHash;
for (std::unordered_map<h256, Transaction>* pool: { &m_current, &m_future })
{
auto pit = pool->find(_txHash);
if (pit != pool->end())
{
auto r = m_senders.equal_range(pit->second.sender());
for (auto i = r.first; i != r.second; ++i)
if (i->second == _txHash)
{
m_senders.erase(i);
break;
}
// cdebug << "=> nonce" << pit->second.nonce();
pool->erase(pit);
return true;
}
}
return false;
auto t = m_currentByHash.find(_txHash);
if (t == m_currentByHash.end())
return false;
Address from = (*t->second).transaction.from();
auto it = m_currentByAddressAndNonce.find(from);
assert (it != m_currentByAddressAndNonce.end());
it->second.erase((*t->second).transaction.nonce());
m_current.erase(t->second);
m_currentByHash.erase(t);
if (it->second.empty())
m_currentByAddressAndNonce.erase(it);
m_known.erase(_txHash);
return true;
}
unsigned TransactionQueue::waiting(Address const& _a) const
{
ReadGuard l(m_lock);
auto it = m_senders.equal_range(_a);
unsigned ret = 0;
for (auto i = it.first; i != it.second; ++i, ++ret) {}
auto cs = m_currentByAddressAndNonce.find(_a);
if (cs != m_currentByAddressAndNonce.end())
ret = cs->second.size();
auto fs = m_future.find(_a);
if (fs != m_future.end())
ret += fs->second.size();
return ret;
}
void TransactionQueue::setFuture(std::pair<h256, Transaction> const& _t)
void TransactionQueue::setFuture(h256 const& _txHash)
{
// cdebug << "txQ::setFuture" << _t.first;
WriteGuard l(m_lock);
if (m_current.count(_t.first))
auto it = m_currentByHash.find(_txHash);
if (it == m_currentByHash.end())
return;
VerifiedTransaction const& st = *(it->second);
Address from = st.transaction.from();
auto& queue = m_currentByAddressAndNonce[from];
auto& target = m_future[from];
auto cutoff = queue.lower_bound(st.transaction.nonce());
for (auto m = cutoff; m != queue.end(); ++m)
{
m_future.insert(_t);
m_current.erase(_t.first);
VerifiedTransaction& t = const_cast<VerifiedTransaction&>(*(m->second)); // set has only const iterators. Since we are moving out of container that's fine
m_currentByHash.erase(t.transaction.sha3());
target.emplace(t.transaction.nonce(), move(t));
m_current.erase(m->second);
++m_futureSize;
}
queue.erase(cutoff, queue.end());
if (queue.empty())
m_currentByAddressAndNonce.erase(from);
}
void TransactionQueue::noteGood(std::pair<h256, Transaction> const& _t)
void TransactionQueue::makeCurrent_WITH_LOCK(Transaction const& _t)
{
// cdebug << "txQ::noteGood" << _t.first;
WriteGuard l(m_lock);
auto r = m_senders.equal_range(_t.second.sender());
for (auto it = r.first; it != r.second; ++it)
auto fs = m_future.find(_t.from());
if (fs != m_future.end())
{
auto fit = m_future.find(it->second);
if (fit != m_future.end())
u256 nonce = _t.nonce() + 1;
auto fb = fs->second.find(nonce);
if (fb != fs->second.end())
{
m_current.insert(*fit);
m_future.erase(fit);
auto ft = fb;
while (ft != fs->second.end() && ft->second.transaction.nonce() == nonce)
{
auto inserted = m_currentByAddressAndNonce[_t.from()].insert(std::make_pair(ft->second.transaction.nonce(), PriorityQueue::iterator()));
PriorityQueue::iterator handle = m_current.emplace(move(ft->second));
inserted.first->second = handle;
m_currentByHash[(*handle).transaction.sha3()] = handle;
--m_futureSize;
++ft;
++nonce;
}
fs->second.erase(fb, ft);
if (fs->second.empty())
m_future.erase(_t.from());
}
}
while (m_futureSize > m_futureLimit)
{
// TODO: priority queue for future transactions
// For now just drop random chain end
--m_futureSize;
clog(TransactionQueueTraceChannel) << "Dropping out of bounds future transaction" << m_future.begin()->second.rbegin()->second.transaction.sha3();
m_future.begin()->second.erase(--m_future.begin()->second.end());
if (m_future.begin()->second.empty())
m_future.erase(m_future.begin());
}
}
void TransactionQueue::drop(h256 const& _txHash)
@ -261,7 +335,67 @@ void TransactionQueue::drop(h256 const& _txHash)
UpgradeGuard ul(l);
m_dropped.insert(_txHash);
m_known.erase(_txHash);
remove_WITH_LOCK(_txHash);
}
void TransactionQueue::dropGood(Transaction const& _t)
{
WriteGuard l(m_lock);
makeCurrent_WITH_LOCK(_t);
if (!m_known.count(_t.sha3()))
return;
m_dropped.insert(_t.sha3());
remove_WITH_LOCK(_t.sha3());
}
void TransactionQueue::clear()
{
WriteGuard l(m_lock);
m_known.clear();
m_current.clear();
m_currentByAddressAndNonce.clear();
m_currentByHash.clear();
m_future.clear();
m_futureSize = 0;
}
void TransactionQueue::enqueue(RLP const& _data, h512 const& _nodeId)
{
{
Guard l(x_queue);
unsigned itemCount = _data.itemCount();
for (unsigned i = 0; i < itemCount; ++i)
m_unverified.emplace_back(UnverifiedTransaction(_data[i].data(), _nodeId));
}
m_queueReady.notify_all();
}
void TransactionQueue::verifierBody()
{
while (!m_aborting)
{
UnverifiedTransaction work;
{
unique_lock<Mutex> l(x_queue);
m_queueReady.wait(l, [&](){ return !m_unverified.empty() || m_aborting; });
if (m_aborting)
return;
work = move(m_unverified.front());
m_unverified.pop_front();
}
try
{
Transaction t(work.transaction, CheckTransaction::Cheap); //Signature will be checked later
ImportResult ir = import(t);
m_onImport(ir, t.sha3(), work.nodeId);
}
catch (...)
{
// should not happen as exceptions are handled in import.
cwarn << "Bad transaction:" << boost::current_exception_diagnostic_information();
}
}
}

99
libethereum/TransactionQueue.h

@ -22,6 +22,9 @@
#pragma once
#include <functional>
#include <condition_variable>
#include <thread>
#include <deque>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
@ -43,46 +46,106 @@ enum class IfDropped { Ignore, Retry };
/**
* @brief A queue of Transactions, each stored as RLP.
* Maintains a transaction queue sorted by nonce diff and gas price
* @threadsafe
*/
class TransactionQueue
{
public:
using ImportCallback = std::function<void(ImportResult)>;
ImportResult import(Transaction const& _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore);
ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore) { return import(&_tx, _cb, _ik); }
ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback(), IfDropped _ik = IfDropped::Ignore);
/// @brief TransactionQueue
/// @param _limit Maximum number of pending transactions in the queue
/// @param _futureLimit Maximum number of future nonce transactions
TransactionQueue(unsigned _limit = 1024, unsigned _futureLimit = 1024);
~TransactionQueue();
void enqueue(RLP const& _data, h512 const& _nodeId);
ImportResult import(bytes const& _tx, IfDropped _ik = IfDropped::Ignore) { return import(&_tx, _ik); }
ImportResult import(Transaction const& _tx, IfDropped _ik = IfDropped::Ignore);
void drop(h256 const& _txHash);
unsigned waiting(Address const& _a) const;
std::unordered_map<h256, Transaction> transactions() const;
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_future.size()); }
Transactions topTransactions(unsigned _limit) const;
h256Hash knownTransactions() const;
u256 maxNonce(Address const& _a) const;
void setFuture(h256 const& _t);
void dropGood(Transaction const& _t);
void setFuture(std::pair<h256, Transaction> const& _t);
void noteGood(std::pair<h256, Transaction> const& _t);
void clear() { WriteGuard l(m_lock); m_senders.clear(); m_known.clear(); m_current.clear(); m_future.clear(); }
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
void clear();
template <class T> Handler<> onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> Handler<ImportResult, h256 const&, h512 const&> onImport(T const& _t) { return m_onImport.add(_t); }
private:
struct VerifiedTransaction
{
VerifiedTransaction(Transaction const& _t): transaction(_t) {}
VerifiedTransaction(VerifiedTransaction&& _t): transaction(std::move(_t.transaction)) {}
VerifiedTransaction(VerifiedTransaction const&) = delete;
VerifiedTransaction& operator=(VerifiedTransaction const&) = delete;
Transaction transaction;
};
struct UnverifiedTransaction
{
UnverifiedTransaction() {}
UnverifiedTransaction(bytesConstRef const& _t, h512 const& _nodeId): transaction(std::move(_t.toBytes())), nodeId(_nodeId) {}
UnverifiedTransaction(UnverifiedTransaction&& _t): transaction(std::move(_t.transaction)) {}
UnverifiedTransaction& operator=(UnverifiedTransaction&& _other) { transaction = std::move(_other.transaction); nodeId = std::move(_other.nodeId); return *this; }
UnverifiedTransaction(UnverifiedTransaction const&) = delete;
UnverifiedTransaction& operator=(UnverifiedTransaction const&) = delete;
bytes transaction;
h512 nodeId;
};
struct PriorityCompare
{
TransactionQueue& queue;
bool operator()(VerifiedTransaction const& _first, VerifiedTransaction const& _second) const
{
u256 const& height1 = _first.transaction.nonce() - queue.m_currentByAddressAndNonce[_first.transaction.sender()].begin()->first;
u256 const& height2 = _second.transaction.nonce() - queue.m_currentByAddressAndNonce[_second.transaction.sender()].begin()->first;
return height1 < height2 || (height1 == height2 && _first.transaction.gasPrice() > _second.transaction.gasPrice());
}
};
// Use a set with dynamic comparator for minmax priority queue. The comparator takes into account min account nonce. Updating it does not affect the order.
using PriorityQueue = std::multiset<VerifiedTransaction, PriorityCompare>;
ImportResult import(bytesConstRef _tx, IfDropped _ik = IfDropped::Ignore);
ImportResult check_WITH_LOCK(h256 const& _h, IfDropped _ik);
ImportResult manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb);
ImportResult manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction);
void insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p);
void makeCurrent_WITH_LOCK(Transaction const& _t);
bool remove_WITH_LOCK(h256 const& _txHash);
u256 maxNonce_WITH_LOCK(Address const& _a) const;
void verifierBody();
mutable SharedMutex m_lock; ///< General lock.
h256Hash m_known; ///< Hashes of transactions in both sets.
std::unordered_multimap<Address, h256> m_senders; ///< Mapping from the sender address to the transaction hash; useful for determining the nonce of a given sender.
std::unordered_map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx.
std::unordered_map<h256, Transaction> m_future; ///< For transactions that have a future nonce; we re-insert into current once the sender has a valid TX.
std::unordered_map<h256, std::function<void(ImportResult)>> m_callbacks; ///< Called once.
h256Hash m_dropped; ///< Transactions that have previously been dropped.
Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
h256Hash m_dropped; ///< Transactions that have previously been dropped
PriorityQueue m_current;
std::unordered_map<h256, PriorityQueue::iterator> m_currentByHash; ///< Transaction hash to set ref
std::unordered_map<Address, std::map<u256, PriorityQueue::iterator>> m_currentByAddressAndNonce; ///< Transactions grouped by account and nonce
std::unordered_map<Address, std::map<u256, VerifiedTransaction>> m_future; /// Future transactions
Signal<> m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
Signal<ImportResult, h256 const&, h512 const&> m_onImport; ///< Called for each import attempt. Arguments are result, transaction id an node id. Be nice and exit fast.
unsigned m_limit; ///< Max number of pending transactions
unsigned m_futureLimit; ///< Max number of future transactions
unsigned m_futureSize = 0; ///< Current number of future transactions
std::condition_variable m_queueReady; ///< Signaled when m_unverified has a new entry.
std::vector<std::thread> m_verifiers;
std::deque<UnverifiedTransaction> m_unverified; ///< Pending verification queue
mutable Mutex x_queue; ///< Verification queue mutex
bool m_aborting = false; ///< Exit condition for verifier.
};
}

4
libethereum/VerifiedBlock.h

@ -43,11 +43,11 @@ struct VerifiedBlockRef
/// @brief Verified block info, combines block data and verified info/transactions
struct VerifiedBlock
{
VerifiedBlock() {};
VerifiedBlock() {}
VerifiedBlock(BlockInfo&& _bi)
{
verified.info = _bi;
verified.info = std::move(_bi);
}
VerifiedBlock(VerifiedBlock&& _other):

6
libevm/ExtVMFace.cpp

@ -21,22 +21,20 @@
#include "ExtVMFace.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth):
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth):
myAddress(_myAddress),
caller(_caller),
origin(_origin),
value(_value),
gasPrice(_gasPrice),
data(_data),
code(_code),
code(std::move(_code)),
codeHash(_codeHash),
lastHashes(_lh),
previousBlock(_previousBlock),
currentBlock(_currentBlock),
depth(_depth)
{}

2
libevm/ExtVMFace.h

@ -161,7 +161,7 @@ public:
ExtVMFace() = default;
/// Full constructor.
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes const& _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth);
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth);
virtual ~ExtVMFace() = default;

82
libevm/SmartVM.cpp

@ -20,8 +20,13 @@
#include "SmartVM.h"
#include <unordered_map>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <libdevcore/Log.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/Guards.h>
#include <evmjit/JIT.h>
#include <evmjit/libevmjit-cpp/Utils.h>
#include "VMFactory.h"
@ -32,6 +37,8 @@ namespace eth
{
namespace
{
struct JitInfo: LogChannel { static const char* name() { return "JIT"; }; static const int verbosity = 11; };
using HitMap = std::unordered_map<h256, uint64_t>;
HitMap& getHitMap()
@ -39,30 +46,93 @@ namespace
static HitMap s_hitMap;
return s_hitMap;
}
struct JitTask
{
bytes code;
h256 codeHash;
};
class JitWorker
{
bool m_finished = false;
std::mutex x_mutex;
std::condition_variable m_cv;
std::thread m_worker;
std::queue<JitTask> m_queue;
bool pop(JitTask& o_task)
{
std::unique_lock<std::mutex> lock{x_mutex};
m_cv.wait(lock, [this]{ return m_finished || !m_queue.empty(); });
if (m_finished)
return false;
assert(!m_queue.empty());
o_task = std::move(m_queue.front());
m_queue.pop();
return true;
}
void work()
{
clog(JitInfo) << "JIT worker started.";
JitTask task;
while (pop(task))
{
clog(JitInfo) << "Compilation... " << task.codeHash;
evmjit::JIT::compile(task.code.data(), task.code.size(), eth2jit(task.codeHash));
clog(JitInfo) << " ...finished " << task.codeHash;
}
clog(JitInfo) << "JIT worker finished.";
}
public:
JitWorker() noexcept: m_worker([this]{ work(); })
{}
~JitWorker()
{
DEV_GUARDED(x_mutex)
m_finished = true;
m_cv.notify_one();
m_worker.join();
}
void push(JitTask&& _task)
{
DEV_GUARDED(x_mutex)
m_queue.push(std::move(_task));
m_cv.notify_one();
}
};
}
bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{
auto codeHash = sha3(_ext.code);
auto codeHash = _ext.codeHash;
auto vmKind = VMKind::Interpreter; // default VM
// Jitted EVM code already in memory?
if (evmjit::JIT::isCodeReady(eth2jit(codeHash)))
{
cnote << "Jitted";
clog(JitInfo) << "JIT: " << codeHash;
vmKind = VMKind::JIT;
}
else
{
static JitWorker s_worker;
// Check EVM code hit count
static const uint64_t c_hitTreshold = 1;
static const uint64_t c_hitTreshold = 2;
auto& hits = getHitMap()[codeHash];
++hits;
if (hits > c_hitTreshold)
if (hits == c_hitTreshold)
{
cnote << "JIT selected";
vmKind = VMKind::JIT;
clog(JitInfo) << "Schedule: " << codeHash;
s_worker.push({_ext.code, codeHash});
}
clog(JitInfo) << "Interpreter: " << codeHash;
}
// TODO: Selected VM must be kept only because it returns reference to its internal memory.

1
libjsengine/CMakeLists.txt

@ -19,7 +19,6 @@ file(GLOB HEADERS "*.h")
include(EthUtils)
eth_add_resources("${CMAKE_CURRENT_SOURCE_DIR}/JSResources.cmake" "JSRES")
message(STATUS "HERE!!! ${JSRES}")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS} ${JSRES})
# macos brew version of v8 needs to be compiled with libstdc++

2
libp2p/Common.h

@ -236,7 +236,7 @@ template <> struct hash<bi::address>
return boost::hash_range(range.begin(), range.end());
}
if (_a.is_unspecified())
return static_cast<size_t>(0x3487194039229152ul); // Chosen by fair dice roll, guaranteed to be random
return static_cast<size_t>(0x3487194039229152ull); // Chosen by fair dice roll, guaranteed to be random
return std::hash<std::string>()(_a.to_string());
}
};

7
libp2p/Host.h

@ -202,6 +202,9 @@ public:
/// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error.
void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameCoder* _io, std::shared_ptr<RLPXSocket> const& _s);
/// Get session by id
std::shared_ptr<Session> peerSession(NodeId const& _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? m_sessions[_id].lock() : std::shared_ptr<Session>(); }
protected:
void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e);
@ -211,8 +214,8 @@ protected:
private:
enum PeerSlotRatio { Egress = 2, Ingress = 9 };
bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; }
bool havePeerSession(NodeId const& _id) { return !!peerSession(_id); }
/// Determines and sets m_tcpPublic to publicly advertised address.
void determinePublic();

4
libp2p/RLPXSocket.h

@ -45,7 +45,7 @@ public:
bool isConnected() const { return m_socket.is_open(); }
void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} }
bi::tcp::endpoint remoteEndpoint() { try { return m_socket.remote_endpoint(); } catch (...){ return bi::tcp::endpoint(); } }
bi::tcp::endpoint remoteEndpoint() { boost::system::error_code ec; return m_socket.remote_endpoint(ec); }
bi::tcp::socket& ref() { return m_socket; }
protected:
@ -53,4 +53,4 @@ protected:
};
}
}
}

10
libp2p/Session.cpp

@ -256,9 +256,8 @@ bool Session::checkPacket(bytesConstRef _msg)
void Session::send(bytes&& _msg)
{
clog(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1));
bytesConstRef msg(&_msg);
clog(NetLeft) << RLP(msg.cropped(1));
if (!checkPacket(msg))
clog(NetWarn) << "INVALID PACKET CONSTRUCTED!";
@ -268,7 +267,7 @@ void Session::send(bytes&& _msg)
bool doWrite = false;
{
Guard l(x_writeQueue);
m_writeQueue.push_back(_msg);
m_writeQueue.push_back(std::move(_msg));
doWrite = (m_writeQueue.size() == 1);
}
@ -334,11 +333,12 @@ void Session::drop(DisconnectReason _reason)
void Session::disconnect(DisconnectReason _reason)
{
clog(NetConnect) << "Disconnecting (our reason:" << reasonOf(_reason) << ")";
size_t peerCount = m_server->peerCount(); //needs to be outside of lock to avoid deadlocking with other thread that capture x_info/x_sessions in reverse order
DEV_GUARDED(x_info)
StructuredLogger::p2pDisconnected(
m_info.id.abridged(),
m_peer->endpoint, // TODO: may not be 100% accurate
m_server->peerCount()
peerCount
);
if (m_socket->ref().is_open())
{
@ -386,7 +386,7 @@ void Session::doRead()
drop(BadProtocol);
return;
}
/// read padded frame and mac
auto tlen = header.length + header.padding + h128::size;
ba::async_read(m_socket->ref(), boost::asio::buffer(m_data, tlen), [this, self, header, tlen](boost::system::error_code ec, std::size_t length)

10
libtestutils/StateLoader.cpp

@ -1,16 +1,16 @@
/*
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/>.
*/
@ -36,10 +36,10 @@ StateLoader::StateLoader(Json::Value const& _json, std::string const& _dbPath):
Address address = Address(name);
bytes code = fromHex(o["code"].asString().substr(2));
if (code.size())
if (!code.empty())
{
m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::ContractConception);
m_state.m_cache[address].setCode(code);
m_state.m_cache[address].setCode(std::move(code));
}
else
m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::NormalCreation);

4
libweb3jsonrpc/AccountHolder.cpp

@ -109,7 +109,7 @@ AddressHash SimpleAccountHolder::realAccounts() const
void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)
{
if (isRealAccount(_t.from))
m_client()->submitTransaction(m_keyManager.secret(_t.from, [&](){ return m_getPassword(_t.from); }), _t);
m_client()->submitTransaction(_t, m_keyManager.secret(_t.from, [&](){ return m_getPassword(_t.from); }));
else if (isProxyAccount(_t.from))
queueTransaction(_t);
}
@ -117,7 +117,7 @@ void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)
void FixedAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t)
{
if (isRealAccount(_t.from))
m_client()->submitTransaction(m_accounts[_t.from], _t);
m_client()->submitTransaction(_t, m_accounts[_t.from]);
else if (isProxyAccount(_t.from))
queueTransaction(_t);
}

3
libweb3jsonrpc/JsonHelper.cpp

@ -269,6 +269,9 @@ TransactionSkeleton toTransactionSkeleton(Json::Value const& _json)
if (!_json["code"].empty())
ret.data = jsToBytes(_json["code"].asString());
if (!_json["nonce"].empty())
ret.nonce = jsToU256(_json["nonce"].asString());
return ret;
}

2
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -248,7 +248,7 @@ string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json)
if (!t.from)
t.from = m_ethAccounts->defaultTransactAccount();
if (t.creation)
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));;
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));
if (t.gasPrice == UndefinedU256)
t.gasPrice = 10 * dev::eth::szabo; // TODO: should be determined by user somehow.
if (t.gas == UndefinedU256)

2
libwebthree/CMakeLists.txt

@ -21,6 +21,8 @@ file(GLOB HEADERS "*.h")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
add_dependencies(${EXECUTABLE} BuildInfo.h)
target_link_libraries(${EXECUTABLE} ethereum)
target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} lll)

54
libwhisper/BloomFilter.h

@ -35,17 +35,20 @@ public:
TopicBloomFilterBase() { init(); }
TopicBloomFilterBase(FixedHash<N> const& _h): FixedHash<N>(_h) { init(); }
void addBloom(dev::shh::AbridgedTopic const& _h) { addRaw(_h.template bloomPart<BitsPerBloom, N>()); }
void removeBloom(dev::shh::AbridgedTopic const& _h) { removeRaw(_h.template bloomPart<BitsPerBloom, N>()); }
bool containsBloom(dev::shh::AbridgedTopic const& _h) const { return this->contains(_h.template bloomPart<BitsPerBloom, N>()); }
void addBloom(AbridgedTopic const& _h) { addRaw(bloom(_h)); }
void removeBloom(AbridgedTopic const& _h) { removeRaw(bloom(_h)); }
bool containsBloom(AbridgedTopic const& _h) const { return this->contains(bloom(_h)); }
void addRaw(FixedHash<N> const& _h);
void removeRaw(FixedHash<N> const& _h);
bool containsRaw(FixedHash<N> const& _h) const { return this->contains(_h); }
static FixedHash<N> bloom(AbridgedTopic const& _h);
static void setBit(FixedHash<N>& _h, unsigned index);
static bool isBitSet(FixedHash<N> const& _h, unsigned _index);
private:
void init() { for (unsigned i = 0; i < CounterSize; ++i) m_refCounter[i] = 0; }
static bool isBitSet(FixedHash<N> const& _h, unsigned _index);
static const unsigned CounterSize = N * 8;
std::array<uint16_t, CounterSize> m_refCounter;
@ -83,12 +86,51 @@ void TopicBloomFilterBase<N>::removeRaw(FixedHash<N> const& _h)
template <unsigned N>
bool TopicBloomFilterBase<N>::isBitSet(FixedHash<N> const& _h, unsigned _index)
{
{
unsigned iByte = _index / 8;
unsigned iBit = _index % 8;
unsigned iBit = _index & 0x7;
return (_h[iByte] & c_powerOfTwoBitMmask[iBit]) != 0;
}
template <unsigned N>
void TopicBloomFilterBase<N>::setBit(FixedHash<N>& _h, unsigned _index)
{
unsigned iByte = _index / 8;
unsigned iBit = _index & 0x7;
_h[iByte] |= c_powerOfTwoBitMmask[iBit];
}
template <unsigned N>
FixedHash<N> TopicBloomFilterBase<N>::bloom(AbridgedTopic const& _h)
{
// The size of AbridgedTopic is 32 bits, and 27 of them participate in this algorithm.
// We need to review the algorithm if any of the following constants will be changed.
static_assert(4 == AbridgedTopic::size, "wrong template parameter in TopicBloomFilterBase<N>::bloom()");
static_assert(3 == BitsPerBloom, "wrong template parameter in TopicBloomFilterBase<N>::bloom()");
FixedHash<N> ret;
if (TopicBloomFilterSize == N)
for (unsigned i = 0; i < BitsPerBloom; ++i)
{
unsigned x = _h[i];
if (_h[BitsPerBloom] & c_powerOfTwoBitMmask[i])
x += 256;
setBit(ret, x);
}
else
for (unsigned i = 0; i < BitsPerBloom; ++i)
{
unsigned x = unsigned(_h[i]) + unsigned(_h[i + 1]);
x %= N * 8;
setBit(ret, x);
}
return ret;
}
using TopicBloomFilter = TopicBloomFilterBase<TopicBloomFilterSize>;
}

2
libwhisper/Common.cpp

@ -100,7 +100,7 @@ TopicBloomFilterHash TopicFilter::exportBloom() const
TopicBloomFilterHash ret;
for (TopicMask const& t: m_topicMasks)
for (auto const& i: t)
ret |= i.first.template bloomPart<BitsPerBloom, TopicBloomFilterSize>();
ret |= TopicBloomFilter::bloom(i.first);
return ret;
}

13
libwhisper/Message.cpp

@ -20,6 +20,7 @@
*/
#include "Message.h"
#include "BloomFilter.h"
using namespace std;
using namespace dev;
@ -161,31 +162,31 @@ unsigned Envelope::workProved() const
void Envelope::proveWork(unsigned _ms)
{
// PoW
h256 d[2];
d[0] = sha3(WithoutNonce);
uint32_t& n = *(uint32_t*)&(d[1][28]);
unsigned bestBitSet = 0;
bytesConstRef chuck(d[0].data(), 64);
chrono::high_resolution_clock::time_point then = chrono::high_resolution_clock::now() + chrono::milliseconds(_ms);
for (n = 0; chrono::high_resolution_clock::now() < then; )
while (chrono::high_resolution_clock::now() < then)
// do it rounds of 1024 for efficiency
for (unsigned i = 0; i < 1024; ++i, ++n)
for (unsigned i = 0; i < 1024; ++i)
{
auto fbs = dev::sha3(chuck).firstBitSet();
if (fbs > bestBitSet)
{
bestBitSet = fbs;
m_nonce = n;
m_nonce = (h256::Arith)d[1];
}
incrementHash(d[1]);
}
}
bool Envelope::matchesBloomFilter(TopicBloomFilterHash const& f) const
{
for (AbridgedTopic t: m_topic)
if (f.contains(t.template bloomPart<BitsPerBloom, TopicBloomFilterSize>()))
if (f.contains(TopicBloomFilter::bloom(t)))
return true;
return false;

1
libwhisper/Message.h

@ -81,6 +81,7 @@ public:
void proveWork(unsigned _ms);
bool matchesBloomFilter(TopicBloomFilterHash const& f) const;
static void incrementHash(h256& _h) { for (unsigned i = h256::size; i > 0 && !++_h[--i]; ) {} }
private:
Envelope(unsigned _exp, unsigned _ttl, AbridgedTopics const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {}

26
libwhisper/WhisperHost.cpp

@ -51,7 +51,8 @@ void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const
void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
{
// this function processes messages originated both by local host (_p == null), and by remote peers (_p != null)
// this function processes both outgoing messages originated both by local host (_p == null)
// and incoming messages from remote peers (_p != null)
cnote << this << ": inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data());
@ -68,18 +69,33 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
m_expiryQueue.insert(make_pair(_m.expiry(), h));
}
int rating = 1; // rating for local host is based upon: 1. installed watch; 2. proof of work
// rating of incoming message from remote host is assessed according to the following criteria:
// 1. installed watch match; 2. bloom filter match; 2. ttl; 3. proof of work
if (_p) // incoming message from remote peer
DEV_GUARDED(m_filterLock)
int rating = 0;
DEV_GUARDED(m_filterLock)
if (_m.matchesBloomFilter(m_bloom))
{
++rating;
for (auto const& f: m_filters)
if (f.second.filter.matches(_m))
for (auto& i: m_watches)
if (i.second.id == f.first)
{
i.second.changes.push_back(h);
rating += 10; // subject to review
rating += 2;
}
}
if (_p) // incoming message from remote peer
{
rating *= 256;
unsigned ttlReward = (256 > _m.ttl() ? 256 - _m.ttl() : 0);
rating += ttlReward;
rating *= 256;
rating += _m.workProved();
}
// TODO p2p: capability-based rating
for (auto i: peerSessions())

20
libwhisper/WhisperPeer.cpp

@ -121,17 +121,21 @@ unsigned WhisperPeer::ratingForPeer(Envelope const& e) const
{
// we try to estimate, how valuable this nessage will be for the remote peer,
// according to the following criteria:
// 1. bloom filter
// 2. proof of work
// 1. bloom filter
// 2. time to live
// 3. proof of work
static const unsigned BloomFilterMatchReward = 256; // vlad todo: move to common.h
unsigned rating = 0;
DEV_GUARDED(x_bloom)
if (e.matchesBloomFilter(m_bloom))
rating += BloomFilterMatchReward;
rating += e.sha3().firstBitSet();
if (e.matchesBloomFilter(bloom()))
++rating;
rating *= 256;
unsigned ttlReward = (256 > e.ttl() ? 256 - e.ttl() : 0);
rating += ttlReward;
rating *= 256;
rating += e.workProved();
return rating;
}

8
mix/DebuggingStateWrapper.h

@ -69,7 +69,7 @@ class QSolState: public QObject
public:
QSolState(QObject* _parent, QVariantMap&& _storage, QVariantList&& _callStack, QVariantMap&& _locals, int _start, int _end, QString _sourceName):
QObject(_parent), m_storage(_storage), m_callStack(_callStack), m_locals(_locals), m_start(_start), m_end(_end), m_sourceName(_sourceName)
QObject(_parent), m_storage(std::move(_storage)), m_callStack(std::move(_callStack)), m_locals(std::move(_locals)), m_start(_start), m_end(_end), m_sourceName(_sourceName)
{ }
private:
@ -92,7 +92,7 @@ class QCode: public QObject
Q_PROPERTY(QString documentId MEMBER m_document CONSTANT)
public:
QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(_instrunctions), m_address(_address) {}
QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(std::move(_instrunctions)), m_address(_address) {}
void setDocument(QString const& _documentId) { m_document = _documentId; }
private:
@ -110,7 +110,7 @@ class QCallData: public QObject
Q_PROPERTY(QVariantList items MEMBER m_items CONSTANT)
public:
QCallData(QObject* _owner, QVariantList&& _items): QObject(_owner), m_items(_items) {}
QCallData(QObject* _owner, QVariantList&& _items): QObject(_owner), m_items(std::move(_items)) {}
private:
QVariantList m_items;
@ -126,7 +126,7 @@ class QDebugData: public QObject
public:
QDebugData() { }
void setStates(QVariantList&& _states) { m_states = _states; }
void setStates(QVariantList&& _states) { m_states = std::move(_states); }
private:
QVariantList m_states;

2
mix/HttpServer.h

@ -46,7 +46,7 @@ class HttpRequest: public QObject
private:
HttpRequest(QObject* _parent, QUrl&& _url, QString&& _content, QVariantMap&& _headers):
QObject(_parent), m_url(_url), m_content(_content), m_headers(_headers)
QObject(_parent), m_url(std::move(_url)), m_content(std::move(_content)), m_headers(std::move(_headers))
{
}

28
mix/MixClient.cpp

@ -303,22 +303,14 @@ State MixClient::asOf(h256 const& _block) const
return ret;
}
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto)
Address MixClient::submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
TransactionSkeleton ts = _ts;
ts.nonce = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(ts, _secret);
executeTransaction(t, m_state, false, _gasAuto, _secret);
}
Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
executeTransaction(t, m_state, false, _gasAuto, _secret);
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address;
return _ts.creation ? right160(sha3(rlpList(ts.to, ts.nonce))) : Address();
}
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff)
@ -335,16 +327,6 @@ dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Add
return lastExecution().result;
}
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, false);
}
Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
{
return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false);
}
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff)
{
return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff);

6
mix/MixClient.h

@ -54,14 +54,12 @@ public:
ExecutionResult lastExecution() const;
ExecutionResult execution(unsigned _index) const;
void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override;
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override;
dev::eth::ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override;
using ClientBase::submitTransaction;
void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto);
Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto);
virtual Address submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret) override { return submitTransaction(_ts, _secret, false); }
Address submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto);
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict);
void setAddress(Address _us) override;

26
neth/main.cpp

@ -101,7 +101,7 @@ void help()
<< " -x,--peers <number> Attempt to connect to given number of peers (default: 5)." << endl
<< " -V,--version Show the version and exit." << endl
#if ETH_EVMJIT
<< " --jit Use EVM JIT (default: off)." << endl
<< " --vm <vm-kind> Select VM. Options are: interpreter, jit, smart. (default: interpreter)" << endl
#endif
;
exit(0);
@ -514,15 +514,23 @@ int main(int argc, char** argv)
return -1;
}
}
else if (arg == "--jit")
{
#if ETH_EVMJIT
jit = true;
#else
cerr << "EVM JIT not enabled" << endl;
return -1;
#endif
else if (arg == "--vm" && i + 1 < argc)
{
string vmKind = argv[++i];
if (vmKind == "interpreter")
VMFactory::setKind(VMKind::Interpreter);
else if (vmKind == "jit")
VMFactory::setKind(VMKind::JIT);
else if (vmKind == "smart")
VMFactory::setKind(VMKind::Smart);
else
{
cerr << "Unknown VM kind: " << vmKind << endl;
return -1;
}
}
#endif
else if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
@ -551,7 +559,7 @@ int main(int argc, char** argv)
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs,
&nodesState);
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;

2
solc/CMakeLists.txt

@ -13,6 +13,8 @@ set(EXECUTABLE solc)
file(GLOB HEADERS "*.h")
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
add_dependencies(${EXECUTABLE} BuildInfo.h)
target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_PROGRAM_OPTIONS_LIBRARIES})
target_link_libraries(${EXECUTABLE} solidity)

1
test/CMakeLists.txt

@ -72,6 +72,7 @@ add_executable(testeth ${SRC_LIST} ${HEADERS})
target_link_libraries(testeth ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(testeth ${CURL_LIBRARIES})
target_link_libraries(testeth ${CRYPTOPP_LIBRARIES})
target_link_libraries(testeth ethereum)
target_link_libraries(testeth ethcore)
if (NOT WIN32)

24
test/TestHelper.cpp

@ -199,10 +199,10 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptio
stateOptions.m_bHasCode = true;
}
if (code.size())
if (!code.empty())
{
_state.m_cache[address] = Account(balance, Account::ContractConception);
_state.m_cache[address].setCode(code);
_state.m_cache[address].setCode(std::move(code));
}
else
_state.m_cache[address] = Account(balance, Account::NormalCreation);
@ -235,7 +235,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state)
}
void ImportTest::importTransaction(json_spirit::mObject& _o)
{
{
if (_o.count("secretKey") > 0)
{
assert(_o.count("nonce") > 0);
@ -728,10 +728,20 @@ Options::Options()
for (auto i = 0; i < argc; ++i)
{
auto arg = std::string{argv[i]};
if (arg == "--jit")
eth::VMFactory::setKind(eth::VMKind::JIT);
else if (arg == "--vm=smart")
eth::VMFactory::setKind(eth::VMKind::Smart);
if (arg == "--vm" && i + 1 < argc)
{
string vmKind = argv[++i];
if (vmKind == "interpreter")
VMFactory::setKind(VMKind::Interpreter);
else if (vmKind == "jit")
VMFactory::setKind(VMKind::JIT);
else if (vmKind == "smart")
VMFactory::setKind(VMKind::Smart);
else
cerr << "Unknown VM kind: " << vmKind << endl;
}
else if (arg == "--jit") // TODO: Remove deprecated option "--jit"
VMFactory::setKind(VMKind::JIT);
else if (arg == "--vmtrace")
vmtrace = true;
else if (arg == "--filltests")

3
test/fuzzTesting/createRandomTest.cpp

@ -220,6 +220,7 @@ void parseTestWithTypes(std::string& _test)
options.setWeight(dev::eth::Instruction::STOP, 10); //default 50
options.setWeight(dev::eth::Instruction::SSTORE, 70);
options.setWeight(dev::eth::Instruction::CALL, 75);
options.setWeight(dev::eth::Instruction::CALLCODE, 55);
options.addAddress(dev::Address("0xffffffffffffffffffffffffffffffffffffffff"));
options.addAddress(dev::Address("0x1000000000000000000000000000000000000000"));
options.addAddress(dev::Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87"));
@ -230,7 +231,7 @@ void parseTestWithTypes(std::string& _test)
options.addAddress(dev::Address("0x0000000000000000000000000000000000000002"));
options.addAddress(dev::Address("0x0000000000000000000000000000000000000003"));
options.addAddress(dev::Address("0x0000000000000000000000000000000000000004"));
options.smartCodeProbability = 35;
options.smartCodeProbability = 60;
std::vector<std::string> types = getTypes();
for (unsigned i = 0; i < types.size(); i++)

136
test/fuzzTesting/fuzzHelper.cpp

@ -84,9 +84,17 @@ std::string RandomCode::generate(int _maxOpNumber, RandomCodeOptions _options)
}
}
else
{
if (info.name.find_first_of("PUSH") > 0)
code += toCompactHex(opcode);
code += fillArguments((dev::eth::Instruction) opcode, _options);
std::string byte = toCompactHex(opcode);
code += (byte == "") ? "00" : byte;
}
if (info.name.find_first_of("PUSH") <= 0)
{
std::string byte = toCompactHex(opcode);
code += (byte == "") ? "00" : byte;
}
}
return code;
}
@ -143,16 +151,136 @@ std::string RandomCode::fillArguments(dev::eth::Instruction _opcode, RandomCodeO
{
switch (_opcode)
{
case dev::eth::Instruction::PUSH1: code += rndByteSequence(1); break;
case dev::eth::Instruction::PUSH2: code += rndByteSequence(2); break;
case dev::eth::Instruction::PUSH3: code += rndByteSequence(3); break;
case dev::eth::Instruction::PUSH4: code += rndByteSequence(4); break;
case dev::eth::Instruction::PUSH5: code += rndByteSequence(5); break;
case dev::eth::Instruction::PUSH6: code += rndByteSequence(6); break;
case dev::eth::Instruction::PUSH7: code += rndByteSequence(7); break;
case dev::eth::Instruction::PUSH8: code += rndByteSequence(8); break;
case dev::eth::Instruction::PUSH9: code += rndByteSequence(9); break;
case dev::eth::Instruction::PUSH10: code += rndByteSequence(10); break;
case dev::eth::Instruction::PUSH11: code += rndByteSequence(11); break;
case dev::eth::Instruction::PUSH12: code += rndByteSequence(12); break;
case dev::eth::Instruction::PUSH13: code += rndByteSequence(13); break;
case dev::eth::Instruction::PUSH14: code += rndByteSequence(14); break;
case dev::eth::Instruction::PUSH15: code += rndByteSequence(15); break;
case dev::eth::Instruction::PUSH16: code += rndByteSequence(16); break;
case dev::eth::Instruction::PUSH17: code += rndByteSequence(17); break;
case dev::eth::Instruction::PUSH18: code += rndByteSequence(18); break;
case dev::eth::Instruction::PUSH19: code += rndByteSequence(19); break;
case dev::eth::Instruction::PUSH20: code += rndByteSequence(20); break;
case dev::eth::Instruction::PUSH21: code += rndByteSequence(21); break;
case dev::eth::Instruction::PUSH22: code += rndByteSequence(22); break;
case dev::eth::Instruction::PUSH23: code += rndByteSequence(23); break;
case dev::eth::Instruction::PUSH24: code += rndByteSequence(24); break;
case dev::eth::Instruction::PUSH25: code += rndByteSequence(25); break;
case dev::eth::Instruction::PUSH26: code += rndByteSequence(26); break;
case dev::eth::Instruction::PUSH27: code += rndByteSequence(27); break;
case dev::eth::Instruction::PUSH28: code += rndByteSequence(28); break;
case dev::eth::Instruction::PUSH29: code += rndByteSequence(29); break;
case dev::eth::Instruction::PUSH30: code += rndByteSequence(30); break;
case dev::eth::Instruction::PUSH31: code += rndByteSequence(31); break;
case dev::eth::Instruction::PUSH32: code += rndByteSequence(32); break;
case dev::eth::Instruction::SWAP1:
case dev::eth::Instruction::SWAP2:
case dev::eth::Instruction::SWAP3:
case dev::eth::Instruction::SWAP4:
case dev::eth::Instruction::SWAP5:
case dev::eth::Instruction::SWAP6:
case dev::eth::Instruction::SWAP7:
case dev::eth::Instruction::SWAP8:
case dev::eth::Instruction::SWAP9:
case dev::eth::Instruction::SWAP10:
case dev::eth::Instruction::SWAP11:
case dev::eth::Instruction::SWAP12:
case dev::eth::Instruction::SWAP13:
case dev::eth::Instruction::SWAP14:
case dev::eth::Instruction::SWAP15:
case dev::eth::Instruction::SWAP16:
case dev::eth::Instruction::DUP1:
case dev::eth::Instruction::DUP2:
case dev::eth::Instruction::DUP3:
case dev::eth::Instruction::DUP4:
case dev::eth::Instruction::DUP5:
case dev::eth::Instruction::DUP6:
case dev::eth::Instruction::DUP7:
case dev::eth::Instruction::DUP8:
case dev::eth::Instruction::DUP9:
case dev::eth::Instruction::DUP10:
case dev::eth::Instruction::DUP11:
case dev::eth::Instruction::DUP12:
case dev::eth::Instruction::DUP13:
case dev::eth::Instruction::DUP14:
case dev::eth::Instruction::DUP15:
case dev::eth::Instruction::DUP16:
int times;
switch (_opcode)
{
case dev::eth::Instruction::DUP1: times = 1; break;
case dev::eth::Instruction::SWAP1:
case dev::eth::Instruction::DUP2: times = 2; break;
case dev::eth::Instruction::SWAP2:
case dev::eth::Instruction::DUP3: times = 3; break;
case dev::eth::Instruction::SWAP3:
case dev::eth::Instruction::DUP4: times = 4; break;
case dev::eth::Instruction::SWAP4:
case dev::eth::Instruction::DUP5: times = 5; break;
case dev::eth::Instruction::SWAP5:
case dev::eth::Instruction::DUP6: times = 6; break;
case dev::eth::Instruction::SWAP6:
case dev::eth::Instruction::DUP7: times = 7; break;
case dev::eth::Instruction::SWAP7:
case dev::eth::Instruction::DUP8: times = 8; break;
case dev::eth::Instruction::SWAP8:
case dev::eth::Instruction::DUP9: times = 9; break;
case dev::eth::Instruction::SWAP9:
case dev::eth::Instruction::DUP10: times = 10; break;
case dev::eth::Instruction::SWAP10:
case dev::eth::Instruction::DUP11: times = 11; break;
case dev::eth::Instruction::SWAP11:
case dev::eth::Instruction::DUP12: times = 12; break;
case dev::eth::Instruction::SWAP12:
case dev::eth::Instruction::DUP13: times = 13; break;
case dev::eth::Instruction::SWAP13:
case dev::eth::Instruction::DUP14: times = 14; break;
case dev::eth::Instruction::SWAP14:
case dev::eth::Instruction::DUP15: times = 15; break;
case dev::eth::Instruction::SWAP15:
case dev::eth::Instruction::DUP16: times = 16; break;
case dev::eth::Instruction::SWAP16: times = 17; break;
default: times = 1;
}
for (int i = 0; i < times; i ++)
code += getPushCode(randUniIntGen() % 32);
break;
case dev::eth::Instruction::CREATE:
//(CREATE value mem1 mem2)
code += getPushCode(randUniIntGen() % 128); //memlen1
code += getPushCode(randUniIntGen() % 32); //memlen1
code += getPushCode(randUniIntGen()); //value
break;
case dev::eth::Instruction::CALL:
case dev::eth::Instruction::CALLCODE:
//(CALL gaslimit address value memstart1 memlen1 memstart2 memlen2)
code += getPushCode(randUniIntGen() % 32); //memlen2
//(CALLCODE gaslimit address value memstart1 memlen1 memstart2 memlen2)
code += getPushCode(randUniIntGen() % 128); //memlen2
code += getPushCode(randUniIntGen() % 32); //memstart2
code += getPushCode(randUniIntGen() % 32); //memlen1
code += getPushCode(randUniIntGen() % 128); //memlen1
code += getPushCode(randUniIntGen() % 32); //memlen1
code += getPushCode(randUniIntGen()); //value
code += getPushCode(toString(_options.getRandomAddress()));//address
code += getPushCode(randUniIntGen()); //gaslimit
break;
case dev::eth::Instruction::SUICIDE: //(SUICIDE address)
code += getPushCode(toString(_options.getRandomAddress()));
break;
case dev::eth::Instruction::RETURN: //(RETURN memlen1 memlen2)
code += getPushCode(randUniIntGen() % 128); //memlen1
code += getPushCode(randUniIntGen() % 32); //memlen1
break;
default:
smart = false;
}

106
test/libdevcore/FixedHash.cpp

@ -0,0 +1,106 @@
/*
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 FixedHash.cpp
* @author Lefterus <lefteris@ethdev.com>
* @date 2015
*/
#include <libdevcore/FixedHash.h>
#include "../TestHelper.h"
using namespace std;
using namespace dev;
namespace dev
{
namespace test
{
BOOST_AUTO_TEST_SUITE(FixedHashTests)
BOOST_AUTO_TEST_CASE(FixedHashComparisons)
{
FixedHash<4> h1(sha3("abcd"));
FixedHash<4> h2(sha3("abcd"));
FixedHash<4> h3(sha3("aadd"));
FixedHash<4> h4(0xBAADF00D);
FixedHash<4> h5(0xAAAAAAAA);
FixedHash<4> h6(0xBAADF00D);
BOOST_CHECK(h1 == h2);
BOOST_CHECK(h2 != h3);
BOOST_CHECK(h4 > h5);
BOOST_CHECK(h5 < h4);
BOOST_CHECK(h6 <= h4);
BOOST_CHECK(h6 >= h4);
}
BOOST_AUTO_TEST_CASE(FixedHashXOR)
{
FixedHash<2> h1("0xAAAA");
FixedHash<2> h2("0xBBBB");
BOOST_CHECK((h1 ^ h2) == FixedHash<2>("0x1111"));
h1 ^= h2;
BOOST_CHECK(h1 == FixedHash<2>("0x1111"));
}
BOOST_AUTO_TEST_CASE(FixedHashOR)
{
FixedHash<4> h1("0xD3ADB33F");
FixedHash<4> h2("0xBAADF00D");
FixedHash<4> res("0xFBADF33F");
BOOST_CHECK((h1 | h2) == res);
h1 |= h2;
BOOST_CHECK(h1 == res);
}
BOOST_AUTO_TEST_CASE(FixedHashAND)
{
FixedHash<4> h1("0xD3ADB33F");
FixedHash<4> h2("0xBAADF00D");
FixedHash<4> h3("0x92aDB00D");
BOOST_CHECK((h1 & h2) == h3);
h1 &= h2;
BOOST_CHECK(h1 = h3);
}
BOOST_AUTO_TEST_CASE(FixedHashInvert)
{
FixedHash<4> h1("0xD3ADB33F");
FixedHash<4> h2("0x2C524CC0");
BOOST_CHECK(~h1 == h2);
}
BOOST_AUTO_TEST_CASE(FixedHashContains)
{
FixedHash<4> h1("0xD3ADB331");
FixedHash<4> h2("0x0000B331");
FixedHash<4> h3("0x0000000C");
BOOST_CHECK(h1.contains(h2));
BOOST_CHECK(!h1.contains(h3));
}
BOOST_AUTO_TEST_SUITE_END()
}
}

1
test/libdevcrypto/crypto.cpp

@ -69,7 +69,6 @@ BOOST_AUTO_TEST_CASE(emptySHA3Types)
#if ETH_HAVE_SECP256K1
BOOST_AUTO_TEST_CASE(secp256k1lib)
{
secp256k1Init();
KeyPair k = KeyPair::create();
BOOST_REQUIRE(!!k.sec());
BOOST_REQUIRE(!!k.pub());

854
test/libethereum/StateTestsFiller/stPreCompiledContractsTransactionFiller.json

File diff suppressed because one or more lines are too long

43
test/libethereum/StateTestsFiller/stSpecialTestFiller.json

@ -488,5 +488,48 @@
"to" : "b03f030056db7d467d778326658bac0d1b35d8f7",
"value" : "0"
}
},
"BadStateRootTx" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "45678256",
"currentGasLimit" : "(2**256)-1",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000"
}
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "2000000",
"code" : "",
"nonce" : "0",
"storage" : {
}
},
"1baf27b88c48dd02b744999cf3522766929d2b2a" : {
"balance" : "1000",
"code" : "0x600073a94f5374fce5edbc8e2a8697c15331677e6ebf0b3314156023573540602035145b15602e576040356000555b",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" :
{
"data" : "0x00000000000000000000000000000000000000000000000000000000000bc03712fac13c68425054e372b0861af05648614d69d32800fba9ad4522238d4b937a0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit" : "200000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "1baf27b88c48dd02b744999cf3522766929d2b2a",
"value" : "0"
}
}
}

5
test/libethereum/blockchain.cpp

@ -24,6 +24,7 @@
#include <libdevcore/FileSystem.h>
#include <libdevcore/TransientDirectory.h>
#include <libethereum/CanonBlockChain.h>
#include <libethereum/TransactionQueue.h>
#include <test/TestHelper.h>
using namespace std;
@ -200,8 +201,8 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
//get valid transactions
Transactions txList;
for (auto const& txi: txs.transactions())
txList.push_back(txi.second);
for (auto const& txi: txs.topTransactions(std::numeric_limits<unsigned>::max()))
txList.push_back(txi);
blObj["transactions"] = writeTransactionsToJson(txList);
BlockInfo current_BlockHeader = state.info();

5
test/libethereum/state.cpp

@ -129,11 +129,6 @@ BOOST_AUTO_TEST_CASE(stPreCompiledContracts)
dev::test::executeTests("stPreCompiledContracts", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stPreCompiledContractsTransaction)
{
dev::test::executeTests("stPreCompiledContractsTransaction", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stLogTests)
{
dev::test::executeTests("stLogTests", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests);

108
test/libethereum/transactionqueue.cpp

@ -61,4 +61,112 @@ BOOST_AUTO_TEST_CASE(maxNonce)
}
BOOST_AUTO_TEST_CASE(priority)
{
dev::eth::TransactionQueue txq;
const u256 gasCostCheap = 10 * szabo;
const u256 gasCostMed = 20 * szabo;
const u256 gasCostHigh = 30 * szabo;
const u256 gas = 25000;
Address dest = Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87");
Secret sender1 = Secret("0x3333333333333333333333333333333333333333333333333333333333333333");
Secret sender2 = Secret("0x4444444444444444444444444444444444444444444444444444444444444444");
Transaction tx0(0, gasCostCheap, gas, dest, bytes(), 0, sender1 );
Transaction tx0_1(1, gasCostMed, gas, dest, bytes(), 0, sender1 );
Transaction tx1(0, gasCostCheap, gas, dest, bytes(), 1, sender1 );
Transaction tx2(0, gasCostHigh, gas, dest, bytes(), 0, sender2 );
Transaction tx3(0, gasCostCheap + 1, gas, dest, bytes(), 1, sender2 );
Transaction tx4(0, gasCostHigh, gas, dest, bytes(), 2, sender1 );
Transaction tx5(0, gasCostMed, gas, dest, bytes(), 2, sender2 );
txq.import(tx0);
BOOST_CHECK(Transactions { tx0 } == txq.topTransactions(256));
txq.import(tx0);
BOOST_CHECK(Transactions { tx0 } == txq.topTransactions(256));
txq.import(tx0_1);
BOOST_CHECK(Transactions { tx0_1 } == txq.topTransactions(256));
txq.import(tx1);
BOOST_CHECK((Transactions { tx0_1, tx1 }) == txq.topTransactions(256));
txq.import(tx2);
BOOST_CHECK((Transactions { tx2, tx0_1, tx1 }) == txq.topTransactions(256));
txq.import(tx3);
BOOST_CHECK((Transactions { tx2, tx0_1, tx1, tx3 }) == txq.topTransactions(256));
txq.import(tx4);
BOOST_CHECK((Transactions { tx2, tx0_1, tx1, tx3, tx4 }) == txq.topTransactions(256));
txq.import(tx5);
BOOST_CHECK((Transactions { tx2, tx0_1, tx1, tx3, tx5, tx4 }) == txq.topTransactions(256));
txq.drop(tx0_1.sha3());
BOOST_CHECK((Transactions { tx2, tx1, tx3, tx5, tx4 }) == txq.topTransactions(256));
txq.drop(tx1.sha3());
BOOST_CHECK((Transactions { tx2, tx3, tx5, tx4 }) == txq.topTransactions(256));
txq.drop(tx5.sha3());
BOOST_CHECK((Transactions { tx2, tx3, tx4 }) == txq.topTransactions(256));
Transaction tx6(0, gasCostMed, gas, dest, bytes(), 20, sender1 );
txq.import(tx6);
BOOST_CHECK((Transactions { tx2, tx3, tx4, tx6 }) == txq.topTransactions(256));
Transaction tx7(0, gasCostMed, gas, dest, bytes(), 2, sender2 );
txq.import(tx7);
BOOST_CHECK((Transactions { tx2, tx3, tx4, tx6, tx7 }) == txq.topTransactions(256));
}
BOOST_AUTO_TEST_CASE(future)
{
dev::eth::TransactionQueue txq;
// from a94f5374fce5edbc8e2a8697c15331677e6ebf0b
const u256 gasCostMed = 20 * szabo;
const u256 gas = 25000;
Address dest = Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87");
Secret sender = Secret("0x3333333333333333333333333333333333333333333333333333333333333333");
Transaction tx0(0, gasCostMed, gas, dest, bytes(), 0, sender );
Transaction tx1(0, gasCostMed, gas, dest, bytes(), 1, sender );
Transaction tx2(0, gasCostMed, gas, dest, bytes(), 2, sender );
Transaction tx3(0, gasCostMed, gas, dest, bytes(), 3, sender );
Transaction tx4(0, gasCostMed, gas, dest, bytes(), 4, sender );
txq.import(tx0);
txq.import(tx1);
txq.import(tx2);
txq.import(tx3);
txq.import(tx4);
BOOST_CHECK((Transactions { tx0, tx1, tx2, tx3, tx4 }) == txq.topTransactions(256));
txq.setFuture(tx2.sha3());
BOOST_CHECK((Transactions { tx0, tx1 }) == txq.topTransactions(256));
Transaction tx2_2(1, gasCostMed, gas, dest, bytes(), 2, sender );
txq.import(tx2_2);
BOOST_CHECK((Transactions { tx0, tx1, tx2_2, tx3, tx4 }) == txq.topTransactions(256));
}
BOOST_AUTO_TEST_CASE(lmits)
{
dev::eth::TransactionQueue txq(3, 3);
const u256 gasCostMed = 20 * szabo;
const u256 gas = 25000;
Address dest = Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87");
Secret sender = Secret("0x3333333333333333333333333333333333333333333333333333333333333333");
Secret sender2 = Secret("0x4444444444444444444444444444444444444444444444444444444444444444");
Transaction tx0(0, gasCostMed, gas, dest, bytes(), 0, sender );
Transaction tx1(0, gasCostMed, gas, dest, bytes(), 1, sender );
Transaction tx2(0, gasCostMed, gas, dest, bytes(), 2, sender );
Transaction tx3(0, gasCostMed, gas, dest, bytes(), 3, sender );
Transaction tx4(0, gasCostMed, gas, dest, bytes(), 4, sender );
Transaction tx5(0, gasCostMed + 1, gas, dest, bytes(), 0, sender2 );
txq.import(tx0);
txq.import(tx1);
txq.import(tx2);
txq.import(tx3);
txq.import(tx4);
txq.import(tx5);
BOOST_CHECK((Transactions { tx5, tx0, tx1 }) == txq.topTransactions(256));
}
BOOST_AUTO_TEST_SUITE_END()

2
test/libp2p/peer.cpp

@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(host)
auto node2 = host2.id();
host2.start();
while (!host2.isStarted())
while (!host2.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(20));
host1.addNode(node2, NodeIPEndpoint(bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort));

1
test/libp2p/rlpx.cpp

@ -21,7 +21,6 @@
*/
#include <random>
#include <secp256k1/secp256k1.h>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/Log.h>

248
test/libsolidity/SolidityWallet.cpp

@ -47,12 +47,18 @@ static char const* walletCode = R"DELIMITER(
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
// interior is executed.
contract multiowned {
// TYPES
// struct for the status of a pending operation.
struct PendingState {
uint yetNeeded;
uint ownersDone;
uint index;
}
// EVENTS
// this contract only has five types of events: it can accept a confirmation, in which case
// we record owner and operation (hash) alongside it.
event Confirmation(address owner, bytes32 operation);
@ -63,14 +69,9 @@ contract multiowned {
event OwnerRemoved(address oldOwner);
// the last one is emitted if the required signatures change
event RequirementChanged(uint newRequirement);
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them.
function multiowned() {
m_required = 1;
m_numOwners = 1;
m_owners[m_numOwners] = uint(msg.sender);
m_ownerIndex[uint(msg.sender)] = m_numOwners;
}
// MODIFIERS
// simple single-sig function modifier.
modifier onlyowner {
if (isOwner(msg.sender))
@ -80,9 +81,21 @@ contract multiowned {
// that later attempts can be realised as the same underlying operation and
// thus count as confirmations.
modifier onlymanyowners(bytes32 _operation) {
if (confirmed(_operation))
if (confirmAndCheck(_operation))
_
}
// METHODS
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them.
function multiowned() {
m_required = 1;
m_numOwners = 1;
m_owners[m_numOwners] = uint(msg.sender);
m_ownerIndex[uint(msg.sender)] = m_numOwners;
}
// Revokes a prior confirmation of the given operation
function revoke(bytes32 _operation) external {
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
@ -96,7 +109,75 @@ contract multiowned {
Revoke(msg.sender, _operation);
}
}
function confirmed(bytes32 _operation) internal returns (bool) {
// Replaces an owner `_from` with another `_to`.
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data, block.number)) external {
if (isOwner(_to)) return;
uint ownerIndex = m_ownerIndex[uint(_from)];
if (ownerIndex == 0) return;
clearPending();
m_owners[ownerIndex] = uint(_to);
m_ownerIndex[uint(_from)] = 0;
m_ownerIndex[uint(_to)] = ownerIndex;
OwnerChanged(_from, _to);
}
function addOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external {
if (isOwner(_owner)) return;
clearPending();
if (m_numOwners >= c_maxOwners)
reorganizeOwners();
if (m_numOwners >= c_maxOwners)
return;
m_numOwners++;
m_owners[m_numOwners] = uint(_owner);
m_ownerIndex[uint(_owner)] = m_numOwners;
OwnerAdded(_owner);
}
function removeOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external {
uint ownerIndex = m_ownerIndex[uint(_owner)];
if (ownerIndex == 0) return;
if (m_required > m_numOwners - 1) return;
m_owners[ownerIndex] = 0;
m_ownerIndex[uint(_owner)] = 0;
clearPending();
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
OwnerRemoved(_owner);
}
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data, block.number)) external {
if (_newRequired > m_numOwners) return;
m_required = _newRequired;
clearPending();
RequirementChanged(_newRequired);
}
function isOwner(address _addr) returns (bool) {
return m_ownerIndex[uint(_addr)] > 0;
}
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
var pending = m_pending[_operation];
uint ownerIndex = m_ownerIndex[uint(_owner)];
// make sure they're an owner
if (ownerIndex == 0) return false;
// determine the bit to set for this owner.
uint ownerIndexBit = 2**ownerIndex;
if (pending.ownersDone & ownerIndexBit == 0) {
return false;
} else {
return true;
}
}
// INTERNAL METHODS
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
// determine what index the present sender is:
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
// make sure they're an owner
@ -132,42 +213,7 @@ contract multiowned {
}
}
}
// Replaces an owner `_from` with another `_to`.
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
if (isOwner(_to)) return;
uint ownerIndex = m_ownerIndex[uint(_from)];
if (ownerIndex == 0) return;
clearPending();
m_owners[ownerIndex] = uint(_to);
m_ownerIndex[uint(_from)] = 0;
m_ownerIndex[uint(_to)] = ownerIndex;
OwnerChanged(_from, _to);
}
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
if (isOwner(_owner)) return;
clearPending();
if (m_numOwners >= c_maxOwners)
reorganizeOwners();
if (m_numOwners >= c_maxOwners)
return;
m_numOwners++;
m_owners[m_numOwners] = uint(_owner);
m_ownerIndex[uint(_owner)] = m_numOwners;
OwnerAdded(_owner);
}
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
uint ownerIndex = m_ownerIndex[uint(_owner)];
if (ownerIndex == 0) return;
if (m_required > m_numOwners - 1) return;
m_owners[ownerIndex] = 0;
m_ownerIndex[uint(_owner)] = 0;
clearPending();
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
OwnerRemoved(_owner);
}
function reorganizeOwners() private returns (bool) {
uint free = 1;
while (free < m_numOwners)
@ -182,6 +228,7 @@ contract multiowned {
}
}
}
function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i)
@ -189,46 +236,54 @@ contract multiowned {
delete m_pending[m_pendingIndex[i]];
delete m_pendingIndex;
}
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
if (_newRequired > m_numOwners) return;
m_required = _newRequired;
clearPending();
RequirementChanged(_newRequired);
}
function isOwner(address _addr) returns (bool) {
return m_ownerIndex[uint(_addr)] > 0;
}
// FIELDS
// the number of owners that must confirm the same operation before it is run.
uint public m_required;
// pointer used to find a free slot in m_owners
uint public m_numOwners;
// list of owners
uint[256] public m_owners;
uint[256] m_owners;
uint constant c_maxOwners = 250;
// index on the list of owners to allow reverse lookup
mapping(uint => uint) public m_ownerIndex;
mapping(uint => uint) m_ownerIndex;
// the ongoing operations.
mapping(bytes32 => PendingState) public m_pending;
bytes32[] public m_pendingIndex;
mapping(bytes32 => PendingState) m_pending;
bytes32[] m_pendingIndex;
}
// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
// on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method
// uses is specified in the modifier.
contract daylimit is multiowned {
// MODIFIERS
// simple modifier for daily limit.
modifier limitedDaily(uint _value) {
if (underLimit(_value))
_
}
// METHODS
// constructor - just records the present day's index.
function daylimit() {
m_lastDay = today();
}
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data, block.number)) external {
m_dailyLimit = _newLimit;
}
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
function resetSpentToday() onlymanyowners(sha3(msg.data, block.number)) external {
m_spentToday = 0;
}
// INTERNAL METHODS
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
// returns true. otherwise just returns false.
function underLimit(uint _value) internal onlyowner returns (bool) {
@ -244,60 +299,76 @@ contract daylimit is multiowned {
}
return false;
}
// simple modifier for daily limit.
modifier limitedDaily(uint _value) {
if (underLimit(_value))
_
}
// determines today's index.
function today() private constant returns (uint) { return now / 1 days; }
uint public m_spentToday;
// FIELDS
uint public m_dailyLimit;
uint public m_lastDay;
uint m_spentToday;
uint m_lastDay;
}
// interface contract for multisig proxy contracts; see below for docs.
contract multisig {
// EVENTS
// logged events:
// Funds has arrived into the wallet (record how much).
event Deposit(address from, uint value);
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
event SingleTransact(address owner, uint value, address to, bytes data);
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
// Confirmation still needed for a transaction.
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
// FUNCTIONS
// TODO: document
function changeOwner(address _from, address _to) external;
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
function confirm(bytes32 _h) returns (bool);
}
// usage:
// bytes32 h = Wallet(w).from(oneOwner).transact(to, value, data);
// Wallet(w).from(anotherOwner).confirm(h);
contract Wallet is multisig, multiowned, daylimit {
// TYPES
// Transaction structure to remember details of transaction lest it need be saved for a later call.
struct Transaction {
address to;
uint value;
bytes data;
}
/*
// logged events:
// Funds has arrived into the wallet (record how much).
event Deposit(address from, uint value);
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
event SingleTransact(address owner, uint value, address to, bytes data);
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);*/
// EVENTS
event Created(bytes32 indexed identifier);
// METHODS
// constructor - just pass on the owner arra to the multiowned.
event Created();
function Wallet() {
Created();
function Wallet(bytes32 identifier) {
Created(identifier);
}
// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
function kill(address _to) onlymanyowners(sha3(msg.data, block.number)) external {
suicide(_to);
}
// gets called when no other function matches
function() {
// just being sent some cash?
if (msg.value > 0)
Deposit(msg.sender, msg.value);
}
// Outside-visible transact entry point. Executes transacion immediately if below daily spend limit.
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
@ -311,7 +382,7 @@ contract Wallet is multisig, multiowned, daylimit {
return 0;
}
// determine our operation hash.
_r = sha3(msg.data);
_r = sha3(msg.data, block.number);
if (!confirm(_r) && m_txs[_r].to == 0) {
m_txs[_r].to = _to;
m_txs[_r].value = _value;
@ -319,6 +390,7 @@ contract Wallet is multisig, multiowned, daylimit {
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
}
}
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
// to determine the body of the transaction from the hash provided.
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
@ -329,20 +401,20 @@ contract Wallet is multisig, multiowned, daylimit {
return true;
}
}
// INTERNAL METHODS
function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i)
delete m_txs[m_pendingIndex[i]];
super.clearPending();
}
// // internally confirm transaction with all of the info. returns true iff confirmed good and executed.
// function confirmVerbose(bytes32 _h, address _to, uint _value, bytes _data) private onlymanyowners(_h) returns (bool) {
// _to.call.value(_value)(_data);
// MultiTransact("out", msg.sender, _h, _value, _to);
// return true;
// }
// FIELDS
// pending transactions we have at present.
mapping (bytes32 => Transaction) public m_txs;
mapping (bytes32 => Transaction) m_txs;
}
)DELIMITER";
@ -443,7 +515,7 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer)
// 4 owners, set required to 3
BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
// check that balance is and stays zero at destination address
h256 opHash("f916231db11c12e0142dc51f23632bc655de87c63f83fc928c443e90f7aa364a");
h256 opHash("8f27f478ebcfaf28b0c354f4809ace8087000d668b89c8bc3b1b608bfdbe6654");
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
m_sender = Address(0x12);
BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));

23
test/libwhisper/bloomFilter.cpp

@ -244,7 +244,7 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw)
BOOST_REQUIRE(!f.contains(b00110111));
}
static const unsigned DistributionTestSize = 8;
static const unsigned DistributionTestSize = TopicBloomFilterSize;
static const unsigned TestArrSize = 8 * DistributionTestSize;
void updateDistribution(FixedHash<DistributionTestSize> const& _h, array<unsigned, TestArrSize>& _distribution)
@ -271,10 +271,10 @@ BOOST_AUTO_TEST_CASE(distributionRate)
Topic x(0xC0FFEE); // deterministic pseudorandom value
for (unsigned i = 0; i < 22000; ++i)
for (unsigned i = 0; i < 26000; ++i)
{
x = sha3(x);
FixedHash<DistributionTestSize> h = x.template bloomPart<BitsPerBloom, DistributionTestSize>();
FixedHash<DistributionTestSize> h = TopicBloomFilter::bloom(abridge(x));
updateDistribution(h, distribution);
}
@ -283,16 +283,25 @@ BOOST_AUTO_TEST_CASE(distributionRate)
average += distribution[i];
average /= TestArrSize;
unsigned deviation = average / 10; // approx. 10%
unsigned deviation = average / 3;
unsigned maxAllowed = average + deviation;
unsigned minAllowed = average - deviation;
unsigned maximum = 0;
unsigned minimum = 0xFFFFFFFF;
for (unsigned i = 0; i < TestArrSize; ++i)
{
//cnote << i << ":" << distribution[i];
BOOST_REQUIRE(distribution[i] > minAllowed);
BOOST_REQUIRE(distribution[i] < maxAllowed);
unsigned const& z = distribution[i];
if (z > maximum)
maximum = z;
else if (z < minimum)
minimum = z;
}
cnote << minimum << average << maximum;
BOOST_REQUIRE(minimum > minAllowed);
BOOST_REQUIRE(maximum < maxAllowed);
}
BOOST_AUTO_TEST_SUITE_END()

20
test/libwhisper/whisperMessage.cpp

@ -89,4 +89,24 @@ BOOST_AUTO_TEST_CASE(seal)
sealAndOpenSingleMessage(i);
}
BOOST_AUTO_TEST_CASE(work)
{
VerbosityHolder setTemporaryLevel(10);
cnote << "Testing proof of work...";
Secret zero;
unsigned r = 0xC0DEFEED;
for (int i = 0; i < 20; ++i)
{
Topics topics = createRandomTopics(++r);
bytes const payload = createRandomPayload(++r);
Message m(payload);
Envelope e = m.seal(zero, topics, 1, 50);
unsigned x = e.workProved();
//cnote << x;
BOOST_REQUIRE(x > 4);
}
}
BOOST_AUTO_TEST_SUITE_END()

36
test/libwhisper/whisperTopic.cpp

@ -78,7 +78,6 @@ BOOST_AUTO_TEST_CASE(topic)
}
this_thread::sleep_for(chrono::milliseconds(50));
}
});
Host host2("Test", NetworkPreferences("127.0.0.1", 30300, false));
@ -384,4 +383,39 @@ BOOST_AUTO_TEST_CASE(topicAdvertising)
whost2->uninstallWatch(w2);
}
BOOST_AUTO_TEST_CASE(selfAddressed)
{
VerbosityHolder setTemporaryLevel(10);
cnote << "Testing self-addressed messaging with bloom filter matching...";
char const* text = "deterministic pseudorandom test";
BuildTopicMask mask(text);
Host host("first", NetworkPreferences("127.0.0.1", 30305, false));
auto wh = host.registerCapability(new WhisperHost());
auto watch = wh->installWatch(BuildTopicMask(text));
unsigned const sample = 0xFEED;
KeyPair us = KeyPair::create();
wh->post(us.sec(), RLPStream().append(sample).out(), BuildTopic(text));
TopicBloomFilterHash f = wh->bloom();
Envelope e = Message(RLPStream().append(sample).out()).seal(us.sec(), BuildTopic(text), 50, 50);
bool ok = e.matchesBloomFilter(f);
BOOST_REQUIRE(ok);
this_thread::sleep_for(chrono::milliseconds(50));
unsigned single = 0;
unsigned result = 0;
for (auto j: wh->checkWatch(watch))
{
Message msg = wh->envelope(j).open(wh->fullTopics(watch));
single = RLP(msg.payload()).toInt<unsigned>();
result += single;
}
BOOST_REQUIRE_EQUAL(sample, result);
}
BOOST_AUTO_TEST_SUITE_END()

Loading…
Cancel
Save