Browse Source

Merge remote-tracking branch 'upstream/develop' into evmjit

cl-refactor
Paweł Bylica 10 years ago
parent
commit
55474cd59a
  1. 2
      .gitignore
  2. 3
      CMakeLists.txt
  3. 2
      README.md
  4. 1
      alethzero/CMakeLists.txt
  5. 139
      alethzero/DappHost.cpp
  6. 58
      alethzero/DappHost.h
  7. 203
      alethzero/DappLoader.cpp
  8. 94
      alethzero/DappLoader.h
  9. 85
      alethzero/MainWin.cpp
  10. 14
      alethzero/MainWin.h
  11. 4
      alethzero/Transact.cpp
  12. 28
      alethzero/WebPage.cpp
  13. 40
      alethzero/WebPage.h
  14. 1140
      chrono_io/chrono_io
  15. 601
      chrono_io/ratio_io
  16. 26
      cmake/EthDependencies.cmake
  17. 2
      libdevcore/Common.cpp
  18. 1
      libdevcore/CommonIO.h
  19. 14
      libdevcore/FixedHash.h
  20. 3
      libdevcore/StructuredLogger.cpp
  21. 11
      libdevcore/StructuredLogger.h
  22. 2
      libdevcore/vector_ref.h
  23. 66
      libdevcrypto/Common.cpp
  24. 17
      libdevcrypto/Common.h
  25. 124
      libdevcrypto/CryptoPP.cpp
  26. 14
      libdevcrypto/CryptoPP.h
  27. 7
      libdevcrypto/ECDHE.cpp
  28. 17
      libdevcrypto/ECDHE.h
  29. 2
      libethcore/Common.cpp
  30. 18
      libethcore/Ethasher.cpp
  31. 126
      libethereum/BlockChain.cpp
  32. 27
      libethereum/BlockChain.h
  33. 17
      libethereum/BlockDetails.h
  34. 66
      libethereum/Client.cpp
  35. 1
      libethereum/Client.h
  36. 44
      libethereum/EthereumPeer.cpp
  37. 14
      libethereum/LogFilter.cpp
  38. 10
      libethereum/LogFilter.h
  39. 16
      libethereum/State.cpp
  40. 2
      libethereum/State.h
  41. 40
      libevmcore/Assembly.cpp
  42. 21
      libevmcore/Assembly.h
  43. 2
      libjsqrc/CMakeLists.txt
  44. 20
      libnatspec/NatspecExpressionEvaluator.cpp
  45. 9
      libnatspec/NatspecExpressionEvaluator.h
  46. 102
      libnatspec/natspec.js
  47. 2
      libnatspec/natspec.qrc
  48. 3
      libnatspec/natspecjs/.gitignore
  49. 8
      libnatspec/natspecjs/.travis.yml
  50. 47
      libnatspec/natspecjs/README.md
  51. 3570
      libnatspec/natspecjs/dist/natspec.js
  52. 1
      libnatspec/natspecjs/dist/natspec.js.map
  53. 2
      libnatspec/natspecjs/dist/natspec.min.js
  54. 13
      libnatspec/natspecjs/example/example.html
  55. 186
      libnatspec/natspecjs/natspec.js
  56. 24
      libnatspec/natspecjs/package.json
  57. 149
      libnatspec/natspecjs/test/test.js
  58. 12
      libp2p/Capability.cpp
  59. 3
      libp2p/Capability.h
  60. 229
      libp2p/Host.cpp
  61. 26
      libp2p/Host.h
  62. 5
      libp2p/HostCapability.cpp
  63. 2
      libp2p/HostCapability.h
  64. 2
      libp2p/Peer.h
  65. 209
      libp2p/RLPxFrameIO.cpp
  66. 133
      libp2p/RLPxFrameIO.h
  67. 266
      libp2p/RLPxHandshake.cpp
  68. 126
      libp2p/RLPxHandshake.h
  69. 249
      libp2p/Session.cpp
  70. 18
      libp2p/Session.h
  71. 3
      libsolidity/Compiler.cpp
  72. 4
      libsolidity/Compiler.h
  73. 7
      libsolidity/CompilerContext.cpp
  74. 4
      libsolidity/CompilerContext.h
  75. 17
      libsolidity/ExpressionCompiler.cpp
  76. 1
      libsolidity/GlobalContext.cpp
  77. 2
      libsolidity/Parser.h
  78. 4
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  79. 2
      libwhisper/WhisperPeer.cpp
  80. 3
      mix/AppContext.cpp
  81. 9
      mix/CodeEditorExtensionManager.cpp
  82. 2
      mix/CodeEditorExtensionManager.h
  83. 2
      mix/CodeModel.h
  84. 2
      mix/FileIo.cpp
  85. 12
      mix/MixClient.cpp
  86. 156
      mix/SortFilterProxyModel.cpp
  87. 97
      mix/SortFilterProxyModel.h
  88. 302
      mix/qml/LogsPane.qml
  89. 29
      mix/qml/LogsPaneStyle.qml
  90. 32
      mix/qml/MainContent.qml
  91. 10
      mix/qml/ProjectModel.qml
  92. 5
      mix/qml/StateListModel.qml
  93. 170
      mix/qml/StatusPane.qml
  94. 7
      mix/qml/WebPreview.qml
  95. BIN
      mix/qml/img/broom.png
  96. BIN
      mix/qml/img/copy.png
  97. 21
      mix/qml/js/ProjectModel.js
  98. 1
      mix/qml/qmldir
  99. 4
      mix/res.qrc
  100. 32
      pullSubtree.sh

2
.gitignore

@ -64,6 +64,8 @@ profile
DerivedData
project.pbxproj
# JetBrains stuff
.idea/
doc/html
*.autosave

3
CMakeLists.txt

@ -22,6 +22,7 @@ function(createDefaultCacheConfig)
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 (requried unless HEADLESS)")
set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed")
endfunction()
@ -125,7 +126,7 @@ configureProject()
set (ETH_HAVE_WEBENGINE 1)
message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}")
message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}")
message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}; FATDB: ${FATDB}; CHROMIUM: ${ETH_HAVE_WEBENGINE}; USENPM: ${USENPM}")
# Default TARGET_PLATFORM to "linux".

2
README.md

@ -1,5 +1,7 @@
## Ethereum C++ Client.
[![Join the chat at https://gitter.im/ethereum/cpp-ethereum](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/cpp-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
By Gav Wood et al, 2013, 2014, 2015.
| Linux | OSX | Windows

1
alethzero/CMakeLists.txt

@ -56,6 +56,7 @@ target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} natspec)
target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES})
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
target_link_libraries(${EXECUTABLE} serpent)

139
alethzero/DappHost.cpp

@ -0,0 +1,139 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file DappHost.cpp
* @author Arkadiy Paronyan <arkadiy@ethdev.org>
* @date 2015
*/
#include "DappHost.h"
#include <QUrl>
#include <microhttpd.h>
#include <boost/algorithm/string.hpp>
#include <libdevcore/Common.h>
using namespace dev;
DappHost::DappHost(int _port, int _threads):
m_port(_port), m_threads(_threads), m_running(false), m_daemon(nullptr)
{
startListening();
}
DappHost::~DappHost()
{
stopListening();
}
void DappHost::startListening()
{
if(!this->m_running)
{
this->m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, this->m_port, nullptr, nullptr, &DappHost::callback, this, MHD_OPTION_THREAD_POOL_SIZE, this->m_threads, MHD_OPTION_END);
if (this->m_daemon != nullptr)
this->m_running = true;
}
}
void DappHost::stopListening()
{
if(this->m_running)
{
MHD_stop_daemon(this->m_daemon);
this->m_running = false;
}
}
void DappHost::sendOptionsResponse(MHD_Connection* _connection)
{
MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1);
MHD_add_response_header(result, "Allow", "GET, OPTIONS");
MHD_add_response_header(result, "Access-Control-Allow-Headers", "origin, content-type, accept");
MHD_add_response_header(result, "DAV", "1");
MHD_queue_response(_connection, MHD_HTTP_OK, result);
MHD_destroy_response(result);
}
void DappHost::sendNotAllowedResponse(MHD_Connection* _connection)
{
MHD_Response *result = MHD_create_response_from_data(0, NULL, 0, 1);
MHD_add_response_header(result, "Allow", "GET, OPTIONS");
MHD_queue_response(_connection, MHD_HTTP_METHOD_NOT_ALLOWED, result);
MHD_destroy_response(result);
}
void DappHost::sendResponse(std::string const& _url, MHD_Connection* _connection)
{
QUrl requestUrl(QString::fromStdString(_url));
QString path = requestUrl.path().toLower();
if (path.isEmpty())
path = "/";
bytesConstRef response;
unsigned code = MHD_HTTP_NOT_FOUND;
std::string contentType;
while (!path.isEmpty())
{
auto iter = m_entriesByPath.find(path);
if (iter != m_entriesByPath.end())
{
ManifestEntry const* entry = iter->second;
auto contentIter = m_dapp.content.find(entry->hash);
if (contentIter == m_dapp.content.end())
break;
response = bytesConstRef(contentIter->second.data(), contentIter->second.size());
code = entry->httpStatus != 0 ? entry->httpStatus : MHD_HTTP_OK;
contentType = entry->contentType;
break;
}
path.truncate(path.length() - 1);
path = path.mid(0, path.lastIndexOf('/'));
}
MHD_Response *result = MHD_create_response_from_data(response.size(), const_cast<byte*>(response.data()), 0, 1);
if (!contentType.empty())
MHD_add_response_header(result, "Content-Type", contentType.c_str());
MHD_queue_response(_connection, code, result);
MHD_destroy_response(result);
}
int DappHost::callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls)
{
(void)_version;
(void)_uploadData;
(void)_uploadDataSize;
(void)_conCls;
DappHost* host = static_cast<DappHost*>(_cls);
if (std::string("GET") == _method)
host->sendResponse(std::string(_url), _connection);
else if (std::string("OPTIONS") == _method)
host->sendOptionsResponse(_connection);
else
host->sendNotAllowedResponse(_connection);
return MHD_YES;
}
QUrl DappHost::hostDapp(Dapp&& _dapp)
{
m_dapp = std::move(_dapp);
m_entriesByPath.clear();
for (ManifestEntry const& entry: m_dapp.manifest.entries)
m_entriesByPath[QString::fromStdString(entry.path)] = &entry;
return QUrl(QString("http://localhost:%1/").arg(m_port));
}

58
alethzero/DappHost.h

@ -0,0 +1,58 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file DappHost.h
* @author Arkadiy Paronyan <arkadiy@ethdev.org>
* @date 2015
*/
#pragma once
#include <map>
#include <QUrl>
#include <QString>
#include "DappLoader.h"
struct MHD_Daemon;
struct MHD_Connection;
/// DApp web server. Servers web content, resolves paths by hashes
class DappHost
{
public:
/// @param _port Network pork to listen for incoming connections
/// @param _threads Max number of threads to process requests
DappHost(int _port, int _threads = 10);
virtual ~DappHost();
/// Load and host a dapp. Previsous dapp in discarded. Synchronous
QUrl hostDapp(Dapp&& _dapp);
private:
void startListening();
void stopListening();
void sendOptionsResponse(MHD_Connection* _connection);
void sendNotAllowedResponse(MHD_Connection* _connection);
void sendResponse(std::string const& _url, MHD_Connection* _connection);
static int callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls);
int m_port;
int m_threads;
bool m_running;
MHD_Daemon* m_daemon;
Dapp m_dapp;
std::map<QString, ManifestEntry const*> m_entriesByPath;
};

203
alethzero/DappLoader.cpp

@ -0,0 +1,203 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file DappLoader.cpp
* @author Arkadiy Paronyan <arkadiy@ethdev.org>
* @date 2015
*/
#include <algorithm>
#include <json/json.h>
#include <QUrl>
#include <QStringList>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/SHA3.h>
#include <libethcore/CommonJS.h>
#include <libethereum/Client.h>
#include <libwebthree/WebThree.h>
#include "DappLoader.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::crypto;
Address c_registrar = Address("0000000000000000000000000000000000000a28");
Address c_urlHint = Address("0000000000000000000000000000000000000a29");
QString contentsOfQResource(std::string const& res);
DappLoader::DappLoader(QObject* _parent, WebThreeDirect* _web3):
QObject(_parent), m_web3(_web3)
{
connect(&m_net, &QNetworkAccessManager::finished, this, &DappLoader::downloadComplete);
}
DappLocation DappLoader::resolveAppUri(QString const& _uri)
{
QUrl url(_uri);
if (!url.scheme().isEmpty() && url.scheme() != "eth")
throw dev::Exception(); //TODO:
QStringList parts = url.host().split('.', QString::SkipEmptyParts);
QStringList domainParts;
std::reverse(parts.begin(), parts.end());
parts.append(url.path().split('/', QString::SkipEmptyParts));
Address address = c_registrar;
Address lastAddress;
int partIndex = 0;
h256 contentHash;
while (address && partIndex < parts.length())
{
lastAddress = address;
string32 name = { 0 };
QByteArray utf8 = parts[partIndex].toUtf8();
std::copy(utf8.data(), utf8.data() + utf8.size(), name.data());
address = abiOut<Address>(web3()->ethereum()->call(address, abiIn("addr(string32)", name)));
domainParts.append(parts[partIndex]);
if (!address)
{
//we have the address of the last part, try to get content hash
contentHash = abiOut<h256>(web3()->ethereum()->call(lastAddress, abiIn("content(string32)", name)));
if (!contentHash)
throw dev::Exception() << errinfo_comment("Can't resolve address");
}
++partIndex;
}
string32 contentUrl = abiOut<string32>(web3()->ethereum()->call(c_urlHint, abiIn("url(hash256)", contentHash)));
QString domain = domainParts.join('/');
parts.erase(parts.begin(), parts.begin() + partIndex);
QString path = parts.join('/');
QString contentUrlString = QString::fromUtf8(std::string(contentUrl.data(), contentUrl.size()).c_str());
if (!contentUrlString.startsWith("http://") || !contentUrlString.startsWith("https://"))
contentUrlString = "http://" + contentUrlString;
return DappLocation { domain, path, contentUrlString, contentHash };
}
void DappLoader::downloadComplete(QNetworkReply* _reply)
{
try
{
//try to interpret as rlp
QByteArray data = _reply->readAll();
_reply->deleteLater();
h256 expected = m_uriHashes[_reply->request().url()];
bytes package(reinterpret_cast<unsigned char const*>(data.constData()), reinterpret_cast<unsigned char const*>(data.constData() + data.size()));
Secp256k1 dec;
dec.decrypt(expected, package);
h256 got = sha3(package);
if (got != expected)
{
//try base64
data = QByteArray::fromBase64(data);
package = bytes(reinterpret_cast<unsigned char const*>(data.constData()), reinterpret_cast<unsigned char const*>(data.constData() + data.size()));
dec.decrypt(expected, package);
got = sha3(package);
if (got != expected)
throw dev::Exception() << errinfo_comment("Dapp content hash does not match");
}
RLP rlp(package);
loadDapp(rlp);
}
catch (...)
{
qWarning() << tr("Error downloading DApp: ") << boost::current_exception_diagnostic_information().c_str();
emit dappError();
}
}
void DappLoader::loadDapp(RLP const& _rlp)
{
Dapp dapp;
unsigned len = _rlp.itemCountStrict();
dapp.manifest = loadManifest(_rlp[0].toString());
for (unsigned c = 1; c < len; ++c)
{
bytesConstRef content = _rlp[c].toBytesConstRef();
h256 hash = sha3(content);
auto entry = std::find_if(dapp.manifest.entries.cbegin(), dapp.manifest.entries.cend(), [=](ManifestEntry const& _e) { return _e.hash == hash; });
if (entry != dapp.manifest.entries.cend())
{
if (entry->path == "/deployment.js")
{
//inject web3 code
QString code;
code += contentsOfQResource(":/js/bignumber.min.js");
code += "\n";
code += contentsOfQResource(":/js/webthree.js");
code += "\n";
code += contentsOfQResource(":/js/setup.js");
code += "\n";
QByteArray res = code.toLatin1();
bytes b(res.data(), res.data() + res.size());
b.insert(b.end(), content.begin(), content.end());
dapp.content[hash] = b;
}
else
dapp.content[hash] = content.toBytes();
}
else
throw dev::Exception() << errinfo_comment("Dapp content hash does not match");
}
emit dappReady(dapp);
}
Manifest DappLoader::loadManifest(std::string const& _manifest)
{
/// https://github.com/ethereum/go-ethereum/wiki/URL-Scheme
Manifest manifest;
Json::Reader jsonReader;
Json::Value root;
jsonReader.parse(_manifest, root, false);
Json::Value entries = root["entries"];
for (Json::ValueIterator it = entries.begin(); it != entries.end(); ++it)
{
Json::Value const& entryValue = *it;
std::string path = entryValue["path"].asString();
if (path.size() == 0 || path[0] != '/')
path = "/" + path;
std::string contentType = entryValue["contentType"].asString();
std::string strHash = entryValue["hash"].asString();
if (strHash.length() == 64)
strHash = "0x" + strHash;
h256 hash = jsToFixed<32>(strHash);
unsigned httpStatus = entryValue["status"].asInt();
manifest.entries.push_back(ManifestEntry{ path, hash, contentType, httpStatus });
}
return manifest;
}
void DappLoader::loadDapp(QString const& _uri)
{
DappLocation location = resolveAppUri(_uri);
QUrl uri(location.contentUri);
QNetworkRequest request(uri);
m_uriHashes[uri] = location.contentHash;
m_net.get(request);
}

94
alethzero/DappLoader.h

@ -0,0 +1,94 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file DappLoader.h
* @author Arkadiy Paronyan <arkadiy@ethdev.org>
* @date 2015
*/
#pragma once
#include <map>
#include <string>
#include <vector>
#include <QObject>
#include <QString>
#include <QUrl>
#include <QNetworkAccessManager>
#include <libdevcore/FixedHash.h>
namespace dev
{
class WebThreeDirect;
class RLP;
}
struct ManifestEntry
{
std::string path;
dev::h256 hash;
std::string contentType;
unsigned httpStatus;
};
struct Manifest
{
std::vector<ManifestEntry> entries;
};
struct Dapp
{
Manifest manifest;
std::map<dev::h256, dev::bytes> content;
};
struct DappLocation
{
QString canonDomain;
QString path;
QString contentUri;
dev::h256 contentHash;
};
///Downloads, unpacks and prepares DApps for hosting
class DappLoader: public QObject
{
Q_OBJECT
public:
DappLoader(QObject* _parent, dev::WebThreeDirect* _web3);
///Load a new DApp. Resolves a name with a name reg contract. Asynchronous. dappReady is emitted once everything is read, dappError othervise
///@param _uri Eth name path
void loadDapp(QString const& _uri);
signals:
void dappReady(Dapp& _dapp);
void dappError();
private slots:
void downloadComplete(QNetworkReply* _reply);
private:
dev::WebThreeDirect* web3() const { return m_web3; }
DappLocation resolveAppUri(QString const& _uri);
void loadDapp(dev::RLP const& _rlp);
Manifest loadManifest(std::string const& _manifest);
dev::WebThreeDirect* m_web3;
QNetworkAccessManager m_net;
std::map<QUrl, dev::h256> m_uriHashes;
};

85
alethzero/MainWin.cpp

