Browse Source

Merge branch 'p2p' into whisper

cl-refactor
subtly 10 years ago
parent
commit
76af659e29
  1. 211
      CMakeLists.txt
  2. 24
      alethzero/DappLoader.cpp
  3. 9
      alethzero/Main.ui
  4. 23
      alethzero/MainWin.cpp
  5. 4
      cmake/EthDependencies.cmake
  6. 2
      libdevcore/Common.cpp
  7. 3
      libethcore/Common.h
  8. 68
      libethereum/BlockChain.cpp
  9. 10
      libethereum/BlockChain.h
  10. 44
      libethereum/BlockQueue.cpp
  11. 14
      libethereum/BlockQueue.h
  12. 49
      libethereum/Client.cpp
  13. 7
      libethereum/Client.h
  14. 1
      libethereum/ClientBase.h
  15. 70
      libethereum/EthereumHost.cpp
  16. 8
      libethereum/EthereumHost.h
  17. 13
      libethereum/EthereumPeer.cpp
  18. 5
      libethereum/EthereumPeer.h
  19. 1
      libevmcore/CMakeLists.txt
  20. 142
      libevmcore/CommonSubexpressionEliminator.cpp
  21. 61
      libevmcore/CommonSubexpressionEliminator.h
  22. 31
      libevmcore/ExpressionClasses.cpp
  23. 11
      libevmcore/ExpressionClasses.h
  24. 42
      libevmcore/Instruction.cpp
  25. 4
      libevmcore/Instruction.h
  26. 2
      libevmcore/SemanticInformation.cpp
  27. 175
      libp2p/Session.cpp
  28. 1
      libsolidity/CMakeLists.txt
  29. 14
      libweb3jsonrpc/CMakeLists.txt
  30. 16
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  31. 2
      mix/CMakeLists.txt
  32. 140
      mix/ClientModel.cpp
  33. 4
      mix/ClientModel.h
  34. 61
      mix/CodeModel.cpp
  35. 12
      mix/CodeModel.h
  36. 49
      mix/ContractCallDataEncoder.cpp
  37. 10
      mix/ContractCallDataEncoder.h
  38. 1
      mix/FileIo.cpp
  39. 19
      mix/MixApplication.cpp
  40. 1
      mix/MixApplication.h
  41. 2
      mix/MixClient.cpp
  42. 2
      mix/QBasicNodeDefinition.cpp
  43. 3
      mix/QBasicNodeDefinition.h
  44. 6
      mix/SolidityType.h
  45. 84
      mix/Web3Server.cpp
  46. 2
      mix/Web3Server.h
  47. 2
      mix/qml.qrc
  48. 2
      mix/qml/Application.qml
  49. 4
      mix/qml/Debugger.qml
  50. 409
      mix/qml/DeploymentDialog.qml
  51. 57
      mix/qml/LogsPane.qml
  52. 94
      mix/qml/NewProjectDialog.qml
  53. 2
      mix/qml/ProjectModel.qml
  54. 564
      mix/qml/StateDialog.qml
  55. 74
      mix/qml/StateList.qml
  56. 34
      mix/qml/StatusPane.qml
  57. 1
      mix/qml/StepActionImage.qml
  58. 9
      mix/qml/StructView.qml
  59. 379
      mix/qml/TransactionDialog.qml
  60. 1
      mix/qml/VariablesView.qml
  61. 14
      mix/qml/WebPreview.qml
  62. 12
      mix/qml/html/WebContainer.html
  63. 90
      mix/qml/js/Printer.js
  64. 42
      mix/qml/js/ProjectModel.js
  65. 16
      mix/qml/js/QEtherHelper.js
  66. 2
      mix/qml/js/TransactionHelper.js
  67. 106
      mix/qml/js/ansi2html.js
  68. 14
      mix/test/TestService.cpp
  69. 1
      mix/test/TestService.h
  70. 110
      mix/test/qml/TestMain.qml
  71. 140
      mix/test/qml/js/TestDebugger.js
  72. 71
      mix/test/qml/js/TestTutorial.js
  73. 2
      test/CMakeLists.txt
  74. 4
      test/SolidityEndToEndTest.cpp
  75. 168
      test/SolidityOptimizer.cpp
  76. 940
      test/bcUncleHeaderValiditiyFiller.json
  77. 93
      test/bcUncleTestFiller.json
  78. 36
      test/blockchain.cpp
  79. 2
      test/natspec.cpp
  80. 83
      test/stCallCreateCallCodeTestFiller.json
  81. 20
      test/ttTransactionTestFiller.json

211
CMakeLists.txt

@ -14,23 +14,22 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# user defined, defaults
# Normally, set(...CACHE...) creates cache variables, but does not modify them.
function(createDefaultCacheConfig)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(SERPENT_DEFAULT OFF)
else ()
set(SERPENT_DEFAULT ON)
endif ()
set(HEADLESS OFF CACHE BOOL "Do not compile GUI (AlethZero)")
set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)")
set(PARANOIA OFF CACHE BOOL "Additional run-time checks")
set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on")
set(EVMJIT OFF CACHE BOOL "Build a just-in-time compiler for EVM code (requires LLVM)")
set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.")
set(JUSTTESTS OFF CACHE BOOL "Build only for tests.")
set(SOLIDITY ON CACHE BOOL "Build the Solidity language components (required unless HEADLESS)")
set(SERPENT ${SERPENT_DEFAULT} CACHE BOOL "Build the Serpent language components (required unless HEADLESS)")
set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed")
set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL")
set(PROFILING OFF CACHE BOOL "Build in support for profiling")
set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).")
set(SOLIDITY ON CACHE BOOL "Build the Solidity language components")
set(SERPENT ON CACHE BOOL "Build the Serpent language components")
set(TOOLS ON CACHE BOOL "Build the tools components")
set(NCURSES ON CACHE BOOL "Build the NCurses components")
set(GUI ON CACHE BOOL "Build GUI components (AlethZero, Mix)")
set(TESTS ON CACHE BOOL "Build the tests.")
set(EVMJIT OFF CACHE BOOL "Build just-in-time compiler for EVM code (requires LLVM)")
set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL")
endfunction()
@ -60,10 +59,9 @@ function(configureProject)
add_definitions(-DETH_SOLIDITY)
endif()
if (HEADLESS OR JUSTTESTS)
add_definitions(-DETH_HEADLESS)
if (GUI)
add_definitions(-DETH_GUI)
endif()
endfunction()
set(CPPETHEREUM 1)
@ -143,6 +141,16 @@ set (ETH_HAVE_WEBENGINE 1)
# Normalise build options
# TODO: Abstract into something sensible and move into a function.
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(DECENT_PLATFORM OFF)
else ()
set(DECENT_PLATFORM ON)
endif ()
# Backwards compatibility
if (HEADLESS)
set(BUNDLE "minimal")
endif ()
if (PARANOIA)
set(PARANOIA ON)
else ()
@ -153,11 +161,6 @@ if (VMTRACE)
else ()
set(VMTRACE OFF)
endif ()
if (ETHASHCL)
set(ETHASHCL ON)
else ()
set(ETHASHCL OFF)
endif()
if (EVMJIT)
set(EVMJIT ON)
else ()
@ -168,44 +171,98 @@ if (FATDB)
else ()
set(FATDB OFF)
endif()
if (JSONRPC)
set(JSONRPC ON)
else ()
set(JSONRPC OFF)
endif ()
if (USENPM)
set(USENPM ON)
else ()
set(USENPM OFF)
endif ()
if (PROFILING)
set(PROFILING ON)
else ()
set(PROFILING OFF)
endif ()
if (SOLIDITY)
set(SOLIDITY ON)
else ()
set(SOLIDITY OFF)
endif()
if (SERPENT)
set(SERPENT ON)
set(SERPENT ${DECENT_PLATFORM})
else ()
set(SERPENT OFF)
endif()
if (HEADLESS)
set(HEADLESS ON)
if (GUI)
set(GUI ON)
set(JSONRPC ON)
else ()
set(HEADLESS OFF)
set(GUI OFF)
endif ()
if (JUSTTESTS)
set(JUSTTESTS ON)
if (TESTS)
set(TESTS ON)
else ()
set(JUSTTESTS OFF)
set(TESTS OFF)
endif ()
if (JSONRPC)
set(JSONRPC ON)
if (TOOLS)
set(TOOLS ON)
else ()
set(JSONRPC OFF)
set(TOOLS OFF)
endif ()
if (USENPM)
set(USENPM ON)
if (ETHASHCL)
set(ETHASHCL ON)
else ()
set(USENPM OFF)
endif ()
if (PROFILING)
set(PROFILING ON)
set(ETHASHCL OFF)
endif()
if (NCURSES)
set(NCURSES ${DECENT_PLATFORM})
else ()
set(PROFILING OFF)
set(NCURSES OFF)
endif ()
if (BUNDLE STREQUAL "minimal")
set(SERPENT OFF)
set(SOLIDITY OFF)
set(USENPM OFF)
set(GUI OFF)
set(NCURSES OFF)
set(TOOLS ON)
set(TESTS OFF)
elseif (BUNDLE STREQUAL "full")
set(SERPENT ${DECENT_PLATFORM})
set(SOLIDITY ON)
set(USENPM ON)
set(GUI ON)
set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "tests")
set(SERPENT ${DECENT_PLATFORM})
set(SOLIDITY ON)
set(USENPM OFF)
set(GUI OFF)
set(NCURSES OFF)
set(TOOLS OFF)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "user")
set(SERPENT OFF)
set(SOLIDITY OFF)
set(USENPM OFF)
set(GUI ON)
set(NCURSES ${DECENT_PLATFORM})
set(TOOLS ON)
set(TESTS OFF)
endif ()
# Default CMAKE_BUILD_TYPE to "Release".
set(CMAKE_BUILD_TYPE CACHE STRING "Relase")
set(CMAKE_BUILD_TYPE CACHE STRING "Release")
if ("x${CMAKE_BUILD_TYPE}" STREQUAL "x")
set(CMAKE_BUILD_TYPE "Release")
endif ()
@ -217,22 +274,27 @@ if ("x${TARGET_PLATFORM}" STREQUAL "x")
endif ()
message("------------------------------------------------------------------------")
message("-- CMake Version ${CMAKE_VERSION}")
message("-- Build type CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}")
message("-- Target platform TARGET_PLATFORM ${TARGET_PLATFORM}")
message("-- VM execution tracing VMTRACE ${VMTRACE}")
message("-- Profiling support PROFILING ${PROFILING}")
message("-- Additional (SLOW) database checking PARANOIA ${PARANOIA}")
message("-- Full database exploring FATDB ${FATDB}")
message("-- Build Javascript components from source USENPM ${USENPM}")
message("-- Build only headless components HEADLESS ${HEADLESS}")
message("-- Build only tests JUSTTESTS ${JUSTTESTS}")
message("-- Build Solidity language components SOLIDITY ${SOLIDITY}")
message("-- Build Serpent language components SERPENT ${SERPENT}")
message("-- Build OpenCL components ETHASHCL ${ETHASHCL}")
message("-- Build LLVM-based JIT EVM EVMJIT ${EVMJIT}")
message("-- Build with support for JSON-RPC JSONRPC ${JSONRPC}")
message("-- Build with support for Chromium ${ETH_HAVE_WEBENGINE}")
message("-- CMake Version ${CMAKE_VERSION}")
message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}")
message("-- TARGET_PLATFORM Target platform ${TARGET_PLATFORM}")
message("-- BUNDLE Build bundle ${BUNDLE}")
message("--------------------------------------------------------------- features")
message("-- Chromium support ${ETH_HAVE_WEBENGINE}")
message("-- VMTRACE VM execution tracing ${VMTRACE}")
message("-- PROFILING Profiling support ${PROFILING}")
message("-- PARANOIA Additional (SLOW) database checking ${PARANOIA}")
message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}")
message("------------------------------------------------------------- components")
message("-- TOOLS Build basic tools ${TOOLS}")
message("-- SOLIDITY Build Solidity language components ${SOLIDITY}")
message("-- SERPENT Build Serpent language components ${SERPENT}")
message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
message("------------------------------------------------------------------------")
message("")
@ -269,7 +331,7 @@ if (SOLIDITY)
add_subdirectory(libsolidity)
endif ()
if (NOT JUSTTESTS)
if (TOOLS)
add_subdirectory(lllc)
if (SOLIDITY)
add_subdirectory(solc)
@ -278,7 +340,6 @@ endif()
if (JSONRPC)
add_subdirectory(libweb3jsonrpc)
add_subdirectory(ethrpctest)
endif()
add_subdirectory(secp256k1)
@ -294,12 +355,17 @@ endif ()
add_subdirectory(libethcore)
add_subdirectory(libevm)
add_subdirectory(libethereum)
add_subdirectory(libwebthree)
add_subdirectory(libtestutils)
add_subdirectory(test)
if (NOT JUSTTESTS)
if (TESTS)
add_subdirectory(libtestutils)
add_subdirectory(test)
if (JSONRPC)
add_subdirectory(ethrpctest)
endif ()
endif ()
if (TOOLS)
add_subdirectory(rlp)
add_subdirectory(abi)
@ -309,25 +375,26 @@ if (NOT JUSTTESTS)
add_subdirectory(exp)
endif ()
# TODO check msvc
if(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(neth)
endif ()
if (NOT HEADLESS)
endif()
add_subdirectory(libnatspec)
add_subdirectory(libjsqrc)
if (NCURSES)
add_subdirectory(neth)
endif ()
if (ETH_HAVE_WEBENGINE)
add_subdirectory(alethzero)
# add_subdirectory(third) // reenable once not qtwebkit.
endif()
if (GUI)
add_subdirectory(mix)
add_subdirectory(libnatspec)
add_subdirectory(libjsqrc)
if (ETH_HAVE_WEBENGINE)
add_subdirectory(alethzero)
# add_subdirectory(third) // reenable once not qtwebkit.
endif()
if (SOLIDITY)
add_subdirectory(mix)
endif ()
endif()
#unset(TARGET_PLATFORM CACHE)

24
alethzero/DappLoader.cpp

@ -193,10 +193,26 @@ Manifest DappLoader::loadManifest(std::string const& _manifest)
void DappLoader::loadDapp(QString const& _uri)
{
DappLocation location = resolveAppUri(_uri);
QUrl uri(location.contentUri);
QNetworkRequest request(uri);
m_uriHashes[uri] = location.contentHash;
QUrl uri(_uri);
QUrl contentUri;
h256 hash;
if (uri.path().endsWith(".dapp") && uri.query().startsWith("hash="))
{
contentUri = uri;
QString query = uri.query();
query.remove("hash=");
if (!query.startsWith("0x"))
query.insert(0, "0x");
hash = jsToFixed<32>(query.toStdString());
}
else
{
DappLocation location = resolveAppUri(_uri);
contentUri = location.contentUri;
hash = location.contentHash;
}
QNetworkRequest request(contentUri);
m_uriHashes[uri] = hash;
m_net.get(request);
}

9
alethzero/Main.ui

@ -45,6 +45,13 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="chainStatus">
<property name="text">
<string>#0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="balance">
<property name="text">
@ -118,7 +125,7 @@
<x>0</x>
<y>0</y>
<width>1617</width>
<height>22</height>
<height>24</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">

23
alethzero/MainWin.cpp

@ -161,8 +161,11 @@ Main::Main(QWidget *parent) :
statusBar()->addPermanentWidget(ui->balance);
statusBar()->addPermanentWidget(ui->peerCount);
statusBar()->addPermanentWidget(ui->mineStatus);
statusBar()->addPermanentWidget(ui->chainStatus);
statusBar()->addPermanentWidget(ui->blockCount);
ui->blockCount->setText(QString("PV%2 D%3 H%4 v%5").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(c_ethashVersion).arg(dev::Version));
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
QSettings s("ethereum", "alethzero");
@ -301,16 +304,25 @@ unsigned Main::installWatch(dev::h256 _tf, WatchHandler const& _f)
void Main::uninstallWatch(unsigned _w)
{
cdebug << "!!! Main: uninstalling watch" << _w;
ethereum()->uninstallWatch(_w);
m_handlers.erase(_w);
}
void Main::installWatches()
{
auto newBlockId = installWatch(ChainChangedFilter, [=](LocalisedLogEntries const&){
onNewBlock();
});
auto newPendingId = installWatch(PendingChangedFilter, [=](LocalisedLogEntries const&){
onNewPending();
});
cdebug << "newBlock watch ID: " << newBlockId;
cdebug << "newPending watch ID: " << newPendingId;
installWatch(LogFilter().address(c_newConfig), [=](LocalisedLogEntries const&) { installNameRegWatch(); });
installWatch(LogFilter().address(c_newConfig), [=](LocalisedLogEntries const&) { installCurrenciesWatch(); });
installWatch(PendingChangedFilter, [=](LocalisedLogEntries const&){ onNewPending(); });
installWatch(ChainChangedFilter, [=](LocalisedLogEntries const&){ onNewBlock(); });
}
Address Main::getNameReg() const
@ -900,7 +912,7 @@ void Main::on_urlEdit_returnPressed()
{
QString s = ui->urlEdit->text();
QUrl url(s);
if (url.scheme().isEmpty() || url.scheme() == "eth")
if (url.scheme().isEmpty() || url.scheme() == "eth" || url.path().endsWith(".dapp"))
{
try
{
@ -1089,9 +1101,9 @@ void Main::refreshAccounts()
void Main::refreshBlockCount()
{
cwatch << "refreshBlockCount()";
auto d = ethereum()->blockChain().details();
ui->blockCount->setText(QString("%4 #%1 PV%2 D%3 H%5").arg(d.number).arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(c_ethashVersion));
BlockQueueStatus b = ethereum()->blockQueueStatus();
ui->chainStatus->setText(QString("%3 ready %4 future %5 unknown %6 bad %1 #%2").arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(d.number).arg(b.ready).arg(b.future).arg(b.unknown).arg(b.bad));
}
void Main::on_turboMining_triggered()
@ -1270,6 +1282,7 @@ void Main::timerEvent(QTimerEvent*)
refreshNetwork();
refreshWhispers();
refreshCache();
refreshBlockCount();
poll();
}
else

4
cmake/EthDependencies.cmake

@ -101,7 +101,7 @@ find_program(ETH_JSON_RPC_STUB jsonrpcstub)
message(" - jsonrpcstub location : ${ETH_JSON_RPC_STUB}")
# do not compile GUI
if (NOT HEADLESS)
if (GUI)
# we need json rpc to build alethzero
if (NOT JSON_RPC_CPP_FOUND)
@ -153,7 +153,7 @@ if (NOT HEADLESS)
endif()
endif()
endif() #HEADLESS
endif() #GUI
# use multithreaded boost libraries, with -mt suffix
set(Boost_USE_MULTITHREADED ON)

2
libdevcore/Common.cpp

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

3
libethcore/Common.h

@ -92,7 +92,8 @@ enum class ImportResult
FutureTime,
AlreadyInChain,
AlreadyKnown,
Malformed
Malformed,
BadChain
};
}

68
libethereum/BlockChain.cpp

@ -306,39 +306,49 @@ LastHashes BlockChain::lastHashes(unsigned _n) const
return m_lastLastHashes;
}
pair<h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max)
tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max)
{
_bq.tick(*this);
vector<bytes> blocks;
_bq.drain(blocks, _max);
h256s ret;
h256s fresh;
h256s dead;
h256s badBlocks;
for (auto const& block: blocks)
{
try
{
for (auto h: import(block, _stateDB))
ret.push_back(h);
auto r = import(block, _stateDB);
bool isOld = true;
for (auto const& h: r.first)
if (h == r.second)
isOld = false;
else if (isOld)
dead.push_back(h);
else
fresh.push_back(h);
}
catch (UnknownParent)
{
cwarn << "Unknown parent of block!!!" << BlockInfo::headerHash(block).abridged() << boost::current_exception_diagnostic_information();
_bq.import(&block, *this);
cwarn << "ODD: Import queue contains block with unknown parent." << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(BlockInfo::headerHash(block));
}
catch (Exception const& _e)
{
cwarn << "Unexpected exception!" << diagnostic_information(_e);
_bq.import(&block, *this);
cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << diagnostic_information(_e);
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks.push_back(BlockInfo::headerHash(block));
}
catch (...)
{}
}
bool yetMore = _bq.doneDrain();
return make_pair(ret, yetMore);
return make_tuple(fresh, dead, _bq.doneDrain(badBlocks));
}
h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force) noexcept
pair<h256s, h256> BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force) noexcept
{
try
{
@ -347,11 +357,11 @@ h256s BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB,
catch (...)
{
cwarn << "Unexpected exception! Could not import block!" << boost::current_exception_diagnostic_information();
return h256s();
return make_pair(h256s(), h256());
}
}
h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force)
pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force)
{
//@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
@ -532,14 +542,14 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force)
);
// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children.";
h256s ret;
h256s route;
h256 common;
// This might be the new best block...
h256 last = currentHash();
if (td > details(last).totalDifficulty)
{
h256 common;
unsigned commonIndex;
tie(ret, common, commonIndex) = treeRoute(last, newHash);
tie(route, common, commonIndex) = treeRoute(last, newHash);
{
WriteGuard l(x_lastBlockHash);
m_lastBlockHash = newHash;
@ -554,7 +564,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force)
clearBlockBlooms(number(common) + 1, number(last) + 1);
// Go through ret backwards until hash != last.parent and update m_transactionAddresses, m_blockHashes
for (auto i = ret.rbegin(); i != ret.rend() && *i != common; ++i)
for (auto i = route.rbegin(); i != route.rend() && *i != common; ++i)
{
auto b = block(*i);
BlockInfo bi(b);
@ -606,7 +616,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force)
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp()));
}
clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret);
clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(route);
noteCanonChanged();
StructuredLogger::chainNewHead(
@ -631,7 +641,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db, bool _force)
cnote << "checkBest:" << checkBest;
#endif
return ret;
return make_pair(route, common);
}
void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end)
@ -704,21 +714,21 @@ tuple<h256s, h256, unsigned> BlockChain::treeRoute(h256 const& _from, h256 const
tn--;
// cdebug << "to:" << tn << _to.abridged();
}
while (from != to)
for (;; from = details(from).parent, to = details(to).parent)
{
if (!from)
assert(from);
if (!to)
assert(to);
from = details(from).parent;
to = details(to).parent;
if (_pre && (from != to || _common))
ret.push_back(from);
if (_post && (from != to || (!_pre && _common)))
back.push_back(to);
fn--;
tn--;
// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged();
// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged();
if (from == to)
break;
if (!from)
assert(from);
if (!to)
assert(to);
}
ret.reserve(ret.size() + back.size());
unsigned i = ret.size() - (int)(_common && !ret.empty() && !back.empty());

