Browse Source

Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into optional_readline

cl-refactor
unknown 10 years ago
parent
commit
67811e219a
  1. 1
      CMakeLists.txt
  2. 3
      CodingStandards.txt
  3. 42
      alethzero/Main.ui
  4. 56
      alethzero/MainWin.cpp
  5. 9
      alethzero/MainWin.h
  6. 26
      eth/main.cpp
  7. 22
      ethvm/main.cpp
  8. 7
      evmjit/include/evmjit/JIT.h
  9. 92
      evmjit/libevmjit/JIT.cpp
  10. 82
      libdevcore/RLP.cpp
  11. 66
      libdevcore/RLP.h
  12. 24
      libdevcrypto/Common.cpp
  13. 2
      libdevcrypto/Common.h
  14. 2
      libdevcrypto/CryptoPP.cpp
  15. 492
      libethereum/BlockChainSync.cpp
  16. 47
      libethereum/BlockChainSync.h
  17. 9
      libethereum/BlockQueue.cpp
  18. 1
      libethereum/CommonNet.h
  19. 59
      libethereum/EthereumHost.cpp
  20. 2
      libethereum/EthereumHost.h
  21. 36
      libethereum/EthereumPeer.cpp
  22. 4
      libethereum/EthereumPeer.h
  23. 63
      libethereum/State.cpp
  24. 2
      libethereum/Transaction.cpp
  25. 215
      libethereum/TransactionQueue.cpp
  26. 55
      libethereum/TransactionQueue.h
  27. 13
      libethereum/Utility.cpp
  28. 2
      libethereum/VerifiedBlock.h
  29. 82
      libevm/SmartVM.cpp
  30. 2
      libp2p/Common.h
  31. 4
      libp2p/RLPXSocket.h
  32. 3
      libp2p/Session.cpp
  33. 162
      libsolidity/AST.cpp
  34. 6
      libsolidity/AST.h
  35. 55
      libsolidity/CompilerUtils.cpp
  36. 79
      libsolidity/ExpressionCompiler.cpp
  37. 74
      libsolidity/LValue.cpp
  38. 2
      libsolidity/LValue.h
  39. 59
      libsolidity/Types.cpp
  40. 9
      libsolidity/Types.h
  41. 2
      libwebthree/CMakeLists.txt
  42. 54
      libwhisper/BloomFilter.h
  43. 2
      libwhisper/Common.cpp
  44. 13
      libwhisper/Message.cpp
  45. 1
      libwhisper/Message.h
  46. 26
      libwhisper/WhisperHost.cpp
  47. 20
      libwhisper/WhisperPeer.cpp
  48. 42
      mix/ClientModel.cpp
  49. 14
      mix/ClientModel.h
  50. 2
      mix/CodeModel.cpp
  51. 2
      mix/qml.qrc
  52. 244
      mix/qml/Block.qml
  53. 84
      mix/qml/BlockChain.qml
  54. 134
      mix/qml/KeyValuePanel.qml
  55. 2
      mix/qml/MainContent.qml
  56. 124
      mix/qml/ScenarioExecution.qml
  57. 9
      mix/qml/ScenarioLoader.qml
  58. 3
      mix/qml/StateListModel.qml
  59. 7
      mix/qml/TransactionDialog.qml
  60. 203
      mix/qml/Watchers.qml
  61. 5
      mix/res.qrc
  62. 26
      neth/main.cpp
  63. 2
      solc/CMakeLists.txt
  64. 1
      test/CMakeLists.txt
  65. 20
      test/TestHelper.cpp
  66. 3
      test/fuzzTesting/createRandomTest.cpp
  67. 136
      test/fuzzTesting/fuzzHelper.cpp
  68. 1
      test/libdevcrypto/crypto.cpp
  69. 43
      test/libethereum/StateTestsFiller/stSpecialTestFiller.json
  70. 4
      test/libethereum/blockchain.cpp
  71. 108
      test/libethereum/transactionqueue.cpp
  72. 2
      test/libp2p/peer.cpp
  73. 1
      test/libp2p/rlpx.cpp
  74. 195
      test/libsolidity/SolidityEndToEndTest.cpp
  75. 41
      test/libsolidity/SolidityNameAndTypeResolution.cpp
  76. 23
      test/libwhisper/bloomFilter.cpp
  77. 20
      test/libwhisper/whisperMessage.cpp
  78. 36
      test/libwhisper/whisperTopic.cpp

1
CMakeLists.txt

@ -398,6 +398,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.

42
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>
@ -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>

56
alethzero/MainWin.cpp

@ -239,6 +239,21 @@ 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);
@ -247,10 +262,6 @@ Main::Main(QWidget *parent) :
#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 +742,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 +834,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 +1032,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 +1290,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)

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;
};

26
eth/main.cpp

@ -194,7 +194,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
@ -1441,8 +1441,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 +1492,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 +1749,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 +1777,3 @@ int main(int argc, char** argv)
writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData);
return 0;
}

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;
}

82
libdevcore/RLP.cpp