@ -19,7 +19,6 @@
* @date 2014
*/
#define QWEBENGINEINSPECTOR 1
#include <fstream>
// Make sure boost/asio.hpp is included before windows.h.
@ -68,6 +67,9 @@
#include "OurWebThreeStubServer.h"
#include "Transact.h"
#include "Debugger.h"
#include "DappLoader.h"
#include "DappHost.h"
#include "WebPage.h"
#include "ui_Main.h"
using namespace std;
using namespace dev;
@ -116,7 +118,9 @@ Address c_newConfig = Address("c6d9d2cd449a754c494264e1809c50e34d64562b");
Main::Main(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Main),
m_transact(this, this)
m_transact(this, this),
m_dappLoader(nullptr),
m_webPage(nullptr)
{
QtWebEngine::initialize();
setWindowFlags(Qt::Window);
@ -166,10 +170,12 @@ Main::Main(QWidget *parent) :
m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening();
WebPage* webPage= new WebPage(this);
m_webPage = webPage;
connect(webPage, &WebPage::consoleMessage, [this](QString const& _msg) { Main::addConsoleMessage(_msg, QString()); });
ui->webView->setPage(m_webPage);
connect(ui->webView, &QWebEngineView::loadFinished, [this]()
{
// f->disconnect();
// f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership);
auto f = ui->webView->page();
f->runJavaScript(contentsOfQResource(":/js/bignumber.min.js"));
f->runJavaScript(contentsOfQResource(":/js/webthree.js"));
@ -181,6 +187,9 @@ Main::Main(QWidget *parent) :
ui->tabWidget->setTabText(0, ui->webView->title());
});
m_dappHost.reset(new DappHost(8081));
m_dappLoader = new DappLoader(this, web3());
connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded);
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page);
@ -425,13 +434,7 @@ void Main::eval(QString const& _js)
s = "<span style=\"color: #840\">" + jsonEv.toString().toHtmlEscaped() + "</span>";
else
s = "<span style=\"color: #888\">unknown type</span>";
m_consoleHistory.push_back(qMakePair(_js, s));
s = "<html><body style=\"margin: 0;\">" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%");
for (auto const& i: m_consoleHistory)
s += "<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em; color: #888; font-weight: bold\">&gt;</span><span style=\"color: #35d\">" + i.first.toHtmlEscaped() + "</span></div>"
"<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em\">&nbsp;</span><span>" + i.second + "</span></div>";
s += "</div></body></html>";
ui->jsConsole->setHtml(s);
addConsoleMessage(_js, s);
};
ui->webView->page()->runJavaScript("JSON.stringify(___RET)", f2);
};
@ -439,6 +442,17 @@ void Main::eval(QString const& _js)
ui->webView->page()->runJavaScript(c, f);
}
void Main::addConsoleMessage(QString const& _js, QString const& _s)
{
m_consoleHistory.push_back(qMakePair(_js, _s));
QString r = "<html><body style=\"margin: 0;\">" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%");
for (auto const& i: m_consoleHistory)
r += "<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em; color: #888; font-weight: bold\">&gt;</span><span style=\"color: #35d\">" + i.first.toHtmlEscaped() + "</span></div>"
"<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em\">&nbsp;</span><span>" + i.second + "</span></div>";
r += "</div></body></html>";
ui->jsConsole->setHtml(r);
}
static Public stringToPublic(QString const& _a)
{
string sn = _a.toStdString();
@ -780,15 +794,28 @@ void Main::on_jitvm_triggered()
void Main::on_urlEdit_returnPressed()
{
QString s = ui->urlEdit->text();
QRegExp r("([a-z]+://)?([^/]*)(.*)");
if (r.exactMatch(s))
if (r.cap(2).isEmpty())
s = (r.cap(1).isEmpty() ? "file://" : r.cap(1)) + r.cap(3);
QUrl url(s);
if (url.scheme().isEmpty() || url.scheme() == "eth")
{
try
{
//try do resolve dapp url
m_dappLoader->loadDapp(s);
}
catch (...)
{
qWarning() << boost::current_exception_diagnostic_information().c_str();
}
}
if (url.scheme().isEmpty())
if (url.path().indexOf('/') < url.path().indexOf('.'))
url.setScheme("file");
else
s = (r.cap(1).isEmpty() ? "http://" : r.cap(1)) + lookup(r.cap(2)) + r.cap(3);
else{}
qDebug() << s;
ui->webView->setUrl(s);
url.setScheme("http");
else {}
qDebug() << url.toString();
ui->webView->page()->setUrl(url);
}
void Main::on_nameReg_textChanged()
@ -973,7 +1000,7 @@ void Main::refreshBlockChain()
// TODO: keep the same thing highlighted.
// TODO: refactor into MVC
// TODO: use get by hash/number
// TODO: transactions, log addresses, log topics
// TODO: transactions
auto const& bc = ethereum()->blockChain();
QStringList filters = ui->blockChainFilter->text().toLower().split(QRegExp("\\s+"), QString::SkipEmptyParts);
@ -985,15 +1012,17 @@ void Main::refreshBlockChain()
h256 h(f.toStdString());
if (bc.isKnown(h))
blocks.insert(h);
for (auto const& b: bc.withBlockBloom(LogBloom().shiftBloom<3, 32>(sha3(h)), 0, -1))
blocks.insert(bc.numberHash(b));
}
else if (f.toLongLong() <= bc.number())
blocks.insert(bc.numberHash(u256(f.toLongLong())));
/*else if (f.size() == 40)
else if (f.size() == 40)
{
Address h(f[0]);
if (bc.(h))
blocks.insert(h);
}*/
Address h(f.toStdString());
for (auto const& b: bc.withBlockBloom(LogBloom().shiftBloom<3, 32>(sha3(h)), 0, -1))
blocks.insert(bc.numberHash(b));
}
QByteArray oldSelected = ui->blocks->count() ? ui->blocks->currentItem()->data(Qt::UserRole).toByteArray() : QByteArray();
ui->blocks->clear();
@ -1835,3 +1864,9 @@ void Main::refreshWhispers()
ui->whispers->addItem(item);
}
}
void Main::dappLoaded(Dapp& _dapp)
{
QUrl url = m_dappHost->hostDapp(std::move(_dapp));
ui->webView->page()->setUrl(url);
}

14
alethzero/MainWin.h

@ -54,8 +54,11 @@ namespace jsonrpc {
class HttpServer;
}
class QQuickView;
class QWebEnginePage;
class OurWebThreeStubServer;
class DappLoader;
class DappHost;
struct Dapp;
using WatchHandler = std::function<void(dev::eth::LocalisedLogEntries const&)>;
@ -99,6 +102,7 @@ public slots:
private slots:
void eval(QString const& _js);
void addConsoleMessage(QString const& _js, QString const& _s);
// Application
void on_about_triggered();
@ -172,6 +176,9 @@ private slots:
void refreshBlockChain();
void addNewId(QString _ids);
// Dapps
void dappLoaded(Dapp& _dapp); //qt does not support rvalue refs for signals
signals:
void poll();
@ -234,8 +241,6 @@ private:
QString m_privateChain;
dev::Address m_nameReg;
QNetworkAccessManager m_webCtrl;
QList<QPair<QString, QString>> m_consoleHistory;
QMutex m_logLock;
QString m_logHistory;
@ -248,4 +253,7 @@ private:
NatspecHandler m_natSpecDB;
Transact m_transact;
std::unique_ptr<DappHost> m_dappHost;
DappLoader* m_dappLoader;
QWebEnginePage* m_webPage;
};

4
alethzero/Transact.cpp

@ -175,7 +175,7 @@ void Transact::rejigData()
m_data = fromHex(src);
else if (sourceIsSolidity(src))
{
dev::solidity::CompilerStack compiler;
dev::solidity::CompilerStack compiler(true);
try
{
// compiler.addSources(dev::solidity::StandardSources);
@ -287,7 +287,7 @@ void Transact::on_send_clicked()
if (sourceIsSolidity(src))
try
{
dev::solidity::CompilerStack compiler;
dev::solidity::CompilerStack compiler(true);
m_data = compiler.compile(src, ui->optimize->isChecked());
for (string const& s: compiler.getContractNames())
{

28
alethzero/WebPage.cpp

@ -0,0 +1,28 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file WebPage.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com>
* @date 2015
*/
#include "WebPage.h"
void WebPage::javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID)
{
QString prefix = _level == QWebEnginePage::ErrorMessageLevel ? "error" : _level == QWebEnginePage::WarningMessageLevel ? "warning" : "";
emit consoleMessage(QString("%1(%2:%3):%4").arg(prefix).arg(_sourceID).arg(_lineNumber).arg(_message));
}

40
alethzero/WebPage.h

@ -0,0 +1,40 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file WebPage.h
* @author Arkadiy Paronyan arkadiy@ethdev.com>
* @date 2015
*/
#pragma once
#include <QString>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" //QtWebEngineWidgets/qwebenginecertificateerror.h:78:348: error: extra ';'
#include <QtWebEngineWidgets/QWebEnginePage>
#pragma GCC diagnostic pop
class WebPage: public QWebEnginePage
{
Q_OBJECT
public:
WebPage(QObject* _parent): QWebEnginePage(_parent) { }
signals:
void consoleMessage(QString const& _msg);
protected:
void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID) override;
};

1140
chrono_io/chrono_io

File diff suppressed because it is too large

601
chrono_io/ratio_io

@ -0,0 +1,601 @@
// ratio_io
//
// (C) Copyright Howard Hinnant
// Use, modification and distribution are subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt).
#ifndef _RATIO_IO
#define _RATIO_IO
/*
ratio_io synopsis
#include <ratio>
#include <string>
namespace std
{
template <class Ratio, class charT>
struct ratio_string
{
static basic_string<charT> symbol();
static basic_string<charT> prefix();
};
} // std
*/
#include <ratio>
#include <string>
#include <sstream>
namespace std {
template <class _Ratio, class _CharT>
struct ratio_string
{
static basic_string<_CharT> symbol() {return prefix();}
static basic_string<_CharT> prefix();
};
template <class _Ratio, class _CharT>
basic_string<_CharT>
ratio_string<_Ratio, _CharT>::prefix()
{
basic_ostringstream<_CharT> __os;
__os << _CharT('[') << _Ratio::num << _CharT('/')
<< _Ratio::den << _CharT(']');
return __os.str();
}
// atto
template <>
struct ratio_string<atto, char>
{
static string symbol() {return string(1, 'a');}
static string prefix() {return string("atto");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<atto, char16_t>
{
static u16string symbol() {return u16string(1, u'a');}
static u16string prefix() {return u16string(u"atto");}
};
template <>
struct ratio_string<atto, char32_t>
{
static u32string symbol() {return u32string(1, U'a');}
static u32string prefix() {return u32string(U"atto");}
};
#endif
template <>
struct ratio_string<atto, wchar_t>
{
static wstring symbol() {return wstring(1, L'a');}
static wstring prefix() {return wstring(L"atto");}
};
// femto
template <>
struct ratio_string<femto, char>
{
static string symbol() {return string(1, 'f');}
static string prefix() {return string("femto");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<femto, char16_t>
{
static u16string symbol() {return u16string(1, u'f');}
static u16string prefix() {return u16string(u"femto");}
};
template <>
struct ratio_string<femto, char32_t>
{
static u32string symbol() {return u32string(1, U'f');}
static u32string prefix() {return u32string(U"femto");}
};
#endif
template <>
struct ratio_string<femto, wchar_t>
{
static wstring symbol() {return wstring(1, L'f');}
static wstring prefix() {return wstring(L"femto");}
};
// pico
template <>
struct ratio_string<pico, char>
{
static string symbol() {return string(1, 'p');}
static string prefix() {return string("pico");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<pico, char16_t>
{
static u16string symbol() {return u16string(1, u'p');}
static u16string prefix() {return u16string(u"pico");}
};
template <>
struct ratio_string<pico, char32_t>
{
static u32string symbol() {return u32string(1, U'p');}
static u32string prefix() {return u32string(U"pico");}
};
#endif
template <>
struct ratio_string<pico, wchar_t>
{
static wstring symbol() {return wstring(1, L'p');}
static wstring prefix() {return wstring(L"pico");}
};
// nano
template <>
struct ratio_string<nano, char>
{
static string symbol() {return string(1, 'n');}
static string prefix() {return string("nano");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<nano, char16_t>
{
static u16string symbol() {return u16string(1, u'n');}
static u16string prefix() {return u16string(u"nano");}
};
template <>
struct ratio_string<nano, char32_t>
{
static u32string symbol() {return u32string(1, U'n');}
static u32string prefix() {return u32string(U"nano");}
};
#endif
template <>
struct ratio_string<nano, wchar_t>
{
static wstring symbol() {return wstring(1, L'n');}
static wstring prefix() {return wstring(L"nano");}
};
// micro
template <>
struct ratio_string<micro, char>
{
static string symbol() {return string("\xC2\xB5");}
static string prefix() {return string("micro");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<micro, char16_t>
{
static u16string symbol() {return u16string(1, u'\xB5');}
static u16string prefix() {return u16string(u"micro");}
};
template <>
struct ratio_string<micro, char32_t>
{
static u32string symbol() {return u32string(1, U'\xB5');}
static u32string prefix() {return u32string(U"micro");}
};
#endif
template <>
struct ratio_string<micro, wchar_t>
{
static wstring symbol() {return wstring(1, L'\xB5');}
static wstring prefix() {return wstring(L"micro");}
};
// milli
template <>
struct ratio_string<milli, char>
{
static string symbol() {return string(1, 'm');}
static string prefix() {return string("milli");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<milli, char16_t>
{
static u16string symbol() {return u16string(1, u'm');}
static u16string prefix() {return u16string(u"milli");}
};
template <>
struct ratio_string<milli, char32_t>
{
static u32string symbol() {return u32string(1, U'm');}
static u32string prefix() {return u32string(U"milli");}
};
#endif
template <>
struct ratio_string<milli, wchar_t>
{
static wstring symbol() {return wstring(1, L'm');}
static wstring prefix() {return wstring(L"milli");}
};
// centi
template <>
struct ratio_string<centi, char>
{
static string symbol() {return string(1, 'c');}
static string prefix() {return string("centi");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<centi, char16_t>
{
static u16string symbol() {return u16string(1, u'c');}
static u16string prefix() {return u16string(u"centi");}
};
template <>
struct ratio_string<centi, char32_t>
{
static u32string symbol() {return u32string(1, U'c');}
static u32string prefix() {return u32string(U"centi");}
};
#endif
template <>
struct ratio_string<centi, wchar_t>
{
static wstring symbol() {return wstring(1, L'c');}
static wstring prefix() {return wstring(L"centi");}
};
// deci
template <>
struct ratio_string<deci, char>
{
static string symbol() {return string(1, 'd');}
static string prefix() {return string("deci");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<deci, char16_t>
{
static u16string symbol() {return u16string(1, u'd');}
static u16string prefix() {return u16string(u"deci");}
};
template <>
struct ratio_string<deci, char32_t>
{
static u32string symbol() {return u32string(1, U'd');}
static u32string prefix() {return u32string(U"deci");}
};
#endif
template <>
struct ratio_string<deci, wchar_t>
{
static wstring symbol() {return wstring(1, L'd');}
static wstring prefix() {return wstring(L"deci");}
};
// deca
template <>
struct ratio_string<deca, char>
{
static string symbol() {return string("da");}
static string prefix() {return string("deca");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<deca, char16_t>
{
static u16string symbol() {return u16string(u"da");}
static u16string prefix() {return u16string(u"deca");}
};
template <>
struct ratio_string<deca, char32_t>
{
static u32string symbol() {return u32string(U"da");}
static u32string prefix() {return u32string(U"deca");}
};
#endif
template <>
struct ratio_string<deca, wchar_t>
{
static wstring symbol() {return wstring(L"da");}
static wstring prefix() {return wstring(L"deca");}
};
// hecto
template <>
struct ratio_string<hecto, char>
{
static string symbol() {return string(1, 'h');}
static string prefix() {return string("hecto");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<hecto, char16_t>
{
static u16string symbol() {return u16string(1, u'h');}
static u16string prefix() {return u16string(u"hecto");}
};
template <>
struct ratio_string<hecto, char32_t>
{
static u32string symbol() {return u32string(1, U'h');}
static u32string prefix() {return u32string(U"hecto");}
};
#endif
template <>
struct ratio_string<hecto, wchar_t>
{
static wstring symbol() {return wstring(1, L'h');}
static wstring prefix() {return wstring(L"hecto");}
};
// kilo
template <>
struct ratio_string<kilo, char>
{
static string symbol() {return string(1, 'k');}
static string prefix() {return string("kilo");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<kilo, char16_t>
{
static u16string symbol() {return u16string(1, u'k');}
static u16string prefix() {return u16string(u"kilo");}
};
template <>
struct ratio_string<kilo, char32_t>
{
static u32string symbol() {return u32string(1, U'k');}
static u32string prefix() {return u32string(U"kilo");}
};
#endif
template <>
struct ratio_string<kilo, wchar_t>
{
static wstring symbol() {return wstring(1, L'k');}
static wstring prefix() {return wstring(L"kilo");}
};
// mega
template <>
struct ratio_string<mega, char>
{
static string symbol() {return string(1, 'M');}
static string prefix() {return string("mega");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<mega, char16_t>
{
static u16string symbol() {return u16string(1, u'M');}
static u16string prefix() {return u16string(u"mega");}
};
template <>
struct ratio_string<mega, char32_t>
{
static u32string symbol() {return u32string(1, U'M');}
static u32string prefix() {return u32string(U"mega");}
};
#endif
template <>
struct ratio_string<mega, wchar_t>
{
static wstring symbol() {return wstring(1, L'M');}
static wstring prefix() {return wstring(L"mega");}
};
// giga
template <>
struct ratio_string<giga, char>
{
static string symbol() {return string(1, 'G');}
static string prefix() {return string("giga");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<giga, char16_t>
{
static u16string symbol() {return u16string(1, u'G');}
static u16string prefix() {return u16string(u"giga");}
};
template <>
struct ratio_string<giga, char32_t>
{
static u32string symbol() {return u32string(1, U'G');}
static u32string prefix() {return u32string(U"giga");}
};
#endif
template <>
struct ratio_string<giga, wchar_t>
{
static wstring symbol() {return wstring(1, L'G');}
static wstring prefix() {return wstring(L"giga");}
};
// tera
template <>
struct ratio_string<tera, char>
{
static string symbol() {return string(1, 'T');}
static string prefix() {return string("tera");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<tera, char16_t>
{
static u16string symbol() {return u16string(1, u'T');}
static u16string prefix() {return u16string(u"tera");}
};
template <>
struct ratio_string<tera, char32_t>
{
static u32string symbol() {return u32string(1, U'T');}
static u32string prefix() {return u32string(U"tera");}
};
#endif
template <>
struct ratio_string<tera, wchar_t>
{
static wstring symbol() {return wstring(1, L'T');}
static wstring prefix() {return wstring(L"tera");}
};
// peta
template <>
struct ratio_string<peta, char>
{
static string symbol() {return string(1, 'P');}
static string prefix() {return string("peta");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<peta, char16_t>
{
static u16string symbol() {return u16string(1, u'P');}
static u16string prefix() {return u16string(u"peta");}
};
template <>
struct ratio_string<peta, char32_t>
{
static u32string symbol() {return u32string(1, U'P');}
static u32string prefix() {return u32string(U"peta");}
};
#endif
template <>
struct ratio_string<peta, wchar_t>
{
static wstring symbol() {return wstring(1, L'P');}
static wstring prefix() {return wstring(L"peta");}
};
// exa
template <>
struct ratio_string<exa, char>
{
static string symbol() {return string(1, 'E');}
static string prefix() {return string("exa");}
};
#if HAS_UNICODE_SUPPORT
template <>
struct ratio_string<exa, char16_t>
{
static u16string symbol() {return u16string(1, u'E');}
static u16string prefix() {return u16string(u"exa");}
};
template <>
struct ratio_string<exa, char32_t>
{
static u32string symbol() {return u32string(1, U'E');}
static u32string prefix() {return u32string(U"exa");}
};
#endif
template <>
struct ratio_string<exa, wchar_t>
{
static wstring symbol() {return wstring(1, L'E');}
static wstring prefix() {return wstring(L"exa");}
};
}
#endif // _RATIO_IO

26
cmake/EthDependencies.cmake

@ -134,14 +134,24 @@ if (NOT HEADLESS)
message(" - windeployqt path: ${WINDEPLOYQT_APP}")
endif()
# TODO check node && npm version
find_program(ETH_NODE node)
string(REGEX REPLACE "node" "" ETH_NODE_DIRECTORY ${ETH_NODE})
message(" - nodejs location : ${ETH_NODE}")
find_program(ETH_NPM npm)
string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM})
message(" - npm location : ${ETH_NPM}")
if (USENPM)
# TODO check node && npm version
find_program(ETH_NODE node)
string(REGEX REPLACE "node" "" ETH_NODE_DIRECTORY ${ETH_NODE})
message(" - nodejs location : ${ETH_NODE}")
find_program(ETH_NPM npm)
string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM})
message(" - npm location : ${ETH_NPM}")
if (NOT ETH_NODE)
message(FATAL_ERROR "node not found!")
endif()
if (NOT ETH_NPM)
message(FATAL_ERROR "npm not found!")
endif()
endif()
endif() #HEADLESS

2
libdevcore/Common.cpp

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

1
libdevcore/CommonIO.h

@ -35,6 +35,7 @@
#include <sstream>
#include <string>
#include <iostream>
#include <chrono_io/chrono_io>
#include "Common.h"
#include "Base64.h"

14
libdevcore/FixedHash.h

@ -154,25 +154,17 @@ public:
}
};
inline FixedHash<32> bloom() const
{
FixedHash<32> ret;
for (auto i: m_data)
ret[i / 8] |= 1 << (i % 8);
return ret;
}
template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h)
{
return (*this |= _h.template nbloom<P, N>());
return (*this |= _h.template bloom<P, N>());
}
template <unsigned P, unsigned M> inline bool containsBloom(FixedHash<M> const& _h)
{
return contains(_h.template nbloom<P, N>());
return contains(_h.template bloom<P, N>());
}
template <unsigned P, unsigned M> inline FixedHash<M> nbloom() const
template <unsigned P, unsigned M> inline FixedHash<M> bloom() const
{
static const unsigned c_bloomBits = M * 8;
unsigned mask = c_bloomBits - 1;

3
libdevcore/StructuredLogger.cpp

@ -22,11 +22,12 @@
*/
#include "StructuredLogger.h"
#include <ctime>
#include <boost/asio/ip/tcp.hpp>
#include <json/json.h>
#include "Guards.h"
namespace ba = boost::asio;
using namespace std;
namespace dev

11
libdevcore/StructuredLogger.h

@ -27,9 +27,10 @@
#include <string>
#include <chrono>
#include <libp2p/Network.h>
namespace Json { class Value; }
namespace boost { namespace asio { namespace ip { template<class T>class basic_endpoint; class tcp; }}}
namespace bi = boost::asio::ip;
namespace dev
{
@ -61,12 +62,16 @@ public:
static void stopping(std::string const& _clientImpl, const char* _ethVersion);
static void p2pConnected(
std::string const& _id,
bi::tcp::endpoint const& _addr,
bi::basic_endpoint<bi::tcp> const& _addr,
std::chrono::system_clock::time_point const& _ts,
std::string const& _remoteVersion,
unsigned int _numConnections
);
static void p2pDisconnected(std::string const& _id, bi::tcp::endpoint const& _addr, unsigned int _numConnections);
static void p2pDisconnected(
std::string const& _id,
bi::basic_endpoint<bi::tcp> const& _addr,
unsigned int _numConnections
);
static void minedNewBlock(
std::string const& _hash,
std::string const& _blockNumber,

2
libdevcore/vector_ref.h

@ -40,7 +40,7 @@ public:
bool empty() const { return !m_count; }
vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); }
vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); }
void retarget(_T const* _d, size_t _s) { m_data = _d; m_count = _s; }
void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; }
void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); }
void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); }
void populate(vector_ref<typename std::remove_const<_T>::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); }

66
libdevcrypto/Common.cpp

@ -15,8 +15,8 @@
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Common.cpp
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
@ -82,6 +82,22 @@ bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
return true;
}
void dev::encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher)
{
bytes io = _plain.toBytes();
s_secp256k1.encryptECIES(_k, io);
o_cipher = std::move(io);
}
bool dev::decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
{
bytes io = _cipher.toBytes();
if (!s_secp256k1.decryptECIES(_k, io))
return false;
o_plaintext = std::move(io);
return true;
}
void dev::encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher)
{
// TOOD: @alex @subtly do this properly.
@ -94,6 +110,54 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain)
return decrypt(_k, _cipher, o_plain);
}
h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher)
{
h128 iv(Nonce::get());
return encryptSymNoAuth(_k, _plain, o_cipher, iv);
}
h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv)
{
o_cipher.resize(_plain.size());
const int c_aesKeyLen = 16;
SecByteBlock key(_k.data(), c_aesKeyLen);
try
{
CTR_Mode<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), _iv.data());
e.ProcessData(o_cipher.data(), _plain.data(), _plain.size());
return _iv;
}
catch (CryptoPP::Exception& _e)
{
cerr << _e.what() << endl;
o_cipher.resize(0);
return h128();
}
}
bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext)
{
o_plaintext.resize(_cipher.size());
const size_t c_aesKeyLen = 16;
SecByteBlock key(_k.data(), c_aesKeyLen);
try
{
CTR_Mode<AES>::Decryption d;
d.SetKeyWithIV(key, key.size(), _iv.data());
d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size());
return true;
}
catch (CryptoPP::Exception& _e)
{
cerr << _e.what() << endl;
o_plaintext.resize(0);
return false;
}
}
Public dev::recover(Signature const& _sig, h256 const& _message)
{
return s_secp256k1.recover(_sig, _message.ref());

17
libdevcrypto/Common.h

@ -15,8 +15,8 @@
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Common.h
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* Ethereum-specific data structures & algorithms.
@ -96,6 +96,21 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Symmetric decryption.
bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Encrypt payload using ECIES standard with AES128-CTR.
void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypt payload using ECIES standard with AES128-CTR.
bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Encrypts payload with random IV/ctr using AES128-CTR.
h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Encrypts payload with specified IV/ctr using AES128-CTR.
h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv);
/// Decrypts payload with specified IV/ctr.
bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext);
/// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash);

124
libdevcrypto/CryptoPP.cpp

@ -19,8 +19,9 @@
* @date 2014
*/
#include "CryptoPP.h"
#include <libdevcore/Guards.h>
#include "ECDHE.h"
#include "CryptoPP.h"
using namespace std;
using namespace dev;
@ -31,6 +32,119 @@ static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes.");
static_assert(dev::Public::size == 64, "Public key must be 64 bytes.");
static_assert(dev::Signature::size == 65, "Signature must be 65 bytes.");
bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen)
{
// interop w/go ecies implementation
// for sha3, blocksize is 136 bytes
// for sha256, blocksize is 64 bytes
auto reps = ((kdByteLen + 7) * 8) / (64 * 8);
bytes ctr({0, 0, 0, 1});
bytes k;
CryptoPP::SHA256 ctx;
for (unsigned i = 0; i <= reps; i++)
{
ctx.Update(ctr.data(), ctr.size());
ctx.Update(_z.data(), Secret::size);
ctx.Update(_s1.data(), _s1.size());
// append hash to k
bytes digest(32);
ctx.Final(digest.data());
ctx.Restart();
k.reserve(k.size() + h256::size);
move(digest.begin(), digest.end(), back_inserter(k));
if (++ctr[3] || ++ctr[2] || ++ctr[1] || ++ctr[0])
continue;
}
k.resize(kdByteLen);
return move(k);
}
void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher)
{
// interop w/go ecies implementation
auto r = KeyPair::create();
h256 z;
ecdh::agree(r.sec(), _k, z);
auto key = eciesKDF(z, bytes(), 32);
bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16);
bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16);
CryptoPP::SHA256 ctx;
ctx.Update(mKeyMaterial.data(), mKeyMaterial.size());
bytes mKey(32);
ctx.Final(mKey.data());
bytes cipherText;
encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128());
if (cipherText.empty())
return;
bytes msg(1 + Public::size + h128::size + cipherText.size() + 32);
msg[0] = 0x04;
r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size));
bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size + h128::size, cipherText.size());
bytesConstRef(&cipherText).copyTo(msgCipherRef);
// tag message
CryptoPP::HMAC<SHA256> hmacctx(mKey.data(), mKey.size());
bytesConstRef cipherWithIV = bytesRef(&msg).cropped(1 + Public::size, h128::size + cipherText.size());
hmacctx.Update(cipherWithIV.data(), cipherWithIV.size());
hmacctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size());
io_cipher.resize(msg.size());
io_cipher.swap(msg);
}
bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text)
{
// interop w/go ecies implementation
// io_cipher[0] must be 2, 3, or 4, else invalidpublickey
if (io_text[0] < 2 || io_text[0] > 4)
// invalid message: publickey
return false;
if (io_text.size() < (1 + Public::size + h128::size + 1 + h256::size))
// invalid message: length
return false;
h256 z;
ecdh::agree(_k, *(Public*)(io_text.data()+1), z);
auto key = eciesKDF(z, bytes(), 64);
bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16);
bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16);
bytes mKey(32);
CryptoPP::SHA256 ctx;
ctx.Update(mKeyMaterial.data(), mKeyMaterial.size());
ctx.Final(mKey.data());
bytes plain;
size_t cipherLen = io_text.size() - 1 - Public::size - h128::size - h256::size;
bytesConstRef cipherWithIV(io_text.data() + 1 + Public::size, h128::size + cipherLen);
bytesConstRef cipherIV = cipherWithIV.cropped(0, h128::size);
bytesConstRef cipherNoIV = cipherWithIV.cropped(h128::size, cipherLen);
bytesConstRef msgMac(cipherNoIV.data() + cipherLen, h256::size);
h128 iv(cipherIV.toBytes());
// verify tag
CryptoPP::HMAC<SHA256> hmacctx(mKey.data(), mKey.size());
hmacctx.Update(cipherWithIV.data(), cipherWithIV.size());
h256 mac;
hmacctx.Final(mac.data());
for (unsigned i = 0; i < h256::size; i++)
if (mac[i] != msgMac[i])
return false;
decryptSymNoAuth(*(Secret*)eKey.data(), iv, cipherNoIV, plain);
io_text.resize(plain.size());
io_text.swap(plain);
return true;
}
void Secp256k1::encrypt(Public const& _k, bytes& io_cipher)
{
ECIES<ECP>::Encryptor e;
@ -199,13 +313,13 @@ bool Secp256k1::verifySecret(Secret const& _s, Public& _p)
void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s)
{
(void)o_s;
(void)_s;
ECDH<ECP>::Domain d(m_oid);
// TODO: mutex ASN1::secp256k1() singleton
// Creating Domain is non-const for m_oid and m_oid is not thread-safe
ECDH<ECP>::Domain d(ASN1::secp256k1());
assert(d.AgreedValueLength() == sizeof(o_s));
byte remote[65] = {0x04};
memcpy(&remote[1], _r.data(), 64);
assert(d.Agree(o_s.data(), _s.data(), remote));
d.Agree(o_s.data(), _s.data(), remote);
}
void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& o_p)