10
libethereum/BlockChain.h

@ -100,15 +100,15 @@ public:
void process();
/// Sync the chain with any incoming blocks. All blocks should, if processed in order
std::pair<h256s, bool> sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
std::tuple<h256s, h256s, bool> sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force = false) noexcept;
std::pair<h256s, h256> attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _force = false) noexcept;
/// Import block into disk-backed DB
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
h256s import(bytes const& _block, OverlayDB const& _stateDB, bool _force = false);
std::pair<h256s, h256> import(bytes const& _block, OverlayDB const& _stateDB, bool _force = false);
/// Returns true if the given block is known (though not necessarily a part of the canon chain).
bool isKnown(h256 const& _hash) const;
@ -178,6 +178,10 @@ public:
bytes transaction(h256 const& _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); }
bytes transaction(unsigned _i) const { return transaction(currentHash(), _i); }
/// Get all transactions from a block.
std::vector<bytes> transactions(h256 const& _blockHash) const { bytes b = block(_blockHash); std::vector<bytes> ret; for (auto const& i: RLP(b)[1]) ret.push_back(i.data().toBytes()); return ret; }
std::vector<bytes> transactions() const { return transactions(currentHash()); }
/// Get a number for the given hash (or the most recent mined if none given). Thread-safe.
unsigned number(h256 const& _hash) const { return details(_hash).number; }
unsigned number() const { return number(currentHash()); }

44
libethereum/BlockQueue.cpp

@ -38,7 +38,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
UpgradableGuard l(m_lock);
if (m_readySet.count(h) || m_drainingSet.count(h) || m_unknownSet.count(h))
if (m_readySet.count(h) || m_drainingSet.count(h) || m_unknownSet.count(h) || m_knownBad.count(h))
{
// Already know about this one.
cblockq << "Already known.";
@ -48,20 +48,17 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi;
#if ETH_CATCH
try
#endif
{
// TODO: quick verify
bi.populate(_block);
bi.verifyInternals(_block);
}
#if ETH_CATCH
catch (Exception const& _e)
{
cwarn << "Ignoring malformed block: " << diagnostic_information(_e);
return ImportResult::Malformed;
}
#endif
// Check block doesn't already exist first!
if (_bc.details(h))
@ -82,7 +79,13 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
else
{
// We now know it.
if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash))
if (m_knownBad.count(bi.parentHash))
{
m_knownBad.insert(bi.hash());
// bad parent; this is bad too, note it as such
return ImportResult::BadChain;
}
else if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash))
{
// We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on.
cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged();
@ -104,6 +107,35 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
}
}
namespace dev {
template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b)
{
for (auto const& i: _b)
_a.insert(i);
return _a;
} }
bool BlockQueue::doneDrain(h256s const& _bad)
{
WriteGuard l(m_lock);
m_drainingSet.clear();
if (_bad.size())
{
vector<bytes> old;
swap(m_ready, old);
for (auto& b: old)
{
BlockInfo bi(b);
if (m_knownBad.count(bi.parentHash))
m_knownBad.insert(bi.hash());
else
m_ready.push_back(std::move(b));
}
}
m_knownBad += _bad;
return !m_readySet.empty();
}
void BlockQueue::tick(BlockChain const& _bc)
{
unsigned t = time(0);

14
libethereum/BlockQueue.h

@ -38,6 +38,14 @@ class BlockChain;
struct BlockQueueChannel: public LogChannel { static const char* name() { return "[]Q"; } static const int verbosity = 4; };
#define cblockq dev::LogOutputStream<dev::eth::BlockQueueChannel, true>()
struct BlockQueueStatus
{
size_t ready;
size_t future;
size_t unknown;
size_t bad;
};
/**
* @brief A queue of blocks. Sits between network or other I/O and the BlockChain.
* Sorts them ready for blockchain insertion (with the BlockChain::sync() method).
@ -58,7 +66,7 @@ public:
/// Must be called after a drain() call. Notes that the drained blocks have been imported into the blockchain, so we can forget about them.
/// @returns true iff there are additional blocks ready to be processed.
bool doneDrain() { WriteGuard l(m_lock); m_drainingSet.clear(); return !m_readySet.empty(); }
bool doneDrain(h256s const& _knownBad = h256s());
/// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain).
void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); }
@ -72,6 +80,9 @@ public:
/// Return first block with an unknown parent.
h256 firstUnknown() const { ReadGuard l(m_lock); return m_unknownSet.size() ? *m_unknownSet.begin() : h256(); }
/// Get some infomration on the current status.
BlockQueueStatus status() const { ReadGuard l(m_lock); return BlockQueueStatus{m_ready.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; }
private:
void noteReadyWithoutWriteGuard(h256 _b);
void notePresentWithoutWriteGuard(bytesConstRef _block);
@ -83,6 +94,7 @@ private:
std::set<h256> m_unknownSet; ///< Set of all blocks whose parents are not ready/in-chain.
std::multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For transactions that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears.
std::multimap<unsigned, bytes> m_future; ///< Set of blocks that are not yet valid.
std::set<h256> m_knownBad; ///< Set of blocks that we know will never be valid.
};
}

49
libethereum/Client.cpp

@ -453,6 +453,7 @@ void Client::doWork()
// TODO: enable a short-circuit option since we mined it. will need to get the end state from the miner.
auto lm = dynamic_cast<LocalMiner*>(&m);
h256s hs;
h256 c;
if (false && lm && !m_verifyOwnBlocks)
{
// TODO: implement
@ -463,12 +464,13 @@ void Client::doWork()
{
cwork << "CHAIN <== postSTATE";
WriteGuard l(x_stateDB);
hs = m_bc.attemptImport(m.blockData(), m_stateDB);
tie(hs, c) = m_bc.attemptImport(m.blockData(), m_stateDB);
}
if (hs.size())
{
for (auto const& h: hs)
appendFromNewBlock(h, changeds);
if (h != c)
appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
}
for (auto& m: m_localMiners)
@ -498,18 +500,42 @@ void Client::doWork()
cwork << "BQ ==> CHAIN ==> STATE";
OverlayDB db = m_stateDB;
x_stateDB.unlock();
h256s newBlocks;
h256s fresh;
h256s dead;
bool sgw;
tie(newBlocks, sgw) = m_bc.sync(m_bq, db, 100); // TODO: remove transactions from m_tq nicely rather than relying on out of date nonce later on.
tie(fresh, dead, sgw) = m_bc.sync(m_bq, db, 100);
// insert transactions that we are declaring the dead part of the chain
for (auto const& h: dead)
{
clog(ClientNote) << "Dead block:" << h.abridged();
for (auto const& t: m_bc.transactions(h))
{
clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckSignature::None);
m_tq.import(t);
}
}
// remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: fresh)
{
clog(ClientChat) << "Mined block:" << h.abridged();
for (auto const& th: m_bc.transactionHashes(h))
{
clog(ClientNote) << "Safely dropping transaction " << th;
m_tq.drop(th);
}
}
stillGotWork = stillGotWork | sgw;
if (newBlocks.size())
if (!fresh.empty())
{
for (auto i: newBlocks)
for (auto i: fresh)
appendFromNewBlock(i, changeds);
changeds.insert(ChainChangedFilter);
}
x_stateDB.lock();
if (newBlocks.size())
if (fresh.size())
m_stateDB = db;
cwork << "preSTATE <== CHAIN";
@ -523,7 +549,7 @@ void Client::doWork()
// TODO: Move transactions pending from m_postMine back to transaction queue.
}
// returns h256s as blooms, once for each transaction.
// returns TransactionReceipts, once for each transaction.
cwork << "postSTATE <== TQ";
TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp);
if (newPendingReceipts.size())
@ -536,8 +562,15 @@ void Client::doWork()
if (isMining())
cnote << "Additional transaction ready: Restarting mining operation.";
resyncStateNeeded = true;
if (auto h = m_host.lock())
h->noteNewTransactions();
}
}
if (!changeds.empty())
if (auto h = m_host.lock())
h->noteNewBlocks();
if (resyncStateNeeded)
{
ReadGuard l(x_localMiners);

7
libethereum/Client.h

@ -114,6 +114,11 @@ private:
std::array<u256, 9> m_octiles;
};
struct ClientNote: public LogChannel { static const char* name() { return "*C*"; } static const int verbosity = 2; };
struct ClientChat: public LogChannel { static const char* name() { return "=C="; } static const int verbosity = 4; };
struct ClientTrace: public LogChannel { static const char* name() { return "-C-"; } static const int verbosity = 7; };
struct ClientDetail: public LogChannel { static const char* name() { return " C "; } static const int verbosity = 14; };
/**
* @brief Main API hub for interfacing with Ethereum.
*/
@ -168,6 +173,8 @@ public:
dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; }
/// Get the object representing the current canonical blockchain.
CanonBlockChain const& blockChain() const { return m_bc; }
/// Get some information on the block queue.
BlockQueueStatus blockQueueStatus() const { return m_bq.status(); }
// Mining stuff:

1
libethereum/ClientBase.h

@ -108,6 +108,7 @@ public:
virtual LocalisedLogEntries peekWatch(unsigned _watchId) const override;
virtual LocalisedLogEntries checkWatch(unsigned _watchId) override;
// TODO: switch all the _blockHash arguments to also accept _blockNumber
virtual h256 hashFromNumber(unsigned _number) const override;
virtual eth::BlockInfo blockInfo(h256 _hash) const override;
virtual eth::BlockDetails blockDetails(h256 _hash) const override;

70
libethereum/EthereumHost.cpp

@ -159,9 +159,22 @@ void EthereumHost::doWork()
// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
{
maintainTransactions();
maintainBlocks(h);
if (m_newTransactions)
{
m_newTransactions = false;
maintainTransactions();
}
if (m_newBlocks)
{
m_newBlocks = false;
maintainBlocks(h);
}
}
for (auto p: peerSessions())
if (shared_ptr<EthereumPeer> const& ep = p.first->cap<EthereumPeer>())
ep->tick();
// return netChange;
// TODO: Figure out what to do with netChange.
(void)netChange;
@ -170,18 +183,27 @@ void EthereumHost::doWork()
void EthereumHost::maintainTransactions()
{
// Send any new transactions.
map<std::shared_ptr<EthereumPeer>, h256s> peerTransactions;
auto ts = m_tq.transactions();
for (auto const& i: ts)
{
bool unsent = !m_transactionsSent.count(i.first);
for (auto const& p: randomSelection(25, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }))
peerTransactions[p].push_back(i.first);
}
for (auto p: peerSessions())
if (auto ep = p.first->cap<EthereumPeer>().get())
if (auto ep = p.first->cap<EthereumPeer>())
{
bytes b;
unsigned n = 0;
for (auto const& i: m_tq.transactions())
if (ep->m_requireTransactions || (!m_transactionsSent.count(i.first) && !ep->m_knownTransactions.count(i.first)))
{
b += i.second.rlp();
++n;
m_transactionsSent.insert(i.first);
}
for (auto const& h: peerTransactions[ep])
{
b += ts[h].rlp();
++n;
}
for (auto const& t: ts)
m_transactionsSent.insert(t.first);
ep->clearKnownTransactions();
if (n || ep->m_requireTransactions)
@ -194,6 +216,27 @@ void EthereumHost::maintainTransactions()
}
}
std::vector<std::shared_ptr<EthereumPeer>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
{
std::vector<std::shared_ptr<EthereumPeer>> candidates;
candidates.reserve(peerSessions().size());
for (auto const& j: peerSessions())
{
auto pp = j.first->cap<EthereumPeer>();
if (_allow(pp.get()))
candidates.push_back(pp);
}
std::vector<std::shared_ptr<EthereumPeer>> ret;
for (unsigned i = (peerSessions().size() * _percent + 99) / 100; i-- && candidates.size();)
{
unsigned n = rand() % candidates.size();
ret.push_back(std::move(candidates[n]));
candidates.erase(candidates.begin() + n);
}
return ret;
}
void EthereumHost::maintainBlocks(h256 _currentHash)
{
// Send any new blocks.
@ -201,16 +244,13 @@ void EthereumHost::maintainBlocks(h256 _currentHash)
{
clog(NetMessageSummary) << "Sending a new block (current is" << _currentHash << ", was" << m_latestBlockSent << ")";
for (auto j: peerSessions())
for (auto const& p: randomSelection(25, [&](EthereumPeer* p){return !p->m_knownBlocks.count(_currentHash); }))
{
auto p = j.first->cap<EthereumPeer>().get();
RLPStream ts;
p->prep(ts, NewBlockPacket, 2).appendRaw(m_chain.block(), 1).append(m_chain.details().totalDifficulty);
Guard l(p->x_knownBlocks);
if (!p->m_knownBlocks.count(_currentHash))
p->sealAndSend(ts);
p->sealAndSend(ts);
p->m_knownBlocks.clear();
}
m_latestBlockSent = _currentHash;

8
libethereum/EthereumHost.h

@ -76,7 +76,12 @@ public:
bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; }
void noteNewBlocks() { m_newBlocks = true; }
private:
std::vector<std::shared_ptr<EthereumPeer>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
/// Session is tell us that we may need (re-)syncing with the peer.
void noteNeedsSyncing(EthereumPeer* _who);
@ -119,6 +124,9 @@ private:
h256Set m_transactionsSent;
std::set<p2p::NodeId> m_banned;
bool m_newTransactions = false;
bool m_newBlocks = false;
};
}