@ -49,12 +49,12 @@ RLP::iterator& RLP::iterator::operator++()
{
if (m_remaining)
{
m_lastItem.retarget(m_lastItem.next().data(), m_remaining);
m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem, ThrowOnFail | FailIfTooSmall).actualSize());
m_remaining -= std::min<unsigned>(m_remaining, m_lastItem.size());
m_currentItem.retarget(m_currentItem.next().data(), m_remaining);
m_currentItem = m_currentItem.cropped(0, sizeAsEncoded(m_currentItem));
m_remaining -= std::min<size_t>(m_remaining, m_currentItem.size());
}
else
m_lastItem.retarget(m_lastItem.next().data(), 0);
m_currentItem.retarget(m_currentItem.next().data(), 0);
return *this;
}
@ -63,28 +63,28 @@ RLP::iterator::iterator(RLP const& _parent, bool _begin)
if (_begin && _parent.isList())
{
auto pl = _parent.payload();
m_lastItem = pl.cropped(0, RLP(pl, ThrowOnFail | FailIfTooSmall).actualSize());
m_remaining = pl.size() - m_lastItem.size();
m_currentItem = pl.cropped(0, sizeAsEncoded(pl));
m_remaining = pl.size() - m_currentItem.size();
}
else
{
m_lastItem = _parent.data().cropped(_parent.data().size());
m_currentItem = _parent.data().cropped(_parent.data().size());
m_remaining = 0;
}
}
RLP RLP::operator[](unsigned _i) const
RLP RLP::operator[](size_t _i) const
{
if (_i < m_lastIndex)
{
m_lastEnd = RLP(payload(), ThrowOnFail | FailIfTooSmall).actualSize();
m_lastEnd = sizeAsEncoded(payload());
m_lastItem = payload().cropped(0, m_lastEnd);
m_lastIndex = 0;
}
for (; m_lastIndex < _i && m_lastItem.size(); ++m_lastIndex)
{
m_lastItem = payload().cropped(m_lastEnd);
m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem, ThrowOnFail | FailIfTooSmall).actualSize());
m_lastItem = m_lastItem.cropped(0, sizeAsEncoded(m_lastItem));
m_lastEnd += m_lastItem.size();
}
return RLP(m_lastItem, ThrowOnFail | FailIfTooSmall);
@ -100,7 +100,7 @@ RLPs RLP::toList() const
return ret;
}
unsigned RLP::actualSize() const
size_t RLP::actualSize() const
{
if (isNull())
return 0;
@ -142,7 +142,7 @@ bool RLP::isInt() const
}
else if (n < c_rlpListStart)
{
if ((int)m_data.size() <= 1 + n - c_rlpDataIndLenZero)
if (m_data.size() <= size_t(1 + n - c_rlpDataIndLenZero))
BOOST_THROW_EXCEPTION(BadRLP());
return m_data[1 + n - c_rlpDataIndLenZero] != 0;
}
@ -151,63 +151,75 @@ bool RLP::isInt() const
return false;
}
unsigned RLP::length() const
size_t RLP::length() const
{
if (isNull())
return 0;
requireGood();
unsigned ret = 0;
byte n = m_data[0];
size_t ret = 0;
byte const n = m_data[0];
if (n < c_rlpDataImmLenStart)
return 1;
else if (n <= c_rlpDataIndLenZero)
return n - c_rlpDataImmLenStart;
else if (n < c_rlpListStart)
{
if ((int)m_data.size() <= n - c_rlpDataIndLenZero)
if (m_data.size() <= size_t(n - c_rlpDataIndLenZero))
BOOST_THROW_EXCEPTION(BadRLP());
if ((int)m_data.size() > 1)
if (m_data.size() > 1)
if (m_data[1] == 0)
BOOST_THROW_EXCEPTION(BadRLP());
for (int i = 0; i < n - c_rlpDataIndLenZero; ++i)
unsigned lengthSize = n - c_rlpDataIndLenZero;
if (lengthSize > sizeof(ret))
// We did not check, but would most probably not fit in our memory.
BOOST_THROW_EXCEPTION(UndersizeRLP());
for (unsigned i = 0; i < lengthSize; ++i)
ret = (ret << 8) | m_data[i + 1];
}
else if (n <= c_rlpListIndLenZero)
return n - c_rlpListStart;
else
{
if ((int)m_data.size() <= n - c_rlpListIndLenZero)
unsigned lengthSize = n - c_rlpListIndLenZero;
if (m_data.size() <= lengthSize)
BOOST_THROW_EXCEPTION(BadRLP());
if ((int)m_data.size() > 1)
if (m_data.size() > 1)
if (m_data[1] == 0)
BOOST_THROW_EXCEPTION(BadRLP());
for (int i = 0; i < n - c_rlpListIndLenZero; ++i)
if (lengthSize > sizeof(ret))
// We did not check, but would most probably not fit in our memory.
BOOST_THROW_EXCEPTION(UndersizeRLP());
for (unsigned i = 0; i < lengthSize; ++i)
ret = (ret << 8) | m_data[i + 1];
}
// We have to be able to add payloadOffset to length without overflow.
// This rejects roughly 4GB-sized RLPs on some platforms.
if (ret >= std::numeric_limits<size_t>::max() - 0x100)
BOOST_THROW_EXCEPTION(UndersizeRLP());
return ret;
}
unsigned RLP::items() const
size_t RLP::items() const
{
if (isList())
{
bytesConstRef d = payload().cropped(0, length());
unsigned i = 0;
size_t i = 0;
for (; d.size(); ++i)
d = d.cropped(RLP(d, ThrowOnFail | FailIfTooSmall).actualSize());
d = d.cropped(sizeAsEncoded(d));
return i;
}
return 0;
}
RLPStream& RLPStream::appendRaw(bytesConstRef _s, unsigned _itemCount)
RLPStream& RLPStream::appendRaw(bytesConstRef _s, size_t _itemCount)
{
m_out.insert(m_out.end(), _s.begin(), _s.end());
noteAppended(_itemCount);
return *this;
}
void RLPStream::noteAppended(unsigned _itemCount)
void RLPStream::noteAppended(size_t _itemCount)
{
if (!_itemCount)
return;
@ -223,7 +235,7 @@ void RLPStream::noteAppended(unsigned _itemCount)
{
auto p = m_listStack.back().second;
m_listStack.pop_back();
unsigned s = m_out.size() - p; // list size
size_t s = m_out.size() - p; // list size
auto brs = bytesRequired(s);
unsigned encodeSize = s < c_rlpListImmLenCount ? 1 : (1 + brs);
// cdebug << "s: " << s << ", p: " << p << ", m_out.size(): " << m_out.size() << ", encodeSize: " << encodeSize << " (br: " << brs << ")";
@ -232,19 +244,21 @@ void RLPStream::noteAppended(unsigned _itemCount)
memmove(m_out.data() + p + encodeSize, m_out.data() + p, os - p);
if (s < c_rlpListImmLenCount)
m_out[p] = (byte)(c_rlpListStart + s);
else
else if (c_rlpListIndLenZero + brs <= 0xff)
{
m_out[p] = (byte)(c_rlpListIndLenZero + brs);
byte* b = &(m_out[p + brs]);
for (; s; s >>= 8)
*(b--) = (byte)s;
}
else
BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("itemCount too large for RLP"));
}
_itemCount = 1; // for all following iterations, we've effectively appended a single item only since we completed a list.
}
}
RLPStream& RLPStream::appendList(unsigned _items)
RLPStream& RLPStream::appendList(size_t _items)
{
// cdebug << "appendList(" << _items << ")";
if (_items)
@ -266,10 +280,10 @@ RLPStream& RLPStream::appendList(bytesConstRef _rlp)
RLPStream& RLPStream::append(bytesConstRef _s, bool _compact)
{
unsigned s = _s.size();
size_t s = _s.size();
byte const* d = _s.data();
if (_compact)
for (unsigned i = 0; i < _s.size() && !*d; ++i, --s, ++d) {}
for (size_t i = 0; i < _s.size() && !*d; ++i, --s, ++d) {}
if (s == 1 && *d < c_rlpDataImmLenStart)
m_out.push_back(*d);
@ -299,6 +313,8 @@ RLPStream& RLPStream::append(bigint _i)
else
{
auto brbr = bytesRequired(br);
if (c_rlpDataIndLenZero + brbr > 0xff)
BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Number too large for RLP"));
m_out.push_back((byte)(c_rlpDataIndLenZero + brbr));
pushInt(br, brbr);
}
@ -308,9 +324,11 @@ RLPStream& RLPStream::append(bigint _i)
return *this;
}
void RLPStream::pushCount(unsigned _count, byte _base)
void RLPStream::pushCount(size_t _count, byte _base)
{
auto br = bytesRequired(_count);
if (int(br) + _base > 0xff)
BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Count too large for RLP"));
m_out.push_back((byte)(br + _base)); // max 8 bytes.
pushInt(_count, br);
}

66
libdevcore/RLP.h

@ -113,12 +113,12 @@ public:
bool isInt() const;
/// @returns the number of items in the list, or zero if it isn't a list.
unsigned itemCount() const { return isList() ? items() : 0; }
unsigned itemCountStrict() const { if (!isList()) BOOST_THROW_EXCEPTION(BadCast()); return items(); }
size_t itemCount() const { return isList() ? items() : 0; }
size_t itemCountStrict() const { if (!isList()) BOOST_THROW_EXCEPTION(BadCast()); return items(); }
/// @returns the number of bytes in the data, or zero if it isn't data.
unsigned size() const { return isData() ? length() : 0; }
unsigned sizeStrict() const { if (!isData()) BOOST_THROW_EXCEPTION(BadCast()); return length(); }
size_t size() const { return isData() ? length() : 0; }
size_t sizeStrict() const { if (!isData()) BOOST_THROW_EXCEPTION(BadCast()); return length(); }
/// Equality operators; does best-effort conversion and checks for equality.
bool operator==(char const* _s) const { return isData() && toString() == _s; }
@ -137,7 +137,7 @@ public:
/// Subscript operator.
/// @returns the list item @a _i if isList() and @a _i < listItems(), or RLP() otherwise.
/// @note if used to access items in ascending order, this is efficient.
RLP operator[](unsigned _i) const;
RLP operator[](size_t _i) const;
using element_type = RLP;
@ -152,16 +152,16 @@ public:
iterator& operator++();
iterator operator++(int) { auto ret = *this; operator++(); return ret; }
RLP operator*() const { return RLP(m_lastItem); }
bool operator==(iterator const& _cmp) const { return m_lastItem == _cmp.m_lastItem; }
RLP operator*() const { return RLP(m_currentItem); }
bool operator==(iterator const& _cmp) const { return m_currentItem == _cmp.m_currentItem; }
bool operator!=(iterator const& _cmp) const { return !operator==(_cmp); }
private:
iterator() {}
iterator(RLP const& _parent, bool _begin);
unsigned m_remaining = 0;
bytesConstRef m_lastItem;
size_t m_remaining = 0;
bytesConstRef m_currentItem;
};
/// @brief Iterator into beginning of sub-item list (valid only if we are a list).
@ -247,7 +247,7 @@ public:
if (itemCount() != N || !isList())
BOOST_THROW_EXCEPTION(BadCast());
std::array<T, N> ret;
for (unsigned i = 0; i < N; ++i)
for (size_t i = 0; i < N; ++i)
{
ret[i] = (T)operator[](i);
}
@ -259,19 +259,21 @@ public:
{
requireGood();
if ((!isInt() && !(_flags & AllowNonCanon)) || isList() || isNull())
{
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
else
return 0;
else {}
}
auto p = payload();
if (p.size() > intTraits<_T>::maxSize && (_flags & FailIfTooBig))
{
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
else
return 0;
else {}
}
return fromBigEndian<_T>(p);
}
@ -280,14 +282,15 @@ public:
{
requireGood();
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
{
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
else
return _N();
else{}
}
_N ret;
size_t s = std::min((size_t)_N::size, (size_t)length());
size_t s = std::min<size_t>(_N::size, length());
memcpy(ret.data() + _N::size - s, payload().data(), s);
return ret;
}
@ -298,9 +301,9 @@ public:
/// @returns the data payload. Valid for all types.
bytesConstRef payload() const { auto l = length(); if (l > m_data.size()) BOOST_THROW_EXCEPTION(BadRLP()); return m_data.cropped(payloadOffset(), l); }
/// @returns the theoretical size of this item.
/// @returns the theoretical size of this item as encoded in the data.
/// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work.
unsigned actualSize() const;
size_t actualSize() const;
private:
/// Disable construction from rvalue
@ -316,20 +319,23 @@ private:
unsigned lengthSize() const { if (isData() && m_data[0] > c_rlpDataIndLenZero) return m_data[0] - c_rlpDataIndLenZero; if (isList() && m_data[0] > c_rlpListIndLenZero) return m_data[0] - c_rlpListIndLenZero; return 0; }
/// @returns the size in bytes of the payload, as given by the RLP as opposed to as inferred from m_data.
unsigned length() const;
size_t length() const;
/// @returns the number of bytes into the data that the payload starts.
unsigned payloadOffset() const { return isSingleByte() ? 0 : (1 + lengthSize()); }
size_t payloadOffset() const { return isSingleByte() ? 0 : (1 + lengthSize()); }
/// @returns the number of data items.
unsigned items() const;
size_t items() const;
/// @returns the size encoded into the RLP in @a _data and throws if _data is too short.
static size_t sizeAsEncoded(bytesConstRef _data) { return RLP(_data, ThrowOnFail | FailIfTooSmall).actualSize(); }
/// Our byte data.
bytesConstRef m_data;
/// The list-indexing cache.
mutable unsigned m_lastIndex = (unsigned)-1;
mutable unsigned m_lastEnd = 0;
mutable size_t m_lastIndex = (size_t)-1;
mutable size_t m_lastEnd = 0;
mutable bytesConstRef m_lastItem;
};
@ -343,7 +349,7 @@ public:
RLPStream() {}
/// Initializes the RLPStream as a list of @a _listItems items.
explicit RLPStream(unsigned _listItems) { appendList(_listItems); }
explicit RLPStream(size_t _listItems) { appendList(_listItems); }
~RLPStream() {}
@ -359,7 +365,7 @@ public:
template <unsigned N> RLPStream& append(FixedHash<N> _s, bool _compact = false, bool _allOrNothing = false) { return _allOrNothing && !_s ? append(bytesConstRef()) : append(_s.ref(), _compact); }
/// Appends an arbitrary RLP fragment - this *must* be a single item unless @a _itemCount is given.
RLPStream& append(RLP const& _rlp, unsigned _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); }
RLPStream& append(RLP const& _rlp, size_t _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); }
/// Appends a sequence of data to the stream as a list.
template <class _T> RLPStream& append(std::vector<_T> const& _s) { return appendVector(_s); }
@ -370,14 +376,14 @@ public:
template <class T, class U> RLPStream& append(std::pair<T, U> const& _s) { appendList(2); append(_s.first); append(_s.second); return *this; }
/// Appends a list.
RLPStream& appendList(unsigned _items);
RLPStream& appendList(size_t _items);
RLPStream& appendList(bytesConstRef _rlp);
RLPStream& appendList(bytes const& _rlp) { return appendList(&_rlp); }
RLPStream& appendList(RLPStream const& _s) { return appendList(&_s.out()); }
/// Appends raw (pre-serialised) RLP data. Use with caution.
RLPStream& appendRaw(bytesConstRef _rlp, unsigned _itemCount = 1);
RLPStream& appendRaw(bytes const& _rlp, unsigned _itemCount = 1) { return appendRaw(&_rlp, _itemCount); }
RLPStream& appendRaw(bytesConstRef _rlp, size_t _itemCount = 1);
RLPStream& appendRaw(bytes const& _rlp, size_t _itemCount = 1) { return appendRaw(&_rlp, _itemCount); }
/// Shift operators for appending data items.
template <class T> RLPStream& operator<<(T _data) { return append(_data); }
@ -392,14 +398,14 @@ public:
void swapOut(bytes& _dest) { if(!m_listStack.empty()) BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("listStack is not empty")); swap(m_out, _dest); }
private:
void noteAppended(unsigned _itemCount = 1);
void noteAppended(size_t _itemCount = 1);
/// Push the node-type byte (using @a _base) along with the item count @a _count.
/// @arg _count is number of characters for strings, data-bytes for ints, or items for lists.
void pushCount(unsigned _count, byte _offset);
void pushCount(size_t _count, byte _offset);
/// Push an integer as a raw big-endian byte-stream.
template <class _T> void pushInt(_T _i, unsigned _br)
template <class _T> void pushInt(_T _i, size_t _br)
{
m_out.resize(m_out.size() + _br);
byte* b = &m_out.back();
@ -410,7 +416,7 @@ private:
/// Our output byte stream.
bytes m_out;
std::vector<std::pair<unsigned, unsigned>> m_listStack;
std::vector<std::pair<size_t, size_t>> m_listStack;
};
template <class _T> void rlpListAux(RLPStream& _out, _T _t) { _out << _t; }

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;