14
libdevcrypto/CryptoPP.h

@ -65,6 +65,7 @@ inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.
/**
* CryptoPP secp256k1 algorithms.
* @todo Collect ECIES methods into class.
*/
class Secp256k1
{
@ -75,12 +76,21 @@ public:
void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); }
/// Encrypts text (replace input).
/// Encrypts text (replace input). (ECIES w/XOR-SHA1)
void encrypt(Public const& _k, bytes& io_cipher);
/// Decrypts text (replace input).
/// Decrypts text (replace input). (ECIES w/XOR-SHA1)
void decrypt(Secret const& _k, bytes& io_text);
/// Encrypts text (replace input). (ECIES w/AES128-CTR-SHA256)
void encryptECIES(Public const& _k, bytes& io_cipher);
/// Decrypts text (replace input). (ECIES w/AES128-CTR-SHA256)
bool decryptECIES(Secret const& _k, bytes& io_text);
/// Key derivation function used by encryptECIES and decryptECIES.
bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256);
/// @returns siganture of message.
Signature sign(Secret const& _k, bytesConstRef _message);

7
libdevcrypto/ECDHE.cpp

@ -29,7 +29,12 @@ using namespace dev::crypto;
static Secp256k1 s_secp256k1;
void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret)
void dev::crypto::ecdh::agree(Secret const& _s, Public const& _r, h256& o_s)
{
s_secp256k1.agree(_s, _r, o_s);
}
void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) const
{
if (m_remoteEphemeral)
// agreement can only occur once

17
libdevcrypto/ECDHE.h

@ -48,6 +48,11 @@ private:
std::map<Address,AliasSession> m_sessions;
Secret m_secret;
};
namespace ecdh
{
void agree(Secret const& _s, Public const& _r, h256& o_s);
}
/**
* @brief Derive DH shared secret from EC keypairs.
@ -62,12 +67,14 @@ public:
/// Public key sent to remote.
Public pubkey() { return m_ephemeral.pub(); }
Secret seckey() { return m_ephemeral.sec(); }
/// Input public key for dh agreement, output generated shared secret.
void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret);
void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret) const;
protected:
KeyPair m_ephemeral; ///< Ephemeral keypair; generated.
Public m_remoteEphemeral; ///< Public key of remote; parameter.
KeyPair m_ephemeral; ///< Ephemeral keypair; generated.
mutable Public m_remoteEphemeral; ///< Public key of remote; parameter. Set once when agree is called, otherwise immutable.
};
/**
@ -80,10 +87,10 @@ class ECDHEKeyExchange: private ECDHE
{
public:
/// Exchange with unknown remote (pass public key for ingress exchange)
ECDHEKeyExchange(Alias& _k): m_alias(_k) {};
ECDHEKeyExchange(Alias& _k): m_alias(_k) {}
/// Exchange with known remote
ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {};
ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {}
/// Provide public key for dh agreement to generate shared secret.
void agree(Public const& _remoteEphemeral);

2
libethcore/Common.cpp

@ -33,7 +33,7 @@ namespace eth
{
const unsigned c_protocolVersion = 56;
const unsigned c_databaseBaseVersion = 7;
const unsigned c_databaseBaseVersion = 8;
#if ETH_FATDB
const unsigned c_databaseVersionModifier = 1000;
#else

18
libethcore/Ethasher.cpp

@ -45,18 +45,9 @@ bytes const& Ethasher::cache(BlockInfo const& _header)
RecursiveGuard l(x_this);
if (!m_caches.count(_header.seedHash))
{
try {
boost::filesystem::create_directories(getDataDir() + "/ethashcache");
} catch (...) {}
std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".cache";
m_caches[_header.seedHash] = contents(memoFile);
if (m_caches[_header.seedHash].empty())
{
ethash_params p = params((unsigned)_header.number);
m_caches[_header.seedHash].resize(p.cache_size);
ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data());
writeFile(memoFile, m_caches[_header.seedHash]);
}
ethash_params p = params((unsigned)_header.number);
m_caches[_header.seedHash].resize(p.cache_size);
ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data());
}
return m_caches[_header.seedHash];
}
@ -71,6 +62,9 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
delete [] m_fulls.begin()->second.data();
m_fulls.erase(m_fulls.begin());
}
try {
boost::filesystem::create_directories(getDataDir() + "/ethashcache");
} catch (...) {}
std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".full";
m_fulls[_header.seedHash] = contentsNew(memoFile);
if (!m_fulls[_header.seedHash])

126
libethereum/BlockChain.cpp

@ -324,6 +324,18 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
WriteGuard l(x_blockHashes);
m_blockHashes[h256(bi.number)].value = newHash;
}
{
WriteGuard l(x_blocksBlooms);
LogBloom blockBloom = bi.logBloom;
blockBloom.shiftBloom<3, 32>(sha3(bi.coinbaseAddress.ref()));
unsigned index = (unsigned)bi.number;
for (unsigned level = 0; level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize)
{
unsigned i = index / c_bloomIndexSize % c_bloomIndexSize;
unsigned o = index % c_bloomIndexSize;
m_blocksBlooms[chunkId(level, i)].blooms[o] |= blockBloom;
}
}
// Collate transaction hashes and remember who they were.
h256s tas;
{
@ -346,14 +358,23 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
m_receipts[newHash] = br;
}
m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp()));
for (auto const& h: tas)
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp()));
{
ReadGuard l1(x_blocksBlooms);
ReadGuard l2(x_details);
ReadGuard l3(x_blockHashes);
ReadGuard l4(x_receipts);
ReadGuard l5(x_logBlooms);
ReadGuard l6(x_transactionAddresses);
m_blocksDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash, ExtraDetails), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(h256(bi.number), ExtraBlockHash), (ldb::Slice)dev::ref(m_blockHashes[h256(bi.number)].rlp()));
for (auto const& h: tas)
m_extrasDB->Put(m_writeOptions, toSlice(h, ExtraTransactionAddress), (ldb::Slice)dev::ref(m_transactionAddresses[h].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraLogBlooms), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraReceipts), (ldb::Slice)dev::ref(m_receipts[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, ExtraBlocksBlooms), (ldb::Slice)dev::ref(m_blocksBlooms[newHash].rlp()));
}
#if ETH_PARANOIA
checkConsistency();
@ -475,29 +496,30 @@ template <class T> static unsigned getHashSize(map<h256, T> const& _map)
void BlockChain::updateStats() const
{
{
ReadGuard l1(x_blocks);
ReadGuard l(x_blocks);
m_lastStats.memBlocks = 0;
for (auto const& i: m_blocks)
m_lastStats.memBlocks += i.second.size() + 64;
}
{
ReadGuard l2(x_details);
ReadGuard l(x_details);
m_lastStats.memDetails = getHashSize(m_details);
}
{
ReadGuard l5(x_logBlooms);
m_lastStats.memLogBlooms = getHashSize(m_logBlooms);
ReadGuard l1(x_logBlooms);
ReadGuard l2(x_blocksBlooms);
m_lastStats.memLogBlooms = getHashSize(m_logBlooms) + getHashSize(m_blocksBlooms);
}
{
ReadGuard l4(x_receipts);
ReadGuard l(x_receipts);
m_lastStats.memReceipts = getHashSize(m_receipts);
}
{
ReadGuard l3(x_blockHashes);
ReadGuard l(x_blockHashes);
m_lastStats.memBlockHashes = getHashSize(m_blockHashes);
}
{
ReadGuard l6(x_transactionAddresses);
ReadGuard l(x_transactionAddresses);
m_lastStats.memTransactionAddresses = getHashSize(m_transactionAddresses);
}
}
@ -520,6 +542,7 @@ void BlockChain::garbageCollect(bool _force)
WriteGuard l4(x_receipts);
WriteGuard l5(x_logBlooms);
WriteGuard l6(x_transactionAddresses);
WriteGuard l7(x_blocksBlooms);
for (CacheID const& id: m_cacheUsage.back())
{
m_inUse.erase(id);
@ -544,6 +567,9 @@ void BlockChain::garbageCollect(bool _force)
case ExtraTransactionAddress:
m_transactionAddresses.erase(id.first);
break;
case ExtraBlocksBlooms:
m_blocksBlooms.erase(id.first);
break;
}
}
m_cacheUsage.pop_back();
@ -579,6 +605,76 @@ void BlockChain::checkConsistency()
delete it;
}
static inline unsigned upow(unsigned a, unsigned b) { while (b-- > 0) a *= a; return a; }
static inline unsigned ceilDiv(unsigned n, unsigned d) { return n / (n + d - 1); }
static inline unsigned floorDivPow(unsigned n, unsigned a, unsigned b) { return n / upow(a, b); }
static inline unsigned ceilDivPow(unsigned n, unsigned a, unsigned b) { return ceilDiv(n, upow(a, b)); }
// Level 1
// [xxx. ]
// Level 0
// [.x............F.]
// [........x.......]
// [T.............x.]
// [............ ]
// F = 14. T = 32
vector<unsigned> BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const
{
vector<unsigned> ret;
// start from the top-level
unsigned u = upow(c_bloomIndexSize, c_bloomIndexLevels);
// run through each of the top-level blockbloom blocks
for (unsigned index = _earliest / u; index <= ceilDiv(_latest, u); ++index) // 0
ret += withBlockBloom(_b, _earliest, _latest, c_bloomIndexLevels - 1, index);
return ret;
}
vector<unsigned> BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _level, unsigned _index) const
{
// 14, 32, 1, 0
// 14, 32, 0, 0
// 14, 32, 0, 1
// 14, 32, 0, 2
vector<unsigned> ret;
unsigned uCourse = upow(c_bloomIndexSize, _level + 1);
// 256
// 16
unsigned uFine = upow(c_bloomIndexSize, _level);
// 16
// 1
unsigned obegin = _index == _earliest / uCourse ? _earliest / uFine % c_bloomIndexSize : 0;
// 0
// 14
// 0
// 0
unsigned oend = _index == _latest / uCourse ? (_latest / uFine) % c_bloomIndexSize + 1 : c_bloomIndexSize;
// 3
// 16
// 16
// 1
BlocksBlooms bb = blocksBlooms(_level, _index);
for (unsigned o = obegin; o < oend; ++o)
if (bb.blooms[o].contains(_b))
{
// This level has something like what we want.
if (_level > 0)
ret += withBlockBloom(_b, _earliest, _latest, _level - 1, o + _index * c_bloomIndexSize);
else
ret.push_back(o + _index * c_bloomIndexSize);
}
return ret;
}
h256Set BlockChain::allUnclesFrom(h256 _parent) const
{
// Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5).

27
libethereum/BlockChain.h

@ -71,7 +71,8 @@ enum {
ExtraBlockHash,
ExtraTransactionAddress,
ExtraLogBlooms,
ExtraReceipts
ExtraReceipts,
ExtraBlocksBlooms
};
/**
@ -132,6 +133,26 @@ public:
/// Get a list of transaction hashes for a given block. Thread-safe.
h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras<BlockHash, ExtraBlockHash>(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; }
/** Get the block blooms for a number of blocks. Thread-safe.
* @returns the object pertaining to the blocks:
* level 0:
* 0x, 0x + 1, .. (1x - 1)
* 1x, 1x + 1, .. (2x - 1)
* ...
* (255x .. (256x - 1))
* level 1:
* 0x .. (1x - 1), 1x .. (2x - 1), ..., (255x .. (256x - 1))
* 256x .. (257x - 1), 257x .. (258x - 1), ..., (511x .. (512x - 1))
* ...
* level n, index i, offset o:
* i * (x ^ n) + o * x ^ (n - 1)
*/
BlocksBlooms blocksBlooms(unsigned _level, unsigned _index) const { return blocksBlooms(chunkId(_level, _index)); }
BlocksBlooms blocksBlooms(h256 const& _chunkId) const { return queryExtras<BlocksBlooms, ExtraBlocksBlooms>(_chunkId, m_blocksBlooms, x_blocksBlooms, NullBlocksBlooms); }
LogBloom blockBloom(unsigned _number) const { return blocksBlooms(chunkId(0, _number / c_bloomIndexSize)).blooms[_number % c_bloomIndexSize]; }
std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const;
std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const;
/// Get a transaction from its hash. Thread-safe.
bytes transaction(h256 _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); }
@ -188,6 +209,8 @@ public:
void garbageCollect(bool _force = false);
private:
static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); }
void open(std::string _path, bool _killExisting = false);
void close();
@ -230,6 +253,8 @@ private:
mutable TransactionAddressHash m_transactionAddresses;
mutable SharedMutex x_blockHashes;
mutable BlockHashHash m_blockHashes;
mutable SharedMutex x_blocksBlooms;
mutable BlocksBloomsHash m_blocksBlooms;
using CacheID = std::pair<h256, unsigned>;
mutable Mutex x_cacheUsage;

17
libethereum/BlockDetails.h

@ -36,6 +36,11 @@ namespace dev
namespace eth
{
// TODO: OPTIMISE: constructors take bytes, RLP used only in necessary classes.
static const unsigned c_bloomIndexSize = 16;
static const unsigned c_bloomIndexLevels = 2;
struct BlockDetails
{
BlockDetails(): number(0), totalDifficulty(0) {}
@ -64,6 +69,16 @@ struct BlockLogBlooms
mutable unsigned size;
};
struct BlocksBlooms
{
BlocksBlooms() {}
BlocksBlooms(RLP const& _r) { blooms = _r.toArray<LogBloom, c_bloomIndexSize>(); size = _r.data().size(); }
bytes rlp() const { RLPStream s; s << blooms; size = s.out().size(); return s.out(); }
std::array<LogBloom, c_bloomIndexSize> blooms;
mutable unsigned size;
};
struct BlockReceipts
{
BlockReceipts() {}
@ -103,12 +118,14 @@ using BlockLogBloomsHash = std::map<h256, BlockLogBlooms>;
using BlockReceiptsHash = std::map<h256, BlockReceipts>;
using TransactionAddressHash = std::map<h256, TransactionAddress>;
using BlockHashHash = std::map<h256, BlockHash>;
using BlocksBloomsHash = std::map<h256, BlocksBlooms>;
static const BlockDetails NullBlockDetails;
static const BlockLogBlooms NullBlockLogBlooms;
static const BlockReceipts NullBlockReceipts;
static const TransactionAddress NullTransactionAddress;
static const BlockHash NullBlockHash;
static const BlocksBlooms NullBlocksBlooms;
}
}

66
libethereum/Client.cpp