13
libethereum/EthereumPeer.cpp

@ -220,6 +220,8 @@ void EthereumPeer::setAsking(Asking _a, bool _isSyncing)
m_syncingNeededBlocks.clear();
}
m_lastAsk = chrono::system_clock::now();
session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?");
session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : ""));
}
@ -235,6 +237,13 @@ void EthereumPeer::setNeedsSyncing(h256 _latestHash, u256 _td)
session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : ""));
}
void EthereumPeer::tick()
{
if (chrono::system_clock::now() - m_lastAsk > chrono::seconds(10) && m_asking != Asking::Nothing)
// timeout
session()->disconnect(PingTimeout);
}
bool EthereumPeer::isSyncing() const
{
return host()->m_syncer == this;
@ -451,6 +460,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
disable("Malformed block received.");
return true;
@ -505,8 +515,9 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
disable("Malformed block received.");
break;
return true;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:

5
libethereum/EthereumPeer.h

@ -105,6 +105,9 @@ private:
/// Check whether the session should bother grabbing the peer's blocks.
bool shouldGrabBlocks() const;
/// Runs period checks to check up on the peer.
void tick();
/// Peer's protocol version.
unsigned m_protocolVersion;
/// Peer's network id.
@ -112,6 +115,8 @@ 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;
/// Whether this peer is in the process of syncing or not. Only one peer can be syncing at once.
bool m_isSyncing = false;

1
libevmcore/CMakeLists.txt

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

142
libevmcore/CommonSubexpressionEliminator.cpp

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

61
libevmcore/CommonSubexpressionEliminator.h

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

31
libevmcore/ExpressionClasses.cpp

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

11
libevmcore/ExpressionClasses.h

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

42
libevmcore/Instruction.cpp

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

4
libevmcore/Instruction.h

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

2
libevmcore/SemanticInformation.cpp

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

175
libp2p/Session.cpp

@ -156,109 +156,104 @@ bool Session::interpret(PacketType _t, RLP const& _r)
clogS(NetRight) << _t << _r;
try // Generic try-catch block designed to capture RLP format errors - TODO: give decent diagnostics, make a bit more specific over what is caught.
{
switch (_t)
{
case DisconnectPacket:
{
string reason = "Unspecified";
auto r = (DisconnectReason)_r[0].toInt<int>();
if (!_r[0].isInt())
drop(BadProtocol);
else
switch (_t)
{
reason = reasonOf(r);
clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")";
drop(DisconnectRequested);
}
break;
}
case PingPacket:
{
clogS(NetTriviaSummary) << "Ping";
RLPStream s;
sealAndSend(prep(s, PongPacket));
break;
}
case PongPacket:
m_info.lastPing = std::chrono::steady_clock::now() - m_ping;
clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_info.lastPing).count() << " ms";
break;
case GetPeersPacket:
{
// Disabled for interop testing.
// GetPeers/PeersPacket will be modified to only exchange new nodes which it's peers are interested in.
break;
clogS(NetTriviaSummary) << "GetPeers";
m_theyRequestedNodes = true;
serviceNodesRequest();
break;
}
case PeersPacket:
// Disabled for interop testing.
// GetPeers/PeersPacket will be modified to only exchange new nodes which it's peers are interested in.
break;
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
m_weRequestedNodes = false;
for (unsigned i = 0; i < _r.itemCount(); ++i)
case DisconnectPacket:
{
bi::address peerAddress;
if (_r[i][0].size() == 16)
peerAddress = bi::address_v6(_r[i][0].toHash<FixedHash<16>>().asArray());
else if (_r[i][0].size() == 4)
peerAddress = bi::address_v4(_r[i][0].toHash<FixedHash<4>>().asArray());
string reason = "Unspecified";
auto r = (DisconnectReason)_r[0].toInt<int>();
if (!_r[0].isInt())
drop(BadProtocol);
else
{
cwarn << "Received bad peer packet:" << _r;
disconnect(BadProtocol);
return true;
reason = reasonOf(r);
clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")";
drop(DisconnectRequested);
}
auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt<short>());
NodeId id = _r[i][2].toHash<NodeId>();
clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")";
break;
}
case PingPacket:
{
clogS(NetTriviaSummary) << "Ping";
RLPStream s;
sealAndSend(prep(s, PongPacket));
break;
}
case PongPacket:
m_info.lastPing = std::chrono::steady_clock::now() - m_ping;
clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_info.lastPing).count() << " ms";
break;
case GetPeersPacket:
// Disabled for interop testing.
// GetPeers/PeersPacket will be modified to only exchange new nodes which it's peers are interested in.
break;
clogS(NetTriviaSummary) << "GetPeers";
m_theyRequestedNodes = true;
serviceNodesRequest();
break;
case PeersPacket:
// Disabled for interop testing.
// GetPeers/PeersPacket will be modified to only exchange new nodes which it's peers are interested in.
break;
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
m_weRequestedNodes = false;
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
bi::address peerAddress;
if (_r[i][0].size() == 16)
peerAddress = bi::address_v6(_r[i][0].toHash<FixedHash<16>>().asArray());
else if (_r[i][0].size() == 4)
peerAddress = bi::address_v4(_r[i][0].toHash<FixedHash<4>>().asArray());
else
{
cwarn << "Received bad peer packet:" << _r;
disconnect(BadProtocol);
return true;
}
auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt<short>());
NodeId id = _r[i][2].toHash<NodeId>();
if (!isPublicAddress(peerAddress))
goto CONTINUE; // Private address. Ignore.
clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")";
if (!id)
goto LAMEPEER; // Null identity. Ignore.
if (!isPublicAddress(peerAddress))
goto CONTINUE; // Private address. Ignore.
if (m_server->id() == id)
goto LAMEPEER; // Just our info - we already have that.
if (!id)
goto LAMEPEER; // Null identity. Ignore.
if (id == this->id())
goto LAMEPEER; // Just their info - we already have that.
if (m_server->id() == id)
goto LAMEPEER; // Just our info - we already have that.
if (!ep.port())
goto LAMEPEER; // Zero port? Don't think so.
if (id == this->id())
goto LAMEPEER; // Just their info - we already have that.
if (ep.port() >= /*49152*/32768)
goto LAMEPEER; // Private port according to IANA.
if (!ep.port())
goto LAMEPEER; // Zero port? Don't think so.
// OK passed all our checks. Assume it's good.
addRating(1000);
m_server->addNode(id, ep.address(), ep.port(), ep.port());
clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")";
CONTINUE:;
LAMEPEER:;
}
break;
default:
{
for (auto const& i: m_capabilities)
if (_t >= i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount())
{
if (i.second->m_enabled)
return i.second->interpret(_t - i.second->m_idOffset, _r);
else
return true;
if (ep.port() >= /*49152*/32768)
goto LAMEPEER; // Private port according to IANA.
// OK passed all our checks. Assume it's good.
addRating(1000);
m_server->addNode(id, ep.address(), ep.port(), ep.port());
clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")";
CONTINUE:;
LAMEPEER:;
}
return false;
}
}
break;
default:
for (auto const& i: m_capabilities)
if (_t >= i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount())
{
if (i.second->m_enabled)
return i.second->interpret(_t - i.second->m_idOffset, _r);
else
return true;
}
return false;
}
}
catch (std::exception const& _e)
{

1
libsolidity/CMakeLists.txt

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

14
libweb3jsonrpc/CMakeLists.txt

@ -22,9 +22,9 @@ file(GLOB HEADERS "*.h")
if (ETH_STATIC)
add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS})
else()
else ()
add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS})
endif()
endif ()
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
@ -33,11 +33,13 @@ target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES})
target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} secp256k1)
target_link_libraries(${EXECUTABLE} solidity)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
if (SOLIDITY)
target_link_libraries(${EXECUTABLE} solidity)
endif ()
if (SERPENT)
target_link_libraries(${EXECUTABLE} serpent)
endif()
endif ()
if (ETH_JSON_RPC_STUB)
add_custom_target(jsonrpcstub)
@ -51,7 +53,7 @@ if (ETH_JSON_RPC_STUB)
-P "${ETH_SCRIPTS_DIR}/jsonrpcstub.cmake"
)
add_dependencies(${EXECUTABLE} jsonrpcstub)
endif()
endif ()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

16
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -26,9 +26,11 @@
#include <jsonrpccpp/common/exception.h>
#include <libdevcore/CommonData.h>
#if ETH_SOLIDITY
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/SourceReferenceFormatter.h>
#endif
#include <libevmcore/Instruction.h>
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
@ -36,7 +38,7 @@
#include <libethcore/CommonJS.h>
#include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h>
#ifndef _MSC_VER
#if ETH_SERPENT
#include <libserpent/funcs.h>
#endif
#include "WebThreeStubServerBase.h"
@ -383,7 +385,7 @@ string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const
{
try
{
return toJS(client()->transactionCount(client()->hashFromNumber(toBlockNumber(_blockNumber))));
return toJS(_blockNumber == "pending" ? client()->pending().size() : client()->transactionCount(client()->hashFromNumber(toBlockNumber(_blockNumber))));
}
catch (...)
{
@ -620,8 +622,10 @@ Json::Value WebThreeStubServerBase::eth_getCompilers()
{
Json::Value ret(Json::arrayValue);
ret.append("lll");
#if SOLIDITY
ret.append("solidity");
#ifndef _MSC_VER
#endif
#if SERPENT
ret.append("serpent");
#endif
return ret;
@ -642,7 +646,8 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code)
{
// TODO throw here jsonrpc errors
string res;
#ifndef _MSC_VER
(void)_code;
#if SERPENT
try
{
res = toJS(dev::asBytes(::compile(_code)));
@ -662,7 +667,9 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _code)
string WebThreeStubServerBase::eth_compileSolidity(string const& _code)
{
// TOOD throw here jsonrpc errors
(void)_code;
string res;
#if SOLIDITY
dev::solidity::CompilerStack compiler;
try
{
@ -678,6 +685,7 @@ string WebThreeStubServerBase::eth_compileSolidity(string const& _code)
{
cwarn << "Uncought solidity compilation exception";
}
#endif
return res;
}

2
mix/CMakeLists.txt

@ -29,7 +29,7 @@ else()
qt5_add_resources(UI_RESOURCES noweb.qrc)
endif()
if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
if (CMAKE_BUILD_TYPE MATCHES Debug)
add_definitions(-DQT_QML_DEBUG)
endif()

140
mix/ClientModel.cpp

@ -43,6 +43,7 @@
using namespace dev;
using namespace dev::eth;
using namespace std;
namespace dev
{
@ -54,7 +55,7 @@ class RpcConnector: public jsonrpc::AbstractServerConnector
public:
virtual bool StartListening() override { return true; }
virtual bool StopListening() override { return true; }
virtual bool SendResponse(std::string const& _response, void*) override
virtual bool SendResponse(string const& _response, void*) override
{
m_response = QString::fromStdString(_response);
return true;
@ -102,7 +103,7 @@ QString ClientModel::apiCall(QString const& _message)
}
catch (...)
{
std::cerr << boost::current_exception_diagnostic_information();
cerr << boost::current_exception_diagnostic_information();
return QString();
}
}
@ -126,7 +127,7 @@ void ClientModel::mine()
catch (...)
{
m_mining = false;
std::cerr << boost::current_exception_diagnostic_information();
cerr << boost::current_exception_diagnostic_information();
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
}
emit miningStateChanged();
@ -139,11 +140,17 @@ QString ClientModel::newAddress()
return QString::fromStdString(toHex(a.secret().ref()));
}
QString ClientModel::encodeAbiString(QString _string)
{
ContractCallDataEncoder encoder;
return QString::fromStdString(toHex(encoder.encodeBytes(_string)));
}
QVariantMap ClientModel::contractAddresses() const
{
QVariantMap res;
for (auto const& c: m_contractAddresses)
res.insert(c.first, QString::fromStdString(dev::toJS(c.second)));
res.insert(c.first, QString::fromStdString(toJS(c.second)));
return res;
}
@ -152,14 +159,14 @@ void ClientModel::setupState(QVariantMap _state)
QVariantList balances = _state.value("accounts").toList();
QVariantList transactions = _state.value("transactions").toList();
std::map<Secret, u256> accounts;
map<Secret, u256> accounts;
for (auto const& b: balances)
{
QVariantMap address = b.toMap();
accounts.insert(std::make_pair(Secret(address.value("secret").toString().toStdString()), (qvariant_cast<QEther*>(address.value("balance")))->toU256Wei()));
accounts.insert(make_pair(Secret(address.value("secret").toString().toStdString()), (qvariant_cast<QEther*>(address.value("balance")))->toU256Wei()));
}
std::vector<TransactionSettings> transactionSequence;
vector<TransactionSettings> transactionSequence;
for (auto const& t: transactions)
{
QVariantMap transaction = t.toMap();
@ -197,7 +204,7 @@ void ClientModel::setupState(QVariantMap _state)
executeSequence(transactionSequence, accounts);
}
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, std::map<Secret, u256> const& _balances)
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence, map<Secret, u256> const& _balances)
{
if (m_running)
BOOST_THROW_EXCEPTION(ExecutionStateException());
@ -219,7 +226,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
if (!transaction.stdContractUrl.isEmpty())
{
//std contract
dev::bytes const& stdContractCode = m_codeModel->getStdContractCode(transaction.contractId, transaction.stdContractUrl);
bytes const& stdContractCode = m_codeModel->getStdContractCode(transaction.contractId, transaction.stdContractUrl);
TransactionSettings stdTransaction = transaction;
stdTransaction.gas = 500000;// TODO: get this from std contracts library
Address address = deployContract(stdContractCode, stdTransaction);
@ -232,7 +239,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
CompiledContract const& compilerRes = m_codeModel->contract(transaction.contractId);
QFunctionDefinition const* f = nullptr;
bytes contractCode = compilerRes.bytes();
std::shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
if (transaction.functionId.isEmpty())
f = contractDef->constructor();
else
@ -291,12 +298,12 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
}
catch(boost::exception const&)
{
std::cerr << boost::current_exception_diagnostic_information();
cerr << boost::current_exception_diagnostic_information();
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
}
catch(std::exception const& e)
catch(exception const& e)
{
std::cerr << boost::current_exception_diagnostic_information();
cerr << boost::current_exception_diagnostic_information();
emit runFailed(e.what());
}
m_running = false;
@ -323,16 +330,16 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
{
QHash<int, int> codeMap;
codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code, codeMap));
codeMaps.push_back(std::move(codeMap));
codeMaps.push_back(move(codeMap));
//try to resolve contract for source level debugging
auto nameIter = m_contractNames.find(code.address);
if (nameIter != m_contractNames.end())
CompiledContract const* compilerRes = nullptr;
if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(nameIter->second)))
{
CompiledContract const& compilerRes = m_codeModel->contract(nameIter->second);
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems();
codes.back()->setDocument(compilerRes.documentId());
codeItems.push_back(std::move(assemblyItems));
contracts.push_back(&compilerRes);
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes->assemblyItems() : compilerRes->constructorAssemblyItems();
codes.back()->setDocument(compilerRes->documentId());
codeItems.push_back(move(assemblyItems));
contracts.push_back(compilerRes);
}
else
{
@ -347,8 +354,8 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
QVariantList states;
QVariantList solCallStack;
std::map<int, QVariableDeclaration*> solLocals; //<stack pos, decl>
std::map<QString, QVariableDeclaration*> storageDeclarations; //<name, decl>
map<int, QVariableDeclaration*> solLocals; //<stack pos, decl>
map<QString, QVariableDeclaration*> storageDeclarations; //<name, decl>
unsigned prevInstructionIndex = 0;
for (MachineState const& s: _t.machineStates)
@ -360,7 +367,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
CompiledContract const* contract = contracts[s.codeIndex];
AssemblyItem const& instruction = codeItems[s.codeIndex][instructionIndex];
if (instruction.type() == dev::eth::Push && !instruction.data())
if (instruction.type() == eth::Push && !instruction.data())
{
//register new local variable initialization
auto localIter = contract->locals().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end));
@ -368,7 +375,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
solLocals[s.stack.size()] = new QVariableDeclaration(debugData, localIter.value().name.toStdString(), localIter.value().type);
}
if (instruction.type() == dev::eth::Tag)
if (instruction.type() == eth::Tag)
{
//track calls into functions
AssemblyItem const& prevInstruction = codeItems[s.codeIndex][prevInstructionIndex];
@ -383,7 +390,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
QVariantMap locals;
QVariantList localDeclarations;
QVariantMap localValues;
for(auto l: solLocals)
for (auto l: solLocals)
if (l.first < (int)s.stack.size())
{
if (l.second->type()->name().startsWith("mapping"))
@ -397,42 +404,50 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
QVariantMap storage;
QVariantList storageDeclarationList;
QVariantMap storageValues;
for(auto st: s.storage)
if (st.first < std::numeric_limits<unsigned>::max())
for (auto st: s.storage)
if (st.first < numeric_limits<unsigned>::max())
{
auto storageIter = contract->storage().find(static_cast<unsigned>(st.first));
if (storageIter != contract->storage().end())
{
QVariableDeclaration* storageDec = nullptr;
auto decIter = storageDeclarations.find(storageIter.value().name);
if (decIter != storageDeclarations.end())
storageDec = decIter->second;
else
for (SolidityDeclaration const& codeDec : storageIter.value())
{
storageDec = new QVariableDeclaration(debugData, storageIter.value().name.toStdString(), storageIter.value().type);
storageDeclarations[storageDec->name()] = storageDec;
if (codeDec.type.name.startsWith("mapping"))
continue; //mapping type not yet managed
auto decIter = storageDeclarations.find(codeDec.name);
if (decIter != storageDeclarations.end())
storageDec = decIter->second;
else
{
storageDec = new QVariableDeclaration(debugData, codeDec.name.toStdString(), codeDec.type);
storageDeclarations[storageDec->name()] = storageDec;
}
storageDeclarationList.push_back(QVariant::fromValue(storageDec));
storageValues[storageDec->name()] = formatStorageValue(storageDec->type()->type(), s.storage, codeDec.offset, codeDec.slot);
}
if (storageDec->type()->name().startsWith("mapping"))
break; //mapping type not yet managed
storageDeclarationList.push_back(QVariant::fromValue(storageDec));
storageValues[storageDec->name()] = formatValue(storageDec->type()->type(), st.second);
}
}
storage["variables"] = storageDeclarationList;
storage["values"] = storageValues;
prevInstructionIndex = instructionIndex;
solState = new QSolState(debugData, std::move(storage), std::move(solCallStack), std::move(locals), instruction.getLocation().start, instruction.getLocation().end, QString::fromUtf8(instruction.getLocation().sourceName->c_str()));
SourceLocation location = instruction.getLocation();
if (contract->contract()->location() == location || contract->functions().contains(LocationPair(location.start, location.end)))
location = dev::SourceLocation(-1, -1, location.sourceName);
solState = new QSolState(debugData, move(storage), move(solCallStack), move(locals), location.start, location.end, QString::fromUtf8(location.sourceName->c_str()));
}
states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState)));
}
debugData->setStates(std::move(states));
debugData->setStates(move(states));
debugDataReady(debugData);
}
QVariant ClientModel::formatValue(SolidityType const& _type, dev::u256 const& _value)
QVariant ClientModel::formatValue(SolidityType const& _type, u256 const& _value)
{
ContractCallDataEncoder decoder;
bytes val = toBigEndian(_value);
@ -440,6 +455,45 @@ QVariant ClientModel::formatValue(SolidityType const& _type, dev::u256 const& _v
return res;
}
QVariant ClientModel::formatStorageValue(SolidityType const& _type, map<u256, u256> const& _storage, unsigned _offset, u256 const& _slot)
{
u256 slot = _slot;
QVariantList values;
ContractCallDataEncoder decoder;
u256 count = 1;
if (_type.dynamicSize)
{
count = _storage.at(slot);
slot = fromBigEndian<u256>(sha3(toBigEndian(slot)).asBytes());
}
else if (_type.array)
count = _type.count;
unsigned offset = _offset;
while (count--)
{
auto slotIter = _storage.find(slot);
u256 slotValue = slotIter != _storage.end() ? slotIter->second : u256();
bytes slotBytes = toBigEndian(slotValue);
auto start = slotBytes.end() - _type.size - offset;
bytes val(32 - _type.size); //prepend with zeroes
val.insert(val.end(), start, start + _type.size);
values.append(decoder.decode(_type, val));
offset += _type.size;
if ((offset + _type.size) > 32)
{
slot++;
offset = 0;
}
}
if (!_type.array)
return values[0];
return QVariant::fromValue(values);
}
void ClientModel::emptyRecord()
{
debugDataReady(new QDebugData());
@ -465,9 +519,9 @@ void ClientModel::callContract(Address const& _contract, bytes const& _data, Tra
RecordLogEntry* ClientModel::lastBlock() const
{
eth::BlockInfo blockInfo = m_client->blockInfo();
std::stringstream strGas;
stringstream strGas;
strGas << blockInfo.gasUsed;
std::stringstream strNumber;
stringstream strNumber;
strNumber << blockInfo.number;
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash().ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block);
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
@ -490,7 +544,7 @@ void ClientModel::onNewTransaction()
unsigned recordIndex = tr.executonIndex;
QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex);
QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(dev::toString(tr.value));
QString value = QString::fromStdString(toString(tr.value));
QString contract = address;
QString function;
QString returned;

4
mix/ClientModel.h

@ -155,7 +155,10 @@ public slots:
Q_INVOKABLE void debugRecord(unsigned _index);
/// Show the debugger for an empty record
Q_INVOKABLE void emptyRecord();
/// Generate new adress
Q_INVOKABLE QString newAddress();
/// Encode a string to ABI parameter. Returns a hex string
Q_INVOKABLE QString encodeAbiString(QString _string);
private slots:
/// Update UI with machine states result. Display a modal dialog.
@ -201,6 +204,7 @@ private:
void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t);
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value);
QVariant formatStorageValue(SolidityType const& _type, std::map<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot);
std::atomic<bool> m_running;
std::atomic<bool> m_mining;