492
libethereum/BlockChainSync.cpp

@ -38,7 +38,8 @@ 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
BlockChainSync::BlockChainSync(EthereumHost& _host):
m_host(_host)
@ -75,15 +76,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 +95,6 @@ void BlockChainSync::onPeerStatus(std::shared_ptr<EthereumPeer> _peer)
_peer->m_expectedHashes = hashes;
onNewPeer(_peer);
}
DEV_INVARIANT_CHECK;
}
unsigned BlockChainSync::estimatedHashes() const
@ -114,7 +117,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 +139,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 +187,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 +286,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 +295,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 +308,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 +348,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 +389,10 @@ void PV60Sync::transition(std::shared_ptr<EthereumPeer> _peer, SyncState _s, boo
RLPStream s;
if (_s == SyncState::Hashes)
{
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, _s, true);
_peer->requestHashes(m_syncingLastReceivedHash ? m_syncingLastReceivedHash : m_syncingLatestHash);
DEV_INVARIANT_CHECK;
return;
}
else if (m_state == SyncState::Hashes)
if (m_state == SyncState::Idle || 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 +454,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 +464,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 +492,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 +554,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 +564,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 +642,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 +702,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 +730,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 +787,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 +821,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;
}

47
libethereum/BlockChainSync.h

@ -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,40 @@ 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
};
}
}

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)

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;

59
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" };
@ -67,8 +68,7 @@ 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);
m_transactionsSent = m_tq.knownTransactions();
return true;
}
return false;
@ -114,25 +114,26 @@ 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);
for (size_t i = 0; i < ts.size(); ++i)
{
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)); }));
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.first);
peerTransactions[p].push_back(i);
}
for (auto const& t: ts)
m_transactionsSent.insert(t.first);
m_transactionsSent.insert(t.sha3());
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 +231,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 +243,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)
@ -312,8 +318,15 @@ void EthereumHost::onPeerTransactions(std::shared_ptr<EthereumPeer> _peer, RLP c
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

2
libethereum/EthereumHost.h

@ -115,7 +115,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.

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.

63
libethereum/State.cpp

@ -47,6 +47,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 ""; }
@ -64,7 +65,7 @@ OverlayDB State::openDB(std::string const& _basePath, WithExisting _we)
}
path += "/" + toHex(CanonBlockChain::genesis().hash().ref().cropped(0, 4)) + "/" + toString(c_databaseVersion);
boost::filesystem::create_directory(path);
boost::filesystem::create_directories(path);
ldb::Options o;
o.max_open_files = 256;
@ -495,7 +496,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 +505,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 +535,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)

2
libethereum/Transaction.cpp

@ -42,7 +42,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, ExecutionResult const& _e
TransactionException dev::eth::toTransactionException(Exception const& _e)
{
// Basic Transaction exceptions
if (!!dynamic_cast<BadRLP const*>(&_e))
if (!!dynamic_cast<RLPException const*>(&_e))
return TransactionException::BadRLP;
if (!!dynamic_cast<OutOfGasIntrinsic const*>(&_e))
return TransactionException::OutOfGasIntrinsic;

215
libethereum/TransactionQueue.cpp

@ -95,10 +95,20 @@ ImportResult TransactionQueue::import(Transaction const& _transaction, ImportCal
return ret;
}
std::unordered_map<h256, Transaction> TransactionQueue::transactions() const
Transactions TransactionQueue::topTransactions(unsigned _limit) const
{
ReadGuard l(m_lock);
return m_current;
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_known;
}
ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb)
@ -108,35 +118,49 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
// 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.
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)
@ -163,93 +187,113 @@ 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;
}
bool TransactionQueue::remove_WITH_LOCK(h256 const& _txHash)
{
// cdebug << "txQ::remove" << _txHash;
for (std::unordered_map<h256, Transaction>* pool: { &m_current, &m_future })
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
auto fs = m_future.find(t.from());
if (fs != m_future.end())
{
auto pit = pool->find(_txHash);
if (pit != pool->end())
u256 nonce = t.nonce() + 1;
auto fb = fs->second.find(nonce);
if (fb != fs->second.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;
auto ft = fb;
while (ft != fs->second.end() && ft->second.transaction.nonce() == nonce)
{
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());
}
}
return false;
m_known.insert(_p.first);
}
bool TransactionQueue::remove_WITH_LOCK(h256 const& _txHash)
{
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))
{
m_future.insert(_t);
m_current.erase(_t.first);
}
}
auto it = m_currentByHash.find(_txHash);
if (it == m_currentByHash.end())
return;
void TransactionQueue::noteGood(std::pair<h256, 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)
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)
{
auto fit = m_future.find(it->second);
if (fit != m_future.end())
{
m_current.insert(*fit);
m_future.erase(fit);
}
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::drop(h256 const& _txHash)
@ -261,7 +305,18 @@ void TransactionQueue::drop(h256 const& _txHash)
UpgradeGuard ul(l);
m_dropped.insert(_txHash);
m_known.erase(_txHash);
remove_WITH_LOCK(_txHash);
}
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;
}

55
libethereum/TransactionQueue.h

@ -43,6 +43,7 @@ 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
@ -50,6 +51,10 @@ class TransactionQueue
public:
using ImportCallback = std::function<void(ImportResult)>;
/// @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): m_current(PriorityCompare { *this }), m_limit(_limit), m_futureLimit(_futureLimit) {}
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);
@ -57,17 +62,40 @@ public:
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 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(); }
void clear();
template <class T> Handler onReady(T const& _t) { return m_onReady.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 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 check_WITH_LOCK(h256 const& _h, IfDropped _ik);
ImportResult manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb);
@ -77,12 +105,19 @@ private:
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.
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.
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
};
}

13
libethereum/Utility.cpp

@ -27,6 +27,7 @@
#include <libdevcore/RLP.h>
#include <libdevcore/Log.h>
#include <libethcore/Common.h>
#include "CanonBlockChain.h"
#include "Defaults.h"
using namespace std;
using namespace dev;
@ -104,7 +105,7 @@ void dev::eth::upgradeDatabase(std::string const& _basePath)
{
auto minorProtocolVersion = (unsigned)status[1];
auto databaseVersion = (unsigned)status[2];
auto genesisHash = (h256)status[3];
auto genesisHash = status.itemCount() > 3 ? (h256)status[3] : CanonBlockChain::genesis().hash();
string chainPath = path + "/" + toHex(genesisHash.ref().cropped(0, 4));
string extrasPath = chainPath + "/" + toString(databaseVersion);
@ -121,15 +122,19 @@ void dev::eth::upgradeDatabase(std::string const& _basePath)
fs::rename(path + "/details", extrasPath + "/extras");
fs::rename(path + "/state", extrasPath + "/state");
writeFile(extrasPath + "/minor", rlp(minorProtocolVersion));
fs::remove_all(path + "/status");
}
}
}
catch (Exception& ex)
{
cwarn << "Couldn't upgrade: " << ex.what() << boost::diagnostic_information(ex);
}
catch (...)
{
cwarn << "Couldn't upgrade - bad status";
cwarn << "Couldn't upgrade. Some issue with moving files around. Probably easiest just to redownload.";
}
fs::rename(path + "/status", path + "/status.old");
}
}

2
libethereum/VerifiedBlock.h

@ -43,7 +43,7 @@ struct VerifiedBlockRef
/// @brief Verified block info, combines block data and verified info/transactions
struct VerifiedBlock
{
VerifiedBlock() {};
VerifiedBlock() {}
VerifiedBlock(BlockInfo&& _bi)
{

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.

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());
}
};

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

3
libp2p/Session.cpp