@ -833,8 +833,6 @@ LocalisedLogEntries Client::logs(LogFilter const& _f) const
LocalisedLogEntries ret;
unsigned begin = min<unsigned>(m_bc.number() + 1, (unsigned)_f.latest());
unsigned end = min(m_bc.number(), min(begin, (unsigned)_f.earliest()));
unsigned m = _f.max();
unsigned s = _f.skip();
// Handle pending transactions differently as they're not on the block chain.
if (begin > m_bc.number())
@ -847,68 +845,52 @@ LocalisedLogEntries Client::logs(LogFilter const& _f) const
auto sha3 = m_postMine.pending()[i].sha3();
LogEntries le = _f.matches(tr);
if (le.size())
{
for (unsigned j = 0; j < le.size() && ret.size() != m; ++j)
if (s)
s--;
else
ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3));
}
for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3));
}
begin = m_bc.number();
}
set<unsigned> matchingBlocks;
for (auto const& i: _f.bloomPossibilities())
for (auto u: m_bc.withBlockBloom(i, end, begin))
matchingBlocks.insert(u);
#if ETH_DEBUG
// fill these params
unsigned skipped = 0;
unsigned falsePos = 0;
#endif
auto h = m_bc.numberHash(begin);
unsigned n = begin;
for (; ret.size() != m && n != end; n--, h = m_bc.details(h).parent)
for (auto n: matchingBlocks)
{
#if ETH_DEBUG
int total = 0;
#endif
// check block bloom
auto info = m_bc.info(h);
auto h = m_bc.numberHash(n);
auto receipts = m_bc.receipts(h).receipts;
if (_f.matches(info.logBloom))
for (size_t i = 0; i < receipts.size(); i++)
for (size_t i = 0; i < receipts.size(); i++)
{
TransactionReceipt receipt = receipts[i];
if (_f.matches(receipt.bloom()))
{
TransactionReceipt receipt = receipts[i];
if (_f.matches(receipt.bloom()))
auto info = m_bc.info(h);
auto h = transaction(info.hash, i).sha3();
LogEntries le = _f.matches(receipt);
if (le.size())
{
auto h = transaction(info.hash, i).sha3();
LogEntries le = _f.matches(receipt);
if (le.size())
{
#if ETH_DEBUG
total += le.size();
total += le.size();
#endif
for (unsigned j = 0; j < le.size() && ret.size() != m; ++j)
{
if (s)
s--;
else
ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h));
}
}
for (unsigned j = 0; j < le.size(); ++j)
ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h));
}
#if ETH_DEBUG
if (!total)
falsePos++;
#endif
}
#if ETH_DEBUG
else
skipped++;
if (!total)
falsePos++;
#endif
if (n == end)
break;
}
}
#if ETH_DEBUG
cdebug << (begin - n) << "searched; " << skipped << "skipped; " << falsePos << "false +ves";
cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves";
#endif
return ret;
}

1
libethereum/Client.h

@ -46,7 +46,6 @@
namespace dev
{
namespace eth
{

44
libethereum/EthereumPeer.cpp

@ -297,13 +297,13 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
case StatusPacket:
{
m_protocolVersion = _r[1].toInt<unsigned>();
m_networkId = _r[2].toInt<u256>();
m_protocolVersion = _r[0].toInt<unsigned>();
m_networkId = _r[1].toInt<u256>();
// a bit dirty as we're misusing these to communicate the values to transition, but harmless.
m_totalDifficulty = _r[3].toInt<u256>();
m_latestHash = _r[4].toHash<h256>();
auto genesisHash = _r[5].toHash<h256>();
m_totalDifficulty = _r[2].toInt<u256>();
m_latestHash = _r[3].toHash<h256>();
auto genesisHash = _r[4].toHash<h256>();
clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged();
@ -324,10 +324,10 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case GetTransactionsPacket: break; // DEPRECATED.
case TransactionsPacket:
{
clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)";
addRating(_r.itemCount() - 1);
clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)";
addRating(_r.itemCount());
Guard l(x_knownTransactions);
for (unsigned i = 1; i < _r.itemCount(); ++i)
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
auto h = sha3(_r[i].data());
m_knownTransactions.insert(h);
@ -339,8 +339,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case GetBlockHashesPacket:
{
h256 later = _r[1].toHash<h256>();
unsigned limit = _r[2].toInt<unsigned>();
h256 later = _r[0].toHash<h256>();
unsigned limit = _r[1].toInt<unsigned>();
clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")";
unsigned c = min<unsigned>(host()->m_chain.number(later), limit);
@ -355,19 +355,19 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case BlockHashesPacket:
{
clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes");
clogS(NetMessageSummary) << "BlockHashes (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreHashes");
if (m_asking != Asking::Hashes)
{
cwarn << "Peer giving us hashes when we didn't ask for them.";
break;
}
if (_r.itemCount() == 1)
if (_r.itemCount() == 0)
{
transition(Asking::Blocks);
return true;
}
for (unsigned i = 1; i < _r.itemCount(); ++i)
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
auto h = _r[i].toHash<h256>();
if (host()->m_chain.isKnown(h))
@ -384,11 +384,11 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case GetBlocksPacket:
{
clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << "entries)";
clogS(NetMessageSummary) << "GetBlocks (" << dec << _r.itemCount() << "entries)";
// return the requested blocks.
bytes rlp;
unsigned n = 0;
for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i)
for (unsigned i = 0; i < _r.itemCount() && i <= c_maxBlocks; ++i)
{
auto b = host()->m_chain.block(_r[i].toHash<h256>());
if (b.size())
@ -404,12 +404,12 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case BlocksPacket:
{
clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks");
clogS(NetMessageSummary) << "Blocks (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreBlocks");
if (m_asking != Asking::Blocks)
clogS(NetWarn) << "Unexpected Blocks received!";
if (_r.itemCount() == 1)
if (_r.itemCount() == 0)
{
// Got to this peer's latest block - just give up.
transition(Asking::Nothing);
@ -422,7 +422,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
unsigned got = 0;
unsigned repeated = 0;
for (unsigned i = 1; i < _r.itemCount(); ++i)
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
auto h = BlockInfo::headerHash(_r[i].data());
if (m_sub.noteBlock(h))
@ -467,14 +467,14 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case NewBlockPacket:
{
auto h = BlockInfo::headerHash(_r[1].data());
auto h = BlockInfo::headerHash(_r[0].data());
clogS(NetMessageSummary) << "NewBlock: " << h.abridged();
if (_r.itemCount() != 3)
if (_r.itemCount() != 2)
disable("NewBlock without 2 data fields.");
else
{
switch (host()->m_bq.import(_r[1].data(), host()->m_chain))
switch (host()->m_bq.import(_r[0].data(), host()->m_chain))
{
case ImportResult::Success:
addRating(100);
@ -493,7 +493,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case ImportResult::UnknownParent:
clogS(NetMessageSummary) << "Received block with no known parent. Resyncing...";
setNeedsSyncing(h, _r[2].toInt<u256>());
setNeedsSyncing(h, _r[1].toInt<u256>());
break;
}
Guard l(x_knownBlocks);

14
libethereum/LogFilter.cpp

@ -30,13 +30,13 @@ using namespace dev::eth;
std::ostream& dev::eth::operator<<(std::ostream& _out, LogFilter const& _s)
{
// TODO
_out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< +" << _s.m_skip << "^" << _s.m_max << ")";
_out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< )";
return _out;
}
void LogFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip;
_s.appendList(4) << m_addresses << m_topics << m_earliest << m_latest;
}
h256 LogFilter::sha3() const
@ -73,6 +73,16 @@ bool LogFilter::matches(State const& _s, unsigned _i) const
return matches(_s.receipt(_i)).size() > 0;
}
vector<LogBloom> LogFilter::bloomPossibilities() const
{
// return combination of each of the addresses/topics
vector<LogBloom> ret;
// TODO proper combinatorics.
for (auto i: m_addresses)
ret.push_back(LogBloom().shiftBloom<3, 32>(dev::sha3(i)));
return ret;
}
LogEntries LogFilter::matches(TransactionReceipt const& _m) const
{
LogEntries ret;

10
libethereum/LogFilter.h

@ -45,23 +45,21 @@ class State;
class LogFilter
{
public:
LogFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {}
LogFilter(int _earliest = 0, int _latest = -1): m_earliest(_earliest), m_latest(_latest) {}
void streamRLP(RLPStream& _s) const;
h256 sha3() const;
int earliest() const { return m_earliest; }
int latest() const { return m_latest; }
unsigned max() const { return m_max; }
unsigned skip() const { return m_skip; }
std::vector<LogBloom> bloomPossibilities() const;
bool matches(LogBloom _bloom) const;
bool matches(State const& _s, unsigned _i) const;
LogEntries matches(TransactionReceipt const& _r) const;
LogFilter address(Address _a) { m_addresses.insert(_a); return *this; }
LogFilter topic(unsigned _index, h256 const& _t) { if (_index < 4) m_topics[_index].insert(_t); return *this; }
LogFilter withMax(unsigned _m) { m_max = _m; return *this; }
LogFilter withSkip(unsigned _m) { m_skip = _m; return *this; }
LogFilter withEarliest(int _e) { m_earliest = _e; return *this; }
LogFilter withLatest(int _e) { m_latest = _e; return *this; }
@ -72,8 +70,6 @@ private:
std::array<h256Set, 4> m_topics;
int m_earliest = 0;
int m_latest = -1;
unsigned m_max = 10;
unsigned m_skip = 0;
};
}

16
libethereum/State.cpp

@ -555,7 +555,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
BOOST_THROW_EXCEPTION(TooManyUncles());
set<Nonce> nonces = { m_currentBlock.nonce };
Addresses rewarded;
vector<BlockInfo> rewarded;
set<h256> knownUncles = _bc.allUnclesFrom(m_currentBlock.parentHash);
for (auto const& i: rlp[2])
@ -574,7 +574,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
nonces.insert(uncle.nonce);
tdIncrease += uncle.difficulty;
rewarded.push_back(uncle.coinbaseAddress);
rewarded.push_back(uncle);
}
applyRewards(rewarded);
@ -696,7 +696,7 @@ void State::commitToMine(BlockChain const& _bc)
m_lastTx = m_db;
Addresses uncleAddresses;
vector<BlockInfo> uncleBlockHeaders;
RLPStream unclesData;
unsigned unclesCount = 0;
@ -716,7 +716,7 @@ void State::commitToMine(BlockChain const& _bc)
BlockInfo ubi(_bc.block(u));
ubi.streamRLP(unclesData, WithNonce);
++unclesCount;
uncleAddresses.push_back(ubi.coinbaseAddress);
uncleBlockHeaders.push_back(ubi);
if (unclesCount == 2)
break;
}
@ -760,7 +760,7 @@ void State::commitToMine(BlockChain const& _bc)
m_currentBlock.sha3Uncles = sha3(m_currentUncles);
// Apply rewards last of all.
applyRewards(uncleAddresses);
applyRewards(uncleBlockHeaders);
// Commit any and all changes to the trie that are in the cache, then update the state root accordingly.
commit();
@ -1148,12 +1148,12 @@ State State::fromPending(unsigned _i) const
return ret;
}
void State::applyRewards(Addresses const& _uncleAddresses)
void State::applyRewards(vector<BlockInfo> const& _uncleBlockHeaders)
{
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
for (auto const& i: _uncleBlockHeaders)
{
addBalance(i, m_blockReward * 15 / 16);
addBalance(i.coinbaseAddress, m_blockReward * (8 + i.number - m_currentBlock.number) / 8);
r += m_blockReward / 32;
}
addBalance(m_currentBlock.coinbaseAddress, r);

2
libethereum/State.h

@ -314,7 +314,7 @@ private:
u256 enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce = true);
/// Finalise the block, applying the earned rewards.
void applyRewards(Addresses const& _uncleAddresses);
void applyRewards(std::vector<BlockInfo> const& _uncleBlockHeaders);
/// @returns gas used by transactions thus far executed.
u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; }

40
libevmcore/Assembly.cpp

@ -77,6 +77,20 @@ int AssemblyItem::deposit() const
return 0;
}
string AssemblyItem::getJumpTypeAsString() const
{
switch (m_jumpType)
{
case JumpType::IntoFunction:
return "[in]";
case JumpType::OutOfFunction:
return "[out]";
case JumpType::Ordinary:
default:
return "";
}
}
unsigned Assembly::bytesRequired() const
{
for (unsigned br = 1;; ++br)
@ -99,6 +113,8 @@ void Assembly::append(Assembly const& _a)
{
if (i.type() == Tag || i.type() == PushTag)
i.m_data += m_usedTags;
else if (i.type() == PushSub || i.type() == PushSubSize)
i.m_data += m_subs.size();
append(i);
}
m_deposit = newDeposit;
@ -108,7 +124,7 @@ void Assembly::append(Assembly const& _a)
for (auto const& i: _a.m_strings)
m_strings.insert(i);
for (auto const& i: _a.m_subs)
m_subs.insert(i);
m_subs.push_back(i);
assert(!_a.m_baseDeposit);
assert(!_a.m_totalDeposit);
@ -194,7 +210,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc
return move(cut);
}
ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
{
_out << _prefix << ".code:" << endl;
for (AssemblyItem const& i: m_items)
@ -203,7 +219,7 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con
switch (i.m_type)
{
case Operation:
_out << " " << instructionInfo((Instruction)(byte)i.m_data).name;
_out << " " << instructionInfo((Instruction)(byte)i.m_data).name << "\t" << i.getJumpTypeAsString();
break;
case Push:
_out << " PUSH " << i.m_data;
@ -238,19 +254,19 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix, StringMap con
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
_out << string("\t\t") << getLocationFromSources(_sourceCodes, i.getLocation()) << endl;
_out << "\t\t" << getLocationFromSources(_sourceCodes, i.getLocation()) << endl;
}
if (!m_data.empty() || !m_subs.empty())
{
_out << _prefix << ".data:" << endl;
for (auto const& i: m_data)
if (!m_subs.count(i.first))
if (u256(i.first) >= m_subs.size())
_out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << toHex(i.second) << endl;
for (auto const& i: m_subs)
for (size_t i = 0; i < m_subs.size(); ++i)
{
_out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << endl;
i.second.streamRLP(_out, _prefix + " ", _sourceCodes);
_out << _prefix << " " << hex << i << ": " << endl;
m_subs[i].stream(_out, _prefix + " ", _sourceCodes);
}
}
return _out;
@ -493,8 +509,8 @@ Assembly& Assembly::optimise(bool _enable)
copt << total << " optimisations done.";
for (auto& i: m_subs)
i.second.optimise(true);
for (auto& sub: m_subs)
sub.optimise(true);
return *this;
}
@ -511,8 +527,8 @@ bytes Assembly::assemble() const
unsigned bytesPerTag = dev::bytesRequired(totalBytes);
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
for (auto const& i: m_subs)
m_data[i.first] = i.second.assemble();
for (size_t i = 0; i < m_subs.size(); ++i)
m_data[u256(i)] = m_subs[i].assemble();
unsigned bytesRequiredIncludingData = bytesRequired();
unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData);

21
libevmcore/Assembly.h

@ -42,6 +42,8 @@ class AssemblyItem
friend class Assembly;
public:
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
AssemblyItem(u256 _push): m_type(Push), m_data(_push) {}
AssemblyItem(Instruction _i): m_type(Operation), m_data((byte)_i) {}
AssemblyItem(AssemblyItemType _type, u256 _data = 0): m_type(_type), m_data(_data) {}
@ -58,13 +60,18 @@ public:
int deposit() const;
bool match(AssemblyItem const& _i) const { return _i.m_type == UndefinedItem || (m_type == _i.m_type && (m_type != Operation || m_data == _i.m_data)); }
void setLocation(SourceLocation const& _location) { m_location = _location;}
void setLocation(SourceLocation const& _location) { m_location = _location; }
SourceLocation const& getLocation() const { return m_location; }
void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; }
JumpType getJumpType() const { return m_jumpType; }
std::string getJumpTypeAsString() const;
private:
AssemblyItemType m_type;
u256 m_data;
SourceLocation m_location;
JumpType m_jumpType = JumpType::Ordinary;
};
using AssemblyItems = std::vector<AssemblyItem>;
@ -81,9 +88,9 @@ public:
AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); }
AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); }
AssemblyItem newData(bytes const& _data) { h256 h = (u256)std::hash<std::string>()(asString(_data)); m_data[h] = _data; return AssemblyItem(PushData, h); }
AssemblyItem newSub(Assembly const& _sub) { h256 h = h256::random(s_fixedHashEngine); m_subs[h] = _sub; return AssemblyItem(PushSub, h); }
AssemblyItem newSub(Assembly const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
AssemblyItem newPushString(std::string const& _data) { h256 h = (u256)std::hash<std::string>()(_data); m_strings[h] = _data; return AssemblyItem(PushString, h); }
AssemblyItem newPushSubSize(h256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
AssemblyItem append() { return append(newTag()); }
void append(Assembly const& _a);
@ -114,7 +121,7 @@ public:
void popTo(int _deposit) { while (m_deposit > _deposit) append(Instruction::POP); }
void injectStart(AssemblyItem const& _i);
std::string out() const { std::stringstream ret; streamRLP(ret); return ret.str(); }
std::string out() const { std::stringstream ret; stream(ret); return ret.str(); }
int deposit() const { return m_deposit; }
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@ -124,7 +131,7 @@ public:
bytes assemble() const;
Assembly& optimise(bool _enable);
std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const;
std::ostream& stream(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const;
protected:
std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
@ -134,7 +141,7 @@ protected:
unsigned m_usedTags = 0;
AssemblyItems m_items;
mutable std::map<h256, bytes> m_data;
std::map<h256, Assembly> m_subs;
std::vector<Assembly> m_subs;
std::map<h256, std::string> m_strings;
int m_deposit = 0;
@ -146,7 +153,7 @@ protected:
inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)
{
_a.streamRLP(_out);
_a.stream(_out);
return _out;
}

2
libjsqrc/CMakeLists.txt

@ -12,7 +12,7 @@ qt5_add_resources(JSQRC js.qrc)
add_library(jsqrc STATIC ${JSQRC})
target_link_libraries(jsqrc Qt5::Core)
if (ETH_NODE AND ETH_NPM)
if (USENPM)
add_custom_target(ethereumjs)
add_custom_command(TARGET ethereumjs
POST_BUILD

20
libnatspec/NatspecExpressionEvaluator.cpp

@ -35,25 +35,23 @@ static QString contentsOfQResource(string const& _res)
return in.readAll();
}
NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _method, QString const& _params)
NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _transaction, QString const& _method)
: m_abi(_abi), m_transaction(_transaction), m_method(_method)
{
Q_INIT_RESOURCE(natspec);
QJSValue result = m_engine.evaluate(contentsOfQResource(":/natspec/natspec.js"));
if (result.isError())
BOOST_THROW_EXCEPTION(FileError());
m_engine.evaluate("globals.abi = " + _abi);
m_engine.evaluate("globals.method = " + _method);
m_engine.evaluate("globals.params = " + _params);
m_engine.evaluate("var natspec = require('natspec')");
}
QString NatspecExpressionEvaluator::evalExpression(QString const& _expression)
{
QJSValue result = m_engine.evaluate("evaluateExpression(\"" + _expression + "\")");
if (result.isError())
{
cerr << "Could not evaluate expression: \"" << _expression.toStdString() << "\"" << endl;
return _expression;
}
QString call = "";
if (!m_abi.isEmpty() && !m_transaction.isEmpty() && !m_method.isEmpty())
call = ", {abi:" + m_abi + ", transaction:" + m_transaction + ", method: '" + m_method + "' }";
QJSValue result = m_engine.evaluate("natspec.evaluateExpressionSafe(\"" + _expression + "\"" + call + ")");
return result.toString();
}

9
libnatspec/NatspecExpressionEvaluator.h

@ -34,12 +34,10 @@ class NatspecExpressionEvaluator
public:
/// Construct natspec expression evaluator
/// @params abi - contract's abi in json format, passed as string
/// @params transaction - json object containing transaction data
/// @params method - name of the contract's method for which we evaluate the natspec.
/// If we want to use raw string, it should be passed with quotation marks eg. "\"helloWorld\""
/// If we pass string "helloWorld", the value of the object with name "helloWorld" will be used
/// @params params - array of method input params, passed as string, objects in array should be
/// javascript valid objects
NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _method = "", QString const& _params = "[]");
NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _transaction = "{}", QString const& _method = "");
/// Should be called to evaluate natspec expression
/// @params expression - natspec expression
@ -48,4 +46,7 @@ public:
private:
QJSEngine m_engine;
QString m_abi;
QString m_transaction;
QString m_method;
};

102
libnatspec/natspec.js