61
mix/CodeModel.cpp

@ -54,8 +54,8 @@ using namespace dev::solidity;
class CollectDeclarationsVisitor: public ASTConstVisitor
{
public:
CollectDeclarationsVisitor(QHash<LocationPair, QString>* _functions, QHash<LocationPair, SolidityDeclaration>* _locals, QHash<unsigned, SolidityDeclaration>* _storage):
m_functions(_functions), m_locals(_locals), m_storage(_storage), m_functionScope(false), m_storageSlot(0) {}
CollectDeclarationsVisitor(QHash<LocationPair, QString>* _functions, QHash<LocationPair, SolidityDeclaration>* _locals):
m_functions(_functions), m_locals(_locals), m_functionScope(false) {}
private:
LocationPair nodeLocation(ASTNode const& _node)
{
@ -79,31 +79,30 @@ private:
SolidityDeclaration decl;
decl.type = CodeModel::nodeType(_node.getType().get());
decl.name = QString::fromStdString(_node.getName());
decl.slot = 0;
decl.offset = 0;
if (m_functionScope)
m_locals->insert(nodeLocation(_node), decl);
else
m_storage->insert(m_storageSlot++, decl);
return true;
}
private:
QHash<LocationPair, QString>* m_functions;
QHash<LocationPair, SolidityDeclaration>* m_locals;
QHash<unsigned, SolidityDeclaration>* m_storage;
bool m_functionScope;
uint m_storageSlot;
};
dev::eth::AssemblyItems filterLocations(dev::eth::AssemblyItems const& _locations, dev::solidity::ContractDefinition const& _contract, QHash<LocationPair, QString> _functions)
QHash<unsigned, SolidityDeclarations> collectStorage(dev::solidity::ContractDefinition const& _contract)
{
dev::eth::AssemblyItems result;
result.reserve(_locations.size());
for (dev::eth::AssemblyItem item : _locations)
QHash<unsigned, SolidityDeclarations> result;
dev::solidity::ContractType contractType(_contract);
for (auto v : contractType.getStateVariables())
{
dev::SourceLocation const& l = item.getLocation();
if (_contract.getLocation() == l || _functions.contains(LocationPair(l.start, l.end)))
item.setLocation(dev::SourceLocation(-1, -1, l.sourceName));
result.push_back(item);
dev::solidity::VariableDeclaration const* declaration = std::get<0>(v);
dev::u256 slot = std::get<1>(v);
unsigned offset = std::get<2>(v);
result[static_cast<unsigned>(slot)].push_back(SolidityDeclaration { QString::fromStdString(declaration->getName()), CodeModel::nodeType(declaration->getType().get()), slot, offset });
}
return result;
}
@ -133,10 +132,11 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
if (contractDefinition.getLocation().sourceName.get())
m_documentId = QString::fromStdString(*contractDefinition.getLocation().sourceName);
CollectDeclarationsVisitor visitor(&m_functions, &m_locals, &m_storage);
CollectDeclarationsVisitor visitor(&m_functions, &m_locals);
m_storage = collectStorage(contractDefinition);
contractDefinition.accept(visitor);
m_assemblyItems = filterLocations(_compiler.getRuntimeAssemblyItems(name), contractDefinition, m_functions);
m_constructorAssemblyItems = filterLocations(_compiler.getAssemblyItems(name), contractDefinition, m_functions);
m_assemblyItems = _compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = _compiler.getAssemblyItems(name);
}
QString CompiledContract::codeHex() const
@ -220,7 +220,7 @@ QVariantMap CodeModel::contracts() const
return result;
}
CompiledContract* CodeModel::contractByDocumentId(QString _documentId) const
CompiledContract* CodeModel::contractByDocumentId(QString const& _documentId) const
{
Guard l(x_contractMap);
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c)
@ -229,7 +229,7 @@ CompiledContract* CodeModel::contractByDocumentId(QString _documentId) const
return nullptr;
}
CompiledContract const& CodeModel::contract(QString _name) const
CompiledContract const& CodeModel::contract(QString const& _name) const
{
Guard l(x_contractMap);
CompiledContract* res = m_contractMap.value(_name);
@ -238,6 +238,13 @@ CompiledContract const& CodeModel::contract(QString _name) const
return *res;
}
CompiledContract const* CodeModel::tryGetContract(QString const& _name) const
{
Guard l(x_contractMap);
CompiledContract* res = m_contractMap.value(_name);
return res;
}
void CodeModel::releaseContracts()
{
for (ContractMap::iterator c = m_contractMap.begin(); c != m_contractMap.end(); ++c)
@ -335,10 +342,9 @@ dev::bytes const& CodeModel::getStdContractCode(const QString& _contractName, co
SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
{
SolidityType r { SolidityType::Type::UnsignedInteger, 32, false, false, QString::fromStdString(_type->toString()), std::vector<SolidityDeclaration>(), std::vector<QString>() };
SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector<SolidityDeclaration>(), std::vector<QString>() };
if (!_type)
return r;
r.dynamicSize = _type->isDynamicallySized();
switch (_type->getCategory())
{
case Type::Category::Integer:
@ -367,7 +373,13 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
if (array->isByteArray())
r.type = SolidityType::Type::Bytes;
else
r = nodeType(array->getBaseType().get());
{
SolidityType elementType = nodeType(array->getBaseType().get());
elementType.name = r.name;
r = elementType;
}
r.count = static_cast<unsigned>(array->getLength());
r.dynamicSize = _type->isDynamicallySized();
r.array = true;
}
break;
@ -384,7 +396,10 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
r.type = SolidityType::Type::Struct;
StructType const* s = dynamic_cast<StructType const*>(_type);
for(auto const& structMember: s->getMembers())
r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.first), nodeType(structMember.second.get()) });
{
auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.first);
r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.first), nodeType(structMember.second.get()), slotAndOffset.first, slotAndOffset.second });
}
}
break;
case Type::Category::Function:

12
mix/CodeModel.h

@ -99,7 +99,7 @@ public:
QHash<LocationPair, QString> const& functions() const { return m_functions; }
QHash<LocationPair, SolidityDeclaration> const& locals() const { return m_locals; }
QHash<unsigned, SolidityDeclaration> const& storage() const { return m_storage; }
QHash<unsigned, SolidityDeclarations> const& storage() const { return m_storage; }
private:
uint m_sourceHash;
@ -112,7 +112,7 @@ private:
eth::AssemblyItems m_constructorAssemblyItems;
QHash<LocationPair, QString> m_functions;
QHash<LocationPair, SolidityDeclaration> m_locals;
QHash<unsigned, SolidityDeclaration> m_storage;
QHash<unsigned, SolidityDeclarations> m_storage;
friend class CodeModel;
};
@ -141,10 +141,14 @@ public:
/// Get contract code by url. Contract is compiled on first access and cached
dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url);
/// Get contract by name
CompiledContract const& contract(QString _name) const;
/// Throws if not found
CompiledContract const& contract(QString const& _name) const;
/// Get contract by name
/// @returns nullptr if not found
CompiledContract const* tryGetContract(QString const& _name) const;
/// Find a contract by document id
/// @returns CompiledContract object or null if not found
Q_INVOKABLE CompiledContract* contractByDocumentId(QString _documentId) const;
Q_INVOKABLE CompiledContract* contractByDocumentId(QString const& _documentId) const;
/// Reset code model
Q_INVOKABLE void reset() { reset(QVariantMap()); }
/// Convert solidity type info to mix type

49
mix/ContractCallDataEncoder.cpp

@ -48,33 +48,57 @@ void ContractCallDataEncoder::encode(QFunctionDefinition const* _function)
void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type)
{
u256 count = 1;
QStringList strList;
if (_type.array)
{
if (_data.type() == QVariant::String)
strList = _data.toString().split(",", QString::SkipEmptyParts); //TODO: proper parsing
else
strList = _data.toStringList();
count = strList.count();
}
else
strList.append(_data.toString());
if (_type.dynamicSize)
{
u256 count = 0;
if (_type.type == SolidityType::Type::Bytes)
count = encodeSingleItem(_data, _type, m_dynamicData);
count = encodeSingleItem(_data.toString(), _type, m_dynamicData);
else
{
QVariantList list = qvariant_cast<QVariantList>(_data);
for (auto const& item: list)
count = strList.count();
for (auto const& item: strList)
encodeSingleItem(item, _type, m_dynamicData);
count = list.size();
}
bytes sizeEnc(32);
toBigEndian(count, sizeEnc);
m_encodedData.insert(m_encodedData.end(), sizeEnc.begin(), sizeEnc.end());
}
else
encodeSingleItem(_data, _type, m_encodedData);
{
if (_type.array)
count = _type.count;
int c = static_cast<int>(count);
if (strList.size() > c)
strList.erase(strList.begin() + c, strList.end());
else
while (strList.size() < c)
strList.append(QString());
for (auto const& item: strList)
encodeSingleItem(item, _type, m_encodedData);
}
}
unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, SolidityType const& _type, bytes& _dest)
unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest)
{
if (_type.type == SolidityType::Type::Struct)
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Struct parameters are not supported yet"));
unsigned const alignSize = 32;
QString src = _data.toString();
QString src = _data;
bytes result;
if ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'")))
@ -104,17 +128,12 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, Solidi
}
unsigned dataSize = _type.dynamicSize ? result.size() : alignSize;
if (result.size() % alignSize != 0)
result.resize((result.size() & ~(alignSize - 1)) + alignSize);
_dest.insert(_dest.end(), result.begin(), result.end());
if ((_dest.size() - 4) % alignSize != 0)
_dest.resize((_dest.size() & ~(alignSize - 1)) + alignSize);
return dataSize;
}
void ContractCallDataEncoder::push(bytes const& _b)
{
m_encodedData.insert(m_encodedData.end(), _b.begin(), _b.end());
}
bigint ContractCallDataEncoder::decodeInt(dev::bytes const& _rawValue)
{
dev::u256 un = dev::fromBigEndian<dev::u256>(_rawValue);

10
mix/ContractCallDataEncoder.h

@ -52,19 +52,19 @@ public:
QVariant decode(SolidityType const& _type, bytes const& _value);
/// Get all encoded data encoded by encode function.
bytes encodedData();
/// Push the given @a _b to the current param context.
void push(bytes const& _b);
/// Encode a string to ABI bytes
dev::bytes encodeBytes(QString const& _str);
/// Decode bytes from ABI
dev::bytes decodeBytes(dev::bytes const& _rawValue);
private:
unsigned encodeSingleItem(QVariant const& _data, SolidityType const& _type, bytes& _dest);
unsigned encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest);
bigint decodeInt(dev::bytes const& _rawValue);
dev::bytes encodeInt(QString const& _str);
QString toString(dev::bigint const& _int);
dev::bytes encodeBool(QString const& _str);
bool decodeBool(dev::bytes const& _rawValue);
QString toString(bool _b);
dev::bytes encodeBytes(QString const& _str);
dev::bytes decodeBytes(dev::bytes const& _rawValue);
QString toString(dev::bytes const& _b);
bool asString(dev::bytes const& _b, QString& _str);

1
mix/FileIo.cpp