@ -334,11 +334,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())
{

162
libsolidity/AST.cpp

@ -802,12 +802,11 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr);
Type const* expressionType = m_expression->getType().get();
TypePointer const& expressionType = m_expression->getType();
FunctionTypePointer functionType;
if (isTypeConversion())
{
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
//@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members
if (m_arguments.size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Exactly one argument expected for explicit type conversion."));
if (!isPositionalCall)
@ -815,87 +814,106 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType();
return;
}
else if (FunctionType const* functionType = dynamic_cast<FunctionType const*>(expressionType))
if (isStructConstructorCall())
{
//@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes();
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
functionType = structType.constructorType();
}
else
functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
if (isPositionalCall)
{
// call by positional arguments
for (size_t i = 0; i < m_arguments.size(); ++i)
if (
!functionType->takesArbitraryParameters() &&
!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])
)
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
m_arguments[i]->getType()->toString() +
" to " +
parameterTypes[i]->toString() +
" requested."
));
}
else
{
// call by named arguments
if (functionType->takesArbitraryParameters())
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions "
"that take arbitrary parameters."));
auto const& parameterNames = functionType->getParameterNames();
if (parameterNames.size() != m_names.size())
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
// check duplicate names
for (size_t i = 0; i < m_names.size(); i++)
for (size_t j = i + 1; j < m_names.size(); j++)
if (*m_names[i] == *m_names[j])
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
for (size_t i = 0; i < m_names.size(); i++) {
bool found = false;
for (size_t j = 0; j < parameterNames.size(); j++) {
if (parameterNames[j] == *m_names[i]) {
// check type convertible
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j]))
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
m_arguments[i]->getType()->toString() +
" to " +
parameterTypes[i]->toString() +
" requested."
));
found = true;
break;
}
if (!functionType)
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
//@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes();
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
if (isPositionalCall)
{
// call by positional arguments
for (size_t i = 0; i < m_arguments.size(); ++i)
if (
!functionType->takesArbitraryParameters() &&
!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])
)
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
m_arguments[i]->getType()->toString() +
" to " +
parameterTypes[i]->toString() +
" requested."
));
}
else
{
// call by named arguments
if (functionType->takesArbitraryParameters())
BOOST_THROW_EXCEPTION(createTypeError(
"Named arguments cannnot be used for functions that take arbitrary parameters."
));
auto const& parameterNames = functionType->getParameterNames();
if (parameterNames.size() != m_names.size())
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
// check duplicate names
for (size_t i = 0; i < m_names.size(); i++)
for (size_t j = i + 1; j < m_names.size(); j++)
if (*m_names[i] == *m_names[j])
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
for (size_t i = 0; i < m_names.size(); i++) {
bool found = false;
for (size_t j = 0; j < parameterNames.size(); j++) {
if (parameterNames[j] == *m_names[i]) {
// check type convertible
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j]))
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
m_arguments[i]->getType()->toString() +
" to " +
parameterTypes[i]->toString() +
" requested."
));
found = true;
break;
}
if (!found)
BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
}
if (!found)
BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
}
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs
if (functionType->getReturnParameterTypes().empty())
m_type = make_shared<VoidType>();
else
m_type = functionType->getReturnParameterTypes().front();
}
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have anonymous
// structs and tuples
if (functionType->getReturnParameterTypes().empty())
m_type = make_shared<VoidType>();
else
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
m_type = functionType->getReturnParameterTypes().front();
}
bool FunctionCall::isTypeConversion() const
{
return m_expression->getType()->getCategory() == Type::Category::TypeType;
return m_expression->getType()->getCategory() == Type::Category::TypeType && !isStructConstructorCall();
}
bool FunctionCall::isStructConstructorCall() const
{
if (auto const* type = dynamic_cast<TypeType const*>(m_expression->getType().get()))
return type->getActualType()->getCategory() == Type::Category::Struct;
else
return false;
}
void NewExpression::checkTypeRequirements(TypePointers const*)

6
libsolidity/AST.h

@ -1136,9 +1136,11 @@ public:
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
std::vector<ASTPointer<ASTString>> const& getNames() const { return m_names; }
/// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call.
/// @returns true if this is not an actual function call, but an explicit type conversion.
/// Returns false for struct constructor calls.
bool isTypeConversion() const;
/// @return true if this is a constructor call for a struct, i.e. StructName(...).
bool isStructConstructorCall() const;
private:
ASTPointer<Expression> m_expression;

55
libsolidity/CompilerUtils.cpp

@ -436,19 +436,54 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
}
case Type::Category::Struct:
{
//@todo we can probably use some of the code for arrays here.
solAssert(targetTypeCategory == stackTypeCategory, "");
auto& targetType = dynamic_cast<StructType const&>(_targetType);
auto& stackType = dynamic_cast<StructType const&>(_typeOnStack);
auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
solAssert(
targetType.location() == DataLocation::Storage &&
stackType.location() == DataLocation::Storage,
"Non-storage structs not yet implemented."
);
solAssert(
targetType.isPointer(),
"Type conversion to non-pointer struct requested."
);
targetType.location() != DataLocation::CallData &&
typeOnStack.location() != DataLocation::CallData
, "");
switch (targetType.location())
{
case DataLocation::Storage:
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
solAssert(
targetType.isPointer() &&
typeOnStack.location() == DataLocation::Storage,
"Invalid conversion to storage type."
);
break;
case DataLocation::Memory:
// Copy the array to a free position in memory, unless it is already in memory.
if (typeOnStack.location() != DataLocation::Memory)
{
solAssert(typeOnStack.location() == DataLocation::Storage, "");
// stack: <source ref> <source byte offset>
m_context << eth::Instruction::POP;
m_context << typeOnStack.memorySize();
allocateMemory();
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
// stack: <memory ptr> <source ref> <memory ptr>
for (auto const& member: typeOnStack.getMembers())
{
if (!member.type->canLiveOutsideStorage())
continue;
pair<u256, unsigned> const& offsets = typeOnStack.getStorageOffsetsOfMember(member.name);
m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD;
m_context << u256(offsets.second);
StorageItem(m_context, *member.type).retrieveValue(SourceLocation(), true);
TypePointer targetMemberType = targetType.getMemberType(member.name);
solAssert(!!targetMemberType, "Member not found in target type.");
convertType(*member.type, *targetMemberType, true);
storeInMemoryDynamic(*targetMemberType, true);
}
m_context << eth::Instruction::POP << eth::Instruction::POP;
}
break;
case DataLocation::CallData:
solAssert(false, "Invalid type conversion target location CallData.");
break;
}
break;
}
default:

79
libsolidity/ExpressionCompiler.cpp

