From e7d210c31b5aff26ef6769530bceddf586a07d73 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 22 Apr 2015 12:59:09 +0200 Subject: [PATCH 01/17] Modifications to docker file to build the json compiler. --- solc/docker_emscripten/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solc/docker_emscripten/Dockerfile b/solc/docker_emscripten/Dockerfile index 1ad1875d7..23a0caba9 100644 --- a/solc/docker_emscripten/Dockerfile +++ b/solc/docker_emscripten/Dockerfile @@ -63,9 +63,9 @@ RUN git remote add -f solidityjs https://github.com/chriseth/cpp-ethereum # NOTE that we only get the latest commit of that branch RUN git cherry-pick solidityjs/solidity-js RUN emcmake cmake -DETH_STATIC=1 -DSOLIDITY=ON -DGUI=0 -DCMAKE_CXX_COMPILER=/home/user/emsdk_portable/emscripten/master/em++ -DCMAKE_C_COMPILER=/home/user/emsdk_portable/emscripten/master/emcc -RUN emmake make -j 6 soljs +RUN emmake make -j 6 soljson -WORKDIR /home/user/cpp-ethereum/soljs +WORKDIR /home/user/cpp-ethereum/solc # somehow it does not work to pipe out both files -#ENTRYPOINT tar -c soljs.js soljs.js.mem | base64 +#ENTRYPOINT tar -c soljson.js soljson.js.mem | base64 From 4eae349bb864d63499b79cddfb9633c716f426e3 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 23 Apr 2015 13:22:13 +0200 Subject: [PATCH 02/17] fix for event topics order --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 212f3728b..66cf458b1 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -194,7 +194,16 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to } if (!_json["topics"].empty()) for (unsigned i = 0; i < _json["topics"].size(); i++) - filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); + { + if (_json["topics"][i].isArray()) + { + for (auto t: _json["topics"][i]) + if (!t.isNull()) + filter.topic(i, jsToFixed<32>(t.asString())); + } + else if (!_json["topics"][i].isNull()) + filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); + } return filter; } From f0f5e48ce2fec9adb821bdcb1efb7a112ab54509 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 23 Apr 2015 13:29:32 +0200 Subject: [PATCH 03/17] fix whisper topics --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 66cf458b1..48bd5f893 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -201,7 +201,7 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to if (!t.isNull()) filter.topic(i, jsToFixed<32>(t.asString())); } - else if (!_json["topics"][i].isNull()) + else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); } return filter; @@ -233,7 +233,16 @@ static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, if (!_json["topics"].empty()) for (auto i: _json["topics"]) - bt.shift(jsToBytes(i.asString())); + { + if (i.isArray()) + { + for (auto j: i) + if (!j.isNull()) + bt.shift(jsToBytes(j.asString())); + } + else if (!i.isNull()) // if it is anything else then string, it should and will fail + bt.shift(jsToBytes(i.asString())); + } return _m.seal(_from, bt, ttl, workToProve); } From 40ef58dfcb66ffb091a49b6bf5f7761eeb6f16d3 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 23 Apr 2015 14:13:35 +0200 Subject: [PATCH 04/17] fixed issues with web preview and having multiple contracts in the same file --- mix/CodeModel.cpp | 15 ++++++++++++++- mix/CodeModel.h | 2 +- mix/HttpServer.cpp | 9 ++++++++- mix/HttpServer.h | 8 ++++++-- mix/MixClient.cpp | 16 +--------------- mix/qml/WebPreview.qml | 8 ++++++-- mix/test/TestService.cpp | 9 +++++++-- mix/test/TestService.h | 1 + mix/test/qml/TestMain.qml | 14 +++++++++++++- mix/test/qml/js/TestDebugger.js | 17 ++++------------- mix/test/qml/js/TestMiner.js | 4 ++++ mix/test/qml/js/TestProject.js | 28 ++++++++++++++++++++++++++++ 12 files changed, 93 insertions(+), 38 deletions(-) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index c14d26935..0407d2aca 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -279,9 +279,22 @@ void CodeModel::runCompilationJob(int _jobId) QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership); result[name] = contract; CompiledContract* prevContract = nullptr; + // find previous contract by name for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) - if (c.value()->documentId() == contract->documentId()) + if (c.value()->contract()->name() == contract->contract()->name()) prevContract = c.value(); + + // if not found, try by documentId + if (!prevContract) + { + for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) + if (c.value()->documentId() == contract->documentId()) + { + //make sure there are no other contracts in the same source, otherwise it is not a rename + if (!std::any_of(result.begin(),result.end(), [=](ContractMap::const_iterator::value_type _v) { return _v != contract && _v->documentId() == contract->documentId(); })) + prevContract = c.value(); + } + } if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface()) emit contractInterfaceChanged(name); if (prevContract == nullptr) diff --git a/mix/CodeModel.h b/mix/CodeModel.h index 6fb914118..dc27da61e 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -117,7 +117,7 @@ private: friend class CodeModel; }; -using ContractMap = QHash; +using ContractMap = QMap; //needs to be sorted /// Code compilation model. Compiles contracts in background an provides compiled contract data class CodeModel: public QObject diff --git a/mix/HttpServer.cpp b/mix/HttpServer.cpp index 968580907..ba03d7795 100644 --- a/mix/HttpServer.cpp +++ b/mix/HttpServer.cpp @@ -145,16 +145,23 @@ void HttpServer::readClient() if (socket->canReadLine()) { QString hdr = QString(socket->readLine()); + QVariantMap headers; if (hdr.startsWith("POST") || hdr.startsWith("GET")) { QUrl url(hdr.split(' ')[1]); QString l; do + { l = socket->readLine(); + //collect headers + int colon = l.indexOf(':'); + if (colon > 0) + headers[l.left(colon)] = l.right(l.length() - colon - 1); + } while (!(l.isEmpty() || l == "\r" || l == "\r\n")); QString content = socket->readAll(); - std::unique_ptr request(new HttpRequest(this, url, content)); + std::unique_ptr request(new HttpRequest(this, std::move(url), std::move(content), std::move(headers))); clientConnected(request.get()); QTextStream os(socket); os.setAutoDetectUnicode(true); diff --git a/mix/HttpServer.h b/mix/HttpServer.h index 8a1e4553a..50a345747 100644 --- a/mix/HttpServer.h +++ b/mix/HttpServer.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace dev @@ -40,10 +41,12 @@ class HttpRequest: public QObject Q_PROPERTY(QUrl url MEMBER m_url CONSTANT) /// Request body contents Q_PROPERTY(QString content MEMBER m_content CONSTANT) + /// Request HTTP headers + Q_PROPERTY(QVariantMap headers MEMBER m_headers CONSTANT) private: - HttpRequest(QObject* _parent, QUrl const& _url, QString const& _content): - QObject(_parent), m_url(_url), m_content(_content) + HttpRequest(QObject* _parent, QUrl&& _url, QString&& _content, QVariantMap&& _headers): + QObject(_parent), m_url(_url), m_content(_content), m_headers(_headers) { } @@ -60,6 +63,7 @@ private: QString m_content; QString m_response; QString m_responseContentType; + QVariantMap m_headers; friend class HttpServer; }; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index b95dc5cf3..5665c48b6 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -251,21 +251,7 @@ void MixClient::mine() WriteGuard l(x_state); m_state.commitToMine(bc()); m_state.completeMine(Ethash::Solution()); - bc().import(m_state.blockData(), m_stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce); - /* - GenericFarm f; - bool completed = false; - f.onSolutionFound([&](ProofOfWork::Solution sol) - { - return completed = m_state.completeMine(sol); - }); - f.setWork(m_state.info()); - f.startCPU(); - while (!completed) - this_thread::sleep_for(chrono::milliseconds(20)); - - bc().import(m_state.blockData(), m_stateDB); - */ + bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); m_state.sync(bc()); m_startState = m_state; h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 4f7451680..df14d0c4f 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -17,6 +17,7 @@ Item { property string webContent; //for testing signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content) signal webContentReady + signal ready function setPreviewUrl(url) { if (!initialized) @@ -184,8 +185,10 @@ Item { content = fileIo.readFile(doc.path); } - if (documentName === urlInput.text.replace(httpServer.url + "/", "")) { - //root page, inject deployment script + var accept = _request.headers["Accept"]; + if (accept && accept.indexOf("text/html") >= 0 && !_request.headers["HTTP_X_REQUESTED_WITH"]) + { + //navigate to page request, inject deployment script content = "\n" + content; _request.setResponseContentType("text/html"); } @@ -325,6 +328,7 @@ Item { webView.runJavaScript("init(\"" + httpServer.url + "/rpc/\")"); if (pendingPageUrl) setPreviewUrl(pendingPageUrl); + ready(); } } } diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp index 75f69a9eb..487dac880 100644 --- a/mix/test/TestService.cpp +++ b/mix/test/TestService.cpp @@ -20,8 +20,9 @@ * Ethereum IDE client. */ -#include #include "TestService.h" +#include +#include #include #include #include @@ -177,7 +178,6 @@ void TestService::setTargetWindow(QObject* _window) window->requestActivate(); } - QWindow* TestService::eventWindow(QObject* _item) { QQuickItem* item = qobject_cast(_item); @@ -201,5 +201,10 @@ QWindow* TestService::eventWindow(QObject* _item) return 0; } +QString TestService::createUuid() const +{ + return QUuid::createUuid().toString(); +} + } } diff --git a/mix/test/TestService.h b/mix/test/TestService.h index be65cf558..55f02479f 100644 --- a/mix/test/TestService.h +++ b/mix/test/TestService.h @@ -41,6 +41,7 @@ public: void setTargetWindow(QObject* _window); public slots: + QString createUuid() const; bool waitForSignal(QObject* _item, QString _signalName, int _timeout); bool waitForRendering(QObject* _item, int timeout); bool keyPress(QObject* _item, int _key, int _modifiers, int _delay); diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml index c0d179707..829364a99 100644 --- a/mix/test/qml/TestMain.qml +++ b/mix/test/qml/TestMain.qml @@ -41,7 +41,7 @@ TestCase var projectDlg = mainApplication.projectModel.newProjectDialog; wait(30); projectDlg.projectTitle = "TestProject"; - projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path + projectDlg.pathFieldText = "/tmp/MixTest/" + ts.createUuid(); //TODO: get platform temp path projectDlg.acceptAndClose(); wait(1); if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) @@ -82,6 +82,16 @@ TestCase ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S } + function createHtml(name, c) + { + mainApplication.projectModel.newHtmlFile(); + ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000); + var doc = mainApplication.projectModel.listModel.get(mainApplication.projectModel.listModel.count - 1); + mainApplication.projectModel.renameDocument(doc.documentId, name); + mainApplication.mainContent.codeEditor.getEditor(doc.documentId).setText(c); + ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S + } + function clickElement(el, x, y) { if (el.contentItem) @@ -101,5 +111,7 @@ TestCase function test_miner_selectMiner() { TestMiner.test_selectMiner(); } function test_miner_mine() { TestMiner.test_mine(); } function test_project_contractRename() { TestProject.test_contractRename(); } + function test_project_multipleWebPages() { TestProject.test_multipleWebPages(); } + function test_project_multipleContractsSameFile() { TestProject.test_multipleContractsSameFile(); } } diff --git a/mix/test/qml/js/TestDebugger.js b/mix/test/qml/js/TestDebugger.js index 2300dd390..4933136ff 100644 --- a/mix/test/qml/js/TestDebugger.js +++ b/mix/test/qml/js/TestDebugger.js @@ -123,8 +123,7 @@ function test_arrayParametersAndStorage() transactionDialog.acceptAndClose(); mainApplication.projectModel.stateDialog.acceptAndClose(); mainApplication.mainContent.startQuickDebugging(); - if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000)) - fail("Error running transaction"); + waitForExecution(); //debug setM mainApplication.clientModel.debugRecord(3); mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue; @@ -158,8 +157,7 @@ function test_solidityDebugging() "}"); mainApplication.mainContent.startQuickDebugging(); - if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000)) - fail("Error running transaction"); + waitForExecution(); tryCompare(mainApplication.mainContent.rightPane.debugSlider, "maximumValue", 20); tryCompare(mainApplication.mainContent.rightPane.debugSlider, "value", 0); @@ -191,8 +189,7 @@ function test_vmDebugging() "}"); mainApplication.mainContent.startQuickDebugging(); - if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000)) - fail("Error running transaction"); + waitForExecution(); mainApplication.mainContent.rightPane.assemblyMode = !mainApplication.mainContent.rightPane.assemblyMode; tryCompare(mainApplication.mainContent.rightPane.debugSlider, "maximumValue", 41); @@ -225,12 +222,7 @@ function test_ctrTypeAsParam() " }\r " + "}"); mainApplication.projectModel.stateListModel.editState(0); //C1 ctor already added - mainApplication.projectModel.stateDialog.model.addTransaction(); var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; - ts.waitForRendering(transactionDialog, 3000); - transactionDialog.selectContract("C2"); - transactionDialog.selectFunction("C2"); - transactionDialog.acceptAndClose(); mainApplication.projectModel.stateDialog.model.addTransaction(); transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; ts.waitForRendering(transactionDialog, 3000); @@ -241,8 +233,7 @@ function test_ctrTypeAsParam() transactionDialog.acceptAndClose(); mainApplication.projectModel.stateDialog.acceptAndClose(); mainApplication.mainContent.startQuickDebugging(); - if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000)) - fail("Error running transaction"); + waitForExecution(); tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(159)"); } diff --git a/mix/test/qml/js/TestMiner.js b/mix/test/qml/js/TestMiner.js index 9d98c9f24..3824f988e 100644 --- a/mix/test/qml/js/TestMiner.js +++ b/mix/test/qml/js/TestMiner.js @@ -25,6 +25,10 @@ function test_mine() waitForExecution(); mainApplication.clientModel.mine(); waitForMining(); + wait(1000); //there need to be at least 1 sec diff between block times + mainApplication.clientModel.mine(); + waitForMining(); tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "contract", " - Block - "); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "contract", " - Block - "); } diff --git a/mix/test/qml/js/TestProject.js b/mix/test/qml/js/TestProject.js index 0d20ec9a8..49b5ea51f 100644 --- a/mix/test/qml/js/TestProject.js +++ b/mix/test/qml/js/TestProject.js @@ -16,3 +16,31 @@ function test_contractRename() transactionDialog.close(); mainApplication.projectModel.stateDialog.close(); } + +function test_multipleWebPages() +{ + newProject(); + editHtml("page1"); + createHtml("page1.html", "
Fail
"); + clickElement(mainApplication.mainContent.webView.webView, 1, 1); + ts.typeString("\t\r"); + wait(300); //TODO: use a signal in qt 5.5 + mainApplication.mainContent.webView.getContent(); + ts.waitForSignal(mainApplication.mainContent.webView, "webContentReady()", 5000); + var body = mainApplication.mainContent.webView.webContent; + verify(body.indexOf("
OK
") != -1, "Web content not updated") +} + +function test_multipleContractsSameFile() +{ + newProject(); + editContract( + "contract C1 {}\r" + + "contract C2 {}\r" + + "contract C3 {}\r"); + waitForExecution(); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "C1"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "contract", "C2"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "contract", "C3"); +} From 4e455b2ba5cc291f83e20d6b3076fb41623fe135 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 23 Apr 2015 14:34:29 +0200 Subject: [PATCH 05/17] prevent Mix from creating DAG --- mix/MixClient.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 5665c48b6..0b7d22f3f 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -42,6 +42,18 @@ namespace mix Secret const c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); u256 const c_mixGenesisDifficulty = 131072; //TODO: make it lower for Mix somehow +namespace +{ + +struct MixPow //dummy POW +{ + typedef int Solution; + static void assignResult(int, BlockInfo const&) {} + static bool verify(BlockInfo const&) { return true; } +}; + +} + bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) { RLPStream block(3); @@ -250,7 +262,7 @@ void MixClient::mine() { WriteGuard l(x_state); m_state.commitToMine(bc()); - m_state.completeMine(Ethash::Solution()); + m_state.completeMine(0); bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); m_state.sync(bc()); m_startState = m_state; From cdc38ab28a3ea6ad11d16f94072e2abec665c11d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 23 Apr 2015 14:45:50 +0200 Subject: [PATCH 06/17] Use non-implemented functions for solidity interface output. --- libsolidity/InterfaceHandler.cpp | 2 +- test/libsolidity/SolidityInterface.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index aacbbfd72..ea787c282 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -107,7 +107,7 @@ unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames()); else if (ret.back() == ' ') ret.pop_back(); - ret += "{}"; + ret += ";"; } return unique_ptr(new string(ret + "}")); diff --git a/test/libsolidity/SolidityInterface.cpp b/test/libsolidity/SolidityInterface.cpp index c8f74e3aa..9c9373f04 100644 --- a/test/libsolidity/SolidityInterface.cpp +++ b/test/libsolidity/SolidityInterface.cpp @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(single_function) "}\n"); BOOST_REQUIRE_EQUAL(1, contract.getDefinedFunctions().size()); BOOST_CHECK_EQUAL(getSourcePart(*contract.getDefinedFunctions().front()), - "function f(uint256 a)returns(uint256 d){}"); + "function f(uint256 a)returns(uint256 d);"); } BOOST_AUTO_TEST_CASE(single_constant_function) @@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(single_constant_function) "contract test { function f(uint a) constant returns(bytes1 x) { 1==2; } }"); BOOST_REQUIRE_EQUAL(1, contract.getDefinedFunctions().size()); BOOST_CHECK_EQUAL(getSourcePart(*contract.getDefinedFunctions().front()), - "function f(uint256 a)constant returns(bytes1 x){}"); + "function f(uint256 a)constant returns(bytes1 x);"); } BOOST_AUTO_TEST_CASE(multiple_functions) @@ -97,8 +97,8 @@ BOOST_AUTO_TEST_CASE(multiple_functions) " function g(uint b) returns(uint e) { return b * 8; }\n" "}\n"; ContractDefinition const& contract = checkInterface(sourceCode); - set expectation({"function f(uint256 a)returns(uint256 d){}", - "function g(uint256 b)returns(uint256 e){}"}); + set expectation({"function f(uint256 a)returns(uint256 d);", + "function g(uint256 b)returns(uint256 e);"}); BOOST_REQUIRE_EQUAL(2, contract.getDefinedFunctions().size()); BOOST_CHECK(expectation == set({getSourcePart(*contract.getDefinedFunctions().at(0)), getSourcePart(*contract.getDefinedFunctions().at(1))})); @@ -135,8 +135,8 @@ BOOST_AUTO_TEST_CASE(inheritance) " event derivedEvent(uint indexed evtArgDerived); \n" " }"; ContractDefinition const& contract = checkInterface(sourceCode); - set expectedFunctions({"function baseFunction(uint256 p)returns(uint256 i){}", - "function derivedFunction(bytes32 p)returns(bytes32 i){}"}); + set expectedFunctions({"function baseFunction(uint256 p)returns(uint256 i);", + "function derivedFunction(bytes32 p)returns(bytes32 i);"}); BOOST_REQUIRE_EQUAL(2, contract.getDefinedFunctions().size()); BOOST_CHECK(expectedFunctions == set({getSourcePart(*contract.getDefinedFunctions().at(0)), getSourcePart(*contract.getDefinedFunctions().at(1))})); From 739f6b4731fd6d8887c2022dcc8dfe7c0840dfa4 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Fri, 24 Apr 2015 10:03:36 +0200 Subject: [PATCH 07/17] fix send via CLI --- eth/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/main.cpp b/eth/main.cpp index f7f6fe372..9c49396f0 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -1317,9 +1317,9 @@ int main(int argc, char** argv) { string hexAddr; u256 amount; - int size = hexAddr.length(); iss >> hexAddr >> amount; + int size = hexAddr.length(); if (size < 40) { if (size > 0) From 962d0eaaf2f8647e7449919b2b3a949952c7c47b Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 24 Apr 2015 10:18:08 +0200 Subject: [PATCH 08/17] - If param is address type, allow to easily fill in param width used accounts --- mix/ContractCallDataEncoder.cpp | 4 ++- mix/qml/QAddressView.qml | 23 +++++++++++------- mix/qml/StateListModel.qml | 2 +- mix/qml/StructView.qml | 43 ++++++++++++++++++++++++--------- mix/qml/TransactionDialog.qml | 4 ++- mix/qml/VariablesView.qml | 2 ++ 6 files changed, 55 insertions(+), 23 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index c561c0017..854623a6a 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -195,7 +195,7 @@ QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& bytes rawParam(32); value.populate(&rawParam); QSolidityType::Type type = _type.type; - if (type == QSolidityType::Type::SignedInteger || type == QSolidityType::Type::UnsignedInteger || type == QSolidityType::Type::Address) + if (type == QSolidityType::Type::SignedInteger || type == QSolidityType::Type::UnsignedInteger) return QVariant::fromValue(toString(decodeInt(rawParam))); else if (type == QSolidityType::Type::Bool) return QVariant::fromValue(toString(decodeBool(rawParam))); @@ -203,6 +203,8 @@ QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& return QVariant::fromValue(toString(decodeBytes(rawParam))); else if (type == QSolidityType::Type::Struct) return QVariant::fromValue(QString("struct")); //TODO + else if (type == QSolidityType::Type::Address) + return QVariant::fromValue(toString(decodeBytes(unpadLeft(rawParam)))); else BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found")); } diff --git a/mix/qml/QAddressView.qml b/mix/qml/QAddressView.qml index 2c1d43276..204e92508 100644 --- a/mix/qml/QAddressView.qml +++ b/mix/qml/QAddressView.qml @@ -5,10 +5,12 @@ import QtQuick.Controls.Styles 1.3 Item { property alias value: textinput.text - property alias contractCreationTr: ctrModel + property alias accountRef: ctrModel + property string subType + property bool readOnly id: editRoot height: 20 - width: 200 + width: 320 SourceSansProBold { @@ -17,19 +19,19 @@ Item function init() { - trCombobox.visible = ctrModel.count > 1; //index 0 is a blank value. - if (value.indexOf("<") === 0) + trCombobox.visible = !readOnly + textinput.readOnly = readOnly + if (!readOnly) { for (var k = 0; k < ctrModel.count; k++) { - if ("<" + ctrModel.get(k).functionId + ">" === value) + if (ctrModel.get(k).value === value) { trCombobox.currentIndex = k; return; } } trCombobox.currentIndex = 0; - value = ""; } } @@ -43,7 +45,7 @@ Item text: value width: parent.width height: parent.width - wrapMode: Text.WrapAnywhere + wrapMode: Text.WordWrap clip: true font.family: boldFont.name MouseArea { @@ -73,7 +75,7 @@ Item property bool selected: false id: trCombobox model: ctrModel - textRole: "functionId" + textRole: "itemid" height: 20 anchors.verticalCenter: parent.verticalCenter anchors.left: textinput.parent.right @@ -84,7 +86,10 @@ Item return; else if (currentText !== " - ") { - textinput.text = "<" + currentText + ">"; + if (model.get(currentIndex).type === "contract") + textinput.text = "<" + currentText + ">"; + else + textinput.text = model.get(currentIndex).value; //address trCombobox.selected = true; } else if (textinput.text.indexOf("<") === 0) diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 35d106b5f..075a049b6 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -183,7 +183,7 @@ Item { _secret = clientModel.newSecret(); var address = clientModel.address(_secret); var name = qsTr("Account") + "-" + address.substring(0, 4); - return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit) }; + return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address }; } function createDefaultState() { diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 32ce3d618..798a634fa 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -7,8 +7,10 @@ Column { id: root property alias members: repeater.model //js array + property variant accounts property var value: ({}) property int transactionIndex + property string context Layout.fillWidth: true spacing: 10 Repeater @@ -69,21 +71,40 @@ Column var vals = value; if (ptype.category === QSolidityType.Address) { - item.contractCreationTr.append({"functionId": " - "}); - var trCr = -1; - for (var k = 0; k < transactionsModel.count; k++) + item.value = getValue(); + item.readOnly = context === "variable"; + if (context === "parameter") { - if (k >= transactionIndex) - break; - var tr = transactionsModel.get(k); - if (tr.functionId === tr.contractId) + var dec = modelData.type.name.split(" "); + item.subType = dec[0]; + item.accountRef.append({"itemid": " - "}); + + if (item.subType === "contract" || item.subType === "address") + { + var trCr = 0; + for (var k = 0; k < transactionsModel.count; k++) + { + if (k >= transactionIndex) + break; + var tr = transactionsModel.get(k); + if (tr.functionId === tr.contractId && (dec[1] === tr.contractId || item.subType === "address")) + { + item.accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" }); + trCr++; + } + } + } + if (item.subType === "address") { - trCr++; - if (modelData.type.name === qsTr("contract") + " " + tr.contractId) - item.contractCreationTr.append({ "functionId": tr.contractId + " - " + trCr }); + for (k = 0; k < accounts.length; k++) + { + if (accounts[k].address === undefined) + accounts[k].address = clientModel.address(accounts[k].secret); + item.accountRef.append({ "itemid": accounts[k].name, "value": "0x" + accounts[k].address, "type": "address" }); + } + } } - item.value = getValue(); item.init(); } else if (ptype.category === QSolidityType.Struct && !item.members) diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 0668bfca2..66a98d19e 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -11,7 +11,7 @@ import "." Dialog { id: modalTransactionDialog modality: Qt.ApplicationModal - width: 520 + width: 570 height: 500 visible: false title: qsTr("Edit Transaction") @@ -387,6 +387,8 @@ Dialog { id: typeLoader Layout.preferredWidth: 150 members: paramsModel; + accounts: senderComboBox.model + context: "parameter" } } diff --git a/mix/qml/VariablesView.qml b/mix/qml/VariablesView.qml index 2670a5cb0..b04739274 100644 --- a/mix/qml/VariablesView.qml +++ b/mix/qml/VariablesView.qml @@ -24,6 +24,8 @@ DebugInfoList members: [] value: {} Layout.preferredWidth: parent.width + context: "variable" + width: parent.width } } } From 5a3a9abae6427c4ba738b2cdacdde74c439289f6 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 24 Apr 2015 11:39:05 +0200 Subject: [PATCH 09/17] extracted collectContracts --- mix/CodeModel.cpp | 101 +++++++++++++++++++++++----------------------- mix/CodeModel.h | 1 + 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 0407d2aca..a56444d02 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -249,7 +249,6 @@ void CodeModel::runCompilationJob(int _jobId) { if (_jobId != m_backgroundJobId) return; //obsolete job - ContractMap result; solidity::CompilerStack cs(true); try { @@ -260,53 +259,7 @@ void CodeModel::runCompilationJob(int _jobId) cs.addSource(c.first.toStdString(), c.second.toStdString()); } cs.compile(false); - - { - Guard pl(x_pendingContracts); - Guard l(x_contractMap); - for (std::string n: cs.getContractNames()) - { - if (c_predefinedContracts.count(n) != 0) - continue; - QString name = QString::fromStdString(n); - ContractDefinition const& contractDefinition = cs.getContractDefinition(n); - if (!contractDefinition.isFullyImplemented()) - continue; - QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName); - auto sourceIter = m_pendingContracts.find(sourceName); - QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString(); - CompiledContract* contract = new CompiledContract(cs, name, source); - QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership); - result[name] = contract; - CompiledContract* prevContract = nullptr; - // find previous contract by name - for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) - if (c.value()->contract()->name() == contract->contract()->name()) - prevContract = c.value(); - - // if not found, try by documentId - if (!prevContract) - { - for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) - if (c.value()->documentId() == contract->documentId()) - { - //make sure there are no other contracts in the same source, otherwise it is not a rename - if (!std::any_of(result.begin(),result.end(), [=](ContractMap::const_iterator::value_type _v) { return _v != contract && _v->documentId() == contract->documentId(); })) - prevContract = c.value(); - } - } - if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface()) - emit contractInterfaceChanged(name); - if (prevContract == nullptr) - emit newContractCompiled(name); - else if (prevContract->contract()->name() != name) - emit contractRenamed(contract->documentId(), prevContract->contract()->name(), name); - } - releaseContracts(); - m_contractMap.swap(result); - emit codeChanged(); - emit compilationComplete(); - } + collectContracts(cs); } catch (dev::Exception const& _exception) { @@ -320,8 +273,7 @@ void CodeModel::runCompilationJob(int _jobId) sourceName = QString::fromStdString(*location->sourceName); if (!sourceName.isEmpty()) if (CompiledContract* contract = contractByDocumentId(sourceName)) - //substitute the location to match our contract names - message = message.replace(sourceName, contract->contract()->name()); + message = message.replace(sourceName, contract->contract()->name()); //substitute the location to match our contract names } compilationError(message, sourceName); } @@ -329,6 +281,55 @@ void CodeModel::runCompilationJob(int _jobId) emit stateChanged(); } +void CodeModel::collectContracts(solidity::CompilerStack const& _cs) +{ + Guard pl(x_pendingContracts); + Guard l(x_contractMap); + ContractMap result; + for (std::string n: _cs.getContractNames()) + { + if (c_predefinedContracts.count(n) != 0) + continue; + QString name = QString::fromStdString(n); + ContractDefinition const& contractDefinition = _cs.getContractDefinition(n); + if (!contractDefinition.isFullyImplemented()) + continue; + QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName); + auto sourceIter = m_pendingContracts.find(sourceName); + QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString(); + CompiledContract* contract = new CompiledContract(_cs, name, source); + QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership); + result[name] = contract; + CompiledContract* prevContract = nullptr; + // find previous contract by name + for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) + if (c.value()->contract()->name() == contract->contract()->name()) + prevContract = c.value(); + + // if not found, try by documentId + if (!prevContract) + { + for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) + if (c.value()->documentId() == contract->documentId()) + { + //make sure there are no other contracts in the same source, otherwise it is not a rename + if (!std::any_of(result.begin(),result.end(), [=](ContractMap::const_iterator::value_type _v) { return _v != contract && _v->documentId() == contract->documentId(); })) + prevContract = c.value(); + } + } + if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface()) + emit contractInterfaceChanged(name); + if (prevContract == nullptr) + emit newContractCompiled(name); + else if (prevContract->contract()->name() != name) + emit contractRenamed(contract->documentId(), prevContract->contract()->name(), name); + } + releaseContracts(); + m_contractMap.swap(result); + emit codeChanged(); + emit compilationComplete(); +} + bool CodeModel::hasContract() const { Guard l(x_contractMap); diff --git a/mix/CodeModel.h b/mix/CodeModel.h index dc27da61e..199e1dee2 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -182,6 +182,7 @@ private: void runCompilationJob(int _jobId); void stop(); void releaseContracts(); + void collectContracts(solidity::CompilerStack const& _cs); std::atomic m_compiling; mutable dev::Mutex x_contractMap; From bd5698b0705c3223d8176690ce4d81738c73c5fd Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 24 Apr 2015 11:48:23 +0200 Subject: [PATCH 10/17] Do not handle combined JSON if option not given. --- solc/CommandLineInterface.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 5e4c901f3..eadc6c56f 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -380,6 +380,9 @@ bool CommandLineInterface::processInput() void CommandLineInterface::handleCombinedJSON() { + if (!m_args.count("combined-json")) + return; + Json::Value output(Json::objectValue); set requests; From ccd39728e9ef606080d0cf42719eb9c90dde0a4b Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 24 Apr 2015 12:11:37 +0200 Subject: [PATCH 11/17] lowercase header names --- mix/HttpServer.cpp | 2 +- mix/qml/WebPreview.qml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix/HttpServer.cpp b/mix/HttpServer.cpp index ba03d7795..fa09f51d0 100644 --- a/mix/HttpServer.cpp +++ b/mix/HttpServer.cpp @@ -156,7 +156,7 @@ void HttpServer::readClient() //collect headers int colon = l.indexOf(':'); if (colon > 0) - headers[l.left(colon)] = l.right(l.length() - colon - 1); + headers[l.left(colon).trimmed().toLower()] = l.right(l.length() - colon - 1).trimmed(); } while (!(l.isEmpty() || l == "\r" || l == "\r\n")); diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index df14d0c4f..e4d0b61a5 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -185,8 +185,8 @@ Item { content = fileIo.readFile(doc.path); } - var accept = _request.headers["Accept"]; - if (accept && accept.indexOf("text/html") >= 0 && !_request.headers["HTTP_X_REQUESTED_WITH"]) + var accept = _request.headers["accept"]; + if (accept && accept.indexOf("text/html") >= 0 && !_request.headers["http_x_requested_with"]) { //navigate to page request, inject deployment script content = "\n" + content; From 7e50d77bafb63c1a30c51a773c74479bcd555a99 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 24 Apr 2015 13:18:14 +0200 Subject: [PATCH 12/17] fixed windows build --- mix/CodeModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index a56444d02..357ce88b9 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -281,7 +281,7 @@ void CodeModel::runCompilationJob(int _jobId) emit stateChanged(); } -void CodeModel::collectContracts(solidity::CompilerStack const& _cs) +void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs) { Guard pl(x_pendingContracts); Guard l(x_contractMap); From 1b71d6a7c28ca074d578fb9b6d20014a824b7b82 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 24 Apr 2015 14:32:49 +0200 Subject: [PATCH 13/17] Standalone miner. --- CMakeLists.txt | 1 + ethminer/CMakeLists.txt | 41 ++++ ethminer/Farm.h | 39 ++++ ethminer/PhoneHome.h | 28 +++ ethminer/farm.json | 4 + ethminer/main.cpp | 483 +++++++++++++++++++++++++++++++++++++++ ethminer/phonehome.json | 3 + libethcore/Ethash.cpp | 4 +- libethcore/EthashAux.cpp | 4 +- libethcore/EthashAux.h | 2 + 10 files changed, 605 insertions(+), 4 deletions(-) create mode 100644 ethminer/CMakeLists.txt create mode 100644 ethminer/Farm.h create mode 100644 ethminer/PhoneHome.h create mode 100644 ethminer/farm.json create mode 100644 ethminer/main.cpp create mode 100644 ethminer/phonehome.json diff --git a/CMakeLists.txt b/CMakeLists.txt index b3773861e..ff8732156 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,6 +354,7 @@ if (TOOLS) add_subdirectory(rlp) add_subdirectory(abi) + add_subdirectory(ethminer) add_subdirectory(eth) if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") diff --git a/ethminer/CMakeLists.txt b/ethminer/CMakeLists.txt new file mode 100644 index 000000000..cd8919bb2 --- /dev/null +++ b/ethminer/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_policy(SET CMP0015 NEW) +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ..) +include_directories(${Boost_INCLUDE_DIRS}) +include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) + +set(EXECUTABLE ethminer) + +file(GLOB HEADERS "*.h") + +add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + +add_dependencies(${EXECUTABLE} BuildInfo.h) + +target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) + +if (READLINE_FOUND) + target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES}) +endif() + +if (JSONRPC) + target_link_libraries(${EXECUTABLE} web3jsonrpc) + target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES}) + target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES}) + if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) + eth_copy_dlls(${EXECUTABLE} CURL_DLLS) + endif() +endif() + +target_link_libraries(${EXECUTABLE} webthree) +target_link_libraries(${EXECUTABLE} ethash) + +if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) + eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) +endif() + +install( TARGETS ${EXECUTABLE} DESTINATION bin ) + diff --git a/ethminer/Farm.h b/ethminer/Farm.h new file mode 100644 index 000000000..ae2ef5a7d --- /dev/null +++ b/ethminer/Farm.h @@ -0,0 +1,39 @@ +/** + * This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY! + */ + +#ifndef JSONRPC_CPP_STUB_FARM_H_ +#define JSONRPC_CPP_STUB_FARM_H_ + +#include + +class Farm : public jsonrpc::Client +{ + public: + Farm(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type = jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {} + + Json::Value eth_getWork() throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p = Json::nullValue; + Json::Value result = this->CallMethod("eth_getWork",p); + if (result.isArray()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool eth_submitWork(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + p.append(param3); + Json::Value result = this->CallMethod("eth_submitWork",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } +}; + +#endif //JSONRPC_CPP_STUB_FARM_H_ diff --git a/ethminer/PhoneHome.h b/ethminer/PhoneHome.h new file mode 100644 index 000000000..ae6091cc2 --- /dev/null +++ b/ethminer/PhoneHome.h @@ -0,0 +1,28 @@ +/** + * This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY! + */ + +#ifndef JSONRPC_CPP_STUB_PHONEHOME_H_ +#define JSONRPC_CPP_STUB_PHONEHOME_H_ + +#include + +class PhoneHome : public jsonrpc::Client +{ + public: + PhoneHome(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type = jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {} + + int report_benchmark(const std::string& param1, int param2) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + p.append(param2); + Json::Value result = this->CallMethod("report_benchmark",p); + if (result.isInt()) + return result.asInt(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } +}; + +#endif //JSONRPC_CPP_STUB_PHONEHOME_H_ diff --git a/ethminer/farm.json b/ethminer/farm.json new file mode 100644 index 000000000..1f4142d00 --- /dev/null +++ b/ethminer/farm.json @@ -0,0 +1,4 @@ +[ + { "name": "eth_getWork", "params": [], "order": [], "returns": []}, + { "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true} +] diff --git a/ethminer/main.cpp b/ethminer/main.cpp new file mode 100644 index 000000000..19324945f --- /dev/null +++ b/ethminer/main.cpp @@ -0,0 +1,483 @@ +/* + 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 . +*/ +/** @file main.cpp + * @author Gav Wood + * @date 2014 + * Ethereum client. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ETH_JSONRPC || !ETH_TRUE +#include +#include +#include +#endif +#include "BuildInfo.h" +#if ETH_JSONRPC || !ETH_TRUE +#include "PhoneHome.h" +#include "Farm.h" +#endif +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace dev::eth; +using namespace boost::algorithm; +using dev::eth::Instruction; + +#undef RETURN + +bool isTrue(std::string const& _m) +{ + return _m == "on" || _m == "yes" || _m == "true" || _m == "1"; +} + +bool isFalse(std::string const& _m) +{ + return _m == "off" || _m == "no" || _m == "false" || _m == "0"; +} + +void help() +{ + cout + << "Usage ethminer [OPTIONS]" << endl + << "Options:" << endl << endl +#if ETH_JSONRPC || !ETH_TRUE + << "Work farming mode:" << endl + << " -F,--farm Put into mining farm mode with the work server at URL (default: http://127.0.0.1:8080)" << endl + << " --farm-recheck Leave n ms between checks for changed work (default: 500)." << endl +#endif + << "Benchmarking mode:" << endl + << " -M,--benchmark Benchmark for mining and exit; use with --cpu and --opencl." << endl + << " --benchmark-warmup Set the duration of warmup for the benchmark tests (default: 3)." << endl + << " --benchmark-trial Set the duration for each trial for the benchmark tests (default: 3)." << endl + << " --benchmark-trials Set the duration of warmup for the benchmark tests (default: 5)." << endl +#if ETH_JSONRPC || !ETH_TRUE + << " --phone-home When benchmarking, publish results (default: on)" << endl +#endif + << "DAG creation mode:" << endl + << " -D,--create-dag Create the DAG in preparation for mining on given block and exit." << endl + << "General Options:" << endl + << " -C,--cpu When mining, use the CPU." << endl + << " -G,--opencl When mining use the GPU via OpenCL." << endl + << " --opencl-platform When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl + << " --opencl-device When mining using -G/--opencl use OpenCL device n (default: 0)." << endl + << " -t, --mining-threads Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl + << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl + << " -V,--version Show the version and exit." << endl + << " -h,--help Show this help message and exit." << endl + ; + exit(0); +} + +string credits() +{ + std::ostringstream cout; + cout + << "Ethereum (++) " << dev::Version << endl + << " Code by Gav Wood et al, (c) 2013, 2014, 2015." << endl + << " Based on a design by Vitalik Buterin." << endl << endl; + return cout.str(); +} + +void version() +{ + cout << "eth version " << dev::Version << endl; + cout << "eth network protocol version: " << dev::eth::c_protocolVersion << endl; + cout << "Client database version: " << dev::eth::c_databaseVersion << endl; + cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; + exit(0); +} + +void doInitDAG(unsigned _n) +{ + BlockInfo bi; + bi.number = _n; + cout << "Initializing DAG for epoch beginning #" << (bi.number / 30000 * 30000) << " (seedhash " << bi.seedHash().abridged() << "). This will take a while." << endl; + Ethash::prep(bi); + exit(0); +} + +enum class OperationMode +{ + DAGInit, + Benchmark, + Farm +}; + +enum class MinerType +{ + CPU, + GPU +}; + +void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, unsigned _trialDuration = 3, unsigned _trials = 5) +{ + BlockInfo genesis = CanonBlockChain::genesis(); + genesis.difficulty = 1 << 18; + cdebug << genesis.boundary(); + + GenericFarm f; + f.onSolutionFound([&](ProofOfWork::Solution) { return false; }); + + string platformInfo = _m == MinerType::CPU ? ProofOfWork::CPUMiner::platformInfo() : _m == MinerType::GPU ? ProofOfWork::GPUMiner::platformInfo() : ""; + cout << "Benchmarking on platform: " << platformInfo << endl; + + cout << "Preparing DAG..." << endl; + Ethash::prep(genesis); + + genesis.difficulty = u256(1) << 63; + genesis.noteDirty(); + f.setWork(genesis); + if (_m == MinerType::CPU) + f.startCPU(); + else if (_m == MinerType::GPU) + f.startGPU(); + + map results; + uint64_t mean = 0; + uint64_t innerMean = 0; + for (unsigned i = 0; i <= _trials; ++i) + { + if (!i) + cout << "Warming up..." << endl; + else + cout << "Trial " << i << "... " << flush; + this_thread::sleep_for(chrono::seconds(i ? _trialDuration : _warmupDuration)); + + auto mp = f.miningProgress(); + f.resetMiningProgress(); + if (!i) + continue; + auto rate = mp.rate(); + + cout << rate << endl; + results[rate] = mp; + mean += rate; + if (i > 1 && i < 5) + innerMean += rate; + } + f.stop(); + cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl; + cout << "inner mean: " << (innerMean / (_trials - 2)) << " H/s" << endl; + + (void)_phoneHome; +#if ETH_JSONRPC || !ETH_TRUE + if (_phoneHome) + { + cout << "Phoning home to find world ranking..." << endl; + jsonrpc::HttpClient client("http://gav.ethdev.com:3000/benchmark"); + PhoneHome rpc(client); + try + { + unsigned ranking = rpc.report_benchmark(platformInfo, innerMean); + cout << "Ranked: " << ranking << " of all benchmarks." << endl; + } + catch (...) + { + cout << "Error phoning home. ET is sad." << endl; + } + } +#endif + exit(0); +} + +struct HappyChannel: public LogChannel { static const char* name() { return ":-D"; } static const int verbosity = 1; }; +struct SadChannel: public LogChannel { static const char* name() { return ":-("; } static const int verbosity = 1; }; + +void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod) +{ + (void)_m; + (void)_remote; + (void)_recheckPeriod; +#if ETH_JSONRPC || !ETH_TRUE + jsonrpc::HttpClient client(_remote); + Farm rpc(client); + GenericFarm f; + if (_m == MinerType::CPU) + f.startCPU(); + else if (_m == MinerType::GPU) + f.startGPU(); + + ProofOfWork::WorkPackage current; + while (true) + try + { + bool completed = false; + ProofOfWork::Solution solution; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + solution = sol; + return completed = true; + }); + for (unsigned i = 0; !completed; ++i) + { + if (current) + cnote << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress(); + else + cnote << "Getting work package..."; + Json::Value v = rpc.eth_getWork(); + h256 hh(v[0].asString()); + if (hh != current.headerHash) + { + current.headerHash = hh; + current.seedHash = h256(v[1].asString()); + current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight); + cnote << "Got work package:" << current.headerHash << " < " << current.boundary; + f.setWork(current); + } + this_thread::sleep_for(chrono::milliseconds(_recheckPeriod)); + } + cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash << "," << solution.mixHash << "] to" << _remote << "..."; + bool ok = rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(current.headerHash), "0x" + toString(solution.mixHash)); + if (ok) + clog(HappyChannel) << "Submitted and accepted."; + else + clog(SadChannel) << "Not accepted."; + current.reset(); + } + catch (jsonrpc::JsonRpcException&) + { + for (auto i = 3; --i; this_thread::sleep_for(chrono::seconds(1))) + cerr << "JSON-RPC problem. Probably couldn't connect. Retrying in " << i << "... \r"; + cerr << endl; + } +#endif + exit(0); +} + +int main(int argc, char** argv) +{ + // Init defaults + Defaults::get(); + + /// Operating mode. + OperationMode mode = OperationMode::Farm; + + /// Mining options + MinerType minerType = MinerType::CPU; + unsigned openclPlatform = 0; + unsigned openclDevice = 0; + unsigned miningThreads = UINT_MAX; + + /// DAG initialisation param. + unsigned initDAG = 0; + + /// Benchmarking params + bool phoneHome = true; + unsigned benchmarkWarmup = 3; + unsigned benchmarkTrial = 3; + unsigned benchmarkTrials = 5; + + /// Farm params + string farmURL = "http://127.0.0.1:8080"; + unsigned farmRecheckPeriod = 500; + + for (int i = 1; i < argc; ++i) + { + string arg = argv[i]; + if ((arg == "-F" || arg == "--farm") && i + 1 < argc) + { + mode = OperationMode::Farm; + farmURL = argv[++i]; + } + else if (arg == "--farm-recheck" && i + 1 < argc) + try { + farmRecheckPeriod = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + else if (arg == "--opencl-platform" && i + 1 < argc) + try { + openclPlatform = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + else if (arg == "--opencl-device" && i + 1 < argc) + try { + openclDevice = stol(argv[++i]); + miningThreads = 1; + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + else if (arg == "--phone-home" && i + 1 < argc) + { + string m = argv[++i]; + if (isTrue(m)) + phoneHome = true; + else if (isFalse(m)) + phoneHome = false; + else + { + cerr << "Bad " << arg << " option: " << m << endl; + return -1; + } + } + else if (arg == "--benchmark-warmup" && i + 1 < argc) + try { + benchmarkWarmup = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + else if (arg == "--benchmark-trial" && i + 1 < argc) + try { + benchmarkTrial = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + else if (arg == "--benchmark-trials" && i + 1 < argc) + try { + benchmarkTrials = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + else if (arg == "-C" || arg == "--cpu") + minerType = MinerType::CPU; + else if (arg == "-G" || arg == "--opencl") + minerType = MinerType::GPU; + else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc) + { + string m = boost::to_lower_copy(string(argv[++i])); + mode = OperationMode::DAGInit; + try + { + initDAG = stol(m); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << m << endl; + return -1; + } + } + else if ((arg == "-w" || arg == "--check-pow") && i + 4 < argc) + { + string m; + try + { + BlockInfo bi; + m = boost::to_lower_copy(string(argv[++i])); + h256 powHash(m); + m = boost::to_lower_copy(string(argv[++i])); + h256 seedHash; + if (m.size() == 64 || m.size() == 66) + seedHash = h256(m); + else + seedHash = EthashAux::seedHash(stol(m)); + m = boost::to_lower_copy(string(argv[++i])); + bi.difficulty = u256(m); + auto boundary = bi.boundary(); + m = boost::to_lower_copy(string(argv[++i])); + bi.nonce = h64(m); + auto r = EthashAux::eval(seedHash, powHash, bi.nonce); + bool valid = r.value < boundary; + cout << (valid ? "VALID :-)" : "INVALID :-(") << endl; + cout << r.value << (valid ? " < " : " >= ") << boundary << endl; + cout << " where " << boundary << " = 2^256 / " << bi.difficulty << endl; + cout << " and " << r.value << " = ethash(" << powHash << ", " << bi.nonce << ")" << endl; + cout << " with seed as " << seedHash << endl; + if (valid) + cout << "(mixHash = " << r.mixHash << ")" << endl; + cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light(seedHash)->data()) << endl; + exit(0); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << m << endl; + return -1; + } + } + else if (arg == "-M" || arg == "--benchmark") + mode = OperationMode::Benchmark; + else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc) + { + try { + miningThreads = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + } + else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) + g_logVerbosity = atoi(argv[++i]); + else if (arg == "-h" || arg == "--help") + help(); + else if (arg == "-V" || arg == "--version") + version(); + else + { + cerr << "Invalid argument: " << arg << endl; + exit(-1); + } + } + + if (minerType == MinerType::CPU) + ProofOfWork::CPUMiner::setNumInstances(miningThreads); + else if (minerType == MinerType::GPU) + { + ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); + ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); + ProofOfWork::GPUMiner::setNumInstances(miningThreads); + } + + if (mode == OperationMode::DAGInit) + doInitDAG(initDAG); + + if (mode == OperationMode::Benchmark) + doBenchmark(minerType, phoneHome, benchmarkWarmup, benchmarkTrial, benchmarkTrials); + + if (mode == OperationMode::Farm) + doFarm(minerType, farmURL, farmRecheckPeriod); + + return 0; +} + diff --git a/ethminer/phonehome.json b/ethminer/phonehome.json new file mode 100644 index 000000000..0bed56d72 --- /dev/null +++ b/ethminer/phonehome.json @@ -0,0 +1,3 @@ +[ + { "name": "report_benchmark", "params": [ "", 0 ], "order": [], "returns": 0 } +] diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 66dc953b7..e932fced4 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -121,7 +121,7 @@ bool Ethash::verify(BlockInfo const& _header) return slow; } -unsigned Ethash::CPUMiner::s_numInstances = 1; +unsigned Ethash::CPUMiner::s_numInstances = 0; void Ethash::CPUMiner::workLoop() { @@ -266,7 +266,7 @@ private: unsigned Ethash::GPUMiner::s_platformId = 0; unsigned Ethash::GPUMiner::s_deviceId = 0; -unsigned Ethash::GPUMiner::s_numInstances = 1; +unsigned Ethash::GPUMiner::s_numInstances = 0; Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Miner(_ci), diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index 44064cde6..19a96f550 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -64,7 +64,7 @@ ethash_params EthashAux::params(unsigned _n) h256 EthashAux::seedHash(unsigned _number) { unsigned epoch = _number / ETHASH_EPOCH_LENGTH; - RecursiveGuard l(get()->x_this); + Guard l(get()->x_epochs); if (epoch >= get()->m_seedHashes.size()) { h256 ret; @@ -87,7 +87,7 @@ h256 EthashAux::seedHash(unsigned _number) ethash_params EthashAux::params(h256 const& _seedHash) { - RecursiveGuard l(get()->x_this); + Guard l(get()->x_epochs); unsigned epoch = 0; try { diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index 40dd88e16..0389697f5 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -78,6 +78,8 @@ private: std::map> m_lights; std::map> m_fulls; FullType m_lastUsedFull; + + Mutex x_epochs; std::map m_epochs; h256s m_seedHashes; }; From d3e1eec18e124f3b82863f8d4aab3fbecdaf3de4 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Fri, 24 Apr 2015 16:16:17 +0200 Subject: [PATCH 14/17] fixed #1393, http connector stop listening --- alethzero/MainWin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index eb2431f10..f617f9ad9 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -217,6 +217,7 @@ Main::Main(QWidget *parent) : Main::~Main() { + m_httpConnector->StopListening(); writeSettings(); // Must do this here since otherwise m_ethereum'll be deleted (and therefore clearWatches() called by the destructor) // *after* the client is dead. From 231dc01bc045ea51fd302f119764150973d66d2e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 24 Apr 2015 18:33:37 +0200 Subject: [PATCH 15/17] ICAP class for everything ICAP. --- alethzero/MainWin.cpp | 76 +------------------- libdevcore/Base64.h | 21 +++++- libethcore/Common.cpp | 5 +- libethcore/Common.h | 2 + libethcore/ICAP.cpp | 159 ++++++++++++++++++++++++++++++++++++++++++ libethcore/ICAP.h | 77 ++++++++++++++++++++ 6 files changed, 265 insertions(+), 75 deletions(-) create mode 100644 libethcore/ICAP.cpp create mode 100644 libethcore/ICAP.h diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index eb2431f10..792ee6242 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -520,82 +521,11 @@ QString Main::pretty(dev::Address _a) const return fromRaw(n); } -template inline string toBase36(FixedHash const& _h) -{ - static char const* c_alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - typename FixedHash::Arith a = _h; - std::string ret; - for (; a > 0; a /= 36) - ret = c_alphabet[(unsigned)a % 36] + ret; - return ret; -} - -template inline FixedHash fromBase36(string const& _h) -{ - typename FixedHash::Arith ret = 0; - for (char c: _h) - ret = ret * 36 + (c < 'A' ? c - '0' : (c - 'A' + 10)); - return ret; -} - -static string iban(std::string _c, std::string _d) -{ - boost::to_upper(_c); - boost::to_upper(_d); - auto totStr = _d + _c + "00"; - bigint tot = 0; - for (char x: totStr) - if (x >= 'A') - tot = tot * 100 + x - 'A' + 10; - else - tot = tot * 10 + x - '0'; - unsigned check = (unsigned)(u256)(98 - tot % 97); - ostringstream out; - out << _c << setfill('0') << setw(2) << check << _d; - return out.str(); -} - -static std::pair fromIban(std::string _iban) -{ - if (_iban.size() < 4) - return std::make_pair(string(), string()); - boost::to_upper(_iban); - std::string c = _iban.substr(0, 2); - std::string d = _iban.substr(4); - if (iban(c, d) != _iban) - return std::make_pair(string(), string()); - return make_pair(c, d); -} - -static string directICAP(dev::Address _a) -{ - if (!!_a[0]) - return string(); - std::string d = toBase36(_a); - while (d.size() < 30) - d = "0" + d; - return iban("XE", d); -} - -static Address fromICAP(std::string const& _s) -{ - std::string country; - std::string data; - std::tie(country, data) = fromIban(_s); - if (country.empty()) - return Address(); - if (country == "XE" && data.size() == 30) - // Direct ICAP - return fromBase36(data); - // TODO: Indirect ICAP - return Address(); -} - QString Main::render(dev::Address _a) const { QString p = pretty(_a); if (!_a[0]) - p += QString(p.isEmpty() ? "" : " ") + QString::fromStdString(directICAP(_a)); + p += QString(p.isEmpty() ? "" : " ") + QString::fromStdString(ICAP(_a).encoded()); if (!p.isEmpty()) return p + " (" + QString::fromStdString(_a.abridged()) + ")"; return QString::fromStdString(_a.abridged()); @@ -639,7 +569,7 @@ Address Main::fromString(QString const& _n) const return Address(); } } - else if (Address a = fromICAP(_n.toStdString())) + else if (Address a = ICAP::decoded(_n.toStdString()).direct()) return a; else return Address(); diff --git a/libdevcore/Base64.h b/libdevcore/Base64.h index 1e9c88429..8ff10cff0 100644 --- a/libdevcore/Base64.h +++ b/libdevcore/Base64.h @@ -30,7 +30,8 @@ #include #include -#include +#include "Common.h" +#include "FixedHash.h" namespace dev { @@ -38,4 +39,22 @@ namespace dev std::string toBase64(bytesConstRef _in); bytes fromBase64(std::string const& _in); +template inline std::string toBase36(FixedHash const& _h) +{ + static char const* c_alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + typename FixedHash::Arith a = _h; + std::string ret; + for (; a > 0; a /= 36) + ret = c_alphabet[(unsigned)a % 36] + ret; + return ret; +} + +template inline FixedHash fromBase36(std::string const& _h) +{ + typename FixedHash::Arith ret = 0; + for (char c: _h) + ret = ret * 36 + (c < 'A' ? c - '0' : (c - 'A' + 10)); + return ret; +} + } diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index 56120471b..01c8cfa4c 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -21,6 +21,8 @@ #include "Common.h" #include +#include +#include #include #include "Exceptions.h" #include "ProofOfWork.h" @@ -100,4 +102,5 @@ std::string formatBalance(bigint const& _b) return ret.str(); } -}} +} +} diff --git a/libethcore/Common.h b/libethcore/Common.h index 199057f91..84459c6bf 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -23,6 +23,8 @@ #pragma once +#include +#include #include #include #include diff --git a/libethcore/ICAP.cpp b/libethcore/ICAP.cpp new file mode 100644 index 000000000..107d79db1 --- /dev/null +++ b/libethcore/ICAP.cpp @@ -0,0 +1,159 @@ +/* + 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 . +*/ +/** @file ICAP.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "ICAP.h" +#include +#include +#include +#include "Exceptions.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +namespace dev +{ +namespace eth +{ + +string ICAP::iban(std::string _c, std::string _d) +{ + boost::to_upper(_c); + boost::to_upper(_d); + auto totStr = _d + _c + "00"; + bigint tot = 0; + for (char x: totStr) + if (x >= 'A') + tot = tot * 100 + x - 'A' + 10; + else + tot = tot * 10 + x - '0'; + unsigned check = (unsigned)(u256)(98 - tot % 97); + ostringstream out; + out << _c << setfill('0') << setw(2) << check << _d; + return out.str(); +} + +std::pair ICAP::fromIBAN(std::string _iban) +{ + if (_iban.size() < 4) + return std::make_pair(string(), string()); + boost::to_upper(_iban); + std::string c = _iban.substr(0, 2); + std::string d = _iban.substr(4); + if (iban(c, d) != _iban) + return std::make_pair(string(), string()); + return make_pair(c, d); +} + +ICAP ICAP::decoded(std::string const& _encoded) +{ + ICAP ret; + std::string country; + std::string data; + std::tie(country, data) = fromIBAN(_encoded); + if (country != "XE") + throw InvalidICAP(); + if (data.size() == 30) + { + ret.m_type = Direct; + // Direct ICAP + ret.m_direct = fromBase36(data); + } + else if (data.size() == 16) + { + ret.m_type = Indirect; + ret.m_asset = data.substr(0, 3); + if (ret.m_asset == "XET") + { + ret.m_institution = data.substr(3, 4); + ret.m_client = data.substr(7); + } + else if (ret.m_asset == "ETH") + ret.m_client = data.substr(4); + else + throw InvalidICAP(); + } + else + throw InvalidICAP(); + + return ret; +} + +std::string ICAP::encoded() const +{ + if (m_type == Direct) + { + if (!!m_direct[0]) + return string(); + std::string d = toBase36(m_direct); + while (d.size() < 30) + d = "0" + d; + return iban("XE", d); + } + else if (m_type == Indirect) + { + if ( + m_asset.find_first_not_of("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890") != string::npos || + m_institution.find_first_not_of("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890") != string::npos || + m_client.find_first_not_of("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890") != string::npos || + m_asset.size() != 3 + ) + throw InvalidICAP(); + if (boost::to_upper_copy(m_asset) == "XET") + { + if (m_institution.size() != 4 || m_client.size() != 9) + throw InvalidICAP(); + } + else if (boost::to_upper_copy(m_asset) == "ETH") + { + if (m_client.size() != 13) + throw InvalidICAP(); + } + else + throw InvalidICAP(); + return iban("XE", m_asset + m_institution + m_client); + } + else + throw InvalidICAP(); +} + +Address ICAP::lookup(std::function const& _call, Address const& _reg) const +{ + (void)_call; + (void)_reg; + if (m_asset == "XET") + { + // TODO +// _call(_reg, ); + return Address(); + } + else if (m_asset == "ETH") + { + // TODO + +// return resolve(m_institution + "/" + m_client).primary(); + return Address(); + } + else + throw InterfaceNotSupported("ICAP::lookup(), non XET asset"); +} + +} +} diff --git a/libethcore/ICAP.h b/libethcore/ICAP.h new file mode 100644 index 000000000..60003a1f9 --- /dev/null +++ b/libethcore/ICAP.h @@ -0,0 +1,77 @@ +/* + 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 . +*/ +/** @file ICAP.h + * @author Gav Wood + * @date 2014 + * + * Ethereum-specific data structures & algorithms. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "Common.h" + +namespace dev +{ +namespace eth +{ + +struct InvalidICAP: virtual public dev::Exception {}; + +class ICAP +{ +public: + ICAP() = default; + ICAP(Address const& _a): m_direct(_a) {} + ICAP(std::string const& _target): m_client(_target), m_asset("ETH") {} + ICAP(std::string const& _client, std::string const& _inst): m_client(_client), m_institution(_inst), m_asset("XET") {} + ICAP(std::string const& _c, std::string const& _i, std::string const& _a): m_client(_c), m_institution(_i), m_asset(_a) {} + + enum Type + { + Invalid, + Direct, + Indirect + }; + + static std::string iban(std::string _c, std::string _d); + static std::pair fromIBAN(std::string _iban); + + static ICAP decoded(std::string const& _encoded); + std::string encoded() const; + Type type() const { return m_type; } + + Address const& direct() const { return m_direct; } + Address lookup(std::function const& _call, Address const& _reg) const; + Address address(std::function const& _call, Address const& _reg) const { return m_type == Direct ? direct() : lookup(_call, _reg); } + +private: + Type m_type = Invalid; + Address m_direct; + std::string m_client; + std::string m_institution; + std::string m_asset; +}; + + +} +} From f375b51f939a75be7f2065a66df67713d4a962d0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 24 Apr 2015 18:53:10 +0200 Subject: [PATCH 16/17] Docs. --- libethcore/ICAP.h | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/libethcore/ICAP.h b/libethcore/ICAP.h index 60003a1f9..7d5ce1f51 100644 --- a/libethcore/ICAP.h +++ b/libethcore/ICAP.h @@ -37,15 +37,23 @@ namespace eth struct InvalidICAP: virtual public dev::Exception {}; +static const std::string EmptyString; + class ICAP { public: + /// Construct null ICAP object. ICAP() = default; - ICAP(Address const& _a): m_direct(_a) {} + /// Construct a direct ICAP object for given target address. Must have a zero first byte. + ICAP(Address const& _target): m_direct(_target) {} + /// Construct an indirect ICAP object for given target name. ICAP(std::string const& _target): m_client(_target), m_asset("ETH") {} + /// Construct an indirect ICAP object for given client and institution names. ICAP(std::string const& _client, std::string const& _inst): m_client(_client), m_institution(_inst), m_asset("XET") {} + /// Construct an indirect ICAP object for given client, institution and asset names. You generally don't want to use this. ICAP(std::string const& _c, std::string const& _i, std::string const& _a): m_client(_c), m_institution(_i), m_asset(_a) {} + /// Type of ICAP address. enum Type { Invalid, @@ -53,16 +61,33 @@ public: Indirect }; + /// @returns IBAN encoding of client and data. static std::string iban(std::string _c, std::string _d); + /// @returns Client and data from given IBAN address. static std::pair fromIBAN(std::string _iban); + /// @returns the ICAP object for the ICAP address given. static ICAP decoded(std::string const& _encoded); + + /// @returns the encoded ICAP address. std::string encoded() const; + /// @returns type of ICAP. Type type() const { return m_type; } + /// @returns target address. Only valid when type() == Direct. + Address const& direct() const { return m_type == Direct ? m_direct : Address(); } + /// @returns asset. Only valid when type() == Indirect. + std::string const& asset() const { return m_type == Indirect ? m_asset : EmptyString; } + /// @returns target name. Only valid when type() == Indirect and asset() == "ETH". + std::string const& target() const { return m_type == Indirect && m_asset == "ETH" ? m_client : EmptyString; } + /// @returns institution name. Only valid when type() == Indirect and asset() == "XET". + std::string const& institution() const { return m_type == Indirect && m_asset == "XET" ? m_institution : EmptyString; } + /// @returns client name. Only valid when type() == Indirect and asset() == "XET". + std::string const& client() const { return m_type == Indirect && m_asset == "XET" ? m_client : EmptyString; } + /// @returns target address. Always valid, but requires the Registry address and a function to make calls. + Address address(std::function const& _call, Address const& _reg) const { return m_type == Direct ? direct() : m_type == Indirect ? lookup(_call, _reg) : Address(); } - Address const& direct() const { return m_direct; } + /// @returns target address. Looks up through the given Registry and call function. Only valid when type() == Indirect. Address lookup(std::function const& _call, Address const& _reg) const; - Address address(std::function const& _call, Address const& _reg) const { return m_type == Direct ? direct() : lookup(_call, _reg); } private: Type m_type = Invalid; From 0f64ad60eed91060262f81cb0db574f806afe212 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 24 Apr 2015 18:56:28 +0200 Subject: [PATCH 17/17] ICAP Cleanups. --- libethcore/ICAP.cpp | 6 ++---- libethcore/ICAP.h | 6 +++++- libethereum/Transaction.cpp | 2 +- libethereum/Transaction.h | 2 -- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libethcore/ICAP.cpp b/libethcore/ICAP.cpp index 107d79db1..d4c4f9c46 100644 --- a/libethcore/ICAP.cpp +++ b/libethcore/ICAP.cpp @@ -141,15 +141,13 @@ Address ICAP::lookup(std::function const& _call, Address if (m_asset == "XET") { // TODO -// _call(_reg, ); - return Address(); + throw InterfaceNotSupported("ICAP::lookup(), XET asset"); } else if (m_asset == "ETH") { // TODO - // return resolve(m_institution + "/" + m_client).primary(); - return Address(); + throw InterfaceNotSupported("ICAP::lookup(), ETH asset"); } else throw InterfaceNotSupported("ICAP::lookup(), non XET asset"); diff --git a/libethcore/ICAP.h b/libethcore/ICAP.h index 7d5ce1f51..fd5db2f0a 100644 --- a/libethcore/ICAP.h +++ b/libethcore/ICAP.h @@ -39,6 +39,10 @@ struct InvalidICAP: virtual public dev::Exception {}; static const std::string EmptyString; +/** + * @brief Encapsulation of an ICAP address. + * Can be encoded, decoded, looked-up and inspected. + */ class ICAP { public: @@ -74,7 +78,7 @@ public: /// @returns type of ICAP. Type type() const { return m_type; } /// @returns target address. Only valid when type() == Direct. - Address const& direct() const { return m_type == Direct ? m_direct : Address(); } + Address const& direct() const { return m_type == Direct ? m_direct : ZeroAddress; } /// @returns asset. Only valid when type() == Indirect. std::string const& asset() const { return m_type == Indirect ? m_asset : EmptyString; } /// @returns target name. Only valid when type() == Indirect and asset() == "ETH". diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 411c8bd99..975af53a5 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -105,7 +105,7 @@ Address const& Transaction::safeSender() const noexcept catch (...) { cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information(); - return NullAddress; + return ZeroAddress; } } diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index faf5ea07a..0ce536b78 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -97,8 +97,6 @@ struct ExecutionResult std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er); -static const Address NullAddress; - /// Encodes a transaction, ready to be exported to or freshly imported from RLP. class Transaction {