@ -196,6 +196,7 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder)
QStringList ret;
ret.append(QString::fromStdString(toHex(dappHash.ref())));
ret.append(qFileBytes.toBase64());
ret.append(url.toString());
return ret;
}

19
mix/MixApplication.cpp

@ -20,6 +20,7 @@
*/
#include "MixApplication.h"
#include <boost/exception/diagnostic_information.hpp>
#include <QQmlApplicationEngine>
#include <QUrl>
#include <QIcon>
@ -36,6 +37,7 @@
#include "Clipboard.h"
#include "HttpServer.h"
extern int qInitResources_js();
using namespace dev::mix;
ApplicationService::ApplicationService()
@ -59,7 +61,6 @@ MixApplication::MixApplication(int& _argc, char* _argv[]):
}
}
void MixApplication::initialize()
{
#if __linux
@ -72,6 +73,9 @@ void MixApplication::initialize()
if (!getenv("OPENSSL_CONF"))
putenv((char*)"OPENSSL_CONF=c:\\");
#endif
#ifdef ETH_HAVE_WEBENGINE
qInitResources_js();
#endif
setOrganizationName(tr("Ethereum"));
setOrganizationDomain(tr("ethereum.org"));
@ -98,3 +102,16 @@ void MixApplication::initialize()
MixApplication::~MixApplication()
{
}
bool MixApplication::notify(QObject * receiver, QEvent * event)
{
try
{
return QApplication::notify(receiver, event);
}
catch (...)
{
std::cerr << boost::current_exception_diagnostic_information();
}
return false;
}

1
mix/MixApplication.h

@ -62,6 +62,7 @@ public:
static void initialize();
virtual ~MixApplication();
QQmlApplicationEngine* engine() { return m_engine.get(); }
bool notify(QObject* _receiver, QEvent* _event) override;
private:
std::unique_ptr<QQmlApplicationEngine> m_engine;

2
mix/MixClient.cpp

@ -185,7 +185,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
// execute on a state
if (!_call)
{
_state.execute(lastHashes, _t);
dev::eth::ExecutionResult er =_state.execute(lastHashes, _t);
if (_t.isCreation() && _state.code(d.contractAddress).empty())
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment"));
// collect watches

2
mix/QBasicNodeDefinition.cpp

@ -28,7 +28,7 @@ namespace mix
{
QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d):
QObject(_parent), m_name(QString::fromStdString(_d->getName()))
QObject(_parent), m_name(QString::fromStdString(_d->getName())), m_location(_d->getLocation())
{
}

3
mix/QBasicNodeDefinition.h

@ -23,6 +23,7 @@
#include <string>
#include <QObject>
#include <libevmcore/SourceLocation.h>
namespace dev
{
@ -47,9 +48,11 @@ public:
QBasicNodeDefinition(QObject* _parent, std::string const& _name);
/// Get the name of the node.
QString name() const { return m_name; }
dev::SourceLocation const& location() { return m_location; }
private:
QString m_name;
dev::SourceLocation m_location;
};
}

6
mix/SolidityType.h

@ -25,6 +25,7 @@ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
#include <QString>
#include <vector>
#include <libdevcore/Common.h>
namespace dev
{
@ -49,6 +50,7 @@ struct SolidityType
};
Type type;
unsigned size; //in bytes,
unsigned count;
bool array;
bool dynamicSize;
QString name;
@ -60,7 +62,11 @@ struct SolidityDeclaration
{
QString name;
SolidityType type;
dev::u256 slot;
unsigned offset;
};
using SolidityDeclarations = std::vector<SolidityDeclaration>;
}
}

84
mix/Web3Server.cpp

@ -23,13 +23,93 @@
#include <libdevcore/Exceptions.h>
#include <libdevcore/Log.h>
#include <libethereum/Interface.h>
#include <libwebthree/WebThree.h>
#include "Web3Server.h"
using namespace dev::mix;
using namespace dev;
namespace
{
class EmptyNetwork : public dev::WebThreeNetworkFace
{
std::vector<p2p::PeerSessionInfo> peers() override
{
return std::vector<p2p::PeerSessionInfo>();
}
size_t peerCount() const override
{
return 0;
}
void addNode(p2p::NodeId const& _node, bi::tcp::endpoint const& _hostEndpoint) override
{
(void)_node;
(void)_hostEndpoint;
}
void requirePeer(p2p::NodeId const& _node, bi::tcp::endpoint const& _endpoint) override
{
(void)_node;
(void)_endpoint;
}
dev::bytes saveNetwork() override
{
return dev::bytes();
}
void setIdealPeerCount(size_t _n) override
{
(void)_n;
}
bool haveNetwork() const override
{
return false;
}
void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers) override
{
(void)_n;
(void)_dropPeers;
}
p2p::NodeId id() const override
{
return p2p::NodeId();
}
p2p::Peers nodes() const override
{
return p2p::Peers();
}
void startNetwork() override
{
}
void stopNetwork() override
{
}
bool isNetworkStarted() const override
{
return false;
}
};
}
Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, _accounts),
m_client(_client)
m_client(_client),
m_network(new EmptyNetwork())
{
}
Web3Server::~Web3Server()
{
}
@ -40,7 +120,7 @@ std::shared_ptr<dev::shh::Interface> Web3Server::face()
dev::WebThreeNetworkFace* Web3Server::network()
{
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::WebThreeNetworkFace"));
return m_network.get();
}
std::string Web3Server::get(std::string const& _name, std::string const& _key)

2
mix/Web3Server.h

@ -39,6 +39,7 @@ class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev
public:
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client);
virtual ~Web3Server();
signals:
void newTransaction();
@ -60,6 +61,7 @@ private:
private:
dev::eth::Interface* m_client;
std::map<std::string, std::string> m_db;
std::unique_ptr<dev::WebThreeNetworkFace> m_network;
};
}

2
mix/qml.qrc

@ -61,5 +61,7 @@
<file>qml/js/ProjectModel.js</file>
<file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file>
<file>qml/js/Printer.js</file>
<file>qml/js/ansi2html.js</file>
</qresource>
</RCC>

2
mix/qml/Application.qml