@ -155,8 +155,6 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
TypePointer type = _assignment.getRightHandSide().getType();
if (!_assignment.getType()->dataStoredIn(DataLocation::Storage))
{
//@todo we should delay conversion here if RHS is not in memory, LHS is a MemoryItem
// and not dynamically-sized.
utils().convertType(*type, *_assignment.getType());
type = _assignment.getType();
}
@ -315,38 +313,66 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
using Location = FunctionType::Location;
if (_functionCall.isTypeConversion())
{
//@todo struct construction
solAssert(_functionCall.getArguments().size() == 1, "");
solAssert(_functionCall.getNames().empty(), "");
Expression const& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this);
utils().convertType(*firstArgument.getType(), *_functionCall.getType());
return false;
}
FunctionTypePointer functionType;
if (_functionCall.isStructConstructorCall())
{
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.getExpression().getType());
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
functionType = structType.constructorType();
}
else
functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.getExpression().getType());
TypePointers const& parameterTypes = functionType->getParameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
if (!functionType->takesArbitraryParameters())
solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments;
if (callArgumentNames.empty())
// normal arguments
arguments = callArguments;
else
// named arguments
for (auto const& parameterName: functionType->getParameterNames())
{
bool found = false;
for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
if ((found = (parameterName == *callArgumentNames[j])))
// we found the actual parameter position
arguments.push_back(callArguments[j]);
solAssert(found, "");
}
if (_functionCall.isStructConstructorCall())
{
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
TypePointers const& parameterTypes = function.getParameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
if (!function.takesArbitraryParameters())
solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments;
if (callArgumentNames.empty())
// normal arguments
arguments = callArguments;
else
// named arguments
for (auto const& parameterName: function.getParameterNames())
{
bool found = false;
for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
if ((found = (parameterName == *callArgumentNames[j])))
// we found the actual parameter position
arguments.push_back(callArguments[j]);
solAssert(found, "");
}
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.getExpression().getType());
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
m_context << u256(max(32u, structType.getCalldataEncodedSize(true)));
utils().allocateMemory();
m_context << eth::Instruction::DUP1;
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
utils().convertType(*arguments[i]->getType(), *functionType->getParameterTypes()[i]);
utils().storeInMemoryDynamic(*functionType->getParameterTypes()[i]);
}
m_context << eth::Instruction::POP;
}
else
{
FunctionType const& function = *functionType;
switch (function.getLocation())
{
case Location::Internal:
@ -691,7 +717,8 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
}
case DataLocation::Memory:
{
solAssert(false, "Member access for memory structs not yet implemented.");
m_context << type.memoryOffsetOfMember(member) << eth::Instruction::ADD;
setLValue<MemoryItem>(_memberAccess, *_memberAccess.getType());
break;
}
default:

74
libsolidity/LValue.cpp

@ -47,6 +47,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
errinfo_sourceLocation(_location) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch.");
for (unsigned i = 0; i < m_size; ++i)
m_context << eth::dupInstruction(stackPos + 1);
}
@ -175,6 +176,7 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
{
CompilerUtils utils(m_context);
// stack: value storage_key storage_offset
if (m_dataType.isValueType())
{
@ -238,52 +240,66 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
dynamic_cast<ArrayType const&>(m_dataType),
dynamic_cast<ArrayType const&>(_sourceType));
if (_move)
CompilerUtils(m_context).popStackElement(_sourceType);
utils.popStackElement(m_dataType);
}
else if (m_dataType.getCategory() == Type::Category::Struct)
{
// stack layout: source_ref source_offset target_ref target_offset
// stack layout: source_ref [source_offset] target_ref target_offset
// note that we have structs, so offsets should be zero and are ignored
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
auto const& sourceType = dynamic_cast<StructType const&>(_sourceType);
solAssert(
structType.structDefinition() ==
dynamic_cast<StructType const&>(_sourceType).structDefinition(),
structType.structDefinition() == sourceType.structDefinition(),
"Struct assignment with conversion."
);
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
for (auto const& member: structType.getMembers())
{
// assign each member that is not a mapping
TypePointer const& memberType = member.type;
if (memberType->getCategory() == Type::Category::Mapping)
continue;
TypePointer sourceMemberType = sourceType.getMemberType(member.name);
if (sourceType.location() == DataLocation::Storage)
{
// stack layout: source_ref source_offset target_ref target_offset
pair<u256, unsigned> const& offsets = sourceType.getStorageOffsetsOfMember(member.name);
m_context << offsets.first << eth::Instruction::DUP5 << eth::Instruction::ADD;
m_context << u256(offsets.second);
// stack: source_ref source_off target_ref target_off source_member_ref source_member_off
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
// stack: source_ref source_off target_ref target_off source_value...
}
else
{
solAssert(sourceType.location() == DataLocation::Memory, "");
// stack layout: source_ref target_ref target_offset
TypePointer sourceMemberType = sourceType.getMemberType(member.name);
m_context << sourceType.memoryOffsetOfMember(member.name);
m_context << eth::Instruction::DUP4 << eth::Instruction::ADD;
MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
// stack layout: source_ref target_ref target_offset source_value...
}
unsigned stackSize = sourceMemberType->getSizeOnStack();
pair<u256, unsigned> const& offsets = structType.getStorageOffsetsOfMember(member.name);
m_context
<< offsets.first << u256(offsets.second)
<< eth::Instruction::DUP6 << eth::Instruction::DUP3
<< eth::Instruction::ADD << eth::Instruction::DUP2;
// stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_member_ref source_member_off
StorageItem(m_context, *memberType).retrieveValue(_location, true);
// stack: source_ref source_off target_ref target_off member_offset source_value...
solAssert(
4 + memberType->getSizeOnStack() <= 16,
"Stack too deep, try removing local varibales."
);
m_context
<< eth::dupInstruction(4 + memberType->getSizeOnStack())
<< eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::Instruction::ADD
<< eth::dupInstruction(2 + memberType->getSizeOnStack());
// stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_value... target_member_ref target_member_byte_off
StorageItem(m_context, *memberType).storeValue(*memberType, _location, true);
m_context << eth::Instruction::POP << eth::Instruction::POP;
m_context << eth::dupInstruction(2 + stackSize) << offsets.first << eth::Instruction::ADD;
m_context << u256(offsets.second);
// stack: source_ref [source_off] target_ref target_off source_value... target_member_ref target_member_byte_off
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
}
// stack layout: source_ref [source_offset] target_ref target_offset
unsigned sourceStackSize = sourceType.getSizeOnStack();
if (_move)
m_context
<< eth::Instruction::POP << eth::Instruction::POP
<< eth::Instruction::POP << eth::Instruction::POP;
else
m_context
<< eth::Instruction::SWAP2 << eth::Instruction::POP
<< eth::Instruction::SWAP2 << eth::Instruction::POP;
utils.popStackSlots(2 + sourceType.getSizeOnStack());
else if (sourceType.getSizeOnStack() >= 1)
{
// remove the source ref
solAssert(sourceStackSize <= 2, "Invalid stack size.");
m_context << eth::swapInstruction(sourceStackSize);
if (sourceStackSize == 2)
m_context << eth::Instruction::POP;
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP;
}
}
else
BOOST_THROW_EXCEPTION(

2
libsolidity/LValue.h

@ -103,7 +103,7 @@ private:
class MemoryItem: public LValue
{
public:
MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded);
MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded = true);
virtual unsigned sizeOnStack() const override { return 1; }
virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
virtual void storeValue(

59
libsolidity/Types.cpp

@ -30,11 +30,8 @@
#include <libsolidity/AST.h>
using namespace std;
namespace dev
{
namespace solidity
{
using namespace dev;
using namespace dev::solidity;
void StorageOffsets::computeOffsets(TypePointers const& _types)
{
@ -999,6 +996,15 @@ unsigned StructType::getCalldataEncodedSize(bool _padded) const
return size;
}
u256 StructType::memorySize() const
{
u256 size;
for (auto const& member: getMembers())
if (member.type->canLiveOutsideStorage())
size += member.type->memoryHeadSize();
return size;
}
u256 StructType::getStorageSize() const
{
return max<u256>(1, getMembers().getStorageSize());
@ -1012,6 +1018,14 @@ bool StructType::canLiveOutsideStorage() const
return true;
}
unsigned StructType::getSizeOnStack() const
{
if (location() == DataLocation::Storage)
return 2; // slot and offset
else
return 1;
}
string StructType::toString(bool _short) const
{
string ret = "struct " + m_struct.getName();
@ -1047,6 +1061,26 @@ TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer)
return copy;
}
FunctionTypePointer StructType::constructorType() const
{
TypePointers paramTypes;
strings paramNames;
for (auto const& member: getMembers())
{
if (!member.type->canLiveOutsideStorage())
continue;
paramNames.push_back(member.name);
paramTypes.push_back(copyForLocationIfReference(DataLocation::Memory, member.type));
}
return make_shared<FunctionType>(
paramTypes,
TypePointers{copyForLocation(DataLocation::Memory, false)},
paramNames,
strings(),
FunctionType::Location::Internal
);
}
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
{
auto const* offsets = getMembers().getMemberStorageOffset(_name);
@ -1054,6 +1088,18 @@ pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const&
return *offsets;
}
u256 StructType::memoryOffsetOfMember(string const& _name) const
{
u256 offset;
for (auto const& member: getMembers())
if (member.name == _name)
return offset;
else
offset += member.type->memoryHeadSize();
solAssert(false, "Member not found in struct.");
return 0;
}
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
@ -1663,6 +1709,3 @@ string MagicType::toString(bool) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
}
}
}
}

9
libsolidity/Types.h

@ -543,21 +543,26 @@ class StructType: public ReferenceType
public:
virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct):
//@todo only storage until we have non-storage structs
ReferenceType(DataLocation::Storage), m_struct(_struct) {}
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize(bool _padded) const override;
u256 memorySize() const;
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override;
virtual unsigned getSizeOnStack() const override { return 2; }
virtual unsigned getSizeOnStack() const override;
virtual std::string toString(bool _short) const override;
virtual MemberList const& getMembers() const override;
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
/// @returns a function that peforms the type conversion between a list of struct members
/// and a memory struct of this type.
FunctionTypePointer constructorType() const;
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
u256 memoryOffsetOfMember(std::string const& _name) const;
StructDefinition const& structDefinition() const { return m_struct; }

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;
}

42
mix/ClientModel.cpp

@ -206,6 +206,21 @@ QVariantList ClientModel::gasCosts() const
return res;
}
void ClientModel::addAccount(QString const& _secret)
{
KeyPair key(Secret(_secret.toStdString()));
m_accountsSecret.push_back(key);
Address address = key.address();
m_accounts[address] = Account(u256(0), Account::NormalCreation);
m_ethAccounts->setAccounts(m_accountsSecret);
}
QString ClientModel::resolveAddress(QString const& _secret)
{
KeyPair key(Secret(_secret.toStdString()));
return "0x" + QString::fromStdString(key.address().hex());
}
void ClientModel::setupScenario(QVariantMap _scenario)
{
onStateReset();
@ -336,6 +351,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
if (!transaction.isFunctionCall)
{
callAddress(Address(address.toStdString()), bytes(), transaction);
onNewTransaction();
continue;
}
@ -674,7 +690,7 @@ RecordLogEntry* ClientModel::lastBlock() const
strGas << blockInfo.gasUsed;
stringstream strNumber;
strNumber << blockInfo.number;
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantList());
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantMap(), QVariantList());
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
return record;
}
@ -735,6 +751,7 @@ void ClientModel::onNewTransaction()
Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress;
auto contractAddressIter = m_contractNames.find(contractAddress);
QVariantMap inputParameters;
QVariantMap returnParameters;
QVariantList logs;
if (contractAddressIter != m_contractNames.end())
{
@ -754,6 +771,11 @@ void ClientModel::onNewTransaction()
returned += "(";
returned += returnValues.join(", ");
returned += ")";
QStringList returnParams = encoder.decode(funcDef->returnParameters(), tr.result.output);
for (int k = 0; k < returnParams.length(); ++k)
returnParameters.insert(funcDef->returnParameters().at(k)->name(), returnParams.at(k));
bytes data = tr.inputParameters;
data.erase(data.begin(), data.begin() + 4);
QStringList parameters = encoder.decode(funcDef->parametersList(), data);
@ -837,9 +859,25 @@ void ClientModel::onNewTransaction()
}
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction,
gasUsed, sender, label, inputParameters, logs);
gasUsed, sender, label, inputParameters, returnParameters, logs);
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newRecord(log);
// retrieving all accounts balance
QVariantMap state;
QVariantMap accountBalances;
for (auto const& ctr : m_contractAddresses)
{
u256 wei = m_client->balanceAt(ctr.second, PendingBlock);
accountBalances.insert("0x" + QString::fromStdString(ctr.second.hex()), QEther(wei, QEther::Wei).format());
}
for (auto const& account : m_accounts)
{
u256 wei = m_client->balanceAt(account.first, PendingBlock);
accountBalances.insert("0x" + QString::fromStdString(account.first.hex()), QEther(wei, QEther::Wei).format());
}
state.insert("accounts", accountBalances);
emit newState(recordIndex, state);
}
}

14
mix/ClientModel.h

@ -32,6 +32,7 @@
#include <QVariableDeclaration.h>
#include <libethereum/Account.h>
#include "MachineStates.h"
#include "QEther.h"
namespace dev
{
@ -115,6 +116,8 @@ class RecordLogEntry: public QObject
Q_PROPERTY(QString label MEMBER m_label CONSTANT)
/// input parameters
Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT)
/// return parameters
Q_PROPERTY(QVariantMap returnParameters MEMBER m_returnParameters CONSTANT)
/// logs
Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT)
@ -128,9 +131,9 @@ public:
RecordLogEntry():
m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {}
RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed,
QString _sender, QString _label, QVariantMap _inputParameters, QVariantList _logs):
QString _sender, QString _label, QVariantMap _inputParameters, QVariantMap _returnParameters, QVariantList _logs):
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed),
m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_logs(_logs) {}
m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_returnParameters(_returnParameters), m_logs(_logs) {}
private:
unsigned m_recordIndex;
@ -146,6 +149,7 @@ private:
QString m_sender;
QString m_label;
QVariantMap m_inputParameters;
QVariantMap m_returnParameters;
QVariantList m_logs;
};
@ -183,6 +187,10 @@ public:
Q_INVOKABLE QString encodeStringParam(QString const& _param);
/// To Hex number
Q_INVOKABLE QString toHex(QString const& _int);
/// Add new account to the model
Q_INVOKABLE void addAccount(QString const& _secret);
/// Return the address associated with the current secret
Q_INVOKABLE QString resolveAddress(QString const& _secret);
public slots:
/// Setup scenario, run transaction sequence, show debugger for the last transaction
@ -236,6 +244,8 @@ signals:
void newRecord(RecordLogEntry* _r);
/// State (transaction log) cleared
void stateCleared();
/// new state has been processed
void newState(unsigned _record, QVariantMap _accounts);
private:
RecordLogEntry* lastBlock() const;