@ -1,102 +0,0 @@
/**
* This plugin exposes 'evaluateExpression' method which should be used
* to evaluate natspec description
*/
/// Object which should be used by NatspecExpressionEvaluator
/// abi - abi of the contract that will be used
/// method - name of the method that is called
/// params - input params of the method that will be called
var globals = {
abi: [],
method: "",
params: []
};
/// Helper method
/// Should be called to copy values from object to global context
var copyToContext = function (obj, context) {
var keys = Object.keys(obj);
keys.forEach(function (key) {
context[key] = obj[key];
});
}
/// Helper method
/// Should be called to get method with given name from the abi
/// @param contract's abi
/// @param name of the method that we are looking for
var getMethodWithName = function(abi, name) {
for (var i = 0; i < abi.length; i++) {
if (abi[i].name === name) {
return abi[i];
}
}
//console.warn('could not find method with name: ' + name);
return undefined;
};
/// Function called to get all contract's storage values
/// @returns hashmap with contract properties which are used
/// TODO: check if this function will be used
var getContractProperties = function (address, abi) {
return {};
};
/// Function called to get all contract's methods
/// @returns hashmap with used contract's methods
/// TODO: check if this function will be used
var getContractMethods = function (address, abi) {
//return web3.eth.contract(address, abi); // commented out web3 usage
return {};
};
/// Function called to get all contract method input variables
/// @returns hashmap with all contract's method input variables
var getMethodInputParams = function (method, params) {
return method.inputs.reduce(function (acc, current, index) {
acc[current.name] = params[index];
return acc;
}, {});
};
/// Should be called to evaluate single expression
/// Is internally using javascript's 'eval' method
/// Should be checked if it is safe
var evaluateExpression = function (expression) {
var self = this;
//var storage = getContractProperties(address, abi);
//var methods = getContractMethods(address, abi);
var method = getMethodWithName(globals.abi, globals.method);
if (method) {
var input = getMethodInputParams(method, globals.params);
copyToContext(input, self);
}
// TODO: test if it is safe
var evaluatedExpression = "";
// match everything in `` quotes
var pattern = /\`(?:\\.|[^`\\])*\`/gim
var match;
var lastIndex = 0;
while ((match = pattern.exec(expression)) !== null) {
var startIndex = pattern.lastIndex - match[0].length;
var toEval = match[0].slice(1, match[0].length - 1);
evaluatedExpression += expression.slice(lastIndex, startIndex);
evaluatedExpression += eval(toEval).toString();
lastIndex = pattern.lastIndex;
}
evaluatedExpression += expression.slice(lastIndex);
return evaluatedExpression;
};

2
libnatspec/natspec.qrc

@ -1,5 +1,5 @@
<RCC>
<qresource prefix="/natspec">
<file>natspec.js</file>
<file alias="natspec.js">natspecjs/dist/natspec.min.js</file>
</qresource>
</RCC>

3
libnatspec/natspecjs/.gitignore

@ -0,0 +1,3 @@
# VIM stuff
*.swp
node_modules/

8
libnatspec/natspecjs/.travis.yml

@ -0,0 +1,8 @@
language: node_js
node_js:
- "0.11"
- "0.10"
before_script:
- npm install
after_script:
- npm run-script test-coveralls

47
libnatspec/natspecjs/README.md

@ -0,0 +1,47 @@
# natspec.js
Javascript Library used to evaluate natspec expressions
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url]
[travis-image]: https://travis-ci.org/ethereum/natspec.js.svg
[travis-url]: https://travis-ci.org/ethereum/natspec.js
[coveralls-image]: https://coveralls.io/repos/ethereum/natspec.js/badge.svg?branch=master
[coveralls-url]: https://coveralls.io/r/ethereum/natspec.js?branch=master
## Usage
It exposes global object `natspec` with method `evaluateExpression`.
```javascript
var natspec = require('natspec');
var natspecExpression = "Will multiply `a` by 7 and return `a * 7`.";
var call = {
method: 'multiply',
abi: abi,
transaction: transaction
};
var evaluatedExpression = natspec.evaluateExpression(natspecExpression, call);
console.log(evaluatedExpression); // "Will multiply 4 by 7 and return 28."
```
More examples are available [here](https://github.com/ethereum/natspec.js/blob/master/test/test.js).
## Building
```bash
npm run-script build
```
## Testing (mocha)
```bash
npm test
```
## Wiki
* [Ethereum Natural Specification Format](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format)
* [Natspec Example](https://github.com/ethereum/wiki/wiki/Natspec-Example)

3570
libnatspec/natspecjs/dist/natspec.js

File diff suppressed because it is too large

1
libnatspec/natspecjs/dist/natspec.js.map

File diff suppressed because one or more lines are too long

2
libnatspec/natspecjs/dist/natspec.min.js

File diff suppressed because one or more lines are too long

13
libnatspec/natspecjs/example/example.html

@ -0,0 +1,13 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="../dist/natspec.js"></script>
<script type="text/javascript">
var natspec = require('natspec');
</script>
</head>
<body>
</body>
</html>

186
libnatspec/natspecjs/natspec.js

@ -0,0 +1,186 @@
/*
This file is part of natspec.js.
natspec.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
natspec.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with natspec.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file natspec.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var abi = require('./node_modules/ethereum.js/lib/abi.js');
/**
* This object should be used to evaluate natspec expression
* It has one method evaluateExpression which shoul be used
*/
var natspec = (function () {
/**
* Helper method
* Should be called to copy values from object to global context
*
* @method copyToContext
* @param {Object} object from which we want to copy properties
* @param {Object} object to which we copy
*/
var copyToContext = function (obj, context) {
Object.keys(obj).forEach(function (key) {
context[key] = obj[key];
});
}
/**
* Should be used to generate codes, which will be evaluated
*
* @method generateCode
* @param {Object} object from which code will be generated
* @return {String} javascript code which is used to initalized variables
*/
var generateCode = function (obj) {
return Object.keys(obj).reduce(function (acc, key) {
return acc + "var " + key + " = context['" + key + "'];\n";
}, "");
};
/**
* Helper method
* Should be called to get method with given name from the abi
*
* @method getMethodWithName
* @param {Array} contract's abi
* @param {String} name of the method that we are looking for
* @return {Object} abi for method with name
*/
var getMethodWithName = function(abi, name) {
return abi.filter(function (method) {
return method.name === name;
})[0];
};
/**
* Should be used to get all contract method input variables
*
* @method getMethodInputParams
* @param {Object} abi for certain method
* @param {Object} transaction object
* @return {Object} object with all contract's method input variables
*/
var getMethodInputParams = function (method, transaction) {
// do it with output formatter (cause we have to decode)
var params = abi.formatOutput(method.inputs, '0x' + transaction.params[0].data.slice(10));
return method.inputs.reduce(function (acc, current, index) {
acc[current.name] = params[index];
return acc;
}, {});
};
/**
* Should be called when we want to evaluate natspec expression
* Replaces all natspec 'subexpressions' with evaluated value
*
* @method mapExpressionToEvaluate
* @param {String} expression to evaluate
* @param {Function} callback which is called to evaluate te expression
* @return {String} evaluated expression
*/
var mapExpressionsToEvaluate = function (expression, cb) {
var evaluatedExpression = "";
// match everything in `` quotes
var pattern = /\`(?:\\.|[^`\\])*\`/gim
var match;
var lastIndex = 0;
try {
while ((match = pattern.exec(expression)) !== null) {
var startIndex = pattern.lastIndex - match[0].length;
var toEval = match[0].slice(1, match[0].length - 1);
evaluatedExpression += expression.slice(lastIndex, startIndex);
var evaluatedPart = cb(toEval);
evaluatedExpression += evaluatedPart;
lastIndex = pattern.lastIndex;
}
evaluatedExpression += expression.slice(lastIndex);
}
catch (err) {
throw new Error("Natspec evaluation failed, wrong input params");
}
return evaluatedExpression;
};
/**
* Should be called to evaluate single expression
* Is internally using javascript's 'eval' method
*
* @method evaluateExpression
* @param {String} expression which should be evaluated
* @param {Object} [call] object containing contract abi, transaction, called method
* @return {String} evaluated expression
* @throws exception if method is not found or we are trying to evaluate input params that does not exists
*/
var evaluateExpression = function (expression, call) {
//var self = this;
var context = {};
if (!!call) {
try {
var method = getMethodWithName(call.abi, call.method);
var params = getMethodInputParams(method, call.transaction);
copyToContext(params, context);
}
catch (err) {
throw new Error("Natspec evaluation failed, method does not exist");
}
}
var code = generateCode(context);
var evaluatedExpression = mapExpressionsToEvaluate(expression, function (toEval) {
var fn = new Function("context", code + "return " + toEval + ";");
return fn(context).toString();
});
return evaluatedExpression;
};
/**
* Safe version of evaluateExpression
* Instead of throwing an exception it returns it as a string
*
* @method evaluateExpressionSafe
* @param {String} expression which should be evaluated
* @param {Object} [call] object containing contract abi, transaction, called method
* @return {String} evaluated expression
*/
var evaluateExpressionSafe = function (expression, call) {
try {
return evaluateExpression(expression, call);
}
catch (err) {
return err.message;
}
};
return {
evaluateExpression: evaluateExpression,
evaluateExpressionSafe: evaluateExpressionSafe
};
})();
module.exports = natspec;

24
libnatspec/natspecjs/package.json

@ -0,0 +1,24 @@
{
"name": "natspec.js",
"version": "0.0.1",
"description": "Javascript Library used to evaluate natspec expressions",
"main": "natspec.js",
"scripts": {
"build": "cd dist && browserify -r ../natspec.js:natspec -i crypto -o natspec.js && uglifyjs natspec.js --source-map natspec.js.map -c -m -o natspec.min.js",
"test": "mocha",
"test-coveralls": "istanbul cover _mocha -- -R spec && cat coverage/lcov.info | coveralls --verbose"
},
"author": "",
"dependencies": {
"ethereum.js": "ethereum/ethereum.js#master"
},
"devDependencies": {
"browserify": "^9.0.3",
"chai": "^2.1.0",
"coveralls": "^2.11.2",
"istanbul": "^0.3.6",
"mocha": "^2.1.0",
"uglify-js": "^2.4.16"
},
"license": "LGPL-3.0"
}

149
libnatspec/natspecjs/test/test.js

@ -0,0 +1,149 @@
var chai = require('chai');
var natspec = require('../natspec.js');
var assert = chai.assert;
describe('natspec', function () {
it('should evaluate simple expression', function () {
// given
var expression = "`x = 1` + `y = 2` will be equal `x + y`";
// when
var result = natspec.evaluateExpression(expression);
var result2 = natspec.evaluateExpressionSafe(expression);
// then
assert.equal(result, "1 + 2 will be equal 3");
assert.equal(result2, "1 + 2 will be equal 3");
});
it('should evalute expression using input params', function () {
//given
var expression = "Will multiply `a` by 7 and return `a * 7`.";
var method = 'multiply';
var abi = [{
"name": "multiply",
"constant": false,
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}],
"outputs": [{
"name": "d",
"type": "uint256"
}]
}];
var transaction = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0x8521742d3f456bd237e312d6e30724960f72517a",
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a"
}],
"id": 6
};
var call = {
method: method,
abi: abi,
transaction: transaction
};
// when
var result = natspec.evaluateExpression(expression, call);
var result2 = natspec.evaluateExpressionSafe(expression, call);
// then
assert.equal(result, "Will multiply 122 by 7 and return 854.");
assert.equal(result2, "Will multiply 122 by 7 and return 854.");
});
it('should evalute expression using input params', function () {
//given
var expression = "Will multiply `a` by 7 and return `a * 7`.";
var method = 'multiply';
var abi = [{
"name": "multiply",
"constant": false,
"type": "function",
"inputs": [{
"name": "b",
"type": "uint256"
}],
"outputs": [{
"name": "d",
"type": "uint256"
}]
}];
var transaction = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0x8521742d3f456bd237e312d6e30724960f72517a",
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a"
}],
"id": 6
};
var call = {
method: method,
abi: abi,
transaction: transaction
};
// when
var exceptionThrow = function () { natspec.evaluateExpression(expression, call);}
var result = natspec.evaluateExpressionSafe(expression, call);
// then
assert.equal(result, "Natspec evaluation failed, wrong input params");
assert.throws(exceptionThrow, "Natspec evaluation failed, wrong input params");
});
it('should evalute expression using input params', function () {
//given
var expression = "Will multiply `a` by 7 and return `a * 7`.";
var method = 'multiply2';
var abi = [{
"name": "multiply",
"constant": false,
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}],
"outputs": [{
"name": "d",
"type": "uint256"
}]
}];
var transaction = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0x8521742d3f456bd237e312d6e30724960f72517a",
"data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a"
}],
"id": 6
};
var call = {
method: method,
abi: abi,
transaction: transaction
};
// when
var exceptionThrow = function () { natspec.evaluateExpression(expression, call);}
var result = natspec.evaluateExpressionSafe(expression, call);
// then
assert.equal(result, "Natspec evaluation failed, method does not exist");
assert.throws(exceptionThrow, "Natspec evaluation failed, method does not exist");
});
});

12
libp2p/Capability.cpp

@ -45,7 +45,7 @@ void Capability::disable(std::string const& _problem)
RLPStream& Capability::prep(RLPStream& _s, unsigned _id, unsigned _args)
{
return Session::prep(_s).appendList(_args + 1).append(_id + m_idOffset);
return _s.appendRaw(bytes(1, _id + m_idOffset)).appendList(_args);
}
void Capability::sealAndSend(RLPStream& _s)
@ -53,16 +53,6 @@ void Capability::sealAndSend(RLPStream& _s)
m_session->sealAndSend(_s);
}
void Capability::send(bytesConstRef _msg)
{
m_session->send(_msg);
}
void Capability::send(bytes&& _msg)
{
m_session->send(move(_msg));
}
void Capability::addRating(unsigned _r)
{
m_session->addRating(_r);

3
libp2p/Capability.h

@ -52,9 +52,6 @@ protected:
RLPStream& prep(RLPStream& _s, unsigned _id, unsigned _args = 0);
void sealAndSend(RLPStream& _s);
void send(bytes&& _msg);
void send(bytesConstRef _msg);
void addRating(unsigned _r);
private:

229
libp2p/Host.cpp

@ -24,9 +24,8 @@
#include <chrono>
#include <thread>
#include <mutex>
#include <memory>
#include <boost/algorithm/string.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/StructuredLogger.h>
@ -36,6 +35,7 @@
#include "Common.h"
#include "Capability.h"
#include "UPnP.h"
#include "RLPxHandshake.h"
#include "Host.h"
using namespace std;
using namespace dev;
@ -121,6 +121,23 @@ void Host::doneWorking()
for (auto const& h: m_capabilities)
h.second->onStopping();
// disconnect pending handshake, before peers, as a handshake may create a peer
for (unsigned n = 0;; n = 0)
{
{
Guard l(x_connecting);
for (auto i: m_connecting)
if (auto h = i.lock())
{
h->cancel();
n++;
}
}
if (!n)
break;
m_ioService.poll();
}
// disconnect peers
for (unsigned n = 0;; n = 0)
{
@ -157,32 +174,65 @@ unsigned Host::protocolVersion() const
return 3;
}
void Host::registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps)
void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint)
{
/// Get or create Peer
shared_ptr<Peer> p;
p = m_peers[_id];
if (!p)
{
p.reset(new Peer()); // this maybe redundant
p->id = _id;
}
p->m_lastDisconnect = NoDisconnect;
if (p->isOffline())
p->m_lastConnected = std::chrono::system_clock::now();
p->m_failedAttempts = 0;
p->endpoint.tcp.address(_endpoint.address());
auto protocolVersion = _rlp[0].toInt<unsigned>();
auto clientVersion = _rlp[1].toString();
auto caps = _rlp[2].toVector<CapDesc>();
auto listenPort = _rlp[3].toInt<unsigned short>();
// clang error (previously: ... << hex << caps ...)
// "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments"
stringstream capslog;
for (auto cap: caps)
capslog << "(" << cap.first << "," << dec << cap.second << ")";
clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort;
// create session so disconnects are managed
auto ps = make_shared<Session>(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet<CapDesc>(), 0, map<string, string>()}));
if (protocolVersion != this->protocolVersion())
{
ps->disconnect(IncompatibleProtocol);
return;
}
{
clog(NetNote) << "p2p.host.peer.register" << _s->m_peer->id.abridged();
StructuredLogger::p2pConnected(
_s->m_peer->id.abridged(),
_s->m_peer->peerEndpoint(),
_s->m_peer->m_lastConnected,
_s->m_info.clientVersion,
peerCount()
);
RecursiveGuard l(x_sessions);
// TODO: temporary loose-coupling; if m_peers already has peer,
// it is same as _s->m_peer. (fixing next PR)
if (!m_peers.count(_s->m_peer->id))
m_peers[_s->m_peer->id] = _s->m_peer;
m_sessions[_s->m_peer->id] = _s;
if (m_sessions.count(_id) && !!m_sessions[_id].lock())
if (auto s = m_sessions[_id].lock())
if(s->isConnected())
{
// Already connected.
clog(NetWarn) << "Session already exists for peer with id" << _id.abridged();
ps->disconnect(DuplicatePeer);
return;
}
m_sessions[_id] = ps;
}
ps->start();
unsigned o = (unsigned)UserPacket;
for (auto const& i: _caps)
for (auto const& i: caps)
if (haveCapability(i))
{
_s->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(_s.get(), o));
ps->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(ps.get(), o));
o += m_capabilities[i]->messageCount();
}
clog(NetNote) << "p2p.host.peer.register" << _id.abridged();
StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount());
}
void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
@ -235,19 +285,6 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
}
}
void Host::seal(bytes& _b)
{
_b[0] = 0x22;
_b[1] = 0x40;
_b[2] = 0x08;
_b[3] = 0x91;
uint32_t len = (uint32_t)_b.size() - 8;
_b[4] = (len >> 24) & 0xff;
_b[5] = (len >> 16) & 0xff;
_b[6] = (len >> 8) & 0xff;
_b[7] = len & 0xff;
}
void Host::determinePublic(string const& _publicAddress, bool _upnp)
{
m_peerAddresses.clear();
@ -320,34 +357,19 @@ void Host::runAcceptor()
clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_tcpPublic << ")";
m_accepting = true;
// socket is created outside of acceptor-callback
// An allocated socket is necessary as asio can use the socket
// until the callback succeeds or fails.
//
// Until callback succeeds or fails, we can't dealloc it.
//
// Callback is guaranteed to be called via asio or when
// m_tcp4Acceptor->stop() is called by Host.
//
// All exceptions are caught so they don't halt asio and so the
// socket is deleted.
//
// It's possible for an accepted connection to return an error in which
// case the socket may be open and must be closed to prevent asio from
// processing socket events after socket is deallocated.
bi::tcp::socket *s = new bi::tcp::socket(m_ioService);
m_tcp4Acceptor.async_accept(*s, [=](boost::system::error_code ec)
auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService));
m_tcp4Acceptor.async_accept(socket->ref(), [=](boost::system::error_code ec)
{
// if no error code, doHandshake takes ownership
// if no error code
bool success = false;
if (!ec)
{
try
{
// doHandshake takes ownersihp of *s via std::move
// incoming connection; we don't yet know nodeid
doHandshake(s, NodeId());
auto handshake = make_shared<RLPXHandshake>(this, socket);
m_connecting.push_back(handshake);
handshake->start();
success = true;
}
catch (Exception const& _e)
@ -360,41 +382,16 @@ void Host::runAcceptor()
}
}
// asio doesn't close socket on error
if (!success && s->is_open())
{
boost::system::error_code ec;
s->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
s->close();
}
if (!success)
socket->ref().close();
m_accepting = false;
delete s;
if (ec.value() < 1)
runAcceptor();
});
}
}
void Host::doHandshake(bi::tcp::socket* _socket, NodeId _nodeId)
{
try {
clog(NetConnect) << "Accepting connection for " << _socket->remote_endpoint();
} catch (...){}
shared_ptr<Peer> p;
if (_nodeId)
p = m_peers[_nodeId];
if (!p)
p.reset(new Peer());
p->endpoint.tcp.address(_socket->remote_endpoint().address());
auto ps = std::make_shared<Session>(this, std::move(*_socket), p);
ps->start();
}
string Host::pocHost()
{
vector<string> strs;
@ -440,15 +437,14 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short
void Host::connect(std::shared_ptr<Peer> const& _p)
{
for (unsigned i = 0; i < 200; i++)
if (isWorking() && !m_run)
this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run)
return;
_p->m_lastAttempted = std::chrono::system_clock::now();
if (havePeerSession(_p->id))
{
clog(NetWarn) << "Aborted connect. Node already connected.";
clog(NetConnect) << "Aborted connect. Node already connected.";
return;
}
@ -469,8 +465,8 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
}
clog(NetConnect) << "Attempting connection to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "from" << id().abridged();
bi::tcp::socket* s = new bi::tcp::socket(m_ioService);
s->async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec)
auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService));
socket->ref().async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec)
{
if (ec)
{
@ -480,16 +476,15 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
}
else
{
clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint();
_p->m_lastDisconnect = NoDisconnect;
_p->m_lastConnected = std::chrono::system_clock::now();
_p->m_failedAttempts = 0;
auto ps = make_shared<Session>(this, std::move(*s), _p);
ps->start();
clog(NetConnect) << "Connecting to" << _p->id.abridged() << "@" << _p->peerEndpoint();
auto handshake = make_shared<RLPXHandshake>(this, socket, _p->id);
{
Guard l(x_connecting);
m_connecting.push_back(handshake);
}
handshake->start();
}
delete s;
Guard l(x_pendingNodeConns);
m_pendingPeerConns.erase(nptr);
});
@ -538,26 +533,42 @@ void Host::run(boost::system::error_code const&)
m_nodeTable->processEvents();
// cleanup zombies
{
Guard l(x_connecting);
m_connecting.remove_if([](std::weak_ptr<RLPXHandshake> h){ return h.lock(); });
}
for (auto p: m_sessions)
if (auto pp = p.second.lock())
pp->serviceNodesRequest();
keepAlivePeers();
disconnectLatePeers();
auto c = peerCount();
if (m_idealPeerCount && !c)
for (auto p: m_peers)
if (p.second->shouldReconnect())
{
// TODO p2p: fixme
p.second->m_lastAttempted = std::chrono::system_clock::now();
connect(p.second);
// At this time peers will be disconnected based on natural TCP timeout.
// disconnectLatePeers needs to be updated for the assumption that Session
// is always live and to ensure reputation and fallback timers are properly
// updated. // disconnectLatePeers();
auto openSlots = m_idealPeerCount - peerCount();
if (openSlots > 0)
{
list<shared_ptr<Peer>> toConnect;
{
RecursiveGuard l(x_sessions);
for (auto p: m_peers)
if (p.second->shouldReconnect())
toConnect.push_back(p.second);
}
for (auto p: toConnect)
if (openSlots--)
connect(p);
else
break;
}
if (c < m_idealPeerCount)
m_nodeTable->discover();
}
auto runcb = [this](boost::system::error_code const& error) { run(error); };
m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval));

26
libp2p/Host.h

@ -35,10 +35,12 @@
#include <libdevcore/Worker.h>
#include <libdevcore/RangeMask.h>
#include <libdevcrypto/Common.h>
#include <libdevcrypto/ECDHE.h>
#include "NodeTable.h"
#include "HostCapability.h"
#include "Network.h"
#include "Peer.h"
#include "RLPxFrameIO.h"
#include "Common.h"
namespace ba = boost::asio;
namespace bi = ba::ip;
@ -68,19 +70,16 @@ private:
* @brief The Host class
* Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe.
*
* @todo exceptions when nodeTable not set (prior to start)
* @todo onNodeTableEvent: move peer-connection logic into ensurePeers
* @todo handshake: gracefully disconnect peer if peer already connected
* @todo abstract socket -> IPConnection
* @todo cleanup startPeerSession
* @todo determinePublic: ipv6, udp
* @todo handle conflict if addNode/requireNode called and Node already exists w/conflicting tcp or udp port
* @todo write host identifier to disk w/nodes
* @todo per-session keepalive/ping instead of broadcast; set ping-timeout via median-latency
* @todo configuration-management (NetworkPrefs+Keys+Topology)
*/
class Host: public Worker
{
friend class HostNodeTableHandler;
friend class RLPXHandshake;
friend class Session;
friend class HostCapabilityFace;
@ -114,8 +113,6 @@ public:
CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; }
template <class T> std::shared_ptr<T> cap() const { try { return std::static_pointer_cast<T>(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } }
bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; }
void addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPort, unsigned short _udpPort);
/// Set ideal number of peers.
@ -153,7 +150,8 @@ public:
NodeId id() const { return m_alias.pub(); }
void registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps);
/// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error.
void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint);
protected:
void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e);
@ -162,6 +160,8 @@ protected:
void restoreNetwork(bytesConstRef _b);
private:
bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; }
/// Populate m_peerAddresses with available public addresses.
void determinePublic(std::string const& _publicAddress, bool _upnp);
@ -176,11 +176,6 @@ private:
/// Called only from startedWorking().
void runAcceptor();
/// Handler for verifying handshake siganture before creating session. _nodeId is passed for outbound connections. If successful, socket is moved to Session via std::move.
void doHandshake(bi::tcp::socket* _socket, NodeId _nodeId = NodeId());
void seal(bytes& _b);
/// Called by Worker. Not thread-safe; to be called only by worker.
virtual void startedWorking();
/// Called by startedWorking. Not thread-safe; to be called only be Worker.
@ -229,6 +224,9 @@ private:
/// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method.
mutable std::map<NodeId, std::weak_ptr<Session>> m_sessions;
mutable RecursiveMutex x_sessions;
std::list<std::weak_ptr<RLPXHandshake>> m_connecting; ///< Pending connections.
Mutex x_connecting; ///< Mutex for m_connecting.
unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to.

5
libp2p/HostCapability.cpp

@ -27,11 +27,6 @@ using namespace std;
using namespace dev;
using namespace dev::p2p;
void HostCapabilityFace::seal(bytes& _b)
{
m_host->seal(_b);
}
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions() const
{
RecursiveGuard l(m_host->x_sessions);

2
libp2p/HostCapability.h

@ -57,8 +57,6 @@ protected:
virtual void onStarting() {}
virtual void onStopping() {}
void seal(bytes& _b);
private:
Host* m_host = nullptr;
};

2
libp2p/Peer.h

@ -55,6 +55,8 @@ class Peer: public Node
friend class Session; /// Allows Session to update score and rating.
friend class Host; /// For Host: saveNetwork(), restoreNetwork()
friend class RLPXHandshake;
public:
bool isOffline() const { return !m_session.lock(); }

209
libp2p/RLPxFrameIO.cpp

@ -0,0 +1,209 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameIO.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#include "Host.h"
#include "Session.h"
#include "Peer.h"
#include "RLPxHandshake.h"
#include "RLPxFrameIO.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
using namespace CryptoPP;
RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket)
{
// we need:
// originated?
// Secret == output of ecdhe agreement
// authCipher
// ackCipher
bytes keyMaterialBytes(64);
bytesRef keyMaterial(&keyMaterialBytes);
// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce))
Secret ephemeralShared;
_init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared);
ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size));
h512 nonceMaterial;
h256 const& leftNonce = _init.m_originated ? _init.m_remoteNonce : _init.m_nonce;
h256 const& rightNonce = _init.m_originated ? _init.m_nonce : _init.m_remoteNonce;
leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size));
rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size));
auto outRef(keyMaterial.cropped(h256::size, h256::size));
sha3(nonceMaterial.ref(), outRef); // output h(nonces)
sha3(keyMaterial, outRef); // output shared-secret
// token: sha3(outRef, bytesRef(&token)); -> m_host (to be saved to disk)
// aes-secret = sha3(ecdhe-shared-secret || shared-secret)
sha3(keyMaterial, outRef); // output aes-secret
m_frameEncKey.resize(h256::size);
memcpy(m_frameEncKey.data(), outRef.data(), h256::size);
m_frameDecKey.resize(h256::size);
memcpy(m_frameDecKey.data(), outRef.data(), h256::size);
h128 iv;
m_frameEnc.SetKeyWithIV(m_frameEncKey, h256::size, iv.data());
m_frameDec.SetKeyWithIV(m_frameDecKey, h256::size, iv.data());
// mac-secret = sha3(ecdhe-shared-secret || aes-secret)
sha3(keyMaterial, outRef); // output mac-secret
m_macEncKey.resize(h256::size);
memcpy(m_macEncKey.data(), outRef.data(), h256::size);
m_macEnc.SetKey(m_macEncKey, h256::size);
// Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init)
// ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack)
// Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack)
// ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init)
(*(h256*)outRef.data() ^ _init.m_remoteNonce).ref().copyTo(keyMaterial);
bytes const& egressCipher = _init.m_originated ? _init.m_authCipher : _init.m_ackCipher;
keyMaterialBytes.resize(h256::size + egressCipher.size());
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size()));
m_egressMac.Update(keyMaterial.data(), keyMaterial.size());
// recover mac-secret by re-xoring remoteNonce
(*(h256*)keyMaterial.data() ^ _init.m_remoteNonce ^ _init.m_nonce).ref().copyTo(keyMaterial);
bytes const& ingressCipher = _init.m_originated ? _init.m_ackCipher : _init.m_authCipher;
keyMaterialBytes.resize(h256::size + ingressCipher.size());
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size()));
m_ingressMac.Update(keyMaterial.data(), keyMaterial.size());
}
void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes)
{
// _packet = type || rlpList()
RLPStream header;
uint32_t len = (uint32_t)_packet.size();
header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
// zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize).
header.appendRaw(bytes({0xc2,0x80,0x80}));
// TODO: SECURITY check that header is <= 16 bytes
bytes headerWithMac(h256::size);
bytesConstRef(&header.out()).copyTo(bytesRef(&headerWithMac));
m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16);
updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16));
egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size));
auto padding = (16 - (_packet.size() % 16)) % 16;
o_bytes.swap(headerWithMac);
o_bytes.resize(32 + _packet.size() + padding + h128::size);
bytesRef packetRef(o_bytes.data() + 32, _packet.size());
m_frameEnc.ProcessData(packetRef.data(), _packet.data(), _packet.size());
bytesRef paddingRef(o_bytes.data() + 32 + _packet.size(), padding);
if (padding)
m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding);
bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding);
updateEgressMACWithFrame(packetWithPaddingRef);
bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size);
egressDigest().ref().copyTo(macRef);
}
bool RLPXFrameIO::authAndDecryptHeader(bytesRef io)
{
asserts(io.size() == h256::size);
updateIngressMACWithHeader(io);
bytesConstRef macRef = io.cropped(h128::size, h128::size);
h128 expected = ingressDigest();
if (*(h128*)macRef.data() != expected)
return false;
m_frameDec.ProcessData(io.data(), io.data(), h128::size);
return true;
}
bool RLPXFrameIO::authAndDecryptFrame(bytesRef io)
{
bytesRef cipherText(io.cropped(0, io.size() - h128::size));
updateIngressMACWithFrame(cipherText);
bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size);
if (*(h128*)frameMac.data() != ingressDigest())
return false;
m_frameDec.ProcessData(io.data(), io.data(), io.size() - h128::size);
return true;
}
h128 RLPXFrameIO::egressDigest()
{
SHA3_256 h(m_egressMac);
h128 digest;
h.TruncatedFinal(digest.data(), h128::size);
return move(digest);
}
h128 RLPXFrameIO::ingressDigest()
{
SHA3_256 h(m_ingressMac);
h128 digest;
h.TruncatedFinal(digest.data(), h128::size);
return move(digest);
}
void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher)
{
updateMAC(m_egressMac, _headerCipher.cropped(0, 16));
}
void RLPXFrameIO::updateEgressMACWithFrame(bytesConstRef _cipher)
{
m_egressMac.Update(_cipher.data(), _cipher.size());
updateMAC(m_egressMac);
}
void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher)
{
updateMAC(m_ingressMac, _headerCipher.cropped(0, 16));
}
void RLPXFrameIO::updateIngressMACWithFrame(bytesConstRef _cipher)
{
m_ingressMac.Update(_cipher.data(), _cipher.size());
updateMAC(m_ingressMac);
}
void RLPXFrameIO::updateMAC(SHA3_256& _mac, bytesConstRef _seed)
{
if (_seed.size() && _seed.size() != h128::size)
asserts(false);
SHA3_256 prevDigest(_mac);
h128 encDigest(h128::size);
prevDigest.TruncatedFinal(encDigest.data(), h128::size);
h128 prevDigestOut = encDigest;
{
Guard l(x_macEnc);
m_macEnc.ProcessData(encDigest.data(), encDigest.data(), 16);
}
if (_seed.size())
encDigest ^= *(h128*)_seed.data();
else
encDigest ^= *(h128*)prevDigestOut.data();
// update mac for final digest
_mac.Update(encDigest.data(), h128::size);
}

133
libp2p/RLPxFrameIO.h

@ -0,0 +1,133 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameIO.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#pragma once
#include <memory>
#include <libdevcrypto/Common.h>
#include <libdevcrypto/ECDHE.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcore/Guards.h>
#include "Common.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace dev
{
namespace p2p
{
class RLPXHandshake;
/**
* @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake.
* Managed (via shared_ptr) socket for use by RLPXHandshake and RLPXFrameIO.
*
* Thread Safety
* Distinct Objects: Safe.
* Shared objects: Unsafe.
* * an instance method must not be called concurrently
* * a writeSingleFramePacket can be called concurrent to authAndDecryptHeader OR authAndDecryptFrame
*/
class RLPXSocket: public std::enable_shared_from_this<RLPXSocket>
{
public:
RLPXSocket(bi::tcp::socket* _socket): m_socket(std::move(*_socket)) {}
~RLPXSocket() { close(); }
bool isConnected() const { return m_socket.is_open(); }
void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} }
bi::tcp::endpoint remoteEndpoint() { try { return m_socket.remote_endpoint(); } catch (...){ return bi::tcp::endpoint(); } }
bi::tcp::socket& ref() { return m_socket; }
protected:
bi::tcp::socket m_socket;
};
/**
* @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake.
*
* Thread Safety
* Distinct Objects: Safe.
* Shared objects: Unsafe.
*/
class RLPXFrameIO
{
friend class Session;
public:
/// Constructor.
/// Requires instance of RLPXHandshake which has completed first two phases of handshake.
RLPXFrameIO(RLPXHandshake const& _init);
~RLPXFrameIO() {}
/// Encrypt _packet as RLPx frame.
void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes);
/// Authenticate and decrypt header in-place.
bool authAndDecryptHeader(bytesRef io_cipherWithMac);
/// Authenticate and decrypt frame in-place.
bool authAndDecryptFrame(bytesRef io_cipherWithMac);
/// Return first 16 bytes of current digest from egress mac.
h128 egressDigest();
/// Return first 16 bytes of current digest from ingress mac.
h128 ingressDigest();
protected:
/// Update state of egress MAC with frame header.
void updateEgressMACWithHeader(bytesConstRef _headerCipher);
/// Update state of egress MAC with frame.
void updateEgressMACWithFrame(bytesConstRef _cipher);
/// Update state of ingress MAC with frame header.
void updateIngressMACWithHeader(bytesConstRef _headerCipher);
/// Update state of ingress MAC with frame.
void updateIngressMACWithFrame(bytesConstRef _cipher);
bi::tcp::socket& socket() { return m_socket->ref(); }
private:
/// Update state of _mac.
void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef());
CryptoPP::SecByteBlock m_frameEncKey; ///< Key for m_frameEnc
CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption m_frameEnc; ///< Encoder for egress plaintext.
CryptoPP::SecByteBlock m_frameDecKey; ///< Key for m_frameDec
CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption m_frameDec; ///< Decoder for egress plaintext.
CryptoPP::SecByteBlock m_macEncKey; /// Key for m_macEnd
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_macEnc; /// One-way coder used by updateMAC for ingress and egress MAC updates.
Mutex x_macEnc; /// Mutex
CryptoPP::SHA3_256 m_egressMac; ///< State of MAC for egress ciphertext.
CryptoPP::SHA3_256 m_ingressMac; ///< State of MAC for ingress ciphertext.
std::shared_ptr<RLPXSocket> m_socket;
};
}
}

266
libp2p/RLPxHandshake.cpp

@ -0,0 +1,266 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXHandshake.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#include "Host.h"
#include "Session.h"
#include "Peer.h"
#include "RLPxHandshake.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
using namespace CryptoPP;
void RLPXHandshake::writeAuth()
{
clog(NetConnect) << "p2p.connect.egress sending auth to " << m_socket->remoteEndpoint();
m_auth.resize(Signature::size + h256::size + Public::size + h256::size + 1);
bytesRef sig(&m_auth[0], Signature::size);
bytesRef hepubk(&m_auth[Signature::size], h256::size);
bytesRef pubk(&m_auth[Signature::size + h256::size], Public::size);
bytesRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size);
// E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0)
Secret staticShared;
crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, staticShared);
sign(m_ecdhe.seckey(), staticShared ^ m_nonce).ref().copyTo(sig);
sha3(m_ecdhe.pubkey().ref(), hepubk);
m_host->m_alias.pub().ref().copyTo(pubk);
m_nonce.ref().copyTo(nonce);
m_auth[m_auth.size() - 1] = 0x0;
encryptECIES(m_remote, &m_auth, m_authCipher);
auto self(shared_from_this());
ba::async_write(m_socket->ref(), ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t)
{
transition(ec);
});
}
void RLPXHandshake::writeAck()
{
clog(NetConnect) << "p2p.connect.ingress sending ack to " << m_socket->remoteEndpoint();
m_ack.resize(Public::size + h256::size + 1);
bytesRef epubk(&m_ack[0], Public::size);
bytesRef nonce(&m_ack[Public::size], h256::size);
m_ecdhe.pubkey().ref().copyTo(epubk);
m_nonce.ref().copyTo(nonce);
m_ack[m_ack.size() - 1] = 0x0;
encryptECIES(m_remote, &m_ack, m_ackCipher);
auto self(shared_from_this());
ba::async_write(m_socket->ref(), ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t)
{
transition(ec);
});
}
void RLPXHandshake::readAuth()
{
clog(NetConnect) << "p2p.connect.ingress recving auth from " << m_socket->remoteEndpoint();
m_authCipher.resize(307);
auto self(shared_from_this());
ba::async_read(m_socket->ref(), ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t)
{
if (ec)
transition(ec);
else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_authCipher), m_auth))
{
bytesConstRef sig(&m_auth[0], Signature::size);
bytesConstRef hepubk(&m_auth[Signature::size], h256::size);
bytesConstRef pubk(&m_auth[Signature::size + h256::size], Public::size);
bytesConstRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size);
pubk.copyTo(m_remote.ref());
nonce.copyTo(m_remoteNonce.ref());
Secret sharedSecret;
crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, sharedSecret);
m_remoteEphemeral = recover(*(Signature*)sig.data(), sharedSecret ^ m_remoteNonce);
assert(sha3(m_remoteEphemeral) == *(h256*)hepubk.data());
transition();
}
else
{
clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << m_socket->remoteEndpoint();
m_nextState = Error;
transition();
}
});
}
void RLPXHandshake::readAck()
{
clog(NetConnect) << "p2p.connect.egress recving ack from " << m_socket->remoteEndpoint();
m_ackCipher.resize(210);
auto self(shared_from_this());
ba::async_read(m_socket->ref(), ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t)
{
if (ec)
transition(ec);
else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_ackCipher), m_ack))
{
bytesConstRef(&m_ack).cropped(0, Public::size).copyTo(m_remoteEphemeral.ref());
bytesConstRef(&m_ack).cropped(Public::size, h256::size).copyTo(m_remoteNonce.ref());
transition();
}
else
{
clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remoteEndpoint();
m_nextState = Error;
transition();
}
});
}
void RLPXHandshake::error()
{
clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)";
m_socket->close();
if (m_io != nullptr)
delete m_io;
}
void RLPXHandshake::transition(boost::system::error_code _ech)
{
if (_ech || m_nextState == Error)
return error();
auto self(shared_from_this());
if (m_nextState == New)
{
m_nextState = AckAuth;
if (m_originated)
writeAuth();
else
readAuth();
}
else if (m_nextState == AckAuth)
{
m_nextState = WriteHello;
if (m_originated)
readAck();
else
writeAck();
}
else if (m_nextState == WriteHello)
{
m_nextState = ReadHello;
if (m_originated)
clog(NetConnect) << "p2p.connect.egress sending capabilities handshake";
else
clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake";
/// This pointer will be freed if there is an error otherwise
/// it will be passed to Host which will take ownership.
m_io = new RLPXFrameIO(*this);
// old packet format
// 5 arguments, HelloPacket
RLPStream s;
s.append((unsigned)0).appendList(5)
<< m_host->protocolVersion()
<< m_host->m_clientVersion
<< m_host->caps()
<< m_host->listenPort()
<< m_host->id();
bytes packet;
s.swapOut(packet);
m_io->writeSingleFramePacket(&packet, m_handshakeOutBuffer);
ba::async_write(m_socket->ref(), ba::buffer(m_handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t)
{
transition(ec);
});
}
else if (m_nextState == ReadHello)
{
// Authenticate and decrypt initial hello frame with initial RLPXFrameIO
// and request m_host to start session.
m_nextState = StartSession;
// read frame header
m_handshakeInBuffer.resize(h256::size);
ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this, self](boost::system::error_code ec, std::size_t)
{
if (ec)
transition(ec);
else
{
/// authenticate and decrypt header
if (!m_io->authAndDecryptHeader(bytesRef(m_handshakeInBuffer.data(), h256::size)))
{
m_nextState = Error;
transition();
return;
}
clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header";
/// check frame size
bytes& header = m_handshakeInBuffer;
uint32_t frameSize = (uint32_t)(header[2]) | (uint32_t)(header[1])<<8 | (uint32_t)(header[0])<<16;
if (frameSize > 1024)
{
// all future frames: 16777216
clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large" << frameSize;
m_nextState = Error;
transition();
return;
}
/// rlp of header has protocol-type, sequence-id[, total-packet-size]
bytes headerRLP(header.size() - 3 - h128::size);
bytesConstRef(&header).cropped(3).copyTo(&headerRLP);
/// read padded frame and mac
m_handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size);
ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t)
{
if (ec)
transition(ec);
else
{
bytesRef frame(&m_handshakeInBuffer);
if (!m_io->authAndDecryptFrame(frame))
{
clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed";
m_nextState = Error;
transition();
return;
}
PacketType packetType = (PacketType)(frame[0] == 0x80 ? 0x0 : frame[0]);
if (packetType != 0)
{
clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type";
m_nextState = Error;
transition();
return;
}
clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
RLP rlp(frame.cropped(1));
m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint());
}
});
}
});
}
}

126
libp2p/RLPxHandshake.h

@ -0,0 +1,126 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXHandshake.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#pragma once
#include <memory>
#include <libdevcrypto/Common.h>
#include <libdevcrypto/ECDHE.h>
#include "RLPxFrameIO.h"
#include "Common.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace dev
{
namespace p2p
{
/**
* @brief Setup inbound or outbound connection for communication over RLPXFrameIO.
* RLPx Spec: https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake
*
* @todo Implement StartSession transition via lambda which is passed to constructor.
*
* Thread Safety
* Distinct Objects: Safe.
* Shared objects: Unsafe.
*/
class RLPXHandshake: public std::enable_shared_from_this<RLPXHandshake>
{
friend class RLPXFrameIO;
/// Sequential states of handshake
enum State
{
Error = -1,
New,
AckAuth,
WriteHello,
ReadHello,
StartSession
};
public:
/// Setup incoming connection.
RLPXHandshake(Host* _host, std::shared_ptr<RLPXSocket> const& _socket): m_host(_host), m_originated(false), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); }
/// Setup outbound connection.
RLPXHandshake(Host* _host, std::shared_ptr<RLPXSocket> const& _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_originated(true), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); }
~RLPXHandshake() {}
/// Start handshake.
void start() { transition(); }
void cancel() { m_nextState = Error; }
protected:
/// Write Auth message to socket and transitions to AckAuth.
void writeAuth();
/// Reads Auth message from socket and transitions to AckAuth.
void readAuth();
/// Write Ack message to socket and transitions to WriteHello.
void writeAck();
/// Reads Auth message from socket and transitions to WriteHello.
void readAck();
/// Closes connection and ends transitions.
void error();
/// Performs transition for m_nextState.
void transition(boost::system::error_code _ech = boost::system::error_code());
State m_nextState = New; ///< Current or expected state of transition.
Host* m_host; ///< Host which provides m_alias, protocolVersion(), m_clientVersion, caps(), and TCP listenPort().
/// Node id of remote host for socket.
NodeId m_remote; ///< Public address of remote host.
bool m_originated = false; ///< True if connection is outbound.
/// Buffers for encoded and decoded handshake phases
bytes m_auth; ///< Plaintext of egress or ingress Auth message.
bytes m_authCipher; ///< Ciphertext of egress or ingress Auth message.
bytes m_ack; ///< Plaintext of egress or ingress Ack message.
bytes m_ackCipher; ///< Ciphertext of egress or ingress Ack message.
bytes m_handshakeOutBuffer; ///< Frame buffer for egress Hello packet.
bytes m_handshakeInBuffer; ///< Frame buffer for ingress Hello packet.
crypto::ECDHE m_ecdhe; ///< Ephemeral ECDH secret and agreement.
h256 m_nonce; ///< Nonce generated by this host for handshake.
Public m_remoteEphemeral; ///< Remote ephemeral public key.
h256 m_remoteNonce; ///< Nonce generated by remote host for handshake.
/// Used to read and write RLPx encrypted frames for last step of handshake authentication.
/// Passed onto Host which will take ownership.
RLPXFrameIO* m_io = nullptr;
std::shared_ptr<RLPXSocket> m_socket; ///< Socket.
};
}
}

249
libp2p/Session.cpp

@ -16,6 +16,7 @@
*/
/** @file Session.cpp
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
@ -37,11 +38,12 @@ using namespace dev::p2p;
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << m_socket.native_handle() << "] "
Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr<Peer> const& _n):
Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr<Peer> const& _n, PeerSessionInfo _info):
m_server(_s),
m_socket(std::move(_socket)),
m_io(_io),
m_socket(m_io->socket()),
m_peer(_n),
m_info({NodeId(), "?", m_socket.remote_endpoint().address().to_string(), 0, chrono::steady_clock::duration(0), CapDescSet(), 0, map<string, string>()}),
m_info(_info),
m_ping(chrono::steady_clock::time_point::max())
{
m_lastReceived = m_connect = chrono::steady_clock::now();
@ -65,6 +67,7 @@ Session::~Session()
}
}
catch (...){}
delete m_io;
}
NodeId Session::id() const
@ -142,101 +145,21 @@ void Session::serviceNodesRequest()
addNote("peers", "done");
}
bool Session::interpret(RLP const& _r)
bool Session::interpret(PacketType _t, RLP const& _r)
{
m_lastReceived = chrono::steady_clock::now();
clogS(NetRight) << _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 ((PacketType)_r[0].toInt<unsigned>())
switch (_t)
{
case HelloPacket:
{
// TODO: P2P first pass, implement signatures. if signature fails, drop connection. if egress, flag node's endpoint as stale.
// Move auth to Host so we consolidate authentication logic and eschew peer deduplication logic.
// Move all node-lifecycle information into Host.
// Finalize peer-lifecycle properties vs node lifecycle.
m_protocolVersion = _r[1].toInt<unsigned>();
auto clientVersion = _r[2].toString();
auto caps = _r[3].toVector<CapDesc>();
auto listenPort = _r[4].toInt<unsigned short>();
auto id = _r[5].toHash<NodeId>();
// clang error (previously: ... << hex << caps ...)
// "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments"
stringstream capslog;
for (auto cap: caps)
capslog << "(" << cap.first << "," << dec << cap.second << ")";
clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << id.abridged() << showbase << capslog.str() << dec << listenPort;
if (m_server->id() == id)
{
// Already connected.
clogS(NetWarn) << "Connected to ourself under a false pretext. We were told this peer was id" << id.abridged();
disconnect(LocalIdentity);
return true;
}
// if peer and connection have id, check for UnexpectedIdentity
if (!id)
{
disconnect(NullIdentity);
return true;
}
else if (!m_peer->id)
{
m_peer->id = id;
m_peer->endpoint.tcp.port(listenPort);
}
else if (m_peer->id != id)
{
// TODO p2p: FIXME. Host should catch this and reattempt adding node to table.
m_peer->id = id;
m_peer->m_score = 0;
m_peer->m_rating = 0;
// disconnect(UnexpectedIdentity);
// return true;
}
if (m_server->havePeerSession(id))
{
// Already connected.
clogS(NetWarn) << "Already connected to a peer with id" << id.abridged();
// Possible that two nodes continually connect to each other with exact same timing.
this_thread::sleep_for(chrono::milliseconds(rand() % 100));
disconnect(DuplicatePeer);
return true;
}
if (m_peer->isOffline())
m_peer->m_lastConnected = chrono::system_clock::now();
if (m_protocolVersion != m_server->protocolVersion())
{
disconnect(IncompatibleProtocol);
return true;
}
m_info.clientVersion = clientVersion;
m_info.host = m_socket.remote_endpoint().address().to_string();
m_info.port = listenPort;
m_info.lastPing = std::chrono::steady_clock::duration();
m_info.caps = _r[3].toSet<CapDesc>();
m_info.socket = (unsigned)m_socket.native_handle();
m_info.notes = map<string, string>();
m_server->registerPeer(shared_from_this(), caps);
break;
}
case DisconnectPacket:
{
string reason = "Unspecified";
auto r = (DisconnectReason)_r[1].toInt<int>();
if (!_r[1].isInt())
auto r = (DisconnectReason)_r[0].toInt<int>();
if (!_r[0].isInt())
drop(BadProtocol);
else
{
@ -275,7 +198,7 @@ bool Session::interpret(RLP const& _r)
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
m_weRequestedNodes = false;
for (unsigned i = 1; i < _r.itemCount(); ++i)
for (unsigned i = 0; i < _r.itemCount(); ++i)
{
bi::address peerAddress;
if (_r[i][0].size() == 16)
@ -325,12 +248,11 @@ bool Session::interpret(RLP const& _r)
break;
default:
{
auto id = _r[0].toInt<unsigned>();
for (auto const& i: m_capabilities)
if (id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount())
if (_t >= i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount())
{
if (i.second->m_enabled)
return i.second->interpret(id - i.second->m_idOffset, _r);
return i.second->interpret(_t - i.second->m_idOffset, _r);
else
return true;
}
@ -356,47 +278,34 @@ void Session::ping()
RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args)
{
return prep(_s).appendList(_args + 1).append((unsigned)_id);
}
RLPStream& Session::prep(RLPStream& _s)
{
return _s.appendRaw(bytes(8, 0));
return _s.append((unsigned)_id).appendList(_args);
}
void Session::sealAndSend(RLPStream& _s)
{
bytes b;
_s.swapOut(b);
m_server->seal(b);
send(move(b));
}
bool Session::checkPacket(bytesConstRef _msg)
{
if (_msg.size() < 8)
if (_msg.size() < 2)
return false;
if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91))
if (_msg[0] > 0x7f)
return false;
uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7];
if (_msg.size() != len + 8)
return false;
RLP r(_msg.cropped(8));
if (r.actualSize() != len)
RLP r(_msg.cropped(1));
if (r.actualSize() + 1 != _msg.size())
return false;
return true;
}
void Session::send(bytesConstRef _msg)
{
send(_msg.toBytes());
}
void Session::send(bytes&& _msg)
{
clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8));
clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1));
if (!checkPacket(bytesConstRef(&_msg)))
bytesConstRef msg(&_msg);
if (!checkPacket(msg))
clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!";
if (!m_socket.is_open())
@ -416,6 +325,7 @@ void Session::send(bytes&& _msg)
void Session::write()
{
const bytes& bytes = m_writeQueue[0];
m_io->writeSingleFramePacket(&bytes, m_writeQueue[0]);
auto self(shared_from_this());
ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/)
{
@ -484,95 +394,88 @@ void Session::disconnect(DisconnectReason _reason)
void Session::start()
{
RLPStream s;
prep(s, HelloPacket, 5)
<< m_server->protocolVersion()
<< m_server->m_clientVersion
<< m_server->caps()
<< m_server->m_tcpPublic.port()
<< m_server->id();
sealAndSend(s);
ping();
doRead();
}
void Session::doRead()
{
// ignore packets received while waiting to disconnect
// ignore packets received while waiting to disconnect.
if (m_dropped)
return;
auto self(shared_from_this());
m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length)
ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length)
{
// If error is end of file, ignore
if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof)
{
// got here with length of 1241...
clogS(NetWarn) << "Error reading: " << ec.message();
drop(TCPError);
}
else if (ec && length == 0)
{
return;
}
else
{
try
/// authenticate and decrypt header
bytesRef header(m_data.data(), h256::size);
if (!m_io->authAndDecryptHeader(header))
{
m_incoming.resize(m_incoming.size() + length);
memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length);
while (m_incoming.size() > 8)
clog(NetWarn) << "header decrypt failed";
drop(BadProtocol); // todo: better error
return;
}
/// check frame size
uint32_t frameSize = (m_data[0] * 256 + m_data[1]) * 256 + m_data[2];
if (frameSize >= (uint32_t)1 << 24)
{
clog(NetWarn) << "frame size too large";
drop(BadProtocol);
return;
}
/// rlp of header has protocol-type, sequence-id[, total-packet-size]
bytes headerRLP(13);
bytesConstRef(m_data.data(), h128::size).cropped(3).copyTo(&headerRLP);
/// read padded frame and mac
auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size;
ba::async_read(m_socket, boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length)
{
if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof)
{
clogS(NetWarn) << "Error reading: " << ec.message();
drop(TCPError);
}
else if (ec && length == 0)
return;
else
{
if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91)
if (!m_io->authAndDecryptFrame(bytesRef(m_data.data(), tlen)))
{
clog(NetWarn) << "frame decrypt failed";
drop(BadProtocol); // todo: better error
return;
}
bytesConstRef frame(m_data.data(), frameSize);
if (!checkPacket(frame))
{
clogS(NetWarn) << "INVALID SYNCHRONISATION TOKEN; expected = 22400891; received = " << toHex(bytesConstRef(m_incoming.data(), 4));
cerr << "Received " << frame.size() << ": " << toHex(frame) << endl;
clogS(NetWarn) << "INVALID MESSAGE RECEIVED";
disconnect(BadProtocol);
return;
}
else
{
uint32_t len = fromBigEndian<uint32_t>(bytesConstRef(m_incoming.data() + 4, 4));
uint32_t tlen = len + 8;
if (m_incoming.size() < tlen)
break;
// enough has come in.
auto data = bytesConstRef(m_incoming.data(), tlen);
if (!checkPacket(data))
{
cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl;
clogS(NetWarn) << "INVALID MESSAGE RECEIVED";
disconnect(BadProtocol);
return;
}
else
{
RLP r(data.cropped(8));
if (!interpret(r))
{
// error - bad protocol
clogS(NetWarn) << "Couldn't interpret packet." << RLP(r);
// Just wasting our bandwidth - perhaps reduce rating?
//return;
}
}
memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen);
m_incoming.resize(m_incoming.size() - tlen);
auto packetType = (PacketType)RLP(frame.cropped(0, 1)).toInt<unsigned>();
RLP r(frame.cropped(1));
if (!interpret(packetType, r))
clogS(NetWarn) << "Couldn't interpret packet." << RLP(r);
}
doRead();
}
doRead();
}
catch (Exception const& _e)
{
clogS(NetWarn) << "ERROR: " << diagnostic_information(_e);
drop(BadProtocol);
}
catch (std::exception const& _e)
{
clogS(NetWarn) << "ERROR: " << _e.what();
drop(BadProtocol);
}
});
}
});
}

18
libp2p/Session.h

@ -16,6 +16,7 @@
*/
/** @file Session.h
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
@ -32,6 +33,7 @@
#include <libdevcore/RLP.h>
#include <libdevcore/RangeMask.h>
#include <libdevcore/Guards.h>
#include "RLPxHandshake.h"
#include "Common.h"
namespace dev
@ -52,7 +54,7 @@ class Session: public std::enable_shared_from_this<Session>
friend class HostCapabilityFace;
public:
Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr<Peer> const& _n);
Session(Host* _server, RLPXFrameIO* _io, std::shared_ptr<Peer> const& _n, PeerSessionInfo _info);
virtual ~Session();
void start();
@ -63,16 +65,13 @@ public:
bool isConnected() const { return m_socket.is_open(); }
NodeId id() const;
unsigned socketId() const { return m_socket.native_handle(); }
unsigned socketId() const { return m_info.socket; }
template <class PeerCap>
std::shared_ptr<PeerCap> cap() const { try { return std::static_pointer_cast<PeerCap>(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } }
static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0);
static RLPStream& prep(RLPStream& _s);
void sealAndSend(RLPStream& _s);
void send(bytes&& _msg);
void send(bytesConstRef _msg);
int rating() const;
void addRating(unsigned _r);
@ -85,6 +84,8 @@ public:
void serviceNodesRequest();
private:
void send(bytes&& _msg);
/// Drop the connection for the reason @a _r.
void drop(DisconnectReason _r);
@ -95,17 +96,18 @@ private:
void write();
/// Interpret an incoming message.
bool interpret(RLP const& _r);
bool interpret(PacketType _t, RLP const& _r);
/// @returns true iff the _msg forms a valid message for sending or receiving on the network.
static bool checkPacket(bytesConstRef _msg);
Host* m_server; ///< The host that owns us. Never null.
mutable bi::tcp::socket m_socket; ///< Socket for the peer's connection. Mutable to ask for native_handle().
RLPXFrameIO* m_io; ///< Transport over which packets are sent.
bi::tcp::socket& m_socket; ///< Socket for the peer's connection.
Mutex x_writeQueue; ///< Mutex for the write queue.
std::deque<bytes> m_writeQueue; ///< The write queue.
std::array<byte, 65536> m_data; ///< Buffer for ingress packet data.
std::array<byte, 16777216> m_data; ///< Buffer for ingress packet data.
bytes m_incoming; ///< Read buffer for ingress bytes.
unsigned m_protocolVersion = 0; ///< The protocol version of the peer.

3
libsolidity/Compiler.cpp

@ -378,8 +378,9 @@ bool Compiler::visit(FunctionDefinition const& _function)
m_context.removeVariable(*localVariable);
m_context.adjustStackOffset(-(int)c_returnValuesSize);
if (!_function.isConstructor())
m_context << eth::Instruction::JUMP;
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
return false;
}

4
libsolidity/Compiler.h

@ -94,8 +94,8 @@ private:
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
unsigned m_modifierDepth = 0;
FunctionDefinition const* m_currentFunction;
unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag
FunctionDefinition const* m_currentFunction = nullptr;
unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
// arguments for base constructors, filled in derived-to-base order
std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments;
};

7
libsolidity/CompilerContext.cpp

@ -177,6 +177,13 @@ u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declarati
return it->second;
}
CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpType)
{
eth::AssemblyItem item(eth::Instruction::JUMP);
item.setJumpType(_jumpType);
return *this << item;
}
void CompilerContext::resetVisitedNodes(ASTNode const* _node)
{
stack<ASTNode const*> newStack;

4
libsolidity/CompilerContext.h

@ -91,7 +91,7 @@ public:
/// Appends a JUMP to a new tag and @returns the tag
eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
/// Appends a JUMP to a tag already on the stack
CompilerContext& appendJump() { return *this << eth::Instruction::JUMP; }
CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
/// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.
@ -120,7 +120,7 @@ public:
eth::Assembly const& getAssembly() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.streamRLP(_stream, "", _sourceCodes); }
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); }
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }

17
libsolidity/ExpressionCompiler.cpp

@ -108,7 +108,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
retSizeOnStack = returnType->getSizeOnStack();
}
solAssert(retSizeOnStack <= 15, "Stack too deep.");
m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP;
m_context << eth::dupInstruction(retSizeOnStack + 1);
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
}
void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
@ -405,7 +406,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
_functionCall.getExpression().accept(*this);
m_context.appendJump();
m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction);
m_context << returnLabel;
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
@ -825,10 +826,20 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{
if (magicVar->getType()->getCategory() == Type::Category::Contract)
switch (magicVar->getType()->getCategory())
{
case Type::Category::Contract:
// "this" or "super"
if (!dynamic_cast<ContractType const&>(*magicVar->getType()).isSuper())
m_context << eth::Instruction::ADDRESS;
break;
case Type::Category::Integer:
// "now"
m_context << eth::Instruction::TIMESTAMP;
break;
default:
break;
}
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();

1
libsolidity/GlobalContext.cpp

@ -37,6 +37,7 @@ GlobalContext::GlobalContext():
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)),
make_shared<MagicVariableDeclaration>("suicide",
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
make_shared<MagicVariableDeclaration>("sha3",

2
libsolidity/Parser.h

@ -34,6 +34,8 @@ class Scanner;
class Parser
{
public:
Parser() {}
ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
std::shared_ptr<std::string const> const& getSourceName() const;

4
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -128,10 +128,6 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to
filter.withEarliest(_json["earliest"].asInt());
if (_json["latest"].isInt())
filter.withLatest(_json["lastest"].asInt());
if (_json["max"].isInt())
filter.withMax(_json["max"].asInt());
if (_json["skip"].isInt())
filter.withSkip(_json["skip"].asInt());
if (!_json["address"].empty())
{
if (_json["address"].isArray())

2
libwhisper/WhisperPeer.cpp

@ -55,7 +55,7 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
{
case StatusPacket:
{
auto protocolVersion = _r[1].toInt<unsigned>();
auto protocolVersion = _r[0].toInt<unsigned>();
clogS(NetMessageSummary) << "Status: " << protocolVersion;

3
mix/AppContext.cpp

@ -37,6 +37,7 @@
#include "QVariableDefinition.h"
#include "HttpServer.h"
#include "AppContext.h"
#include "SortFilterProxyModel.h"
using namespace dev;
using namespace dev::eth;
@ -74,6 +75,7 @@ void AppContext::load()
qmlRegisterType<QBoolType>("org.ethereum.qml.QBoolType", 1, 0, "QBoolType");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration");
qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry");
qmlRegisterType<SortFilterProxyModel>("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel");
QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml"));
QObject* projectModel = projectModelComponent.create();
if (projectModelComponent.isError())
@ -86,6 +88,7 @@ void AppContext::load()
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
QWindow *window = qobject_cast<QWindow*>(m_applicationEngine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png"));

9
mix/CodeEditorExtensionManager.cpp

@ -51,14 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
return;
}
void CodeEditorExtensionManager::initExtensions()
{
std::shared_ptr<StatusPane> output = std::make_shared<StatusPane>(m_appContext);
QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight);
initExtension(output);
}
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
{
if (!_ext->contentUrl().isEmpty())
@ -93,5 +85,4 @@ void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView)
void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView)
{
m_headerView = _headerView;
initExtensions(); //TODO: move this to a proper place
}

2
mix/CodeEditorExtensionManager.h

@ -49,8 +49,6 @@ class CodeEditorExtensionManager: public QObject
public:
CodeEditorExtensionManager();
~CodeEditorExtensionManager();
/// Initialize all extensions.
void initExtensions();
/// Initialize extension.
void initExtension(std::shared_ptr<Extension>);
/// Set current tab view

2
mix/CodeModel.h

@ -164,6 +164,8 @@ public:
/// Find a contract by document id
/// @returns CompiledContract object or null if not found
Q_INVOKABLE CompiledContract* contractByDocumentId(QString _documentId) const;
/// Reset code model
Q_INVOKABLE void reset() { reset(QVariantMap()); }
signals:
/// Emited on compilation state change

2
mix/FileIo.cpp

@ -158,6 +158,7 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder)
}
rlpStr.appendList(k);
manifest["entries"] = entries;
std::stringstream jsonStr;
jsonStr << manifest;
QByteArray b = QString::fromStdString(jsonStr.str()).toUtf8();
@ -166,7 +167,6 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder)
for (unsigned int k = 0; k < files.size(); k++)
rlpStr.append(files.at(k));
manifest["entries"] = entries;
bytes dapp = rlpStr.out();
dev::h256 dappHash = dev::sha3(dapp);
//encrypt

12
mix/MixClient.cpp

@ -331,7 +331,6 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
unsigned lastBlock = bc().number();
unsigned block = std::min<unsigned>(lastBlock, (unsigned)_f.latest());
unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest()));
unsigned skip = _f.skip();
// Pending transactions
if (block > bc().number())
{
@ -341,9 +340,8 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
// Might have a transaction that contains a matching log.
TransactionReceipt const& tr = m_state.receipt(i);
LogEntries logEntries = _f.matches(tr);
for (unsigned entry = 0; entry < logEntries.size() && ret.size() != _f.max(); ++entry)
for (unsigned entry = 0; entry < logEntries.size(); ++entry)
ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block));
skip -= std::min(skip, static_cast<unsigned>(logEntries.size()));
}
block = bc().number();
}
@ -355,12 +353,8 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
if (_f.matches(bc().info(h).logBloom))
for (TransactionReceipt receipt: bc().receipts(h).receipts)
if (_f.matches(receipt.bloom()))
{
LogEntries logEntries = _f.matches(receipt);
for (unsigned entry = skip; entry < logEntries.size() && ret.size() != _f.max(); ++entry)
ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block));
skip -= std::min(skip, static_cast<unsigned>(logEntries.size()));
}
for (auto const& e: _f.matches(receipt))
ret.insert(ret.begin(), LocalisedLogEntry(e, block));
h = bc().details(h).parent;
}
return ret;

156
mix/SortFilterProxyModel.cpp

@ -0,0 +1,156 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Yann <yann@ethdev.com>
* @date 2015
* Proxy used to filter a QML TableView.
*/
#include "SortFilterProxyModel.h"
#include <QtDebug>
#include <QtQml>
using namespace dev::mix;
SortFilterProxyModel::SortFilterProxyModel(QObject* _parent) : QSortFilterProxyModel(_parent)
{
connect(this, &SortFilterProxyModel::rowsInserted, this, &SortFilterProxyModel::countChanged);
connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged);
}
int SortFilterProxyModel::count() const
{
return rowCount();
}
QObject* SortFilterProxyModel::source() const
{
return sourceModel();
}
void SortFilterProxyModel::setSource(QObject* _source)
{
setSourceModel(qobject_cast<QAbstractItemModel*>(_source));
}
QByteArray SortFilterProxyModel::sortRole() const
{
return roleNames().value(QSortFilterProxyModel::sortRole());
}
void SortFilterProxyModel::setSortRole(QByteArray const& _role)
{
QSortFilterProxyModel::setSortRole(roleKey(_role));
}
void SortFilterProxyModel::setSortOrder(Qt::SortOrder _order)
{
QSortFilterProxyModel::sort(0, _order);
}
QString SortFilterProxyModel::filterString() const
{
return filterRegExp().pattern();
}
void SortFilterProxyModel::setFilterString(QString const& _filter)
{
setFilterRegExp(QRegExp(_filter, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())));
}
SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const
{
return static_cast<FilterSyntax>(filterRegExp().patternSyntax());
}
void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax _syntax)
{
setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(_syntax)));
}
QJSValue SortFilterProxyModel::get(int _idx) const
{
QJSEngine *engine = qmlEngine(this);
QJSValue value = engine->newObject();
if (_idx >= 0 && _idx < count())
{
QHash<int, QByteArray> roles = roleNames();
QHashIterator<int, QByteArray> it(roles);
while (it.hasNext())
{
it.next();
value.setProperty(QString::fromUtf8(it.value()), data(index(_idx, 0), it.key()).toString());
}
}
return value;
}
int SortFilterProxyModel::roleKey(QByteArray const& _role) const
{
QHash<int, QByteArray> roles = roleNames();
QHashIterator<int, QByteArray> it(roles);
while (it.hasNext())
{
it.next();
if (it.value() == _role)
return it.key();
}
return -1;
}
QHash<int, QByteArray> SortFilterProxyModel::roleNames() const
{
if (QAbstractItemModel* source = sourceModel())
return source->roleNames();
return QHash<int, QByteArray>();
}
bool SortFilterProxyModel::filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const
{
QAbstractItemModel* model = sourceModel();
QModelIndex sourceIndex = model->index(_sourceRow, 0, _sourceParent);
if (!sourceIndex.isValid())
return true;
QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString();
QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString();
return keyType.contains(m_filterType) && keyContent.contains(m_filterContent);
}
void SortFilterProxyModel::setFilterType(QString const& _type)
{
m_filterType = QRegExp(_type, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax()));
setFilterRegExp(_type);
}
QString SortFilterProxyModel::filterType() const
{
return m_filterType.pattern();
}
void SortFilterProxyModel::setFilterContent(QString const& _content)
{
m_filterContent = QRegExp(_content, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax()));
setFilterRegExp(_content);
}
QString SortFilterProxyModel::filterContent() const
{
return m_filterContent.pattern();
}

97
mix/SortFilterProxyModel.h

@ -0,0 +1,97 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Yann <yann@ethdev.com>
* @date 2015
* Proxy used to filter a QML TableView.
*/
#pragma once
#include <QtCore/qsortfilterproxymodel.h>
#include <QtQml/qjsvalue.h>
namespace dev
{
namespace mix
{
class SortFilterProxyModel: public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QObject* source READ source WRITE setSource)
Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole)
Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder)
Q_PROPERTY(QString filterContent READ filterContent WRITE setFilterContent)
Q_PROPERTY(QString filterType READ filterType WRITE setFilterType)
Q_PROPERTY(QString filterString READ filterString WRITE setFilterString)
Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax)
Q_ENUMS(FilterSyntax)
public:
explicit SortFilterProxyModel(QObject* _parent = 0);
QObject* source() const;
void setSource(QObject* _source);
QByteArray sortRole() const;
void setSortRole(QByteArray const& _role);
void setSortOrder(Qt::SortOrder _order);
QString filterContent() const;
void setFilterContent(QString const& _content);
QString filterType() const;
void setFilterType(QString const& _type);
QString filterString() const;
void setFilterString(QString const& _filter);
enum FilterSyntax {
RegExp,
Wildcard,
FixedString
};
FilterSyntax filterSyntax() const;
void setFilterSyntax(FilterSyntax _syntax);
int count() const;
Q_INVOKABLE QJSValue get(int _index) const;
signals:
void countChanged();
protected:
int roleKey(QByteArray const& _role) const;
QHash<int, QByteArray> roleNames() const;
bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const;
private:
QRegExp m_filterType;
QRegExp m_filterContent;
const QString type = "type";
const QString content = "content";
};
}
}

302
mix/qml/LogsPane.qml

@ -0,0 +1,302 @@
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.SortFilterProxyModel 1.0
import "."
Rectangle
{
function push(_level, _type, _content)
{
_content = _content.replace(/\n/g, " ")
logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level });
}
anchors.fill: parent
radius: 5
color: LogsPaneStyle.generic.layout.backgroundColor
border.color: LogsPaneStyle.generic.layout.borderColor
border.width: LogsPaneStyle.generic.layout.borderWidth
ColumnLayout {
z: 2
height: parent.height
width: parent.width
spacing: 0
Row
{
id: rowAction
Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight
height: LogsPaneStyle.generic.layout.headerHeight
anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin
anchors.left: parent.left
spacing: LogsPaneStyle.generic.layout.headerButtonSpacing
Button
{
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
action: clearAction
iconSource: "qrc:/qml/img/broom.png"
}
Action {
id: clearAction
enabled: logsModel.count > 0
tooltip: qsTr("Clear")
onTriggered: {
logsModel.clear()
}
}
Button
{
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
action: copytoClipBoardAction
iconSource: "qrc:/qml/img/copy.png"
}
Action {
id: copytoClipBoardAction
enabled: logsModel.count > 0
tooltip: qsTr("Copy to Clipboard")
onTriggered: {
var content = "";
for (var k = 0; k < logsModel.count; k++)
{
var log = logsModel.get(k);
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n";
}
appContext.toClipboard(content);
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height - 10
color : "#808080"
}
ToolButton {
id: javascriptButton
checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("javascript")
}
tooltip: qsTr("JavaScript")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
color: LogsPaneStyle.generic.layout.logLabelColor
anchors.centerIn: parent
text: qsTr("JavaScript")
}
}
}
}
ToolButton {
id: runButton
checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("run")
}
tooltip: qsTr("Run")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
color: LogsPaneStyle.generic.layout.logLabelColor
anchors.centerIn: parent
text: qsTr("Run")
}
}
}
}
ToolButton {
id: stateButton
checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("state")
}
tooltip: qsTr("State")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
color: "#5391d8"
anchors.centerIn: parent
text: qsTr("State")
}
}
}
}
ToolButton {
id: compilationButton
checkable: true
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
checked: false
onCheckedChanged: {
proxyModel.toogleFilter("compilation")
}
tooltip: qsTr("Compilation")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
color: "#5391d8"
anchors.centerIn: parent
text: qsTr("Compilation")
}
}
}
}
DefaultTextField
{
id: searchBox
height: LogsPaneStyle.generic.layout.headerButtonHeight
anchors.verticalCenter: parent.verticalCenter
width: LogsPaneStyle.generic.layout.headerInputWidth
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-3)
font.italic: true
onTextChanged: {
proxyModel.search(text);
}
}
}
ListModel {
id: logsModel
}
TableView {
id: logsTable
clip: true
Layout.fillWidth: true
Layout.preferredHeight: parent.height - rowAction.height
headerVisible : false
onDoubleClicked:
{
var log = logsModel.get((logsTable.currentRow));
if (log)
appContext.toClipboard(log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content);
}
model: SortFilterProxyModel {
id: proxyModel
source: logsModel
property var roles: ["-", "javascript", "run", "state"]
Component.onCompleted: {
filterType = regEx(proxyModel.roles);
}
function search(_value)
{
filterContent = _value;
}
function toogleFilter(_value)
{
var count = roles.length;
for (var i in roles)
{
if (roles[i] === _value)
{
roles.splice(i, 1);
break;
}
}
if (count === roles.length)
roles.push(_value);
filterType = regEx(proxyModel.roles);
}
function regEx(_value)
{
return "(?:" + roles.join('|') + ")";
}
filterType: "(?:javascript|run|state)"
filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive
}
TableViewColumn
{
role: "date"
title: qsTr("date")
width: LogsPaneStyle.generic.layout.dateWidth
delegate: itemDelegate
}
TableViewColumn
{
role: "type"
title: qsTr("type")
width: LogsPaneStyle.generic.layout.typeWidth
delegate: itemDelegate
}
TableViewColumn
{
role: "content"
title: qsTr("content")
width: LogsPaneStyle.generic.layout.contentWidth
delegate: itemDelegate
}
rowDelegate: Item {
Rectangle {
width: logsTable.width - 4
height: 17
color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor
}
}
}
Component {
id: itemDelegate
DefaultLabel {
text: styleData.value;
font.family: LogsPaneStyle.generic.layout.logLabelFont
font.pointSize: Style.absoluteSize(-1)
color: {
if (proxyModel.get(styleData.row).level === "error")
return "red";
else if (proxyModel.get(styleData.row).level === "warning")
return "orange";
else
return "#808080";
}
}
}
}
}

29
mix/qml/LogsPaneStyle.qml

@ -0,0 +1,29 @@
pragma Singleton
import QtQuick 2.0
QtObject {
function absoluteSize(rel)
{
return systemPointSize + rel;
}
property QtObject generic: QtObject {
property QtObject layout: QtObject {
property string backgroundColor: "#f7f7f7"
property string borderColor: "#5391d8"
property int borderWidth: 1
property int headerHeight: 35
property int headerButtonSpacing: 5
property int leftMargin: 10
property int headerButtonHeight: 30
property string logLabelColor: "#5391d8"
property string logLabelFont: "sans serif"
property int headerInputWidth: 200
property int dateWidth: 150
property int typeWidth: 80
property int contentWidth: 700
property string logAlternateColor: "#f0f0f0"
}
}
}

32
mix/qml/MainContent.qml

@ -35,8 +35,8 @@ Rectangle {
onCompilationComplete: {
if (firstCompile) {
firstCompile = false;
if (runOnProjectLoad)
startQuickDebugging();
if (runOnProjectLoad)
startQuickDebugging();
}
}
}
@ -102,7 +102,6 @@ Rectangle {
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
}
Settings {
@ -116,6 +115,7 @@ Rectangle {
ColumnLayout
{
id: mainColumn
anchors.fill: parent
spacing: 0
Rectangle {
@ -133,21 +133,15 @@ Rectangle {
}
id: headerPaneContainer
anchors.fill: parent
TabView {
id: headerPaneTabs
tabsVisible: false
antialiasing: true
StatusPane
{
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle { color: "transparent" }
}
webPreview: webPreview
}
}
}
Rectangle{
Rectangle {
Layout.fillWidth: true
height: 1
color: "#8c8c8c"
@ -168,9 +162,9 @@ Rectangle {
{
anchors.fill: parent
handleDelegate: Rectangle {
width: 1
height: 1
color: "#8c8c8c"
width: 1
height: 1
color: "#8c8c8c"
}
orientation: Qt.Horizontal
@ -180,16 +174,18 @@ Rectangle {
Layout.minimumWidth: 250
Layout.fillHeight: true
}
Rectangle {
id: contentView
Layout.fillHeight: true
Layout.fillWidth: true
SplitView {
handleDelegate: Rectangle {
handleDelegate: Rectangle {
width: 1
height: 1
color: "#8c8c8c"
}
}
id: codeWebSplitter
anchors.fill: parent
orientation: Qt.Vertical

10
mix/qml/ProjectModel.qml

@ -64,7 +64,7 @@ Item {
Connections {
target: appContext
onAppLoaded: {
if (projectSettings.lastProjectPath)
if (projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "")
projectModel.loadProject(projectSettings.lastProjectPath)
}
}
@ -130,6 +130,14 @@ Item {
id: projectStateListModel
}
Connections
{
target: projectModel
onProjectClosed: {
projectSettings.lastProjectPath = "";
}
}
Settings {
id: projectSettings
property string lastProjectPath;

5
mix/qml/StateListModel.qml

@ -134,6 +134,7 @@ Item {
onProjectClosed: {
stateListModel.clear();
stateList = [];
codeModel.reset();
}
onProjectLoading: stateListModel.loadStatesFromProject(projectData);
onProjectSaving: {
@ -148,6 +149,7 @@ Item {
state.title = qsTr("Default");
projectData.states = [ state ];
projectData.defaultStateIndex = 0;
stateListModel.loadStatesFromProject(projectData);
}
}
@ -264,6 +266,7 @@ Item {
defaultStateIndex--;
save();
}
function save() {
@ -284,6 +287,8 @@ Item {
else
defaultStateIndex = 0;
var items = projectData.states;
stateListModel.clear();
stateList = [];
for(var i = 0; i < items.length; i++) {
var item = fromPlainStateItem(items[i]);
stateListModel.append(item);

170
mix/qml/StatusPane.qml

@ -8,6 +8,7 @@ import "."
Rectangle {
id: statusHeader
objectName: "statusPane"
property variant webPreview
function updateStatus(message)
{
@ -15,7 +16,6 @@ Rectangle {
{
status.state = "";
status.text = qsTr("Compile successfully.");
logslink.visible = false;
debugImg.state = "active";
}
else
@ -23,39 +23,76 @@ Rectangle {
status.state = "error";
var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true);
status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail;
logslink.visible = true;
debugImg.state = "";
errorMessage(status.text, "Compilation");
}
debugRunActionIcon.enabled = codeModel.hasContract;
}
function infoMessage(text)
function infoMessage(text, type)
{
status.state = "";
status.text = text
logslink.visible = false;
logPane.push("info", type, text);
}
function errorMessage(text)
function warningMessage(text, type)
{
status.state = "warning";
status.text = text
logPane.push("warning", type, text);
}
function errorMessage(text, type)
{
status.state = "error";
status.text = text
logslink.visible = false;
logPane.push("error", type, text);
}
Connections {
target: webPreview
onJavaScriptMessage:
{
if (_level === 0)
infoMessage(_content, "JavaScript")
else
{
var message = _sourceId.substring(_sourceId.lastIndexOf("/") + 1) + " - " + qsTr("line") + " " + _lineNb + " - " + _content;
if (_level === 1)
warningMessage(message, "JavaScript")
else
errorMessage(message, "JavaScript")
}
}
}
Connections {
target:clientModel
onRunStarted: infoMessage(qsTr("Running transactions..."));
onRunFailed: errorMessage(qsTr("Error running transactions: " + _message));
onRunComplete: infoMessage(qsTr("Run complete"));
onNewBlock: infoMessage(qsTr("New block created"));
onRunStarted: infoMessage(qsTr("Running transactions..."), "Run");
onRunFailed: errorMessage(format(_message), "Run");
onRunComplete: infoMessage(qsTr("Run complete"), "Run");
onNewBlock: infoMessage(qsTr("New block created"), "State");
function format(_message)
{
var formatted = _message.match(/(?:<dev::eth::)(.+)(?:>)/);
if (formatted.length > 1)
formatted = formatted[1] + ": ";
else
return _message;
var exceptionInfos = _message.match(/(?:tag_)(.+)/g);
for (var k in exceptionInfos)
formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", "").replace("=", "");
return formatted;
}
}
Connections {
target:projectModel
onDeploymentStarted: infoMessage(qsTr("Running deployment..."));
onDeploymentError: errorMessage(error);
onDeploymentComplete: infoMessage(qsTr("Deployment complete"));
onDeploymentStepChanged: infoMessage(message);
onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment");
onDeploymentError: errorMessage(error, "Deployment");
onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "Deployment");
onDeploymentStepChanged: infoMessage(message, "Deployment");
}
Connections {
target: codeModel
@ -74,6 +111,24 @@ Rectangle {
width: 500
height: 30
color: "#fcfbfc"
states: [
State {
name: "logsOpened"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 1
}
},
State {
name: "logsClosed"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 0
}
}
]
Text {
anchors.verticalCenter: parent.verticalCenter
@ -98,6 +153,17 @@ Rectangle {
target: statusContainer
color: "#fffcd5"
}
},
State {
name: "warning"
PropertyChanges {
target: status
color: "orange"
}
PropertyChanges {
target: statusContainer
color: "#fffcd5"
}
}
]
onTextChanged:
@ -127,30 +193,73 @@ Rectangle {
color: "transparent"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
logsContainer.toggle();
}
}
}
Action {
id: toolTipInfo
tooltip: ""
}
}
Button
{
id: logslink
anchors.left: statusContainer.right
anchors.leftMargin: 9
visible: false
anchors.verticalCenter: parent.verticalCenter
action: displayLogAction
iconSource: "qrc:/qml/img/search_filled.png"
}
Rectangle
{
function toggle()
{
if (logsContainer.state === "opened")
{
statusContainer.state = "logsClosed";
logsContainer.state = "closed"
}
else
{
statusContainer.state = "logsOpened";
logsContainer.state = "opened";
logsContainer.focus = true;
forceActiveFocus();
}
}
Action {
id: displayLogAction
tooltip: qsTr("Display Log")
onTriggered: {
mainContent.displayCompilationErrorIfAny();
id: logsContainer
width: 1000
height: 0
anchors.topMargin: 10
anchors.top: statusContainer.bottom
anchors.horizontalCenter: parent.horizontalCenter
visible: false
radius: 5
Component.onCompleted:
{
var top = logsContainer;
while (top.parent)
top = top.parent
var coordinates = logsContainer.mapToItem(top, 0, 0)
logsContainer.parent = top;
logsContainer.x = coordinates.x
logsContainer.y = coordinates.y
}
LogsPane
{
id: logPane
}
states: [
State {
name: "opened";
PropertyChanges { target: logsContainer; height: 500; visible: true }
},
State {
name: "closed";
PropertyChanges { target: logsContainer; height: 0; visible: false }
}
]
transitions: Transition {
NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 }
NumberAnimation { properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 }
}
}
}
@ -168,7 +277,6 @@ Rectangle {
{
color: "transparent"
anchors.fill: parent
Button
{
anchors.right: parent.right

7
mix/qml/WebPreview.qml

@ -12,6 +12,7 @@ Item {
id: webPreview
property string pendingPageUrl: ""
property bool initialized: false
signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content)
function setPreviewUrl(url) {
if (!initialized)
@ -198,7 +199,6 @@ Item {
{
setPreviewUrl(text);
}
focus: true
}
@ -216,7 +216,9 @@ Item {
anchors.verticalCenter: parent.verticalCenter
width: 21
height: 21
focus: true
}
CheckBox {
id: autoReloadOnSave
checked: true
@ -227,6 +229,7 @@ Item {
text: qsTr("Auto reload on save")
}
}
focus: true
}
}
}
@ -240,7 +243,7 @@ Item {
id: webView
experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ":" + message);
webPreview.javaScriptMessage(level, sourceID, lineNumber, message);
}
onLoadingChanged: {
if (!loading) {

BIN
mix/qml/img/broom.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

BIN
mix/qml/img/copy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

21
mix/qml/js/ProjectModel.js

@ -105,7 +105,6 @@ function loadProject(path) {
contractSources[doc.documentId] = fileIo.readFile(doc.path);
}
codeModel.reset(contractSources);
}
function addFile(fileName) {
@ -377,8 +376,6 @@ function finalizeDeployment(deploymentId, addresses) {
else
insertAt += 6;
html = html.substr(0, insertAt) +
"<script src=\"bignumber.min.js\"></script>" +
"<script src=\"ethereum.js\"></script>" +
"<script src=\"deployment.js\"></script>" +
html.substr(insertAt);
fileIo.writeFile(deploymentDir + doc.fileName, html);
@ -388,9 +385,8 @@ function finalizeDeployment(deploymentId, addresses) {
}
//write deployment js
var deploymentJs =
"// Autogenerated by Mix\n" +
"web3 = require(\"web3\");\n" +
"contracts = {};\n";
"// Autogenerated by Mix\n" +
"contracts = {};\n";
for (var c in codeModel.contracts) {
var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]";
deploymentJs += contractAccessor + " = {\n" +
@ -400,9 +396,6 @@ function finalizeDeployment(deploymentId, addresses) {
contractAccessor + ".contract = web3.eth.contract(" + contractAccessor + ".address, " + contractAccessor + ".interface);\n";
}
fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
//copy scripts
fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js");
fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js");
deploymentAddresses = addresses;
saveProject();
@ -435,7 +428,7 @@ function checkEthPath(dappUrl, callBack)
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ],
params: [ { "gas": 150000, "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
@ -472,7 +465,7 @@ function checkRegistration(dappUrl, addr, callBack)
//getOwner()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ],
params: [ { "gas" : 2000, "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ],
id: jsonRpcRequestId++
});
@ -537,7 +530,7 @@ function checkRegistration(dappUrl, addr, callBack)
//setRegister()
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "from": deploymentDialog.currentAccount, "gas": 2000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
id: jsonRpcRequestId++
});
@ -570,7 +563,7 @@ function registerContentHash(registrar, callBack)
//setContent()
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "from": deploymentDialog.currentAccount, "gas": 2000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ],
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
@ -587,7 +580,7 @@ function registerToUrlHint()
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 2000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
id: jsonRpcRequestId++
});

1
mix/qml/qmldir

@ -5,3 +5,4 @@ singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml
singleton StateStyle 1.0 StateStyle.qml
singleton StatusPaneStyle 1.0 StatusPaneStyle.qml
singleton WebPreviewStyle 1.0 WebPreviewStyle.qml
singleton LogsPaneStyle 1.0 LogsPaneStyle.qml

4
mix/res.qrc

@ -111,5 +111,9 @@
<file>qml/img/exit.png</file>
<file>qml/img/run.png</file>
<file>qml/img/note.png</file>
<file>qml/LogsPane.qml</file>
<file>qml/img/copy.png</file>
<file>qml/img/broom.png</file>
<file>qml/LogsPaneStyle.qml</file>
</qresource>
</RCC>

32
pullSubtree.sh

@ -0,0 +1,32 @@
# usage
# ./pullsubtree [repository branch] [repository2 branch2]
#
# example
# ./pullSubtree evmjit master
# ./pullSubtree ethereumjs develop
# ./pullSubtree evmjit master ethereumjs master
evmjit_repo="https://github.com/ethereum/evmjit"
evmjit_location="evmjit"
ethereumjs_repo="https://github.com/ethereum/ethereum.js"
ethereumjs_location="libjsqrc/ethereumjs"
natspecjs_repo="https://github.com/ethereum/natspec.js"
natspecjs_location="libnatspec/natspecjs"
while [ "$1" != "" ]; do
case $1 in
evmjit | ethereumjs | natspecjs )
REPO="${1}_repo"
REPO=${!REPO}
LOCATION="${1}_location"
LOCATION=${!LOCATION}
shift
BRANCH=$1
git subtree pull --prefix=${LOCATION} ${REPO} ${BRANCH} --squash
;;
esac
shift
done

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save