@ -178,7 +178,7 @@ ApplicationWindow {
id: editStatesAction
text: qsTr("Edit States")
shortcut: "Ctrl+Alt+E"
onTriggered: stateList.show();
onTriggered: stateList.open();
}
Connections {

4
mix/qml/Debugger.qml

@ -12,6 +12,10 @@ Rectangle {
id: debugPanel
property alias transactionLog: transactionLog
property alias debugSlider: statesSlider
property alias solLocals: solLocals
property alias solStorage: solStorage
property alias solCallStack: solCallStack
signal debugExecuteLocation(string documentId, var location)
property string compilationErrorMessage
property bool assemblyMode: false

409
mix/qml/DeploymentDialog.qml

@ -2,7 +2,7 @@ import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
@ -11,28 +11,22 @@ import "js/QEtherHelper.js" as QEtherHelper
import "."
Window {
Dialog {
id: modalDeploymentDialog
modality: Qt.ApplicationModal
width: 735
height: 320
maximumWidth: width
minimumWidth: width
maximumHeight: height
minimumHeight: height
height: 400
visible: false
property alias applicationUrlEth: applicationUrlEth.text
property alias applicationUrlHttp: applicationUrlHttp.text
property string urlHintContract: urlHintAddr.text
property alias urlHintContract: urlHintAddr.text
property alias localPackageUrl: localPackageUrl.text
property string packageHash
property string packageBase64
property string eth: registrarAddr.text
property string currentAccount
property alias gasToUse: gasToUseInput.text
color: appStyle.generic.layout.backgroundColor
function close()
{
visible = false;
@ -40,10 +34,7 @@ Window {
function open()
{
modalDeploymentDialog.setX((Screen.width - width) / 2);
modalDeploymentDialog.setY((Screen.height - height) / 2);
visible = true;
var requests = [{
//accounts
jsonrpc: "2.0",
@ -131,8 +122,8 @@ Window {
var jsonRpcRequestId = 0;
requests.push({
jsonrpc: "2.0",
method: "eth_countAt",
params: [ currentAccount ],
method: "eth_getTransactionCount",
params: [ currentAccount, "pending" ],
id: jsonRpcRequestId++
});
TransactionHelper.rpcCall(requests, function (httpRequest, response){
@ -159,136 +150,140 @@ Window {
id: lightFont
}
Column
{
spacing: 5
contentItem: Rectangle {
color: appStyle.generic.layout.backgroundColor
anchors.fill: parent
anchors.margins: 10
ColumnLayout
Column
{
id: containerDeploy
Layout.fillWidth: true
Layout.preferredHeight: 500
RowLayout
spacing: 5
anchors.fill: parent
anchors.margins: 10
ColumnLayout
{
Rectangle
id: containerDeploy
Layout.fillWidth: true
Layout.preferredHeight: 500
RowLayout
{
Layout.preferredWidth: 357
DefaultLabel
Rectangle
{
text: qsTr("Deployment")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
Layout.preferredWidth: 357
DefaultLabel
{
text: qsTr("Deployment")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
}
}
}
Button
{
action: displayHelpAction
iconSource: "qrc:/qml/img/help.png"
}
Action {
id: displayHelpAction
tooltip: qsTr("Help")
onTriggered: {
Qt.openUrlExternally("https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network")
Button
{
action: displayHelpAction
iconSource: "qrc:/qml/img/help.png"
}
}
Button
{
action: openFolderAction
iconSource: "qrc:/qml/img/openedfolder.png"
}
Action {
id: openFolderAction
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Open Package Folder")
onTriggered: {
fileIo.openFileBrowser(projectModel.deploymentDir);
Action {
id: displayHelpAction
tooltip: qsTr("Help")
onTriggered: {
Qt.openUrlExternally("https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network")
}
}
}
Button
{
action: b64Action
iconSource: "qrc:/qml/img/b64.png"
}
Button
{
action: openFolderAction
iconSource: "qrc:/qml/img/openedfolder.png"
}
Action {
id: b64Action
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Copy Base64 conversion to ClipBoard")
onTriggered: {
clipboard.text = deploymentDialog.packageBase64;
Action {
id: openFolderAction
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Open Package Folder")
onTriggered: {
fileIo.openFileBrowser(projectModel.deploymentDir);
}
}
}
Button
{
action: exitAction
iconSource: "qrc:/qml/img/exit.png"
}
Button
{
action: b64Action
iconSource: "qrc:/qml/img/b64.png"
}
Action {
id: exitAction
tooltip: qsTr("Exit")
onTriggered: {
close()
Action {
id: b64Action
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Copy Base64 conversion to ClipBoard")
onTriggered: {
clipboard.text = deploymentDialog.packageBase64;
}
}
}
}
GridLayout
{
columns: 2
width: parent.width
Button
{
action: exitAction
iconSource: "qrc:/qml/img/exit.png"
}
DefaultLabel
{
text: qsTr("Root Registrar address:")
Action {
id: exitAction
tooltip: qsTr("Exit")
onTriggered: {
close()
}
}
}
DefaultTextField
GridLayout
{
Layout.preferredWidth: 350
id: registrarAddr
}
columns: 2
width: parent.width
DefaultLabel
{
text: qsTr("Account used to deploy:")
}
DefaultLabel
{
text: qsTr("Root Registrar address:")
}
Rectangle
{
width: 300
height: 25
color: "transparent"
ComboBox {
id: comboAccounts
property var balances: []
onCurrentIndexChanged : {
if (modelAccounts.count > 0)
{
currentAccount = modelAccounts.get(currentIndex).id;
balance.text = balances[currentIndex];
}
}
model: ListModel {
id: modelAccounts
}
DefaultTextField
{
Layout.preferredWidth: 350
id: registrarAddr
}
DefaultLabel
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: comboAccounts.right
anchors.leftMargin: 20
id: balance;
text: qsTr("Account used to deploy:")
}
Rectangle
{
width: 300
height: 25
color: "transparent"
ComboBox {
id: comboAccounts
property var balances: []
onCurrentIndexChanged : {
if (modelAccounts.count > 0)
{
currentAccount = modelAccounts.get(currentIndex).id;
balance.text = balances[currentIndex];
}
}
model: ListModel {
id: modelAccounts
}
}
DefaultLabel
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: comboAccounts.right
anchors.leftMargin: 20
id: balance;
}
}
}
@ -299,7 +294,7 @@ Window {
DefaultTextField
{
text: "20000"
text: "1000000"
Layout.preferredWidth: 350
id: gasToUseInput
}
@ -381,106 +376,120 @@ Window {
anchors.verticalCenter: parent.verticalCenter
}
}
}
Rectangle
{
width: parent.width
height: 1
color: "#5891d3"
}
ColumnLayout
{
id: containerRegister
Layout.fillWidth: true
Layout.preferredHeight: 500
RowLayout
Rectangle
{
Layout.preferredHeight: 25
Rectangle
{
Layout.preferredWidth: 356
DefaultLabel
{
text: qsTr("Registration")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
}
}
width: parent.width
height: 1
color: "#5891d3"
}
GridLayout
ColumnLayout
{
columns: 2
id: containerRegister
Layout.fillWidth: true
DefaultLabel
Layout.preferredHeight: 500
RowLayout
{
Layout.preferredWidth: 355
text: qsTr("URL Hint contract address:")
Layout.preferredHeight: 25
Rectangle
{
Layout.preferredWidth: 356
DefaultLabel
{
text: qsTr("Registration")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
}
}
}
DefaultTextField
GridLayout
{
Layout.preferredWidth: 350
id: urlHintAddr
enabled: rowRegister.isOkToRegister()
}
columns: 2
Layout.fillWidth: true
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("Web Application Resources URL: ")
}
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("Local package URL")
}
DefaultTextField
{
Layout.preferredWidth: 350
id: applicationUrlHttp
enabled: rowRegister.isOkToRegister()
}
}
DefaultTextField
{
Layout.preferredWidth: 350
id: localPackageUrl
readOnly: true
enabled: rowRegister.isOkToRegister()
}
RowLayout
{
id: rowRegister
Layout.fillWidth: true
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("URL Hint contract address:")
}
Rectangle
{
Layout.preferredWidth: 357
color: "transparent"
DefaultTextField
{
Layout.preferredWidth: 350
id: urlHintAddr
enabled: rowRegister.isOkToRegister()
}
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("Web Application Resources URL: ")
}
DefaultTextField
{
Layout.preferredWidth: 350
id: applicationUrlHttp
enabled: rowRegister.isOkToRegister()
}
}
function isOkToRegister()
RowLayout
{
return Object.keys(projectModel.deploymentAddresses).length > 0 && deploymentDialog.packageHash !== "";
}
id: rowRegister
Layout.fillWidth: true
Button {
action: registerAction
iconSource: "qrc:/qml/img/note.png"
}
Rectangle
{
Layout.preferredWidth: 357
color: "transparent"
}
Action {
id: registerAction
enabled: rowRegister.isOkToRegister()
tooltip: qsTr("Register hosted Web Application.")
onTriggered: {
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "")
{
deployDialog.title = text;
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step.")
deployDialog.open();
return;
function isOkToRegister()
{
return Object.keys(projectModel.deploymentAddresses).length > 0 && deploymentDialog.packageHash !== "";
}
Button {
action: registerAction
iconSource: "qrc:/qml/img/note.png"
}
Action {
id: registerAction
enabled: rowRegister.isOkToRegister()
tooltip: qsTr("Register hosted Web Application.")
onTriggered: {
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "")
{
deployDialog.title = text;
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step.")
deployDialog.open();
return;
}
var inError = [];
if (applicationUrlHttp.text.length > 32)
inError.push(qsTr(applicationUrlHttp.text));
if (!stopForInputError(inError))
ProjectModelCode.registerToUrlHint();
}
var inError = [];
if (applicationUrlHttp.text.length > 32)
inError.push(qsTr(applicationUrlHttp.text));
if (!stopForInputError(inError))
ProjectModelCode.registerToUrlHint();
}
}
}

57
mix/qml/LogsPane.qml

@ -7,6 +7,11 @@ import org.ethereum.qml.SortFilterProxyModel 1.0
Rectangle
{
property variant currentStatus;
function clear()
{
logsModel.clear();
}
function push(_level, _type, _content)
{
_content = _content.replace(/\n/g, " ")
@ -27,10 +32,6 @@ Rectangle
}
}
LogsPaneStyle {
id: logStyle
}
anchors.fill: parent
radius: 10
color: "transparent"
@ -61,7 +62,7 @@ Rectangle
model: SortFilterProxyModel {
id: proxyModel
source: logsModel
property var roles: ["-", "javascript", "run", "state", "comp"]
property var roles: ["-", "javascript", "run", "state", "comp", "deployment"]
Component.onCompleted: {
filterType = regEx(proxyModel.roles);
@ -94,7 +95,7 @@ Rectangle
return "(?:" + roles.join('|') + ")";
}
filterType: "(?:javascript|run|state|comp)"
filterType: "(?:javascript|run|state|comp|deployment)"
filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive
@ -388,6 +389,50 @@ Rectangle
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: deloyButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
width: 50
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("deployment")
}
tooltip: qsTr("Deployment")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
color: logStyle.generic.layout.logLabelColor
anchors.centerIn: parent
text: qsTr("Deploy.")
}
}
background:
Rectangle {
color: deloyButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
}
Row

94
mix/qml/NewProjectDialog.qml

@ -1,10 +1,11 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
Window {
Dialog {
id: newProjectWin
modality: Qt.ApplicationModal
@ -33,62 +34,65 @@ Window {
close();
accepted();
}
GridLayout {
id: dialogContent
columns: 2
contentItem: Rectangle {
anchors.fill: parent
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
GridLayout
{
id: dialogContent
columns: 2
anchors.fill: parent
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
Label {
text: qsTr("Title")
}
TextField {
id: titleField
focus: true
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
Label {
text: qsTr("Title")
}
}
Label {
text: qsTr("Path")
}
RowLayout {
TextField {
id: pathField
id: titleField
focus: true
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
}
}
Button {
text: qsTr("Browse")
onClicked: createProjectFileDialog.open()
Label {
text: qsTr("Path")
}
RowLayout {
TextField {
id: pathField
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
}
}
Button {
text: qsTr("Browse")
onClicked: createProjectFileDialog.open()
}
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
id: okButton;
enabled: titleField.text != "" && pathField.text != ""
text: qsTr("OK");
onClicked: {
acceptAndClose();
Button {
id: okButton;
enabled: titleField.text != "" && pathField.text != ""
text: qsTr("OK");
onClicked: {
acceptAndClose();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
}
@ -102,8 +106,8 @@ Window {
var u = createProjectFileDialog.fileUrl.toString();
if (u.indexOf("file://") == 0)
u = u.substring(7, u.length)
if (Qt.platform.os == "windows" && u.indexOf("/") == 0)
u = u.substring(1, u.length);
if (Qt.platform.os == "windows" && u.indexOf("/") == 0)
u = u.substring(1, u.length);
pathField.text = u;
}
}

2
mix/qml/ProjectModel.qml

@ -160,7 +160,7 @@ Item {
}
MessageDialog {
id: deployRessourcesDialog
id: deployResourcesDialog
title: qsTr("Project")
standardButtons: StandardButton.Ok
}

564
mix/qml/StateDialog.qml

@ -1,6 +1,6 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Controls.Styles 1.3
@ -9,7 +9,7 @@ import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
import "."
Window {
Dialog {
id: modalStateDialog
modality: Qt.ApplicationModal
@ -17,7 +17,6 @@ Window {
height: 480
title: qsTr("Edit State")
visible: false
color: stateDialogStyle.generic.backgroundColor
property alias stateTitle: titleField.text
property alias isDefault: defaultCheckBox.checked
@ -28,6 +27,10 @@ Window {
property var stateAccounts: []
signal accepted
StateDialogStyle {
id: stateDialogStyle
}
function open(index, item, setDefault) {
stateIndex = index;
stateTitle = item.title;
@ -48,9 +51,6 @@ Window {
stateAccounts.push(item.accounts[k]);
}
modalStateDialog.setX((Screen.width - width) / 2);
modalStateDialog.setY((Screen.height - height) / 2);
visible = true;
isDefault = setDefault;
titleField.focus = true;
@ -77,338 +77,334 @@ Window {
item.accounts = stateAccounts;
return item;
}
StateDialogStyle {
id: stateDialogStyle
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
contentItem: Rectangle {
color: stateDialogStyle.generic.backgroundColor
ColumnLayout {
id: dialogContent
anchors.top: parent.top
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Title")
}
DefaultTextField
{
id: titleField
Layout.fillWidth: true
}
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
Layout.fillWidth: true
Rectangle
{
Layout.preferredWidth: 75
DefaultLabel {
id: accountsLabel
Layout.preferredWidth: 75
text: qsTr("Accounts")
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ColumnLayout {
id: dialogContent
anchors.top: parent.top
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Title")
}
DefaultTextField
{
id: titleField
Layout.fillWidth: true
}
}
Button
CommonSeparator
{
anchors.top: accountsLabel.bottom
anchors.topMargin: 10
iconSource: "qrc:/qml/img/plus.png"
action: newAccountAction
Layout.fillWidth: true
}
Action {
id: newAccountAction
tooltip: qsTr("Add new Account")
onTriggered:
{
var account = stateListModel.newAccount("1000000", QEther.Ether);
stateAccounts.push(account);
accountsModel.append(account);
}
}
}
RowLayout
{
Layout.fillWidth: true
MessageDialog
{
id: alertAlreadyUsed
text: qsTr("This account is in use. You cannot remove it. The first account is used to deploy config contract and cannot be removed.")
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok
}
Rectangle
{
Layout.preferredWidth: 75
DefaultLabel {
id: accountsLabel
Layout.preferredWidth: 75
text: qsTr("Accounts")
}
TableView
{
id: accountsView
Layout.fillWidth: true
model: accountsModel
headerVisible: false
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 150
delegate: Item {
RowLayout
Button
{
height: 25
width: parent.width
Button
anchors.top: accountsLabel.bottom
anchors.topMargin: 10
iconSource: "qrc:/qml/img/plus.png"
action: newAccountAction
}
Action {
id: newAccountAction
tooltip: qsTr("Add new Account")
onTriggered:
{
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAccountAction
var account = stateListModel.newAccount("1000000", QEther.Ether);
stateAccounts.push(account);
accountsModel.append(account);
}
}
}
Action {
id: deleteAccountAction
tooltip: qsTr("Delete Account")
onTriggered:
MessageDialog
{
id: alertAlreadyUsed
text: qsTr("This account is in use. You cannot remove it. The first account is used to deploy config contract and cannot be removed.")
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok
}
TableView
{
id: accountsView
Layout.fillWidth: true
model: accountsModel
headerVisible: false
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 150
delegate: Item {
RowLayout
{
if (transactionsModel.isUsed(stateAccounts[styleData.row].secret))
alertAlreadyUsed.open();
else
height: 25
width: parent.width
Button
{
stateAccounts.splice(styleData.row, 1);
accountsModel.remove(styleData.row);
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAccountAction
}
Action {
id: deleteAccountAction
tooltip: qsTr("Delete Account")
onTriggered:
{
if (transactionsModel.isUsed(stateAccounts[styleData.row].secret))
alertAlreadyUsed.open();
else
{
stateAccounts.splice(styleData.row, 1);
accountsModel.remove(styleData.row);
}
}
}
DefaultTextField {
anchors.verticalCenter: parent.verticalCenter
onTextChanged: {
if (styleData.row > -1)
stateAccounts[styleData.row].name = text;
}
text: {
return styleData.value
}
}
}
}
}
DefaultTextField {
anchors.verticalCenter: parent.verticalCenter
onTextChanged: {
if (styleData.row > -1)
stateAccounts[styleData.row].name = text;
}
text: {
return styleData.value
TableViewColumn {
role: "balance"
title: qsTr("Balance")
width: 200
delegate: Item {
Ether {
id: balanceField
edit: true
displayFormattedValue: false
value: styleData.value
}
}
}
rowDelegate:
Rectangle {
color: styleData.alternate ? "transparent" : "#f0f0f0"
height: 30;
}
}
}
TableViewColumn {
role: "balance"
title: qsTr("Balance")
width: 200
delegate: Item {
Ether {
id: balanceField
edit: true
displayFormattedValue: false
value: styleData.value
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Default")
}
CheckBox {
id: defaultCheckBox
Layout.fillWidth: true
}
}
rowDelegate:
Rectangle {
color: styleData.alternate ? "transparent" : "#f0f0f0"
height: 30;
CommonSeparator
{
Layout.fillWidth: true
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
ColumnLayout {
anchors.top: dialogContent.bottom
anchors.topMargin: 5
spacing: 0
RowLayout
{
Layout.preferredWidth: 150
DefaultLabel {
text: qsTr("Transactions: ")
}
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Default")
}
CheckBox {
id: defaultCheckBox
Layout.fillWidth: true
}
}
Button
{
iconSource: "qrc:/qml/img/plus.png"
action: newTrAction
width: 10
height: 10
anchors.right: parent.right
}
CommonSeparator
{
Layout.fillWidth: true
}
}
Action {
id: newTrAction
tooltip: qsTr("Create a new transaction")
onTriggered: transactionsModel.addTransaction()
}
}
ColumnLayout {
anchors.top: dialogContent.bottom
anchors.topMargin: 5
spacing: 0
RowLayout
{
Layout.preferredWidth: 150
DefaultLabel {
text: qsTr("Transactions: ")
ScrollView
{
Layout.fillHeight: true
Layout.preferredWidth: 300
Column
{
Layout.fillHeight: true
Repeater
{
id: trRepeater
model: transactionsModel
delegate: transactionRenderDelegate
visible: transactionsModel.count > 0
height: 20 * transactionsModel.count
}
}
}
}
Button
RowLayout
{
iconSource: "qrc:/qml/img/plus.png"
action: newTrAction
width: 10
height: 10
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
close();
accepted();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
Action {
id: newTrAction
tooltip: qsTr("Create a new transaction")
onTriggered: transactionsModel.addTransaction()
}
}
ListModel {
id: accountsModel
ScrollView
{
Layout.fillHeight: true
Layout.preferredWidth: 300
Column
{
Layout.fillHeight: true
Repeater
function removeAccount(_i)
{
id: trRepeater
model: transactionsModel
delegate: transactionRenderDelegate
visible: transactionsModel.count > 0
height: 20 * transactionsModel.count
accountsModel.remove(_i);
stateAccounts.splice(_i, 1);
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
acceptAndClose();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
}
ListModel {
id: accountsModel
function removeAccount(_i)
{
accountsModel.remove(_i);
stateAccounts.splice(_i, 1);
}
}
ListModel {
id: transactionsModel
ListModel {
id: transactionsModel
function editTransaction(index) {
transactionDialog.stateAccounts = stateAccounts;
transactionDialog.open(index, transactionsModel.get(index));
}
function editTransaction(index) {
transactionDialog.stateAccounts = stateAccounts;
transactionDialog.open(index, transactionsModel.get(index));
}
function addTransaction() {
function addTransaction() {
// Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handler would just edit the item that was just created, no harm done
var item = TransactionHelper.defaultTransaction();
transactionDialog.stateAccounts = stateAccounts;
transactionDialog.open(transactionsModel.count, item);
}
// Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handler would just edit the item that was just created, no harm done
var item = TransactionHelper.defaultTransaction();
transactionDialog.stateAccounts = stateAccounts;
transactionDialog.open(transactionsModel.count, item);
}
function deleteTransaction(index) {
stateTransactions.splice(index, 1);
transactionsModel.remove(index);
}
function deleteTransaction(index) {
stateTransactions.splice(index, 1);
transactionsModel.remove(index);
}
function isUsed(secret)
{
for (var i in stateTransactions)
{
if (stateTransactions[i].sender === secret)
return true;
}
return false;
}
}
function isUsed(secret)
{
for (var i in stateTransactions)
{
if (stateTransactions[i].sender === secret)
return true;
}
return false;
}
}
Component {
id: transactionRenderDelegate
RowLayout {
DefaultLabel {
Layout.preferredWidth: 150
text: functionId
}
Component {
id: transactionRenderDelegate
RowLayout {
DefaultLabel {
Layout.preferredWidth: 150
text: functionId
}
Button
{
id: deleteBtn
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAction
width: 10
height: 10
Action {
id: deleteAction
tooltip: qsTr("Delete")
onTriggered: transactionsModel.deleteTransaction(index)
}
}
Button
{
id: deleteBtn
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAction
width: 10
height: 10
Action {
id: deleteAction
tooltip: qsTr("Delete")
onTriggered: transactionsModel.deleteTransaction(index)
Button
{
iconSource: "qrc:/qml/img/edit.png"
action: editAction
visible: stdContract === false
width: 10
height: 10
Action {
id: editAction
tooltip: qsTr("Edit")
onTriggered: transactionsModel.editTransaction(index)
}
}
}
}
}
Button
{
iconSource: "qrc:/qml/img/edit.png"
action: editAction
visible: stdContract === false
width: 10
height: 10
Action {
id: editAction
tooltip: qsTr("Edit")
onTriggered: transactionsModel.editTransaction(index)
TransactionDialog
{
id: transactionDialog
onAccepted:
{
var item = transactionDialog.getItem();
if (transactionDialog.transactionIndex < transactionsModel.count) {
transactionsModel.set(transactionDialog.transactionIndex, item);
stateTransactions[transactionDialog.transactionIndex] = item;
} else {
transactionsModel.append(item);
stateTransactions.push(item);
}
}
}
}
}
}
TransactionDialog
{
id: transactionDialog
onAccepted:
{
var item = transactionDialog.getItem();
if (transactionDialog.transactionIndex < transactionsModel.count) {
transactionsModel.set(transactionDialog.transactionIndex, item);
stateTransactions[transactionDialog.transactionIndex] = item;
} else {
transactionsModel.append(item);
stateTransactions.push(item);
}
}
}
}

74
mix/qml/StateList.qml

@ -1,39 +1,50 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import "."
Window {
Dialog {
id: stateListContainer
modality: Qt.WindowModal
width: 640
height: 480
visible: false
ColumnLayout
{
contentItem: Rectangle {
anchors.fill: parent
TableView {
id: list
Layout.fillHeight: true
Layout.fillWidth: true
model: projectModel.stateListModel
itemDelegate: renderDelegate
headerDelegate: null
TableViewColumn {
role: "title"
title: qsTr("State")
width: list.width
ColumnLayout
{
anchors.fill: parent
TableView {
id: list
Layout.fillHeight: true
Layout.fillWidth: true
model: projectModel.stateListModel
itemDelegate: renderDelegate
headerDelegate: null
frameVisible: false
TableViewColumn {
role: "title"
title: qsTr("State")
width: list.width
}
}
}
Button {
anchors.bottom: parent.bottom
action: addStateAction
Row{
spacing: 5
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 10
Button {
action: addStateAction
}
Button {
action: closeAction
}
}
}
}
@ -69,12 +80,21 @@ Window {
}
}
Action {
id: addStateAction
text: "&Add State"
shortcut: "Ctrl+T"
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: list.model.addState();
Row
{
Action {
id: addStateAction
text: qsTr("Add State")
shortcut: "Ctrl+T"
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: list.model.addState();
}
Action {
id: closeAction
text: qsTr("Close")
onTriggered: stateListContainer.close();
}
}
}

34
mix/qml/StatusPane.qml

@ -27,7 +27,6 @@ Rectangle {
debugImg.state = "";
currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "error" }
}
debugRunActionIcon.enabled = codeModel.hasContract;
}
function infoMessage(text, type)
@ -77,7 +76,11 @@ Rectangle {
Connections {
target:clientModel
onRunStarted: infoMessage(qsTr("Running transactions..."), "Run");
onRunStarted:
{
logPane.clear()
infoMessage(qsTr("Running transactions..."), "Run");
}
onRunFailed: errorMessage(format(_message), "Run");
onRunComplete: infoMessage(qsTr("Run complete"), "Run");
onNewBlock: infoMessage(qsTr("New block created"), "State");
@ -231,7 +234,11 @@ Rectangle {
top = top.parent
var coordinates = logsContainer.mapToItem(top, 0, 0);
logsContainer.parent = top;
logsContainer.x = status.x + statusContainer.x - LogsPaneStyle.generic.layout.dateWidth - LogsPaneStyle.generic.layout.typeWidth + 70
logsContainer.x = status.x + statusContainer.x - logStyle.generic.layout.dateWidth - logStyle.generic.layout.typeWidth + 70
}
LogsPaneStyle {
id: logStyle
}
LogsPane
@ -277,24 +284,9 @@ Rectangle {
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
id: debugImg
iconSource: "qrc:/qml/img/bugiconinactive.png"
action: debugRunActionIcon
states: [
State{
name: "active"
PropertyChanges { target: debugImg; iconSource: "qrc:/qml/img/bugiconactive.png"}
}
]
}
Action {
id: debugRunActionIcon
onTriggered: {
if (mainContent.rightViewIsVisible())
mainContent.hideRightView()
else
mainContent.startQuickDebugging();
}
enabled: false
text: ""
iconSource: "qrc:/qml/img/bugiconactive.png"
action: showHideRightPanelAction
}
}
}

1
mix/qml/StepActionImage.qml

@ -6,6 +6,7 @@ import QtQuick.Controls.Styles 1.1
Rectangle {
id: buttonActionContainer
color: "transparent"
property string disableStateImg
property string enabledStateImg
property string buttonTooltip

9
mix/qml/StructView.qml

@ -79,11 +79,14 @@ Column
function getValue()
{
var r = "";
if (value && value[modelData.name] !== undefined)
return value[modelData.name];
r = value[modelData.name];
else if (modelData.type.category === QSolidityType.Struct)
return {};
return "";
r = {};
if (Array.isArray(r))
r = r.join(", ");
return r;
}
}
}

379
mix/qml/TransactionDialog.qml

@ -1,19 +1,19 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "."
Window {
Dialog {
id: modalTransactionDialog
modality: Qt.ApplicationModal
width: 520
height: 500;
height: 500
visible: false
color: transactionDialogStyle.generic.backgroundColor
title: qsTr("Edit Transaction")
property int transactionIndex
property alias gas: gasValueEdit.gasValue;
@ -27,6 +27,10 @@ Window {
property alias stateAccounts: senderComboBox.model
signal accepted;
StateDialogStyle {
id: transactionDialogStyle
}
function open(index, item) {
rowFunction.visible = !useTransactionDefaultValue;
rowValue.visible = !useTransactionDefaultValue;
@ -73,8 +77,6 @@ Window {
}
}
initTypeLoader();
modalTransactionDialog.setX((Screen.width - width) / 2);
modalTransactionDialog.setY((Screen.height - height) / 2);
visible = true;
valueField.focus = true;
@ -178,214 +180,219 @@ Window {
item.parameters = paramValues;
return item;
}
StateDialogStyle {
id: transactionDialogStyle
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
contentItem: Rectangle {
color: transactionDialogStyle.generic.backgroundColor
ColumnLayout {
id: dialogContent
anchors.top: parent.top
spacing: 10
RowLayout
{
id: rowSender
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Sender")
}
ComboBox {
function select(secret)
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ColumnLayout {
id: dialogContent
anchors.top: parent.top
spacing: 10
RowLayout
{
for (var i in model)
if (model[i].secret === secret)
id: rowSender
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Sender")
}
ComboBox {
function select(secret)
{
currentIndex = i;
break;
for (var i in model)
if (model[i].secret === secret)
{
currentIndex = i;
break;
}
}
}
id: senderComboBox
Layout.preferredWidth: 350
currentIndex: 0
textRole: "name"
editable: false
}
}
id: senderComboBox
Layout.preferredWidth: 350
currentIndex: 0
textRole: "name"
editable: false
}
}
RowLayout
{
id: rowContract
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Contract")
}
ComboBox {
id: contractComboBox
function currentValue() {
return (currentIndex >=0 && currentIndex < contractsModel.count) ? contractsModel.get(currentIndex).cid : "";
RowLayout
{
id: rowContract
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Contract")
}
ComboBox {
id: contractComboBox
function currentValue() {
return (currentIndex >=0 && currentIndex < contractsModel.count) ? contractsModel.get(currentIndex).cid : "";
}
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: contractsModel
}
onCurrentIndexChanged: {
loadFunctions(currentValue());
}
}
}
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: contractsModel
RowLayout
{
id: rowFunction
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Function")
}
ComboBox {
id: functionComboBox
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
}
onCurrentIndexChanged: {
loadParameters();
}
}
}
onCurrentIndexChanged: {
loadFunctions(currentValue());
CommonSeparator
{
Layout.fillWidth: true
}
}
}
RowLayout
{
id: rowFunction
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Function")
}
ComboBox {
id: functionComboBox
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
RowLayout
{
id: rowValue
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Value")
}
Ether {
id: valueField
edit: true
displayFormattedValue: true
}
}
onCurrentIndexChanged: {
loadParameters();
CommonSeparator
{
Layout.fillWidth: true
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
id: rowGas
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Gas")
}
DefaultTextField
{
property variant gasValue
onGasValueChanged: text = gasValue.value();
onTextChanged: gasValue.setValue(text);
implicitWidth: 200
id: gasValueEdit;
}
}
RowLayout
{
id: rowValue
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Value")
}
Ether {
id: valueField
edit: true
displayFormattedValue: true
}
}
CommonSeparator
{
Layout.fillWidth: true
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
id: rowGasPrice
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Gas Price")
}
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
}
}
RowLayout
{
id: rowGas
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Gas")
}
CommonSeparator
{
Layout.fillWidth: true
}
DefaultTextField
{
property variant gasValue
onGasValueChanged: text = gasValue.value();
onTextChanged: gasValue.setValue(text);
implicitWidth: 200
id: gasValueEdit;
}
}
DefaultLabel {
id: paramLabel
text: qsTr("Parameters:")
Layout.preferredWidth: 75
}
CommonSeparator
{
Layout.fillWidth: true
}
ScrollView
{
id: paramScroll
anchors.top: paramLabel.bottom
anchors.topMargin: 10
Layout.fillWidth: true
Layout.fillHeight: true
StructView
{
id: typeLoader
Layout.preferredWidth: 150
members: paramsModel;
}
}
RowLayout
{
id: rowGasPrice
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Gas Price")
}
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
CommonSeparator
{
Layout.fillWidth: true
visible: paramsModel.length > 0
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
DefaultLabel {
id: paramLabel
text: qsTr("Parameters:")
Layout.preferredWidth: 75
}
ScrollView
RowLayout
{
id: paramScroll
anchors.top: paramLabel.bottom
anchors.topMargin: 10
Layout.fillWidth: true
Layout.fillHeight: true
StructView
{
id: typeLoader
Layout.preferredWidth: 150
members: paramsModel;
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
close();
accepted();
}
}
}
CommonSeparator
{
Layout.fillWidth: true
visible: paramsModel.length > 0
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
acceptAndClose();
Button {
text: qsTr("Cancel");
onClicked: close();
Layout.fillWidth: true
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
}
}

1
mix/qml/VariablesView.qml

@ -2,7 +2,6 @@ import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import "."
DebugInfoList
{

14
mix/qml/WebPreview.qml

@ -13,7 +13,10 @@ Item {
property string pendingPageUrl: ""
property bool initialized: false
property alias urlInput: urlInput
property alias webView: webView
property string webContent; //for testing
signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content)
signal webContentReady
function setPreviewUrl(url) {
if (!initialized)
@ -57,6 +60,13 @@ Item {
action(i);
}
function getContent() {
webView.runJavaScript("getContent()", function(result) {
webContent = result;
webContentReady();
});
}
function changePage() {
setPreviewUrl(urlInput.text);
}
@ -304,6 +314,7 @@ Item {
id: webView
experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ": " + message);
webPreview.javaScriptMessage(level, sourceID, lineNumber, message);
}
onLoadingChanged: {
@ -344,7 +355,7 @@ Item {
height: 22
width: 22
action: clearAction
iconSource: "qrc:/qml/img/broom.png"
iconSource: "qrc:/qml/img/cleariconactive.png"
}
Action {
@ -422,6 +433,7 @@ Item {
id: resultTextArea
width: expressionPanel.width
wrapMode: Text.Wrap
textFormat: Text.RichText
font.family: webPreviewStyle.general.fontName
font.pointSize: appStyle.absoluteSize(-3)
backgroundVisible: true

12
mix/qml/html/WebContainer.html

@ -3,6 +3,8 @@
<head>
<script src="qrc:///js/bignumber.min.js"></script>
<script src="qrc:///js/webthree.js"></script>
<script src="qrc:///qml/js/Printer.js"></script>
<script src="qrc:///qml/js/ansi2html.js"></script>
<script>
loadPage = function(url) {
var preview = document.getElementById('preview');
@ -40,7 +42,15 @@ init = function(url) {
executeJavaScript = function(script) {
var preview = document.getElementById('preview');
var obj = preview.contentWindow.eval(script);
return JSON.stringify(obj, null, 2);
var printed = prettyPrint(obj);
return ansi2html(printed);
}
getContent = function() {
var preview = document.getElementById('preview');
var doc = preview.contentDocument? preview.contentDocument: preview.contentWindow.document;
var body = doc.getElementsByTagName('body')[0];
return body.innerHTML;
}
</script>

90
mix/qml/js/Printer.js

@ -0,0 +1,90 @@
var prettyPrint = (function () {
function pp(object, indent) {
try {
JSON.stringify(object, null, 2);
} catch (e) {
return pp(e, indent);
}
var str = "";
if(object instanceof Array) {
str += "[";
for(var i = 0, l = object.length; i < l; i++) {
str += pp(object[i], indent);
if(i < l-1) {
str += ", ";
}
}
str += " ]";
} else if (object instanceof Error) {
str += "\e[31m" + "Error:\e[0m " + object.message;
} else if (isBigNumber(object)) {
str += "\e[32m'" + object.toString(10) + "'";
} else if(typeof(object) === "object") {
str += "{\n";
indent += " ";
var last = getFields(object).pop()
getFields(object).forEach(function (k) {
str += indent + k + ": ";
try {
str += pp(object[k], indent);
} catch (e) {
str += pp(e, indent);
}
if(k !== last) {
str += ",";
}
str += "\n";
});
str += indent.substr(2, indent.length) + "}";
} else if(typeof(object) === "string") {
str += "\e[32m'" + object + "'";
} else if(typeof(object) === "undefined") {
str += "\e[1m\e[30m" + object;
} else if(typeof(object) === "number") {
str += "\e[31m" + object;
} else if(typeof(object) === "function") {
str += "\e[35m[Function]";
} else {
str += object;
}
str += "\e[0m";
return str;
}
var redundantFields = [
'valueOf',
'toString',
'toLocaleString',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
'__proto__'
];
var getFields = function (object) {
var result = Object.getOwnPropertyNames(object);
if (object.constructor && object.constructor.prototype) {
result = result.concat(Object.getOwnPropertyNames(object.constructor.prototype));
}
return result.filter(function (field) {
return redundantFields.indexOf(field) === -1;
});
};
var isBigNumber = function (object) {
return typeof BigNumber !== 'undefined' && object instanceof BigNumber;
};
function prettyPrintI(/* */) {
var args = arguments;
var ret = "";
for (var i = 0, l = args.length; i < l; i++) {
ret += pp(args[i], "") + "\n";
}
return ret;
}
return prettyPrintI;
})();

42
mix/qml/js/ProjectModel.js

@ -367,23 +367,8 @@ function startDeployProject(erasePrevious)
var ctrNames = Object.keys(codeModel.contracts);
var ctrAddresses = {};
setDefaultBlock(0, function() {
deployContracts(0, ctrAddresses, ctrNames, function (){
finalizeDeployment(deploymentId, ctrAddresses);
});
});
}
function setDefaultBlock(val, callBack)
{
var requests = [{
jsonrpc: "2.0",
method: "eth_setDefaultBlock",
params: [val],
id: 0
}];
rpcCall(requests, function (httpCall, response){
callBack();
deployContracts(0, ctrAddresses, ctrNames, function (){
finalizeDeployment(deploymentId, ctrAddresses);
});
}
@ -392,7 +377,7 @@ function deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack)
var code = codeModel.contracts[ctrNames[ctrIndex]].codeHex;
var requests = [{
jsonrpc: "2.0",
method: "eth_transact",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": deploymentDialog.gasToUse, "code": code } ],
id: 0
}];
@ -452,7 +437,8 @@ function finalizeDeployment(deploymentId, addresses) {
"\tinterface: " + codeModel.contracts[c].contractInterface + ",\n" +
"\taddress: \"" + addresses[c] + "\"\n" +
"};\n" +
contractAccessor + ".contract = web3.eth.contract(" + contractAccessor + ".address, " + contractAccessor + ".interface);\n";
contractAccessor + ".contractClass = web3.eth.contract(" + contractAccessor + ".interface);\n" +
contractAccessor + ".contract = new " + contractAccessor + ".contractClass(" + contractAccessor + ".address);\n";
}
fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
deploymentAddresses = addresses;
@ -461,6 +447,7 @@ function finalizeDeployment(deploymentId, addresses) {
var packageRet = fileIo.makePackage(deploymentDir);
deploymentDialog.packageHash = packageRet[0];
deploymentDialog.packageBase64 = packageRet[1];
deploymentDialog.localPackageUrl = packageRet[2] + "?hash=" + packageRet[0];
var applicationUrlEth = deploymentDialog.applicationUrlEth;
@ -468,9 +455,8 @@ function finalizeDeployment(deploymentId, addresses) {
deploymentStepChanged(qsTr("Registering application on the Ethereum network ..."));
checkEthPath(applicationUrlEth, function () {
deploymentComplete();
deployRessourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployRessourcesDialog.open();
setDefaultBlock(-1, function() {});
deployResourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployResourcesDialog.open();
});
}
@ -567,7 +553,7 @@ function checkRegistration(dappUrl, addr, callBack)
requests.push({
jsonrpc: "2.0",
method: "eth_transact",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 20000, "code": "0x60056013565b61059e8061001d6000396000f35b33600081905550560060003560e060020a90048063019848921461009a578063449c2090146100af5780635d574e32146100cd5780635fd4b08a146100e1578063618242da146100f65780636be16bed1461010b5780636c4489b414610129578063893d20e8146101585780639607730714610173578063c284bc2a14610187578063e50f599a14610198578063e5811b35146101af578063ec7b9200146101cd57005b6100a560043561031b565b8060005260206000f35b6100ba6004356103a0565b80600160a060020a031660005260206000f35b6100db600435602435610537565b60006000f35b6100ec600435610529565b8060005260206000f35b6101016004356103dd565b8060005260206000f35b6101166004356103bd565b80600160a060020a031660005260206000f35b61013460043561034b565b82600160a060020a031660005281600160a060020a03166020528060405260606000f35b610160610341565b80600160a060020a031660005260206000f35b6101816004356024356102b4565b60006000f35b6101926004356103fd565b60006000f35b6101a96004356024356044356101f2565b60006000f35b6101ba6004356101eb565b80600160a060020a031660005260206000f35b6101d8600435610530565b80600160a060020a031660005260206000f35b6000919050565b600054600160a060020a031633600160a060020a031614610212576102af565b8160026000858152602001908152602001600020819055508061023457610287565b81600160a060020a0316837f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a3826001600084600160a060020a03168152602001908152602001600020819055505b827f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505050565b600054600160a060020a031633600160a060020a0316146102d457610317565b806002600084815260200190815260200160002060010181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b5050565b60006001600083600160a060020a03168152602001908152602001600020549050919050565b6000600054905090565b6000600060006002600085815260200190815260200160002054925060026000858152602001908152602001600020600101549150600260008581526020019081526020016000206002015490509193909250565b600060026000838152602001908152602001600020549050919050565b600060026000838152602001908152602001600020600101549050919050565b600060026000838152602001908152602001600020600201549050919050565b600054600160a060020a031633600160a060020a03161461041d57610526565b80600160006002600085815260200190815260200160002054600160a060020a031681526020019081526020016000205414610458576104d2565b6002600082815260200190815260200160002054600160a060020a0316817f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a36000600160006002600085815260200190815260200160002054600160a060020a03168152602001908152602001600020819055505b6002600082815260200190815260200160002060008101600090556001810160009055600281016000905550807f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b50565b6000919050565b6000919050565b600054600160a060020a031633600160a060020a0316146105575761059a565b806002600084815260200190815260200160002060020181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505056" } ],
id: jsonRpcRequestId++
});
@ -588,7 +574,7 @@ function checkRegistration(dappUrl, addr, callBack)
requests.push({
//setRegister()
jsonrpc: "2.0",
method: "eth_transact",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
id: jsonRpcRequestId++
});
@ -617,12 +603,12 @@ function registerContentHash(registrar, callBack)
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var paramTitle = createString(projectModel.projectTitle);
var paramTitle = clientModel.encodeAbiString(projectModel.projectTitle);
requests.push({
//setContent()
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ],
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle + deploymentDialog.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
@ -638,7 +624,7 @@ function registerToUrlHint()
requests.push({
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_transact",
method: "eth_sendTransaction",
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
id: jsonRpcRequestId++
});

16
mix/qml/js/QEtherHelper.js

@ -15,19 +15,3 @@ function createBigInt(_value)
return bigint;
}
function createString(_value)
{
var stringComponent = Qt.createComponent("qrc:/qml/QStringType.qml");
var stringC = stringComponent.createObject();
stringC.setValue(_value);
return stringC;
}
function createHash(_value)
{
var hComponent = Qt.createComponent("qrc:/qml/QHashType.qml");
var hC = hComponent.createObject();
hC.setValue(_value);
return hC;
}

2
mix/qml/js/TransactionHelper.js

@ -25,7 +25,7 @@ function rpcCall(requests, callBack)
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status !== 200 || httpRequest.responseText === "")
{
var errorText = qsTr("Deployment error: Error while registering Dapp ") + httpRequest.status;
var errorText = qsTr("Unable to initiate request to the live network. Please verify your ethereum node is up.") + qsTr(" Error status: ") + httpRequest.status;
console.log(errorText);
deploymentError(errorText);
}

106
mix/qml/js/ansi2html.js

@ -0,0 +1,106 @@
var ansi2html = (function(){
function ansi2htmlI(str) {
// this lib do not support \e
str = str.replace(/e\[/g, '[');
// nor line breaks
str = '<div>' + str.replace(/\n/g, '</div><div>') + '</div>';
var props = {}
, open = false
var stylemap =
{ bold: "font-weight"
, underline: "text-decoration"
, color: "color"
, background: "background"
}
function style() {
var key, val, style = []
for (var key in props) {
val = props[key]
if (!val) continue
if (val == true) {
style.push(stylemap[key] + ':' + key)
} else {
style.push(stylemap[key] + ':' + val)
}
}
return style.join(';')
}
function tag(code) {
var i
, tag = ''
, n = ansi2htmlI.table[code]
if (open) tag += '</span>'
open = false
if (n) {
for (i in n) props[i] = n[i]
tag += '<span style="' + style() + '">'
open = true
} else {
props = {}
}
return tag
}
return str.replace(/\[(\d+;)?(\d+)*m/g, function(match, b1, b2) {
var i, code, res = ''
if (b2 == '' || b2 == null) b2 = '0'
for (i = 1; i < arguments.length - 2; i++) {
if (!arguments[i]) continue
code = parseInt(arguments[i])
res += tag(code)
}
return res
}) + tag()
}
/* not implemented:
* italic
* blink
* invert
* strikethrough
*/
ansi2htmlI.table =
{ 0: null
, 1: { bold: true }
, 3: { italic: true }
, 4: { underline: true }
, 5: { blink: true }
, 6: { blink: true }
, 7: { invert: true }
, 9: { strikethrough: true }
, 23: { italic: false }
, 24: { underline: false }
, 25: { blink: false }
, 27: { invert: false }
, 29: { strikethrough: false }
, 30: { color: 'black' }
, 31: { color: 'red' }
, 32: { color: 'green' }
, 33: { color: 'yellow' }
, 34: { color: 'blue' }
, 35: { color: 'magenta' }
, 36: { color: 'cyan' }
, 37: { color: 'white' }
, 39: { color: null }
, 40: { background: 'black' }
, 41: { background: 'red' }
, 42: { background: 'green' }
, 43: { background: 'yellow' }
, 44: { background: 'blue' }
, 45: { background: 'magenta' }
, 46: { background: 'cyan' }
, 47: { background: 'white' }
, 49: { background: null }
}
return ansi2htmlI;
})();

14
mix/test/TestService.cpp

@ -20,6 +20,7 @@
* Ethereum IDE client.
*/
#include <iostream>
#include "TestService.h"
#include <QtTest/QSignalSpy>
#include <QElapsedTimer>
@ -111,6 +112,12 @@ bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeou
return spy.size();
}
bool TestService::waitForRendering(QObject* _item, int timeout)
{
QWindow* window = eventWindow(_item);
return waitForSignal(window, "frameSwapped()", timeout);
}
bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay)
{
QWindow* window = eventWindow(_item);
@ -156,6 +163,7 @@ bool TestService::keyClickChar(QObject* _item, QString const& _character, int _m
bool TestService::mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay)
{
QWindow* window = qobject_cast<QWindow*>(_item);
QMetaObject const* mo = _item->metaObject();
if (!window)
window = eventWindow(_item);
mouseEvent(MouseClick, window, _item, Qt::MouseButton(_button), Qt::KeyboardModifiers(_modifiers), QPointF(_x, _y), _delay);
@ -170,18 +178,22 @@ void TestService::setTargetWindow(QObject* _window)
window->requestActivate();
}
QWindow* TestService::eventWindow(QObject* _item)
{
QQuickItem* item = qobject_cast<QQuickItem*>(_item);
if (item && item->window())
return item->window();
QQuickWindow* window = qobject_cast<QQuickWindow*>(_item);
QWindow* window = qobject_cast<QQuickWindow*>(_item);
if (!window && _item->parent())
window = eventWindow(_item->parent());
if (!window)
window = qobject_cast<QQuickWindow*>(m_targetWindow);
if (window)
{
window->requestActivate();
std::cout << window->title().toStdString();
return window;
}
item = qobject_cast<QQuickItem*>(m_targetWindow);

1
mix/test/TestService.h

@ -42,6 +42,7 @@ public:
public slots:
bool waitForSignal(QObject* _item, QString _signalName, int _timeout);
bool waitForRendering(QObject* _item, int timeout);
bool keyPress(QObject* _item, int _key, int _modifiers, int _delay);
bool keyRelease(QObject* _item, int _key, int _modifiers, int _delay);
bool keyClick(QObject* _item, int _key, int _modifiers, int _delay);

110
mix/test/qml/TestMain.qml

@ -2,6 +2,8 @@ import QtQuick 2.2
import QtTest 1.1
import org.ethereum.qml.TestService 1.0
import "../../qml"
import "js/TestDebugger.js" as TestDebugger
import "js/TestTutorial.js" as TestTutorial
TestCase
{
@ -14,6 +16,8 @@ TestCase
{
if (el === undefined)
el = mainApplication;
if (el.contentItem) //for dialgos
el = el.contentItem
for (var c in str)
{
@ -23,6 +27,11 @@ TestCase
}
}
Application
{
id: mainApplication
}
function newProject()
{
waitForRendering(mainApplication.mainContent, 10000);
@ -43,100 +52,25 @@ TestCase
fail("not compiled");
}
function clickElement(el, x, y)
function editHtml(c)
{
ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10)
}
function test_defaultTransactionSequence()
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract() {\r" +
" uint x = 69;\r" +
" uint y = 5;\r" +
" for (uint i = 0; i < y; ++i) {\r" +
" x += 42;\r" +
" z += x;\r" +
" }\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3);
}
function test_transactionWithParameter()
{
newProject();
editContract(
"contract Contract {\r" +
" function setZ(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
transactionDialog.selectFunction("setZ");
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
mainApplication.projectModel.openDocument("index.html");
wait(1);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)");
mainApplication.mainContent.codeEditor.getEditor("index.html").setText(c);
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
}
function test_constructorParameters()
function clickElement(el, x, y)
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
wait(1);
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 4);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "returned", "(442)");
if (el.contentItem)
el = el.contentItem;
ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10)
}
Application
{
id: mainApplication
}
function test_tutorial() { TestTutorial.test_tutorial(); }
function test_dbg_defaultTransactionSequence() { TestDebugger.test_defaultTransactionSequence(); }
function test_dbg_transactionWithParameter() { TestDebugger.test_transactionWithParameter(); }
function test_dbg_constructorParameters() { TestDebugger.test_constructorParameters(); }
function test_dbg_arrayParametersAndStorage() { TestDebugger.test_arrayParametersAndStorage(); }
}

140
mix/test/qml/js/TestDebugger.js

@ -0,0 +1,140 @@
function test_defaultTransactionSequence()
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract() {\r" +
" uint x = 69;\r" +
" uint y = 5;\r" +
" for (uint i = 0; i < y; ++i) {\r" +
" x += 42;\r" +
" z += x;\r" +
" }\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("Error running transaction");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3);
}
function test_transactionWithParameter()
{
newProject();
editContract(
"contract Contract {\r" +
" function setZ(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("setZ");
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("Error running transaction");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)");
}
function test_constructorParameters()
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("Error running transaction");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 4);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "returned", "(442)");
}
function test_arrayParametersAndStorage()
{
newProject();
editContract(
" contract ArrayTest {\r" +
" function setM(uint256[] x) external\r" +
" {\r" +
" m = x;\r" +
" s = 5;\r" +
" }\r" +
" \r" +
" function setMV(uint72[5] x) external\r" +
" {\r" +
" mv = x;\r" +
" s = 42;\r" +
" }\r" +
" \r" +
" uint256[] m;\r" +
" uint72[5] mv;\r" +
" uint256 s;\r" +
" }\r");
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("setM");
clickElement(transactionDialog, 140, 300);
ts.typeString("4,5,6,2,10", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("setMV");
clickElement(transactionDialog, 140, 300);
ts.typeString("13,35,1,4", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000))
fail("Error running transaction");
//debug setM
mainApplication.clientModel.debugRecord(3);
mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue;
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "m", ["4","5","6","2","10"]);
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "s", "5");
//debug setMV
mainApplication.clientModel.debugRecord(4);
mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue - 1;
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "mv", ["13","35","1","4","0"]);
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "s", "42");
tryCompare(mainApplication.mainContent.rightPane.solCallStack.listModel, 0, "setMV");
}

71
mix/test/qml/js/TestTutorial.js

@ -0,0 +1,71 @@
//Test case to cover Mix tutorial
function test_tutorial()
{
newProject();
editContract(
"contract Rating {\r" +
" function setRating(bytes32 _key, uint256 _value) {\r" +
" ratings[_key] = _value;\r" +
" }\r" +
" mapping (bytes32 => uint256) public ratings;\r" +
"}\r"
);
editHtml(
"<!doctype>\r" +
"<html>\r" +
"<head>\r" +
"<script type='text/javascript'>\r" +
"function getRating() {\r" +
" var param = document.getElementById('query').value;\r" +
" var res = contracts['Rating'].contract.ratings(param);\r" +
" document.getElementById('queryres').innerText = res;\r" +
"}\r" +
"function setRating() {\r" +
" var key = document.getElementById('key').value;\r" +
" var value = parseInt(document.getElementById('value').value);\r" +
" var res = contracts['Rating'].contract.setRating(key, value);\r" +
"}\r" +
"</script>\r" +
"</head>\r" +
"<body bgcolor='#E6E6FA'>\r" +
" <h1>Ratings</h1>\r" +
" <div>\r" +
" Store:\r" +
" <input type='string' id='key'>\r" +
" <input type='number' id='value'>\r" +
" <button onclick='setRating()'>Save</button>\r" +
" </div>\r" +
" <div>\r" +
" Query:\r" +
" <input type='string' id='query' onkeyup='getRating()'>\r" +
" <div id='queryres'></div>\r" +
" </div>\r" +
"</body>\r" +
"</html>\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("setRating");
clickElement(transactionDialog, 200, 310);
ts.typeString("Titanic", transactionDialog);
clickElement(transactionDialog, 200, 330);
ts.typeString("2", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000))
fail("Error running transaction");
wait(1);
clickElement(mainApplication.mainContent.webView.webView, 1, 1);
ts.typeString("\t\t\t\t");
ts.typeString("Titanic");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.callModel, "count", 8); //wait for 8 calls
mainApplication.mainContent.webView.getContent();
ts.waitForSignal(mainApplication.mainContent.webView, "webContentReady()", 5000);
var body = mainApplication.mainContent.webView.webContent;
verify(body.indexOf("<div id=\"queryres\">2</div>") != -1, "Web content not updated")
}

2
test/CMakeLists.txt

@ -48,7 +48,7 @@ if (SOLIDITY)
target_link_libraries(testeth solidity)
endif ()
target_link_libraries(testeth testutils)
if (NOT HEADLESS AND NOT JUSTTESTS)
if (GUI AND NOT JUSTTESTS)
target_link_libraries(testeth webthree)
target_link_libraries(testeth natspec)
endif()

4
test/SolidityEndToEndTest.cpp

@ -1414,6 +1414,8 @@ BOOST_AUTO_TEST_CASE(sha3)
testSolidityAgainstCpp("a(bytes32)", f, u256(-1));
}
#ifdef PRECOMPILED_CONTRACTS_GAS_FIXED_IN_SOLIDITY
BOOST_AUTO_TEST_CASE(sha256)
{
char const* sourceCode = "contract test {\n"
@ -1468,6 +1470,8 @@ BOOST_AUTO_TEST_CASE(ecrecover)
BOOST_CHECK(callContractFunction("a(bytes32,uint8,bytes32,bytes32)", h, v, r, s) == encodeArgs(addr));
}
#endif
BOOST_AUTO_TEST_CASE(inter_contract_calls)
{
char const* sourceCode = R"(

168
test/SolidityOptimizer.cpp

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

940
test/bcUncleHeaderValiditiyFiller.json

@ -0,0 +1,940 @@
{
"correct" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "131072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "131071",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "142813170",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"diffTooLow" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "131072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "difficulty",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "131071",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "142813170",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"diffTooHigh" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "131072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "difficulty",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "131073",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "142813170",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"diffTooLow2" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "231072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "difficulty",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "230847",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "142813170",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"gasLimitTooHigh" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "231072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "gasLimit",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "230847",
"extraData" : "0x",
"gasLimit" : "3141593",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "142813170",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"gasLimitTooLow" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "231072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "gasLimit",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "230847",
"extraData" : "0x",
"gasLimit" : "3141591",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "142813170",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"wrongParentHash" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "231072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "parentHash",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "230848",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "bad4fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "bad40b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "1528353694",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"wrongStateRoot" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "231072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "stateRoot",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "230848",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "bad40b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "1528353694",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"timestampTooLow" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "231072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "timestamp",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "230848",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "1428353694",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"timestampTooHigh" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "231072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "timestamp",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "230848",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "1528353694",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
}
}

93
test/bcUncleTestFiller.json

@ -263,6 +263,99 @@
]
},
"uncleWithSameBlockNumber" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "131072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "bcde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "131072",
"extraData" : "0x",
"gasLimit" : "3141592",
"gasUsed" : "0",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "3",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "0x54c98c82",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"InChainUncle" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",

36
test/blockchain.cpp

@ -126,6 +126,12 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
vBiUncles.push_back(vBiBlocks[(size_t)toInt(uncleHeaderObj["sameAsBlock"])]);
continue;
}
string overwrite = "false";
if (uncleHeaderObj.count("overwriteAndRedoPoW"))
{
overwrite = uncleHeaderObj["overwriteAndRedoPoW"].get_str();
uncleHeaderObj.erase("overwriteAndRedoPoW");
}
BlockInfo uncleBlockFromFields = constructBlock(uncleHeaderObj);
@ -141,6 +147,20 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
else
continue;
if (overwrite != "false")
{
uncleBlockFromFields.difficulty = overwrite == "difficulty" ? toInt(uncleHeaderObj["difficulty"]) : uncleBlockFromFields.difficulty;
uncleBlockFromFields.gasLimit = overwrite == "gasLimit" ? toInt(uncleHeaderObj["gasLimit"]) : uncleBlockFromFields.gasLimit;
uncleBlockFromFields.gasUsed = overwrite == "gasUsed" ? toInt(uncleHeaderObj["gasUsed"]) : uncleBlockFromFields.gasUsed;
uncleBlockFromFields.parentHash = overwrite == "parentHash" ? h256(uncleHeaderObj["parentHash"].get_str()) : uncleBlockFromFields.parentHash;
uncleBlockFromFields.stateRoot = overwrite == "stateRoot" ? h256(uncleHeaderObj["stateRoot"].get_str()) : uncleBlockFromFields.stateRoot;
if (overwrite == "timestamp")
{
uncleBlockFromFields.timestamp = toInt(uncleHeaderObj["timestamp"]);
uncleBlockFromFields.difficulty = uncleBlockFromFields.calculateDifficulty(vBiBlocks[(size_t)uncleBlockFromFields.number - 1]);
}
}
updatePoW(uncleBlockFromFields);
writeBlockHeaderToJson(uncleHeaderObj, uncleBlockFromFields);
@ -148,8 +168,16 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
vBiUncles.push_back(uncleBlockFromFields);
cnote << "import uncle in blockQueue";
RLPStream uncle = createFullBlockFromHeader(uncleBlockFromFields);
uncleBlockQueue.import(&uncle.out(), bc);
try
{
uncleBlockQueue.import(&uncle.out(), bc);
}
catch(...)
{
cnote << "error in importing uncle! This produces an invalid block (May be by purpose for testing).";
}
uncleHeaderObj_pre = uncleHeaderObj;
}
@ -402,7 +430,6 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
for (auto const& uBlHeaderObj: blObj["uncleHeaders"].get_array())
{
mObject uBlH = uBlHeaderObj.get_obj();
cout << "uBlH.size(): " << uBlH.size() << endl;
BOOST_REQUIRE(uBlH.size() == 16);
bytes uncleRLP = createBlockRLPFromFields(uBlH);
const RLP c_uRLP(uncleRLP);
@ -664,6 +691,11 @@ BOOST_AUTO_TEST_CASE(bcUncleTest)
dev::test::executeTests("bcUncleTest", "/BlockTests", dev::test::doBlockchainTests);
}
BOOST_AUTO_TEST_CASE(bcUncleHeaderValiditiy)
{
dev::test::executeTests("bcUncleHeaderValiditiy", "/BlockTests", dev::test::doBlockchainTests);
}
BOOST_AUTO_TEST_CASE(userDefinedFile)
{
dev::test::userDefinedTest("--singletest", dev::test::doBlockchainTests);

2
test/natspec.cpp

@ -19,7 +19,7 @@
* @date 2015
*/
#if !ETH_HEADLESS
#if ETH_GUI
#include <boost/test/unit_test.hpp>
#include <libdevcore/Log.h>

83
test/stCallCreateCallCodeTestFiller.json

@ -125,6 +125,48 @@
}
},
"callWithHighValueOOGinCall": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "30000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "100000",
"nonce" : "0",
"code" : "{ [[ 0 ]] (ADD (CALL 10000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0 0 0 ) 1) }",
"storage": {}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23",
"code" : "0x6001600155603760005360026000f3",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "3000000",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"Callcode1024BalanceTooLow" : {
"env" : {
"currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
@ -530,5 +572,46 @@
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"createJS_ExampleContract": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"6295ee1b4f6dd65047762f924ecd367c17eabf8f" : {
"balance" : "100000",
"code" : "0x60003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b5056",
"nonce" : "0",
"storage" : {
"0x" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"0x01" : "0x42",
"0x02" : "0x23",
"0x03" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"0x05" : "0x54c98c81"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "600000",
"to" : "",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : "0x60406103ca600439600451602451336000819055506000600481905550816001819055508060028190555042600581905550336003819055505050610381806100496000396000f30060003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b505600000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000023"
}
}
}

20
test/ttTransactionTestFiller.json

@ -347,7 +347,7 @@
}
},
"TransactionWithSvalueWrongSize" : {
"TransactionWithSvalueWrongSize" : {
"transaction" :
{
"data" : "",
@ -407,7 +407,7 @@
}
},
"TransactionWithHihghGasPrice" : {
"TransactionWithHihghGasPrice" : {
"transaction" :
{
"data" : "",
@ -422,7 +422,7 @@
}
},
"TransactionWithGasLimitxPriceOverflow" : {
"TransactionWithGasLimitxPriceOverflow" : {
"transaction" :
{
"data" : "",
@ -598,5 +598,19 @@
"v": "27",
"value": ""
}
},
"dataTx_bcValidBlockTest": {
"transaction": {
"nonce": "0",
"gasPrice": "50",
"gasLimit": "50000",
"to": "",
"data": "0x60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b56",
"r" : "0xc5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0",
"s" : "0xe221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884",
"v": "28",
"value": "0"
}
}
}

Loading…
Cancel
Save