2
mix/CodeModel.cpp

@ -543,7 +543,7 @@ void CodeModel::retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type
SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
{
SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector<SolidityDeclaration>(), std::vector<QString>(), nullptr };
SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString(true)), std::vector<SolidityDeclaration>(), std::vector<QString>(), nullptr };
if (!_type)
return r;
switch (_type->getCategory())

2
mix/qml.qrc

@ -69,5 +69,7 @@
<file>qml/ScenarioExecution.qml</file>
<file>qml/ScenarioLoader.qml</file>
<file>qml/ScenarioButton.qml</file>
<file>qml/Watchers.qml</file>
<file>qml/KeyValuePanel.qml</file>
</qresource>
</RCC>

244
mix/qml/Block.qml

@ -22,6 +22,7 @@ ColumnLayout
property int blockIndex
property variant scenario
property string labelColor: "#414141"
signal txSelected(var txIndex)
function calculateHeight()
{
@ -36,6 +37,14 @@ ColumnLayout
return trHeight
}
function editTx(txIndex)
{
transactionDialog.stateAccounts = scenario.accounts
transactionDialog.execute = false
transactionDialog.editMode = true
transactionDialog.open(txIndex, blockIndex, transactions.get(txIndex))
}
onOpenedTrChanged:
{
Layout.preferredHeight = calculateHeight()
@ -156,11 +165,11 @@ ColumnLayout
Image {
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: -9
anchors.topMargin: -9
anchors.leftMargin: -4
anchors.topMargin: 0
id: saveStatusImage
source: "qrc:/qml/img/recyclediscard@2x.png"
width: statusWidth + 20
width: statusWidth + 10
fillMode: Image.PreserveAspectFit
}
@ -194,185 +203,125 @@ ColumnLayout
Rectangle
{
Layout.preferredWidth: blockWidth
Layout.preferredHeight: parent.height
Layout.preferredHeight: trHeight
height: trHeight
color: "#DEDCDC"
id: rowContentTr
anchors.top: parent.top
property bool selected: false
Connections
{
target: blockChainPanel
onTxSelected: {
if (root.blockIndex !== blockIndex || index !== txIndex)
rowContentTr.deselect()
}
}
function deselect()
{
rowContentTr.selected = false
rowContentTr.color = "#DEDCDC"
hash.color = labelColor
func.color = labelColor
}
MouseArea
{
anchors.fill: parent
onClicked: {
if (!rowContentTr.selected)
{
rowContentTr.selected = true
rowContentTr.color = "#4F4F4F"
hash.color = "#EAB920"
func.color = "#EAB920"
txSelected(index)
}
else
rowContentTr.deselect()
}
onDoubleClicked:
{
transactionDialog.stateAccounts = scenario.accounts
transactionDialog.execute = false
transactionDialog.open(index, blockIndex, transactions.get(index))
root.editTx(index)
}
}
ColumnLayout
RowLayout
{
anchors.top: parent.top
width: parent.width
spacing: 20
RowLayout
Layout.fillWidth: true
Layout.preferredHeight: trHeight - 10
anchors.verticalCenter: parent.verticalCenter
Rectangle
{
anchors.top: parent.top
Layout.fillWidth: true
Rectangle
Layout.preferredWidth: fromWidth
anchors.left: parent.left
anchors.leftMargin: horizontalMargin
Text
{
Layout.preferredWidth: fromWidth
anchors.left: parent.left
anchors.leftMargin: horizontalMargin
Text
{
id: hash
width: parent.width - 30
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1
color: labelColor
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
text: {
if (index >= 0)
return transactions.get(index).sender
else
return ""
}
}
}
Rectangle
{
Layout.preferredWidth: toWidth
Text
{
id: func
text: {
if (index >= 0)
parent.parent.userFrienldyToken(transactions.get(index).label)
else
return ""
}
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
color: labelColor
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
maximumLineCount: 1
width: parent.width
}
}
function userFrienldyToken(value)
{
if (value && value.indexOf("<") === 0)
{
if (value.split("> ")[1] === " - ")
return value.split(" - ")[0].replace("<", "")
id: hash
width: parent.width - 30
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1
color: labelColor
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
text: {
if (index >= 0)
return clientModel.resolveAddress(transactions.get(index).sender)
else
return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()";
}
else
return value
}
Rectangle
{
Layout.preferredWidth: valueWidth
Text
{
id: returnValue
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1
color: labelColor
font.bold: true
font.pointSize: dbgStyle.absoluteSize(1)
width: parent.width - 30
text: {
if (index >= 0 && transactions.get(index).returned)
return transactions.get(index).returned
else
return ""
}
return ""
}
}
}
Rectangle
Rectangle
{
Layout.preferredWidth: toWidth
Text
{
Layout.preferredWidth: logsWidth
Layout.preferredHeight: trHeight - 10
width: logsWidth
color: "transparent"
Text
{
id: logs
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
color: labelColor
font.bold: true
font.pointSize: dbgStyle.absoluteSize(1)
text: {
if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count)
return transactions.get(index).logs.count
else
return ""
}
}
MouseArea {
anchors.fill: parent
onClicked: {
rowTransaction.displayContent();
}
id: func
text: {
if (index >= 0)
parent.parent.userFrienldyToken(transactions.get(index).label)
else
return ""
}
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
color: labelColor
font.pointSize: dbgStyle.absoluteSize(1)
font.bold: true
maximumLineCount: 1
width: parent.width
}
}
RowLayout
function userFrienldyToken(value)
{
id: rowDetailedContent
visible: false
Layout.preferredHeight:{
if (index >= 0 && transactions.get(index).logs)
return 100 * transactions.get(index).logs.count
else
return 100
}
onVisibleChanged:
if (value && value.indexOf("<") === 0)
{
var lognb = transactions.get(index).logs.count
if (visible)
{
rowContentTr.Layout.preferredHeight = trHeight + 100 * lognb
openedTr += 100 * lognb
}
if (value.split("> ")[1] === " - ")
return value.split(" - ")[0].replace("<", "")
else
{
rowContentTr.Layout.preferredHeight = trHeight
openedTr -= 100 * lognb
}
return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()";
}
Text {
anchors.left: parent.left
anchors.leftMargin: horizontalMargin
id: logsText
}
}
else
return value
}
}
}
Rectangle
{
width: debugActionWidth
height: trHeight
anchors.left: rowContentTr.right
anchors.topMargin: -6
height: trHeight - 10
anchors.right: rowContentTr.right
anchors.top: rowContentTr.top
anchors.leftMargin: -50
anchors.rightMargin: 10
color: "transparent"
Image {
@ -380,7 +329,6 @@ ColumnLayout
source: "qrc:/qml/img/rightarrow@2x.png"
width: debugActionWidth
fillMode: Image.PreserveAspectFit
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
visible: transactions.get(index).recordIndex !== undefined
}

84
mix/qml/BlockChain.qml

@ -13,12 +13,18 @@ import "."
ColumnLayout {
id: blockChainPanel
property alias trDialog: transactionDialog
property alias blockChainRepeater: blockChainRepeater
property variant model
property var states: ({})
spacing: 0
property int previousWidth
property variant debugTrRequested: []
signal chainChanged
signal chainReloaded
signal txSelected(var blockIndex, var txIndex)
signal rebuilding
signal accountAdded(string address, string amount)
Connections
{
@ -40,20 +46,23 @@ ColumnLayout {
var minWidth = scenarioMinWidth - 20 // margin
if (width <= minWidth || previousWidth <= minWidth)
{
fromWidth = 100
toWidth = 100
valueWidth = 200
fromWidth = 250
toWidth = 240
}
else
{
var diff = (width - previousWidth) / 3;
fromWidth = fromWidth + diff < 100 ? 100 : fromWidth + diff
toWidth = toWidth + diff < 100 ? 100 : toWidth + diff
valueWidth = valueWidth + diff < 200 ? 200 : valueWidth + diff
fromWidth = fromWidth + diff < 250 ? 250 : fromWidth + diff
toWidth = toWidth + diff < 240 ? 240 : toWidth + diff
}
previousWidth = width
}
function getState(record)
{
return states[record]
}
function load(scenario)
{
if (!scenario)
@ -61,6 +70,7 @@ ColumnLayout {
if (model)
rebuild.startBlinking()
model = scenario
states = []
blockModel.clear()
for (var b in model.blocks)
blockModel.append(model.blocks[b])
@ -68,10 +78,8 @@ ColumnLayout {
}
property int statusWidth: 30
property int fromWidth: 150
property int toWidth: 100
property int valueWidth: 200
property int logsWidth: 40
property int fromWidth: 250
property int toWidth: 240
property int debugActionWidth: 40
property int horizontalMargin: 10
property int cellSpacing: 10
@ -80,7 +88,7 @@ ColumnLayout {
{
id: header
spacing: 0
Layout.preferredHeight: 30
Layout.preferredHeight: 24
Rectangle
{
Layout.preferredWidth: statusWidth
@ -91,12 +99,13 @@ ColumnLayout {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
source: "qrc:/qml/img/recycleicon@2x.png"
width: statusWidth + 20
width: statusWidth + 10
fillMode: Image.PreserveAspectFit
}
}
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: fromWidth
Label
{
@ -109,21 +118,13 @@ ColumnLayout {
Label
{
text: "To"
anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: toWidth + cellSpacing
}
Label
{
text: "Value"
Layout.preferredWidth: valueWidth + cellSpacing
}
Label
{
text: "Logs"
Layout.preferredWidth: logsWidth + cellSpacing
}
Label
{
text: ""
anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: debugActionWidth
}
}
@ -162,8 +163,22 @@ ColumnLayout {
{
id: blockChainRepeater
model: blockModel
function editTx(blockIndex, txIndex)
{
itemAt(blockIndex).editTx(txIndex)
}
Block
{
Connections
{
target: block
onTxSelected: {
blockChainPanel.txSelected(index, txIndex)
}
}
id: block
scenario: blockChainPanel.model
Layout.preferredWidth: blockChainScrollView.width
Layout.preferredHeight:
@ -248,6 +263,8 @@ ColumnLayout {
Rectangle
{
Layout.preferredWidth: parent.width
Layout.preferredHeight: 70
color: "transparent"
RowLayout
{
anchors.horizontalCenter: parent.horizontalCenter
@ -270,7 +287,9 @@ ColumnLayout {
{
if (ensureNotFuturetime.running)
return;
rebuilding()
stopBlinking()
states = []
var retBlocks = [];
var bAdded = 0;
for (var j = 0; j < model.blocks.length; j++)
@ -316,7 +335,7 @@ ColumnLayout {
blockModel.append(model.blocks[j])
ensureNotFuturetime.start()
clientModel.setupScenario(model);
clientModel.setupScenario(model);
}
buttonShortcut: ""
sourceImg: "qrc:/qml/img/recycleicon@2x.png"
@ -346,6 +365,7 @@ ColumnLayout {
var item = TransactionHelper.defaultTransaction()
transactionDialog.stateAccounts = model.accounts
transactionDialog.execute = true
transactionDialog.editMode = false
transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item)
}
width: 100
@ -397,7 +417,6 @@ ColumnLayout {
}
else
addNewBlock()
}
function addNewBlock()
@ -410,7 +429,7 @@ ColumnLayout {
height: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/addblock@2x.png"
sourceImg: "qrc:/qml/img/newblock@2x.png"
}
}
@ -448,11 +467,13 @@ ColumnLayout {
tr.recordIndex = _r.recordIndex
tr.logs = _r.logs
tr.sender = _r.sender
tr.returnParameters = _r.returnParameters
var trModel = blockModel.getTransaction(blockIndex, trIndex)
trModel.returned = _r.returned
trModel.recordIndex = _r.recordIndex
trModel.logs = _r.logs
trModel.sender = _r.sender
trModel.returnParameters = _r.returnParameters
blockModel.setTransaction(blockIndex, trIndex, trModel)
return;
}
@ -472,9 +493,15 @@ ColumnLayout {
itemTr.sender = _r.sender
itemTr.recordIndex = _r.recordIndex
itemTr.logs = _r.logs
itemTr.returnParameters = _r.returnParameters
model.blocks[model.blocks.length - 1].transactions.push(itemTr)
blockModel.appendTransaction(itemTr)
}
onNewState: {
states[_record] = _accounts
}
onMiningComplete:
{
}
@ -484,7 +511,12 @@ ColumnLayout {
id: newAccount
text: qsTr("New Account..")
onClicked: {
model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether))
var ac = projectModel.stateListModel.newAccount("O", QEther.Wei)
model.accounts.push(ac)
clientModel.addAccount(ac.secret);
for (var k in Object.keys(blockChainPanel.states))
blockChainPanel.states[k].accounts["0x" + ac.address] = "0 wei" // add the account in all the previous state (balance at O)
accountAdded(ac.address, "0")
}
Layout.preferredWidth: 100
Layout.preferredHeight: 30

134
mix/qml/KeyValuePanel.qml

@ -0,0 +1,134 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "js/TransactionHelper.js" as TransactionHelper
import "js/QEtherHelper.js" as QEtherHelper
import "."
ColumnLayout {
id: root
property alias title: titleLabel.text
property variant _data
property string role
property alias model: modelKeyValue
function add(key, value)
{
modelKeyValue.append({ "key": key, "value": value })
}
function clear()
{
modelKeyValue.clear()
}
function init()
{
modelKeyValue.clear()
if (typeof(computeData) !== "undefined" && computeData instanceof Function)
computeData()
else
{
if (_data !== undefined && _data[role] !== undefined)
{
var keys = Object.keys(_data[role])
for (var k in keys)
{
modelKeyValue.append({ "key": keys[k] === "" ? "undefined" : keys[k], "value": _data[role][keys[k]] })
}
}
}
}
RowLayout
{
Layout.preferredHeight: 20
Layout.fillWidth: true
Label
{
id: titleLabel
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
color: "white"
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 100
ListModel
{
id: modelKeyValue
}
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
color: "white"
radius: 2
ScrollView
{
id: columnValues
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
anchors.fill: parent
clip: true
ColumnLayout
{
anchors.margins: 10
Repeater
{
id: repeaterKeyValue
model: modelKeyValue
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 30
spacing: 0
Rectangle
{
Layout.preferredWidth: columnValues.width / 2
Label
{
anchors.left: parent.left
anchors.leftMargin: 10
text: {
if (index >= 0 && repeaterKeyValue.model.get(index).key !== undefined)
return repeaterKeyValue.model.get(index).key
else
return ""
}
}
}
Rectangle
{
Layout.preferredWidth: columnValues.width / 2 - 10
Label
{
anchors.right: parent.right
anchors.rightMargin: 10
text: {
if (index >= 0 && repeaterKeyValue.model.get(index).value !== undefined)
return repeaterKeyValue.model.get(index).value
else
return ""
}
}
}
}
}
}
}
}
}
}

2
mix/qml/MainContent.qml

@ -31,7 +31,7 @@ Rectangle {
property alias codeEditor: codeEditor
property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally
property bool firstCompile: true
property int scenarioMinWidth: 590
property int scenarioMinWidth: 620
Connections {
target: codeModel

124
mix/qml/ScenarioExecution.qml

@ -8,6 +8,7 @@ import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "."
Rectangle {
color: "#ededed"
property alias bc: blockChain
@ -18,50 +19,109 @@ Rectangle {
onProjectLoaded: {
loader.init()
}
}
Column
ScrollView
{
anchors.margins: 10
anchors.fill: parent
spacing: 10
ScenarioLoader
{
height: 100
width: parent.width
id: loader
onWidthChanged: {
columnExe.width = width - 40
}
Connections
ColumnLayout
{
target: blockChain
onChainChanged:
{
loader.needSaveOrReload()
}
}
id: columnExe
Layout.preferredWidth: parent.width
width: parent.width - 40
anchors.left: parent.left
anchors.leftMargin: 15
ColumnLayout
{
id: scenarioColumn
width: parent.width
spacing: 10
ScenarioLoader
{
anchors.horizontalCenter: parent.horizontalCenter
height: 100
Layout.preferredWidth: 400
width: 400
id: loader
}
Rectangle
{
width: parent.parent.width
height: 1
color: "#cccccc"
}
Connections
{
target: blockChain
onChainChanged:
{
loader.needSaveOrReload()
}
}
Connections
{
target: loader
onLoaded:
Rectangle
{
Layout.preferredWidth: parent.width
height: 1
color: "#cccccc"
}
Connections
{
target: loader
onLoaded:
{
watchers.clear()
blockChain.load(scenario)
}
}
BlockChain
{
id: blockChain
width: parent.width
}
Connections
{
target: blockChain
property var currentSelectedBlock
property var currentSelectedTx
onTxSelected: {
currentSelectedBlock = blockIndex
currentSelectedTx = txIndex
updateWatchers(blockIndex, txIndex)
}
function updateWatchers(blockIndex, txIndex){
var tx = blockChain.model.blocks[blockIndex].transactions[txIndex]
var state = blockChain.getState(tx.recordIndex)
watchers.updateWidthTx(tx, state, blockIndex, txIndex)
}
onRebuilding: {
watchers.clear()
}
onAccountAdded: {
watchers.addAccount(address, "0 wei")
}
}
}
Watchers
{
blockChain.load(scenario)
id: watchers
bc: blockChain
Layout.fillWidth: true
Layout.preferredHeight: 740
}
}
BlockChain
{
id: blockChain
width: parent.width
Rectangle
{
color: "transparent"
Layout.preferredHeight: 50
Layout.fillWidth: true
}
}
}
}

9
mix/qml/ScenarioLoader.qml

@ -107,11 +107,13 @@ ColumnLayout
Rectangle
{
id: editIconRect
anchors.top: scenarioName.top
anchors.topMargin: 6
anchors.left: scenarioName.right
anchors.leftMargin: 15
anchors.leftMargin: 20
Image {
source: "qrc:/qml/img/edit.png"
width: 10
source: "qrc:/qml/img/edittransaction.png"
width: 30
fillMode: Image.PreserveAspectFit
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
@ -126,7 +128,6 @@ ColumnLayout
scenarioNameEdit.save()
else
scenarioNameEdit.edit()
}
}
}

3
mix/qml/StateListModel.qml

@ -257,7 +257,8 @@ Item {
_secret = clientModel.newSecret();
var address = clientModel.address(_secret);
var name = qsTr("Account") + "-" + address.substring(0, 4);
return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address };
var amount = QEtherHelper.createEther(_balance, _unit)
return { name: name, secret: _secret, balance: amount, address: address };
}
function duplicateState(index)

7
mix/qml/TransactionDialog.qml

@ -16,7 +16,8 @@ Dialog {
width: 580
height: 500
visible: false
title: qsTr("Edit Transaction")
title: editMode ? qsTr("Edit Transaction") : qsTr("Add Transaction")
property bool editMode
property int transactionIndex
property int blockIndex
property alias gas: gasValueEdit.gasValue;
@ -390,7 +391,7 @@ Dialog {
objectName: "trTypeExecute"
exclusiveGroup: rbbuttonList
height: 30
text: qsTr("Execute Contract")
text: qsTr("Transact with Contract")
}
}
}
@ -687,7 +688,7 @@ Dialog {
}
Button {
text: qsTr("Update");
text: editMode ? qsTr("Update") : qsTr("Ok")
onClicked: {
var invalid = InputValidator.validate(paramsModel, paramValues);
if (invalid.length === 0)

203
mix/qml/Watchers.qml

@ -0,0 +1,203 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "js/TransactionHelper.js" as TransactionHelper
import "js/QEtherHelper.js" as QEtherHelper
import "."
Rectangle {
color: "#4F4F4F"
radius: 4
property variant tx
property variant currentState
property variant bc
property var blockIndex
property var txIndex
function clear()
{
from.text = ""
to.text = ""
value.text = ""
inputParams.clear()
returnParams.clear()
accounts.clear()
events.clear()
}
function addAccount(address, amount)
{
accounts.add(address, amount)
}
function updateWidthTx(_tx, _state, _blockIndex, _txIndex)
{
from.text = clientModel.resolveAddress(_tx.sender)
to.text = _tx.label
value.text = _tx.value.format()
tx = _tx
blockIndex = _blockIndex
txIndex = _txIndex
currentState = _state
inputParams.init()
if (_tx.isContractCreation)
{
returnParams.role = "creationAddr"
returnParams._data = {
creationAddr : {
}
}
returnParams._data.creationAddr[qsTr("contract address")] = _tx.returned
}
else
{
returnParams.role = "returnParameters"
returnParams._data = tx
}
returnParams.init()
accounts.init()
events.init()
}
Column {
anchors.fill: parent
spacing: 15
Rectangle
{
height: 15
width: parent.width - 30
color: "transparent"
Row
{
id: rowHeader
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: rowHeader.parent.top
anchors.topMargin: 6
spacing: 5
Label {
id: fromLabel
text: qsTr("from")
visible: from.text !== ""
color: "#EAB920"
}
Label {
id: from
color: "#EAB920"
elide: Text.ElideRight
maximumLineCount: 1
clip: true
width: 200
}
Label {
id: toLabel
text: qsTr("to")
visible: from.text !== ""
color: "#EAB920"
}
Label {
id: to
color: "#EAB920"
elide: Text.ElideRight
maximumLineCount: 1
clip: true
width: 100
}
Label {
id: value
color: "#EAB920"
font.italic: true
clip: true
}
}
Image {
anchors.right: rowHeader.parent.right
anchors.top: rowHeader.parent.top
anchors.topMargin: -3
source: "qrc:/qml/img/edittransaction2.png"
height: 30
fillMode: Image.PreserveAspectFit
visible: from.text !== ""
MouseArea
{
anchors.fill: parent
onClicked:
{
bc.blockChainRepeater.editTx(blockIndex, txIndex)
}
}
}
}
Rectangle {
height: 1
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
border.color: "#cccccc"
border.width: 1
}
KeyValuePanel
{
height: 150
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
id: inputParams
title: qsTr("INPUT PARAMETERS")
role: "parameters"
_data: tx
}
KeyValuePanel
{
height: 150
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
id: returnParams
title: qsTr("RETURN PARAMETERS")
role: "returnParameters"
_data: tx
}
KeyValuePanel
{
height: 150
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
id: accounts
title: qsTr("ACCOUNTS")
role: "accounts"
_data: currentState
}
KeyValuePanel
{
height: 150
width: parent.width - 30
anchors.horizontalCenter: parent.horizontalCenter
id: events
title: qsTr("EVENTS")
function computeData()
{
model.clear()
var ret = []
for (var k in tx.logs)
{
var param = ""
for (var p in tx.logs[k].param)
{
param += " " + tx.logs[k].param[p].value + " "
}
param = "(" + param + ")"
model.append({ "key": tx.logs[k].name, "value": param })
}
}
}
}
}

5
mix/res.qrc

@ -90,5 +90,10 @@
<file>qml/img/saveicon@2x.png</file>
<file>qml/img/sendtransactionicon.png</file>
<file>qml/img/sendtransactionicon@2x.png</file>
<file>qml/img/edittransaction@2x.png</file>
<file>qml/img/newblock@2x.png</file>
<file>qml/img/edittransaction2@2x.png</file>
<file>qml/img/edittransaction.png</file>
<file>qml/img/edittransaction2.png</file>
</qresource>
</RCC>

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)

20
test/TestHelper.cpp

@ -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;
}

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());

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"
}
}
}

4
test/libethereum/blockchain.cpp

@ -200,8 +200,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();

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>

195
test/libsolidity/SolidityEndToEndTest.cpp

@ -4806,6 +4806,201 @@ BOOST_AUTO_TEST_CASE(memory_arrays_dynamic_index_access_write)
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x20), u256(4), data));
}
BOOST_AUTO_TEST_CASE(memory_structs_read_write)
{
char const* sourceCode = R"(
contract Test {
struct S { uint8 x; uint16 y; uint z; uint8[2] a; }
S[5] data;
function testInit() returns (uint8 x, uint16 y, uint z, uint8 a, bool flag) {
S[2] memory d;
x = d[0].x;
y = d[0].y;
z = d[0].z;
a = d[0].a[1];
flag = true;
}
function testCopyRead() returns (uint8 x, uint16 y, uint z, uint8 a) {
data[2].x = 1;
data[2].y = 2;
data[2].z = 3;
data[2].a[1] = 4;
S memory s = data[2];
x = s.x;
y = s.y;
z = s.z;
a = s.a[1];
}
function testAssign() returns (uint8 x, uint16 y, uint z, uint8 a) {
S memory s;
s.x = 1;
s.y = 2;
s.z = 3;
s.a[1] = 4;
x = s.x;
y = s.y;
z = s.z;
a = s.a[1];
}
}
)";
compileAndRun(sourceCode, 0, "Test");
BOOST_CHECK(callContractFunction("testInit()") == encodeArgs(u256(0), u256(0), u256(0), u256(0), true));
BOOST_CHECK(callContractFunction("testCopyRead()") == encodeArgs(u256(1), u256(2), u256(3), u256(4)));
BOOST_CHECK(callContractFunction("testAssign()") == encodeArgs(u256(1), u256(2), u256(3), u256(4)));
}
BOOST_AUTO_TEST_CASE(memory_structs_as_function_args)
{
char const* sourceCode = R"(
contract Test {
struct S { uint8 x; uint16 y; uint z; }
function test() returns (uint x, uint y, uint z) {
S memory data = combine(1, 2, 3);
x = extract(data, 0);
y = extract(data, 1);
z = extract(data, 2);
}
function extract(S s, uint which) internal returns (uint x) {
if (which == 0) return s.x;
else if (which == 1) return s.y;
else return s.z;
}
function combine(uint8 x, uint16 y, uint z) internal returns (S s) {
s.x = x;
s.y = y;
s.z = z;
}
}
)";
compileAndRun(sourceCode, 0, "Test");
BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(1), u256(2), u256(3)));
}
BOOST_AUTO_TEST_CASE(memory_structs_nested)
{
char const* sourceCode = R"(
contract Test {
struct S { uint8 x; uint16 y; uint z; }
struct X { uint8 x; S s; }
function test() returns (uint a, uint x, uint y, uint z) {
X memory d = combine(1, 2, 3, 4);
a = extract(d, 0);
x = extract(d, 1);
y = extract(d, 2);
z = extract(d, 3);
}
function extract(X s, uint which) internal returns (uint x) {
if (which == 0) return s.x;
else if (which == 1) return s.s.x;
else if (which == 2) return s.s.y;
else return s.s.z;
}
function combine(uint8 a, uint8 x, uint16 y, uint z) internal returns (X s) {
s.x = a;
s.s.x = x;
s.s.y = y;
s.s.z = z;
}
}
)";
compileAndRun(sourceCode, 0, "Test");
BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(1), u256(2), u256(3), u256(4)));
}
BOOST_AUTO_TEST_CASE(memory_structs_nested_load)
{
char const* sourceCode = R"(
contract Test {
struct S { uint8 x; uint16 y; uint z; }
struct X { uint8 x; S s; uint8[2] a; }
X m_x;
function load() returns (uint a, uint x, uint y, uint z, uint a1, uint a2) {
m_x.x = 1;
m_x.s.x = 2;
m_x.s.y = 3;
m_x.s.z = 4;
m_x.a[0] = 5;
m_x.a[1] = 6;
X memory d = m_x;
a = d.x;
x = d.s.x;
y = d.s.y;
z = d.s.z;
a1 = d.a[0];
a2 = d.a[1];
}
function store() returns (uint a, uint x, uint y, uint z, uint a1, uint a2) {
X memory d;
d.x = 1;
d.s.x = 2;
d.s.y = 3;
d.s.z = 4;
d.a[0] = 5;
d.a[1] = 6;
m_x = d;
a = m_x.x;
x = m_x.s.x;
y = m_x.s.y;
z = m_x.s.z;
a1 = m_x.a[0];
a2 = m_x.a[1];
}
}
)";
compileAndRun(sourceCode, 0, "Test");
auto out = encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5), u256(6));
BOOST_CHECK(callContractFunction("load()") == out);
BOOST_CHECK(callContractFunction("store()") == out);
}
BOOST_AUTO_TEST_CASE(struct_constructor_nested)
{
char const* sourceCode = R"(
contract C {
struct X { uint x1; uint x2; }
struct S { uint s1; uint[3] s2; X s3; }
S s;
function C() {
uint[3] memory s2;
s2[1] = 9;
s = S(1, s2, X(4, 5));
}
function get() returns (uint s1, uint[3] s2, uint x1, uint x2)
{
s1 = s.s1;
s2 = s.s2;
x1 = s.s3.x1;
x2 = s.s3.x2;
}
}
)";
compileAndRun(sourceCode, 0, "C");
auto out = encodeArgs(u256(1), u256(0), u256(9), u256(0), u256(4), u256(5));
BOOST_CHECK(callContractFunction("get()") == out);
}
BOOST_AUTO_TEST_CASE(struct_named_constructor)
{
char const* sourceCode = R"(
contract C {
struct S { uint a; bool x; }
S public s;
function C() {
s = S({a: 1, x: true});
}
}
)";
compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunction("s()") == encodeArgs(u256(1), true));
}
BOOST_AUTO_TEST_SUITE_END()
}

41
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -2056,6 +2056,47 @@ BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable)
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(struct_constructor)
{
char const* sourceCode = R"(
contract C {
struct S { uint a; bool x; }
function f() {
S memory s = S(1, true);
}
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode));
}
BOOST_AUTO_TEST_CASE(struct_constructor_nested)
{
char const* sourceCode = R"(
contract C {
struct X { uint x1; uint x2; }
struct S { uint s1; uint[3] s2; X s3; }
function f() {
uint[3] memory s2;
S memory s = S(1, s2, X(4, 5));
}
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode));
}
BOOST_AUTO_TEST_CASE(struct_named_constructor)
{
char const* sourceCode = R"(
contract C {
struct S { uint a; bool x; }
function f() {
S memory s = S({a: 1, x: true});
}
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode));
}
BOOST_AUTO_TEST_SUITE_END()
}

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