From 40cf37dbb8ba6b5a4162f0c61e283a01cb0871df Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 8 May 2015 10:31:10 +0200 Subject: [PATCH 01/35] - basic transaction creation. --- mix/ClientModel.cpp | 36 +++++++++++++++------ mix/ClientModel.h | 11 ++++--- mix/qml/QAddressView.qml | 57 +++++++++++++++++++++++++++++++++ mix/qml/StateListModel.qml | 8 +++-- mix/qml/StructView.qml | 29 ++--------------- mix/qml/TransactionDialog.qml | 55 +++++++++++++++++++++++++++++++ mix/qml/js/TransactionHelper.js | 3 +- 7 files changed, 156 insertions(+), 43 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 717757c70..7f4817c91 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -250,7 +250,10 @@ void ClientModel::setupState(QVariantMap _state) u256 value = (qvariant_cast(transaction.value("value")))->toU256Wei(); u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); QString sender = transaction.value("sender").toString(); - bool isStdContract = (transaction.value("stdContract").toBool()); + bool isStdContract = transaction.value("stdContract").toBool(); + bool isContractCall; + if (!transaction.value("isContractCall").isNull()) + isContractCall = transaction.value("isContractCall").toBool(); if (isStdContract) { if (contractId.isEmpty()) //TODO: This is to support old project files, remove later @@ -266,7 +269,7 @@ void ClientModel::setupState(QVariantMap _state) { if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later contractId = m_codeModel->contracts().keys()[0]; - TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString())); + TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCall); transactionSettings.parameterValues = transaction.value("parameters").toMap(); if (contractId == functionId || functionId == "Constructor") @@ -301,6 +304,13 @@ void ClientModel::executeSequence(vector const& _sequence, onStateReset(); for (TransactionSettings const& transaction: _sequence) { + if (!transaction.isContractCall) + { + QString address = resolveToken(transaction.contractId, deployedContracts); + callAddress(Address(address.toStdString()), bytes(), transaction); + onNewTransaction(); + continue; + } ContractCallDataEncoder encoder; if (!transaction.stdContractUrl.isEmpty()) { @@ -342,11 +352,8 @@ void ClientModel::executeSequence(vector const& _sequence, { QSolidityType const* type = p->type(); QVariant value = transaction.parameterValues.value(p->name()); - if (type->type().type == SolidityType::Type::Address && value.toString().startsWith("<") && value.toString().endsWith(">")) - { - QStringList nb = value.toString().remove("<").remove(">").split(" - "); - value = QVariant(QString::fromStdString("0x" + toHex(deployedContracts.at(nb.back().toInt()).ref()))); - } + if (type->type().type == SolidityType::Type::Address) + value = QVariant(resolveToken(value.toString(), deployedContracts)); encoder.encode(value, type->type()); } @@ -376,7 +383,7 @@ void ClientModel::executeSequence(vector const& _sequence, emit runStateChanged(); return; } - callContract(contractAddressIter->second, encoder.encodedData(), transaction); + callAddress(contractAddressIter->second, encoder.encodedData(), transaction); } } onNewTransaction(); @@ -399,6 +406,17 @@ void ClientModel::executeSequence(vector const& _sequence, }); } +QString ClientModel::resolveToken(QString const& _value, vector
const& _contracts) +{ + QString ret = _value; + if (_value.startsWith("<") && _value.endsWith(">")) + { + QStringList nb = ret.remove("<").remove(">").split(" - "); + ret = QString::fromStdString("0x" + toHex(_contracts.at(nb.back().toInt()).ref())); + } + return ret; +} + void ClientModel::showDebugger() { ExecutionResult last = m_client->lastExecution(); @@ -603,7 +621,7 @@ Address ClientModel::deployContract(bytes const& _code, TransactionSettings cons return newAddress; } -void ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) +void ClientModel::callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) { m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice, _tr.gasAuto); } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 9b0529ae6..db11406d6 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -53,10 +53,10 @@ struct SolidityType; struct TransactionSettings { TransactionSettings() {} - TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender): - contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender) {} + TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender, int _isContractCall): + contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCall(_isContractCall) {} TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl): - contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl) {} + contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCall(true) {} /// Contract name QString contractId; @@ -76,6 +76,8 @@ struct TransactionSettings QString stdContractUrl; /// Sender Secret sender; + /// Is a call to a contract + bool isContractCall; }; @@ -220,12 +222,13 @@ private: QVariantMap gasCosts() const; void executeSequence(std::vector const& _sequence, std::map const& _accounts, Secret const& _miner); dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings()); - void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); + void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); void onNewTransaction(); void onStateReset(); void showDebuggerForTransaction(ExecutionResult const& _t); QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); QVariant formatStorageValue(SolidityType const& _type, std::map const& _storage, unsigned _offset, dev::u256 const& _slot); + QString resolveToken(QString const& _value, std::vector
const& _contracts); std::atomic m_running; std::atomic m_mining; diff --git a/mix/qml/QAddressView.qml b/mix/qml/QAddressView.qml index 204e92508..19d98b166 100644 --- a/mix/qml/QAddressView.qml +++ b/mix/qml/QAddressView.qml @@ -8,6 +8,10 @@ Item property alias accountRef: ctrModel property string subType property bool readOnly + property alias currentIndex: trCombobox.currentIndex + property alias currentText: textinput.text + property variant accounts + signal indexChanged() id: editRoot height: 20 width: 320 @@ -17,6 +21,46 @@ Item id: boldFont } + function currentValue() { + return currentText; + } + + function currentType() + { + return accountRef.get(trCombobox.currentIndex).type; + } + + function load() + { + accountRef.clear(); + accountRef.append({"itemid": " - "}); + + if (subType === "contract" || 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")*/) + { + accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" }); + trCr++; + } + } + } + if (subType === "address") + { + for (k = 0; k < accounts.length; k++) + { + if (accounts[k].address === undefined) + accounts[k].address = clientModel.address(accounts[k].secret); + accountRef.append({ "itemid": accounts[k].name, "value": "0x" + accounts[k].address, "type": "address" }); + } + } + } + function init() { trCombobox.visible = !readOnly @@ -35,6 +79,18 @@ Item } } + function select(address) + { + for (var k = 0; k < accountRef.count; k++) + { + if (accountRef.get(k).value === address) + { + trCombobox.currentIndex = k; + break; + } + } + } + Rectangle { anchors.fill: parent radius: 4 @@ -96,6 +152,7 @@ Item { textinput.text = ""; } + indexChanged(); } } } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 889a2d940..d4aa678a6 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -46,6 +46,7 @@ Item { t.sender = defaultAccount; //support for old project var r = { + type: t.type, contractId: t.contractId, functionId: t.functionId, url: t.url, @@ -55,7 +56,8 @@ Item { gasAuto: t.gasAuto, stdContract: t.stdContract ? true : false, parameters: {}, - sender: t.sender + sender: t.sender, + isContractCall: t.isContractCall }; for (var key in t.parameters) r.parameters[key] = t.parameters[key]; @@ -100,6 +102,7 @@ Item { function toPlainTransactionItem(t) { var r = { + type: t.type, contractId: t.contractId, functionId: t.functionId, url: t.url, @@ -109,7 +112,8 @@ Item { gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit }, stdContract: t.stdContract, sender: t.sender, - parameters: {} + parameters: {}, + isContractCall: t.isContractCall }; for (var key in t.parameters) r.parameters[key] = t.parameters[key]; diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 4df9ace67..4feab2166 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -75,38 +75,13 @@ Column item.readOnly = context === "variable"; if (ptype.category === QSolidityType.Address) { + item.accounts = accounts item.value = getValue(); if (context === "parameter") { 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") - { - 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.load(); } item.init(); } diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 66a98d19e..f56093336 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -82,6 +82,14 @@ Dialog { } initTypeLoader(); + trType.checked = item.isContractCall + trType.init(); + + recipients.accounts = senderComboBox.model; + recipients.subType = "address"; + recipients.load(); + recipients.init(); + recipients.select(contractId); visible = true; valueField.focus = true; } @@ -193,6 +201,13 @@ Dialog { item.functionId = transactionDialog.functionId; } + item.isContractCall = trType.checked; + if (!item.isContractCall) + { + item.functionId = recipients.currentText; + item.contractId = recipients.currentText; + } + item.sender = senderComboBox.model[senderComboBox.currentIndex].secret; item.parameters = paramValues; return item; @@ -238,6 +253,46 @@ Dialog { } } + RowLayout + { + id: rowIsContract + Layout.fillWidth: true + height: 150 + CheckBox { + id: trType + onCheckedChanged: + { + init(); + } + + function init() + { + rowFunction.visible = checked; + rowContract.visible = checked; + rowRecipient.visible = !checked; + } + + text: qsTr("is contract call") + checked: true + } + } + + RowLayout + { + id: rowRecipient + Layout.fillWidth: true + height: 150 + DefaultLabel { + Layout.preferredWidth: 75 + text: qsTr("Recipient") + } + + QAddressView + { + id: recipients + } + } + RowLayout { id: rowContract diff --git a/mix/qml/js/TransactionHelper.js b/mix/qml/js/TransactionHelper.js index be057917c..57b03757a 100644 --- a/mix/qml/js/TransactionHelper.js +++ b/mix/qml/js/TransactionHelper.js @@ -9,7 +9,8 @@ function defaultTransaction() gasAuto: true, gasPrice: createEther("100000", QEther.Wei), parameters: {}, - stdContract: false + stdContract: false, + isContractCall: true }; } From 446abc57e55b3d6ba70875a06fd09b7964ecc7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 8 May 2015 11:00:17 +0200 Subject: [PATCH 02/35] testeth: fix --singletest option --- test/TestHelper.cpp | 89 +++++++++++++++----------------- test/TestHelper.h | 3 +- test/libethereum/blockchain.cpp | 2 +- test/libethereum/state.cpp | 2 +- test/libethereum/transaction.cpp | 2 +- test/libevm/vm.cpp | 2 +- 6 files changed, 47 insertions(+), 53 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 144a1a286..e0aad310f 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -549,58 +549,50 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e } } -void userDefinedTest(string testTypeFlag, std::function doTests) +void userDefinedTest(std::function doTests) { - Options::get(); // parse command line options, e.g. to enable JIT + if (!Options::get().singleTest) + { + cnote << "Missing user test specification\nUsage: testeth --singletest \n"; + return; + } - for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) + auto& filename = Options::get().singleTestFile; + auto& testname = Options::get().singleTestName; + int currentVerbosity = g_logVerbosity; + g_logVerbosity = 12; + try { - string arg = boost::unit_test::framework::master_test_suite().argv[i]; - if (arg == testTypeFlag) - { - if (boost::unit_test::framework::master_test_suite().argc <= i + 2) - { - cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " \n"; - return; - } - string filename = boost::unit_test::framework::master_test_suite().argv[i + 1]; - string testname = boost::unit_test::framework::master_test_suite().argv[i + 2]; - int currentVerbosity = g_logVerbosity; - g_logVerbosity = 12; - try - { - cnote << "Testing user defined test: " << filename; - json_spirit::mValue v; - string s = asString(contents(filename)); - BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. "); - json_spirit::read_string(s, v); - json_spirit::mObject oSingleTest; - - json_spirit::mObject::const_iterator pos = v.get_obj().find(testname); - if (pos == v.get_obj().end()) - { - cnote << "Could not find test: " << testname << " in " << filename << "\n"; - return; - } - else - oSingleTest[pos->first] = pos->second; + cnote << "Testing user defined test: " << filename; + json_spirit::mValue v; + string s = asString(contents(filename)); + BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. "); + json_spirit::read_string(s, v); + json_spirit::mObject oSingleTest; - json_spirit::mValue v_singleTest(oSingleTest); - doTests(v_singleTest, false); - } - catch (Exception const& _e) - { - BOOST_ERROR("Failed Test with Exception: " << diagnostic_information(_e)); - g_logVerbosity = currentVerbosity; - } - catch (std::exception const& _e) - { - BOOST_ERROR("Failed Test with Exception: " << _e.what()); - g_logVerbosity = currentVerbosity; - } - g_logVerbosity = currentVerbosity; + json_spirit::mObject::const_iterator pos = v.get_obj().find(testname); + if (pos == v.get_obj().end()) + { + cnote << "Could not find test: " << testname << " in " << filename << "\n"; + return; } + else + oSingleTest[pos->first] = pos->second; + + json_spirit::mValue v_singleTest(oSingleTest); + doTests(v_singleTest, false); + } + catch (Exception const& _e) + { + BOOST_ERROR("Failed Test with Exception: " << diagnostic_information(_e)); + g_logVerbosity = currentVerbosity; + } + catch (std::exception const& _e) + { + BOOST_ERROR("Failed Test with Exception: " << _e.what()); + g_logVerbosity = currentVerbosity; } + g_logVerbosity = currentVerbosity; } void executeTests(const string& _name, const string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function doTests) @@ -740,10 +732,11 @@ Options::Options() inputLimits = true; bigData = true; } - else if (arg == "--singletest" && i + 1 < argc) + else if (arg == "--singletest" && i + 2 < argc) { singleTest = true; - singleTestName = argv[i + 1]; + singleTestFile = argv[i + 1]; + singleTestName = argv[i + 2]; } } } diff --git a/test/TestHelper.h b/test/TestHelper.h index 02f509e4c..631bc4df2 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -157,7 +157,7 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates); void executeTests(const std::string& _name, const std::string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function doTests); -void userDefinedTest(std::string testTypeFlag, std::function doTests); +void userDefinedTest(std::function doTests); RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj); eth::LastHashes lastHashes(u256 _currentBlockNumber); json_spirit::mObject fillJsonWithState(eth::State _state); @@ -189,6 +189,7 @@ public: /// Test selection /// @{ bool singleTest = false; + std::string singleTestFile; std::string singleTestName; bool performance = false; bool quadratic = false; diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index 954e65c8a..9eddb4657 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -711,7 +711,7 @@ BOOST_AUTO_TEST_CASE(bcWalletTest) BOOST_AUTO_TEST_CASE(userDefinedFile) { - dev::test::userDefinedTest("--singletest", dev::test::doBlockchainTests); + dev::test::userDefinedTest(dev::test::doBlockchainTests); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libethereum/state.cpp b/test/libethereum/state.cpp index 93f7498b8..b7b45f93a 100644 --- a/test/libethereum/state.cpp +++ b/test/libethereum/state.cpp @@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(stRandom) BOOST_AUTO_TEST_CASE(userDefinedFileState) { - dev::test::userDefinedTest("--singletest", dev::test::doStateTests); + dev::test::userDefinedTest(dev::test::doStateTests); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libethereum/transaction.cpp b/test/libethereum/transaction.cpp index 058d03322..017e51ded 100644 --- a/test/libethereum/transaction.cpp +++ b/test/libethereum/transaction.cpp @@ -195,7 +195,7 @@ BOOST_AUTO_TEST_CASE(ttCreateTest) BOOST_AUTO_TEST_CASE(userDefinedFile) { - dev::test::userDefinedTest("--singletest", dev::test::doTransactionTests); + dev::test::userDefinedTest(dev::test::doTransactionTests); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libevm/vm.cpp b/test/libevm/vm.cpp index 8b5a7c5d3..2d67d7670 100644 --- a/test/libevm/vm.cpp +++ b/test/libevm/vm.cpp @@ -548,7 +548,7 @@ BOOST_AUTO_TEST_CASE(vmRandom) BOOST_AUTO_TEST_CASE(userDefinedFile) { - dev::test::userDefinedTest("--singletest", dev::test::doVMTests); + dev::test::userDefinedTest(dev::test::doVMTests); } BOOST_AUTO_TEST_SUITE_END() From 5fe68b07c1236c64f58fa7b42bcfb6cc6506e957 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 8 May 2015 14:14:27 +0200 Subject: [PATCH 03/35] small changes --- mix/ClientModel.cpp | 2 +- mix/qml/StateListModel.qml | 2 ++ mix/qml/TransactionDialog.qml | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index d802d9a7b..95d4d497b 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -418,7 +418,7 @@ QString ClientModel::resolveToken(QString const& _value, vector
const& if (_value.startsWith("<") && _value.endsWith(">")) { QStringList nb = ret.remove("<").remove(">").split(" - "); - ret = QString::fromStdString("0x" + toHex(_contracts.at(nb.back().toInt()).ref())); + ret = QString::fromStdString("0x" + dev::toHex(_contracts.at(nb.back().toInt()).ref())); } return ret; } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index d4aa678a6..3eac14a11 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -59,6 +59,8 @@ Item { sender: t.sender, isContractCall: t.isContractCall }; + if (r.isContractCall === undefined) + r.isContractCall = true; //support for old project for (var key in t.parameters) r.parameters[key] = t.parameters[key]; diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index f56093336..84abb854b 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -270,6 +270,8 @@ Dialog { rowFunction.visible = checked; rowContract.visible = checked; rowRecipient.visible = !checked; + paramLabel.visible = checked; + paramScroll.visible = checked; } text: qsTr("is contract call") From 7802a9e00cca9070a7d20f8157611cfed26c2ecb Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 8 May 2015 17:27:15 +0200 Subject: [PATCH 04/35] bug fix --- mix/ClientModel.cpp | 36 ++++++++----- mix/ClientModel.h | 11 ++-- mix/qml/QAddressView.qml | 5 ++ mix/qml/StateDialog.qml | 4 +- mix/qml/StateListModel.qml | 14 +++-- mix/qml/TransactionDialog.qml | 92 +++++++++++++++++++++++---------- mix/qml/js/TransactionHelper.js | 3 +- 7 files changed, 112 insertions(+), 53 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 95d4d497b..a58366046 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -256,9 +256,9 @@ void ClientModel::setupState(QVariantMap _state) u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); QString sender = transaction.value("sender").toString(); bool isStdContract = transaction.value("stdContract").toBool(); - bool isContractCall; - if (!transaction.value("isContractCall").isNull()) - isContractCall = transaction.value("isContractCall").toBool(); + bool isContractCreation; + if (!transaction.value("isContractCreation").isNull()) + isContractCreation = transaction.value("isContractCreation").toBool(); if (isStdContract) { if (contractId.isEmpty()) //TODO: This is to support old project files, remove later @@ -274,7 +274,7 @@ void ClientModel::setupState(QVariantMap _state) { if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later contractId = m_codeModel->contracts().keys()[0]; - TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCall); + TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation); transactionSettings.parameterValues = transaction.value("parameters").toMap(); if (contractId == functionId || functionId == "Constructor") @@ -310,9 +310,10 @@ void ClientModel::executeSequence(vector const& _sequence, m_gasCosts.clear(); for (TransactionSettings const& transaction: _sequence) { - if (!transaction.isContractCall) + QString contractName = resolveContractName(transaction.contractId); + QString address = resolveToken(transaction.contractId, deployedContracts); + if (transaction.functionId == "(transfert)") { - QString address = resolveToken(transaction.contractId, deployedContracts); callAddress(Address(address.toStdString()), bytes(), transaction); onNewTransaction(); continue; @@ -332,7 +333,7 @@ void ClientModel::executeSequence(vector const& _sequence, else { //encode data - CompiledContract const& compilerRes = m_codeModel->contract(transaction.contractId); + CompiledContract const& compilerRes = m_codeModel->contract(contractName); QFunctionDefinition const* f = nullptr; bytes contractCode = compilerRes.bytes(); shared_ptr contractDef = compilerRes.sharedContract(); @@ -363,7 +364,7 @@ void ClientModel::executeSequence(vector const& _sequence, encoder.encode(value, type->type()); } - if (transaction.functionId.isEmpty() || transaction.functionId == transaction.contractId) + if (transaction.functionId.isEmpty() || transaction.functionId == contractName) { bytes param = encoder.encodedData(); contractCode.insert(contractCode.end(), param.begin(), param.end()); @@ -372,8 +373,9 @@ void ClientModel::executeSequence(vector const& _sequence, auto contractAddressIter = m_contractAddresses.find(transaction.contractId); if (contractAddressIter == m_contractAddresses.end() || newAddress != contractAddressIter->second) { - m_contractAddresses[transaction.contractId] = newAddress; - m_contractNames[newAddress] = transaction.contractId; + QString contractToken = "<" + transaction.contractId + " - " + QString::number(deployedContracts.size() - 1) + ">"; + m_contractAddresses[contractToken] = newAddress; + m_contractNames[newAddress] = contractToken; contractAddressesChanged(); } gasCostsChanged(); @@ -388,7 +390,7 @@ void ClientModel::executeSequence(vector const& _sequence, emit runStateChanged(); return; } - callAddress(contractAddressIter->second, encoder.encodedData(), transaction); + callAddress(Address(address.toStdString()), encoder.encodedData(), transaction); } m_gasCosts.append(m_client->lastExecution().gasUsed); } @@ -423,6 +425,14 @@ QString ClientModel::resolveToken(QString const& _value, vector
const& return ret; } +QString ClientModel::resolveContractName(QString const& _value) +{ + QString ret = _value; + if (_value.startsWith("<") && _value.endsWith(">")) + ret = ret.remove("<").remove(">").split(" - ").first(); + return ret; +} + void ClientModel::showDebugger() { ExecutionResult last = m_client->lastExecution(); @@ -446,7 +456,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) //try to resolve contract for source level debugging auto nameIter = m_contractNames.find(code.address); CompiledContract const* compilerRes = nullptr; - if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(nameIter->second))) //returned object is guaranteed to live till the end of event handler in main thread + if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(resolveContractName(nameIter->second)))) //returned object is guaranteed to live till the end of event handler in main thread { eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes->assemblyItems() : compilerRes->constructorAssemblyItems(); codes.back()->setDocument(compilerRes->documentId()); @@ -707,7 +717,7 @@ void ClientModel::onNewTransaction() auto contractAddressIter = m_contractNames.find(contractAddress); if (contractAddressIter != m_contractNames.end()) { - CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); + CompiledContract const& compilerRes = m_codeModel->contract(resolveContractName(contractAddressIter->second)); const QContractDefinition* def = compilerRes.contract(); contract = def->name(); if (abi) diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 46cabe450..15cdc7086 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -53,10 +53,10 @@ struct SolidityType; struct TransactionSettings { TransactionSettings() {} - TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender, int _isContractCall): - contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCall(_isContractCall) {} + TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender, int _isContractCreation): + contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCreation(_isContractCreation) {} TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl): - contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCall(true) {} + contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true) {} /// Contract name QString contractId; @@ -76,8 +76,8 @@ struct TransactionSettings QString stdContractUrl; /// Sender Secret sender; - /// Is a call to a contract - bool isContractCall; + /// Tr deploys a contract + bool isContractCreation; }; @@ -231,6 +231,7 @@ private: QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); QVariant formatStorageValue(SolidityType const& _type, std::map const& _storage, unsigned _offset, dev::u256 const& _slot); QString resolveToken(QString const& _value, std::vector
const& _contracts); + QString resolveContractName(QString const& _value); std::atomic m_running; std::atomic m_mining; diff --git a/mix/qml/QAddressView.qml b/mix/qml/QAddressView.qml index 19d98b166..c880f0904 100644 --- a/mix/qml/QAddressView.qml +++ b/mix/qml/QAddressView.qml @@ -30,6 +30,11 @@ Item return accountRef.get(trCombobox.currentIndex).type; } + function current() + { + return accountRef.get(trCombobox.currentIndex); + } + function load() { accountRef.clear(); diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 13d01d474..f8da6dabd 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -436,7 +436,7 @@ Dialog { model: transactionsModel headerVisible: false TableViewColumn { - role: "name" + role: "label" title: qsTr("Name") width: 150 delegate: Item { @@ -476,7 +476,7 @@ Dialog { text: { if (styleData.row >= 0) return transactionsModel.get( - styleData.row).functionId + styleData.row).label else return "" } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 3eac14a11..a6069d620 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -57,10 +57,15 @@ Item { stdContract: t.stdContract ? true : false, parameters: {}, sender: t.sender, - isContractCall: t.isContractCall + isContractCreation: t.isContractCreation, + label: t.label }; - if (r.isContractCall === undefined) - r.isContractCall = true; //support for old project + + if (!r.label) + r.label = r.contractId + " " + r.functionId; + + if (r.isContractCreation === undefined) + r.isContractCreation = true; //support for old project for (var key in t.parameters) r.parameters[key] = t.parameters[key]; @@ -115,7 +120,8 @@ Item { stdContract: t.stdContract, sender: t.sender, parameters: {}, - isContractCall: t.isContractCall + isContractCreation: t.isContractCreation, + label: t.label }; for (var key in t.parameters) r.parameters[key] = t.parameters[key]; diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 84abb854b..d26fbaf5a 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -66,34 +66,46 @@ Dialog { contractIndex = 0; //@todo suggest unused contract contractComboBox.currentIndex = contractIndex; - loadFunctions(contractComboBox.currentValue()); + recipients.accounts = senderComboBox.model; + recipients.subType = "address"; + recipients.load(); + recipients.init(); + recipients.select(contractId); + + if (item.isContractCreation) + loadFunctions(contractComboBox.currentValue()); + else + loadFunctions(contractFromToken(recipients.currentValue())) selectFunction(functionId); + trType.checked = item.isContractCreation + trType.init(); + paramsModel = []; - if (functionId !== contractComboBox.currentValue()) + if (item.isContractCreation) + loadCtorParameters(); + else loadParameters(); - else { - var contract = codeModel.contracts[contractId]; - if (contract) { - var params = contract.contract.constructor.parameters; - for (var p = 0; p < params.length; p++) - loadParameter(params[p]); - } - } - initTypeLoader(); - trType.checked = item.isContractCall - trType.init(); - recipients.accounts = senderComboBox.model; - recipients.subType = "address"; - recipients.load(); - recipients.init(); - recipients.select(contractId); + visible = true; valueField.focus = true; } + function loadCtorParameters(contractId) + { + paramsModel = []; + console.log(contractId); + var contract = codeModel.contracts[contractId]; + if (contract) { + var params = contract.contract.constructor.parameters; + for (var p = 0; p < params.length; p++) + loadParameter(params[p]); + } + initTypeLoader(); + } + function loadFunctions(contractId) { functionsModel.clear(); @@ -104,9 +116,7 @@ Dialog { functionsModel.append({ text: functions[f].name }); } } - //append constructor - functionsModel.append({ text: contractId }); - + functionsModel.append({ text: "(transfert)" }); } function selectContract(contractName) @@ -144,7 +154,7 @@ Dialog { function loadParameters() { paramsModel = [] if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) { - var contract = codeModel.contracts[contractComboBox.currentValue()]; + var contract = codeModel.contracts[contractFromToken(recipients.currentValue())]; if (contract) { var func = contract.contract.functions[functionComboBox.currentIndex]; if (func) { @@ -201,17 +211,32 @@ Dialog { item.functionId = transactionDialog.functionId; } - item.isContractCall = trType.checked; - if (!item.isContractCall) + item.isContractCreation = trType.checked; + if (!item.isContractCreation) { - item.functionId = recipients.currentText; item.contractId = recipients.currentText; + item.label = item.contractId + " " + item.functionId; + if (recipients.current().type === "address") + item.functionId = "(transfert)"; + } + else + { + item.functionId = item.contractId; + item.label = qsTr("Deploy") + " " + item.contractId; } item.sender = senderComboBox.model[senderComboBox.currentIndex].secret; item.parameters = paramValues; return item; } + + function contractFromToken(token) + { + if (token.indexOf('<') === 0) + return token.replace("<", "").replace(">", "").split(" - ")[0]; + return token; + } + contentItem: Rectangle { color: transactionDialogStyle.generic.backgroundColor ColumnLayout { @@ -267,14 +292,17 @@ Dialog { function init() { - rowFunction.visible = checked; + rowFunction.visible = !checked; rowContract.visible = checked; rowRecipient.visible = !checked; paramLabel.visible = checked; paramScroll.visible = checked; + functionComboBox.enabled = !checked; + if (checked) + loadCtorParameters(contractComboBox.currentValue()); } - text: qsTr("is contract call") + text: qsTr("is contract creation") checked: true } } @@ -292,6 +320,14 @@ Dialog { QAddressView { id: recipients + onIndexChanged: + { + rowFunction.visible = current().type === "contract"; + paramLabel.visible = current().type === "contract"; + paramScroll.visible = current().type === "contract"; + if (!rowIsContract.checked) + loadFunctions(contractFromToken(recipients.currentValue())) + } } } @@ -317,7 +353,7 @@ Dialog { id: contractsModel } onCurrentIndexChanged: { - loadFunctions(currentValue()); + loadCtorParameters(currentValue()); } } } diff --git a/mix/qml/js/TransactionHelper.js b/mix/qml/js/TransactionHelper.js index 01fe65b5d..33072fdba 100644 --- a/mix/qml/js/TransactionHelper.js +++ b/mix/qml/js/TransactionHelper.js @@ -10,7 +10,8 @@ function defaultTransaction() gasPrice: createEther("100000", QEther.Wei), parameters: {}, stdContract: false, - isContractCall: true + isContractCreation: true, + label: "" }; } From 0e7d6d77e5be791b2479440b9b6b427a6d978f28 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 8 May 2015 17:36:29 +0200 Subject: [PATCH 05/35] small changes --- mix/qml/StateListModel.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index a6069d620..a7b83aec9 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -62,10 +62,8 @@ Item { }; if (!r.label) - r.label = r.contractId + " " + r.functionId; + r.label = r.contractId + " - " + r.functionId; - if (r.isContractCreation === undefined) - r.isContractCreation = true; //support for old project for (var key in t.parameters) r.parameters[key] = t.parameters[key]; From 3fc50e3615348b86e97725afe63a61ebb712da41 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 8 May 2015 18:12:49 +0200 Subject: [PATCH 06/35] bux fix --- mix/ClientModel.cpp | 12 ++++++++++-- mix/ClientModel.h | 1 + mix/qml/StateListModel.qml | 3 +++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index a58366046..8067f62c4 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -373,7 +373,7 @@ void ClientModel::executeSequence(vector const& _sequence, auto contractAddressIter = m_contractAddresses.find(transaction.contractId); if (contractAddressIter == m_contractAddresses.end() || newAddress != contractAddressIter->second) { - QString contractToken = "<" + transaction.contractId + " - " + QString::number(deployedContracts.size() - 1) + ">"; + QString contractToken = retrieveToken(transaction.contractId, deployedContracts); m_contractAddresses[contractToken] = newAddress; m_contractNames[newAddress] = contractToken; contractAddressesChanged(); @@ -382,7 +382,7 @@ void ClientModel::executeSequence(vector const& _sequence, } else { - auto contractAddressIter = m_contractAddresses.find(transaction.contractId); + auto contractAddressIter = m_contractAddresses.find(retrieveToken(transaction.contractId, deployedContracts)); if (contractAddressIter == m_contractAddresses.end()) { emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); @@ -425,6 +425,14 @@ QString ClientModel::resolveToken(QString const& _value, vector
const& return ret; } +QString ClientModel::retrieveToken(QString const& _value, vector
const& _contracts) +{ + QString ret = _value; + if (!_value.startsWith("<") && !_value.endsWith(">")) + return "<" + _value + " - " + QString::number(_contracts.size() - 1) + ">"; + return ret; +} + QString ClientModel::resolveContractName(QString const& _value) { QString ret = _value; diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 15cdc7086..2ba37f593 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -232,6 +232,7 @@ private: QVariant formatStorageValue(SolidityType const& _type, std::map const& _storage, unsigned _offset, dev::u256 const& _slot); QString resolveToken(QString const& _value, std::vector
const& _contracts); QString resolveContractName(QString const& _value); + QString retrieveToken(QString const& _value, std::vector
const& _contracts); std::atomic m_running; std::atomic m_mining; diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index a7b83aec9..b4eed525d 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -64,6 +64,9 @@ Item { if (!r.label) r.label = r.contractId + " - " + r.functionId; + if (r.isContractCreation === undefined) + r.isContractCreation = r.functionId === r.contractId; + for (var key in t.parameters) r.parameters[key] = t.parameters[key]; From dc7df3cc0713561526bf547eb8dfaa8dda42d0da Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Sun, 10 May 2015 15:18:13 +0200 Subject: [PATCH 07/35] Test for Capability class Basic configuration for sending and receiving messgaes. --- test/libp2p/capability.cpp | 157 +++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 test/libp2p/capability.cpp diff --git a/test/libp2p/capability.cpp b/test/libp2p/capability.cpp new file mode 100644 index 000000000..2dcf9b4ba --- /dev/null +++ b/test/libp2p/capability.cpp @@ -0,0 +1,157 @@ +/* +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 capability.cpp +* @author Vladislav Gluhovsky +* @date May 2015 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::p2p; + +struct P2PFixture +{ + P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = true; } + ~P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = false; } +}; + +struct VerbosityHolder +{ + int m_oldLogVerbosity; + VerbosityHolder() : m_oldLogVerbosity(g_logVerbosity) { g_logVerbosity = 10; } + ~VerbosityHolder() { g_logVerbosity = m_oldLogVerbosity; } +}; + +class TestCapability : public Capability +{ + int m_cntReceivedMessages; + int m_testSum; + +public: + TestCapability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset) : Capability(_s, _h, _idOffset), m_cntReceivedMessages(0), m_testSum(0) {} + virtual ~TestCapability() {} + int countReceivedMessages() { return m_cntReceivedMessages; } + int testSum() { return m_testSum; } + static std::string name() { return "test"; } + static u256 version() { return 2; } + static unsigned messageCount() { return UserPacket + 1; } + + void sendTestMessage(int _i) + { + RLPStream s; + prep(s, UserPacket, 1); + s << _i; + sealAndSend(s); + } + +protected: + virtual bool interpret(unsigned _id, RLP const& _r) override + { + cnote << "Capability::interpret(): custom message received"; + BOOST_REQUIRE_EQUAL(_id, UserPacket); + ++m_cntReceivedMessages; + int i = _r[0].toInt(); + m_testSum += i; + return true; + } +}; + +class TestHostCapability : public HostCapability, public Worker +{ +public: + TestHostCapability() : Worker("test") {} + virtual ~TestHostCapability() {} + + void sendTestMessage(NodeId const& _id, int _x) + { + for (auto i: peerSessions()) + if (_id == i.second->id) + i.first->cap().get()->sendTestMessage(_x); + } + + std::pair retrieveTestData(NodeId const& _id) + { + int cnt = 0; + int checksum = 0; + for (auto i : peerSessions()) + if (_id == i.second->id) + { + cnt += i.first->cap().get()->countReceivedMessages(); + checksum += i.first->cap().get()->testSum(); + } + + return std::pair(cnt, checksum); + } +}; + +BOOST_FIXTURE_TEST_SUITE(p2pCapability, P2PFixture) + +BOOST_AUTO_TEST_CASE(capability) +{ + VerbosityHolder verbosityHolder; + cnote << "Testing Capability..."; + + const char* const localhost = "127.0.0.1"; + NetworkPreferences prefs1(localhost, 30301, false); + NetworkPreferences prefs2(localhost, 30302, false); + + Host host1("Test", prefs1); + auto thc1 = host1.registerCapability(new TestHostCapability()); + host1.start(); + + Host host2("Test", prefs2); + auto thc2 = host2.registerCapability(new TestHostCapability()); + host2.start(); + + int const step = 10; + + for (int i = 0; i < 3000; i += step) + if (!host1.isStarted() || !host2.isStarted()) + this_thread::sleep_for(chrono::milliseconds(step)); + + BOOST_REQUIRE(host1.isStarted() && host2.isStarted()); + host1.requirePeer(host2.id(), NodeIPEndpoint(bi::address::from_string(localhost), prefs2.listenPort, prefs2.listenPort)); + + for (int i = 0; i < 3000; i += step) + if (!host1.peerCount() || !host2.peerCount()) + this_thread::sleep_for(chrono::milliseconds(step)); + + BOOST_REQUIRE(host1.peerCount() > 0 && host2.peerCount() > 0); + + int const target = 7; + int checksum = 0; + for (int i = 0; i < target; checksum += i++) + thc2->sendTestMessage(host1.id(), i); + + this_thread::sleep_for(chrono::seconds(1)); + std::pair testData = thc1->retrieveTestData(host2.id()); + BOOST_REQUIRE_EQUAL(target, testData.first); + BOOST_REQUIRE_EQUAL(checksum, testData.second); +} + +BOOST_AUTO_TEST_SUITE_END() + + From 438d2b44edd22348a0233c49884a45239454f4bd Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 May 2015 09:56:17 +0200 Subject: [PATCH 08/35] small changes --- mix/ClientModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 8067f62c4..519b7ddf3 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -390,7 +390,7 @@ void ClientModel::executeSequence(vector const& _sequence, emit runStateChanged(); return; } - callAddress(Address(address.toStdString()), encoder.encodedData(), transaction); + callAddress(contractAddressIter->second, encoder.encodedData(), transaction); } m_gasCosts.append(m_client->lastExecution().gasUsed); } From 33d7e42866566d9fe225a4ab67eae05f7f52df5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 11 May 2015 11:38:11 +0200 Subject: [PATCH 09/35] CMake: set default RUNTIME_OUTPUT_DIRECTORY property to "bin" This commit changes output directory for runtime components (executables and DLLs) to "bin" directory. That allows running executables on Windows without need of install step. Closes ethereum/cpp-ethereum#1821 --- CMakeLists.txt | 1 + cmake/EthExecutableHelper.cmake | 33 +++++++++++++++++---------------- cmake/scripts/copydlls.cmake | 3 +-- libevmasm/CMakeLists.txt | 7 +------ 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ba214cce..4e7003692 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,6 +295,7 @@ message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}" message("------------------------------------------------------------------------") message("") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") set(CMAKE_THREAD_LIBS_INIT pthread) include(EthCompilerSettings) diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake index 1d1cb887b..d971a5f92 100644 --- a/cmake/EthExecutableHelper.cmake +++ b/cmake/EthExecutableHelper.cmake @@ -45,25 +45,26 @@ endmacro() macro(eth_copy_dlls EXECUTABLE DLLS) # dlls must be unsubstitud list variable (without ${}) in format - # optimized;path_to_dll.dll;debug;path_to_dlld.dll + # optimized;path_to_dll.dll;debug;path_to_dlld.dll list(GET ${DLLS} 1 DLL_RELEASE) list(GET ${DLLS} 3 DLL_DEBUG) + get_target_property(TARGET_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE} RUNTIME_OUTPUT_DIRECTORY) add_custom_command(TARGET ${EXECUTABLE} - POST_BUILD - COMMAND ${CMAKE_COMMAND} ARGS - -DDLL_RELEASE="${DLL_RELEASE}" - -DDLL_DEBUG="${DLL_DEBUG}" + POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS + -DDLL_RELEASE="${DLL_RELEASE}" + -DDLL_DEBUG="${DLL_DEBUG}" -DCONF="$" - -DDESTINATION="${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" + -DDESTINATION="${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}" -P "${ETH_SCRIPTS_DIR}/copydlls.cmake" ) endmacro() -# +# # this function requires the following variables to be specified: # ETH_DEPENDENCY_INSTALL_DIR # -# params: +# params: # QMLDIR # @@ -74,7 +75,7 @@ macro(eth_install_executable EXECUTABLE) set (one_value_args QMLDIR) set (multi_value_args DLLS) cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") - + if (ETH_INSTALL_EXECUTABLE_QMLDIR) if (APPLE) set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}") @@ -91,13 +92,14 @@ macro(eth_install_executable EXECUTABLE) WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents ) - + + get_target_property(TARGET_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE} RUNTIME_OUTPUT_DIRECTORY) # This tool and next will inspect linked libraries in order to determine which dependencies are required if (${CMAKE_CFG_INTDIR} STREQUAL ".") # TODO: This should only happen for GUI application - set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXECUTABLE}.app") + set(APP_BUNDLE_PATH "${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${EXECUTABLE}.app") else () - set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app") + set(APP_BUNDLE_PATH "${TARGET_RUNTIME_OUTPUT_DIRECTORY}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app") endif () install(CODE " @@ -111,14 +113,15 @@ macro(eth_install_executable EXECUTABLE) get_target_property(TARGET_LIBS ${EXECUTABLE} INTERFACE_LINK_LIBRARIES) string(REGEX MATCH "Qt5::Core" HAVE_QT ${TARGET_LIBS}) if ("${HAVE_QT}" STREQUAL "Qt5::Core") + get_target_property(TARGET_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE} RUNTIME_OUTPUT_DIRECTORY) add_custom_command(TARGET ${EXECUTABLE} POST_BUILD - COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}" + COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}" WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) #workaround for https://bugreports.qt.io/browse/QTBUG-42083 add_custom_command(TARGET ${EXECUTABLE} POST_BUILD COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM + WORKING_DIRECTORY ${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR} VERBATIM ) endif() @@ -144,5 +147,3 @@ macro(eth_install_executable EXECUTABLE) endif () endmacro() - - diff --git a/cmake/scripts/copydlls.cmake b/cmake/scripts/copydlls.cmake index 6d86b8e4e..57eb0ffd4 100644 --- a/cmake/scripts/copydlls.cmake +++ b/cmake/scripts/copydlls.cmake @@ -14,5 +14,4 @@ else () # Debug set(DLL ${DLL_DEBUG}) endif() -execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${DLL}" "${DESTINATION}") - +execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${DLL}" "${DESTINATION}") diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt index f8150806f..eb8fea95c 100644 --- a/libevmasm/CMakeLists.txt +++ b/libevmasm/CMakeLists.txt @@ -19,15 +19,10 @@ set(EXECUTABLE evmasm) file(GLOB HEADERS "*.h") -if (ETH_STATIC) - add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) -else() - add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) -endif() +add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcrypto) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) - From faee123b4a0e337fc275b828637f51c02e63e0b9 Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Mon, 11 May 2015 12:39:37 +0200 Subject: [PATCH 10/35] Coding Standards fix --- test/libp2p/capability.cpp | 50 +++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/test/libp2p/capability.cpp b/test/libp2p/capability.cpp index 2dcf9b4ba..2f1509e80 100644 --- a/test/libp2p/capability.cpp +++ b/test/libp2p/capability.cpp @@ -40,49 +40,43 @@ struct P2PFixture struct VerbosityHolder { - int m_oldLogVerbosity; - VerbosityHolder() : m_oldLogVerbosity(g_logVerbosity) { g_logVerbosity = 10; } - ~VerbosityHolder() { g_logVerbosity = m_oldLogVerbosity; } + int oldLogVerbosity; + VerbosityHolder(): oldLogVerbosity(g_logVerbosity) { g_logVerbosity = 10; } + ~VerbosityHolder() { g_logVerbosity = oldLogVerbosity; } }; -class TestCapability : public Capability +class TestCapability: public Capability { - int m_cntReceivedMessages; - int m_testSum; - public: - TestCapability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset) : Capability(_s, _h, _idOffset), m_cntReceivedMessages(0), m_testSum(0) {} + TestCapability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): Capability(_s, _h, _idOffset), m_cntReceivedMessages(0), m_testSum(0) {} virtual ~TestCapability() {} int countReceivedMessages() { return m_cntReceivedMessages; } int testSum() { return m_testSum; } static std::string name() { return "test"; } static u256 version() { return 2; } static unsigned messageCount() { return UserPacket + 1; } - - void sendTestMessage(int _i) - { - RLPStream s; - prep(s, UserPacket, 1); - s << _i; - sealAndSend(s); - } + void sendTestMessage(int _i) { RLPStream s; sealAndSend(prep(s, UserPacket, 1) << _i); } protected: - virtual bool interpret(unsigned _id, RLP const& _r) override - { - cnote << "Capability::interpret(): custom message received"; - BOOST_REQUIRE_EQUAL(_id, UserPacket); - ++m_cntReceivedMessages; - int i = _r[0].toInt(); - m_testSum += i; - return true; - } + virtual bool interpret(unsigned _id, RLP const& _r) override; + + int m_cntReceivedMessages; + int m_testSum; }; -class TestHostCapability : public HostCapability, public Worker +bool TestCapability::interpret(unsigned _id, RLP const& _r) +{ + cnote << "Capability::interpret(): custom message received"; + BOOST_ASSERT(_id == UserPacket); + ++m_cntReceivedMessages; + m_testSum += _r[0].toInt(); + return true; +} + +class TestHostCapability: public HostCapability, public Worker { public: - TestHostCapability() : Worker("test") {} + TestHostCapability(): Worker("test") {} virtual ~TestHostCapability() {} void sendTestMessage(NodeId const& _id, int _x) @@ -96,7 +90,7 @@ public: { int cnt = 0; int checksum = 0; - for (auto i : peerSessions()) + for (auto i: peerSessions()) if (_id == i.second->id) { cnt += i.first->cap().get()->countReceivedMessages(); From 979c5851f9f846b7dfbde0be8e9c8a92b873083f Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 6 May 2015 19:15:14 +0200 Subject: [PATCH 11/35] Reuse state during common subexpression elimination. --- libevmasm/Assembly.cpp | 80 ++++++++++----------- libevmasm/CommonSubexpressionEliminator.cpp | 51 +++++++++---- libevmasm/CommonSubexpressionEliminator.h | 14 ++-- libevmasm/ControlFlowGraph.cpp | 22 ++++-- 4 files changed, 94 insertions(+), 73 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 9530ded49..abcd44516 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -311,54 +311,45 @@ Assembly& Assembly::optimise(bool _enable) copt << toString(*this); count = 0; - //@todo CFG interface should be a generator, that returns an item and a pointer to a - // knownstate, which has to replace the current state if it is not null. - // Feed these items to the CSE, but also store them and replace the stored version - // if the items generated by the CSE are shorter. (or even use less gas?) - copt << "Performing control flow analysis..."; + copt << "Performing optimisation..."; { ControlFlowGraph cfg(m_items); - AssemblyItems optItems; + AssemblyItems optimisedItems; for (BasicBlock const& block: cfg.optimisedBlocks()) - copy(m_items.begin() + block.begin, m_items.begin() + block.end, - back_inserter(optItems)); - if (optItems.size() < m_items.size()) { - copt << "Old size: " << m_items.size() << ", new size: " << optItems.size(); - m_items = move(optItems); - count++; - } - } - - copt << "Performing common subexpression elimination..."; - for (auto iter = m_items.begin(); iter != m_items.end();) - { - //@todo use only a single state / expression classes instance. - KnownState state(make_shared()); - CommonSubexpressionEliminator eliminator(state); - auto orig = iter; - iter = eliminator.feedItems(iter, m_items.end()); - AssemblyItems optItems; - bool shouldReplace = false; - try - { - optItems = eliminator.getOptimizedItems(); - shouldReplace = (optItems.size() < size_t(iter - orig)); - } - catch (StackTooDeepException const&) - { - // This might happen if the opcode reconstruction is not as efficient - // as the hand-crafted code. - } - - if (shouldReplace) - { - copt << "Old size: " << (iter - orig) << ", new size: " << optItems.size(); - count++; - for (auto moveIter = optItems.begin(); moveIter != optItems.end(); ++orig, ++moveIter) - *orig = move(*moveIter); - iter = m_items.erase(orig, iter); + assertThrow(!!block.startState, OptimizerException, ""); + CommonSubexpressionEliminator eliminator(*block.startState); + auto iter = m_items.begin() + block.begin; + auto const end = m_items.begin() + block.end; + while (iter < end) + { + auto orig = iter; + iter = eliminator.feedItems(iter, end); + bool shouldReplace = false; + AssemblyItems optimisedChunk; + try + { + optimisedChunk = eliminator.getOptimizedItems(); + shouldReplace = (optimisedChunk.size() < size_t(iter - orig)); + } + catch (StackTooDeepException const&) + { + // This might happen if the opcode reconstruction is not as efficient + // as the hand-crafted code. + } + + if (shouldReplace) + { + copt << "Old size: " << (iter - orig) << ", new size: " << optimisedChunk.size(); + count++; + optimisedItems += optimisedChunk; + } + else + copy(orig, iter, back_inserter(optimisedItems)); + } } + if (optimisedItems.size() < m_items.size()) + m_items = move(optimisedItems); } } @@ -461,7 +452,8 @@ bytes Assembly::assemble() const for (auto const& i: tagRef) { bytesRef r(ret.data() + i.first, bytesPerTag); - toBigEndian(tagPos[i.second], r); + //@todo in the failure case, we could use the position of the invalid jumpdest + toBigEndian(i.second < tagPos.size() ? tagPos[i.second] : (1 << (8 * bytesPerTag)) - 1, r); } if (!m_data.empty()) diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 4b85eba40..5beb7966f 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -45,16 +45,22 @@ vector CommonSubexpressionEliminator::getOptimizedItems() for (int height = minHeight; height <= m_state.stackHeight(); ++height) targetStackContents[height] = m_state.stackElement(height, SourceLocation()); - // Debug info: - //stream(cout, initialStackContents, targetStackContents); - AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations).generateCode( m_initialState.stackHeight(), initialStackContents, targetStackContents ); if (m_breakingItem) + { items.push_back(*m_breakingItem); + m_state.feedItem(*m_breakingItem); + } + + // cleanup + m_initialState = m_state; + m_breakingItem = nullptr; + m_storeOperations.clear(); + return items; } @@ -113,6 +119,7 @@ AssemblyItems CSECodeGenerator::generateCode( { m_stackHeight = _initialStackHeight; m_stack = _initialStack; + m_targetStack = _targetStackContents; for (auto const& item: m_stack) if (!m_classPositions.count(item.second)) m_classPositions[item.second] = item.first; @@ -122,7 +129,7 @@ AssemblyItems CSECodeGenerator::generateCode( // generate the dependency graph starting from final storage and memory writes and target stack contents for (auto const& p: m_storeOperations) addDependencies(p.second.back().expression); - for (auto const& targetItem: _targetStackContents) + for (auto const& targetItem: m_targetStack) { m_finalClasses.insert(targetItem.second); addDependencies(targetItem.second); @@ -141,8 +148,10 @@ AssemblyItems CSECodeGenerator::generateCode( generateClassElement(seqAndId.second, true); // generate the target stack elements - for (auto const& targetItem: _targetStackContents) + for (auto const& targetItem: m_targetStack) { + if (m_stack.count(targetItem.first) && m_stack.at(targetItem.first) == targetItem.second) + continue; // already there int position = generateClassElement(targetItem.second); assertThrow(position != c_invalidPosition, OptimizerException, ""); if (position == targetItem.first) @@ -164,21 +173,24 @@ AssemblyItems CSECodeGenerator::generateCode( // check validity int finalHeight = 0; - if (!_targetStackContents.empty()) + if (!m_targetStack.empty()) // have target stack, so its height should be the final height - finalHeight = (--_targetStackContents.end())->first; + finalHeight = (--m_targetStack.end())->first; else if (!_initialStack.empty()) // no target stack, only erase the initial stack finalHeight = _initialStack.begin()->first - 1; else // neither initial no target stack, no change in height - finalHeight = 0; + finalHeight = _initialStackHeight; assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); + return m_generatedItems; } void CSECodeGenerator::addDependencies(Id _c) { + if (m_classPositions.count(_c)) + return; // it is already on the stack if (m_neededBy.count(_c)) return; // we already computed the dependencies for _c ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); @@ -340,7 +352,7 @@ int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) // this will not append a swap but remove the one that is already there appendOrRemoveSwap(m_stackHeight - 1, location); for (auto arg: arguments) - if (canBeRemoved(arg, _c)) + if (m_classPositions[arg] != c_invalidPosition && canBeRemoved(arg, _c)) m_classPositions[arg] = c_invalidPosition; for (size_t i = 0; i < arguments.size(); ++i) m_stack.erase(m_stackHeight - i); @@ -371,13 +383,22 @@ int CSECodeGenerator::classElementPosition(Id _id) const return m_classPositions.at(_id); } -bool CSECodeGenerator::canBeRemoved(Id _element, Id _result) +bool CSECodeGenerator::canBeRemoved(Id _element, Id _result, int _fromPosition) { - // Returns false if _element is finally needed or is needed by a class that has not been - // computed yet. Note that m_classPositions also includes classes that were deleted in the meantime. - if (m_finalClasses.count(_element)) - return false; + // Default for _fromPosition is the canonical position of the element. + if (_fromPosition == c_invalidPosition) + _fromPosition = classElementPosition(_element); + bool isCopy = _fromPosition != classElementPosition(_element); + if (m_finalClasses.count(_element)) + // It is part of the target stack. It can be removed if it is a copy that is not in the target position. + return isCopy && (!m_targetStack.count(_fromPosition) || m_targetStack[_fromPosition] != _element); + else if (isCopy) + // It is only a copy, can be removed. + return true; + + // Can be removed unless it is needed by a class that has not been computed yet. + // Note that m_classPositions also includes classes that were deleted in the meantime. auto range = m_neededBy.equal_range(_element); for (auto it = range.first; it != range.second; ++it) if (it->second != _result && !m_classPositions.count(it->second)) @@ -391,7 +412,7 @@ bool CSECodeGenerator::removeStackTopIfPossible() return false; assertThrow(m_stack.count(m_stackHeight) > 0, OptimizerException, ""); Id top = m_stack[m_stackHeight]; - if (!canBeRemoved(top)) + if (!canBeRemoved(top, Id(-1), m_stackHeight)) return false; m_generatedItems.push_back(AssemblyItem(Instruction::POP)); m_stack.erase(m_stackHeight); diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index 6e1ba40b3..2a9a31255 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -71,13 +71,6 @@ public: /// @returns the resulting items after optimization. AssemblyItems getOptimizedItems(); - /// Streams debugging information to @a _out. - std::ostream& stream( - std::ostream& _out, - std::map _initialStack = std::map(), - std::map _targetStack = std::map() - ) const; - private: /// Feeds the item into the system for analysis. void feedItem(AssemblyItem const& _item, bool _copyItem = false); @@ -134,8 +127,9 @@ private: /// @note throws an exception if it is not on the stack. int classElementPosition(Id _id) const; - /// @returns true if @a _element can be removed - in general or, if given, while computing @a _result. - bool canBeRemoved(Id _element, Id _result = Id(-1)); + /// @returns true if the copy of @a _element can be removed from stack position _fromPosition + /// - in general or, if given, while computing @a _result. + bool canBeRemoved(Id _element, Id _result = Id(-1), int _fromPosition = c_invalidPosition); /// Appends code to remove the topmost stack element if it can be removed. bool removeStackTopIfPossible(); @@ -167,6 +161,7 @@ private: std::map, StoreOperations> m_storeOperations; /// The set of equivalence classes that should be present on the stack at the end. std::set m_finalClasses; + std::map m_targetStack; }; template @@ -175,6 +170,7 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems( _AssemblyItemIterator _end ) { + assertThrow(!m_breakingItem, OptimizerException, "Invalid use of CommonSubexpressionEliminator."); for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator) feedItem(*_iterator); if (_iterator != _end) diff --git a/libevmasm/ControlFlowGraph.cpp b/libevmasm/ControlFlowGraph.cpp index 2e28317a3..7ed56ff1a 100644 --- a/libevmasm/ControlFlowGraph.cpp +++ b/libevmasm/ControlFlowGraph.cpp @@ -142,7 +142,7 @@ void ControlFlowGraph::removeUnusedBlocks() BasicBlock const& block = m_blocks.at(blocksToProcess.back()); blocksToProcess.pop_back(); for (BlockId tag: block.pushedTags) - if (!neededBlocks.count(tag)) + if (!neededBlocks.count(tag) && m_blocks.count(tag)) { neededBlocks.insert(tag); blocksToProcess.push_back(tag); @@ -191,12 +191,12 @@ void ControlFlowGraph::setPrevLinks() if (push.type() != PushTag) continue; BlockId nextId(push.data()); - if (m_blocks.at(nextId).prev) + if (m_blocks.count(nextId) && m_blocks.at(nextId).prev) continue; bool hasLoop = false; - for (BlockId id = nextId; id && !hasLoop; id = m_blocks.at(id).next) + for (BlockId id = nextId; id && m_blocks.count(id) && !hasLoop; id = m_blocks.at(id).next) hasLoop = (id == blockId); - if (hasLoop) + if (hasLoop || !m_blocks.count(nextId)) continue; m_blocks[nextId].prev = blockId; @@ -225,6 +225,8 @@ void ControlFlowGraph::gatherKnowledge() { //@todo we might have to do something like incrementing the sequence number for each JUMPDEST assertThrow(!!workQueue.back().first, OptimizerException, ""); + if (!m_blocks.count(workQueue.back().first)) + continue; // too bad, we do not know the tag, probably an invalid jump BasicBlock& block = m_blocks.at(workQueue.back().first); KnownStatePointer state = workQueue.back().second; workQueue.pop_back(); @@ -281,6 +283,15 @@ void ControlFlowGraph::gatherKnowledge() ) workQueue.push_back(make_pair(block.next, state->copy())); } + + // Remove all blocks we never visited here. This might happen because a tag is pushed but + // never used for a JUMP. + // Note that this invalidates some contents of pushedTags + for (auto it = m_blocks.begin(); it != m_blocks.end();) + if (!it->second.startState) + m_blocks.erase(it++); + else + it++; } BasicBlocks ControlFlowGraph::rebuildCode() @@ -288,7 +299,8 @@ BasicBlocks ControlFlowGraph::rebuildCode() map pushes; for (auto& idAndBlock: m_blocks) for (BlockId ref: idAndBlock.second.pushedTags) - pushes[ref]++; + if (m_blocks.count(ref)) + pushes[ref]++; set blocksToAdd; for (auto it: m_blocks) From 1492fe2a88a67c12a544ef48857e94c2f10e1892 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 7 May 2015 18:31:21 +0200 Subject: [PATCH 12/35] Store alternative stack locations during code generation. --- libevmasm/CommonSubexpressionEliminator.cpp | 80 +++++++++++---------- libevmasm/CommonSubexpressionEliminator.h | 8 +-- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 5beb7966f..e369c9dbc 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -121,10 +121,7 @@ AssemblyItems CSECodeGenerator::generateCode( m_stack = _initialStack; m_targetStack = _targetStackContents; for (auto const& item: m_stack) - if (!m_classPositions.count(item.second)) - m_classPositions[item.second] = item.first; - - // @todo: provide information about the positions of copies of class elements + m_classPositions[item.second].insert(item.first); // generate the dependency graph starting from final storage and memory writes and target stack contents for (auto const& p: m_storeOperations) @@ -152,11 +149,12 @@ AssemblyItems CSECodeGenerator::generateCode( { if (m_stack.count(targetItem.first) && m_stack.at(targetItem.first) == targetItem.second) continue; // already there - int position = generateClassElement(targetItem.second); - assertThrow(position != c_invalidPosition, OptimizerException, ""); - if (position == targetItem.first) + generateClassElement(targetItem.second); + assertThrow(!m_classPositions[targetItem.second].empty(), OptimizerException, ""); + if (m_classPositions[targetItem.second].count(targetItem.first)) continue; SourceLocation const& location = m_expressionClasses.representative(targetItem.second).item->getLocation(); + int position = classElementPosition(targetItem.second); if (position < targetItem.first) // it is already at its target, we need another copy appendDup(position, location); @@ -266,19 +264,23 @@ void CSECodeGenerator::addDependencies(Id _c) } } -int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) +void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) { + for (auto it: m_classPositions) + for (auto p: it.second) + if (p > m_stackHeight) + assertThrow(false, OptimizerException, ""); // do some cleanup removeStackTopIfPossible(); if (m_classPositions.count(_c)) { assertThrow( - m_classPositions[_c] != c_invalidPosition, + !m_classPositions[_c].empty(), OptimizerException, "Element already removed but still needed." ); - return m_classPositions[_c]; + return; } ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c); assertThrow( @@ -351,16 +353,16 @@ int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) // this will not append a swap but remove the one that is already there appendOrRemoveSwap(m_stackHeight - 1, location); - for (auto arg: arguments) - if (m_classPositions[arg] != c_invalidPosition && canBeRemoved(arg, _c)) - m_classPositions[arg] = c_invalidPosition; for (size_t i = 0; i < arguments.size(); ++i) + { + m_classPositions[m_stack[m_stackHeight - i]].erase(m_stackHeight - i); m_stack.erase(m_stackHeight - i); + } appendItem(*expr.item); if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1) { m_stack[m_stackHeight] = _c; - return m_classPositions[_c] = m_stackHeight; + m_classPositions[_c].insert(m_stackHeight); } else { @@ -369,18 +371,18 @@ int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) OptimizerException, "Invalid number of return values." ); - return m_classPositions[_c] = c_invalidPosition; + m_classPositions[_c]; // ensure it is created to mark the expression as generated } } int CSECodeGenerator::classElementPosition(Id _id) const { assertThrow( - m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition, + m_classPositions.count(_id) && !m_classPositions.at(_id).empty(), OptimizerException, "Element requested but is not present." ); - return m_classPositions.at(_id); + return *max_element(m_classPositions.at(_id).begin(), m_classPositions.at(_id).end()); } bool CSECodeGenerator::canBeRemoved(Id _element, Id _result, int _fromPosition) @@ -389,20 +391,19 @@ bool CSECodeGenerator::canBeRemoved(Id _element, Id _result, int _fromPosition) if (_fromPosition == c_invalidPosition) _fromPosition = classElementPosition(_element); - bool isCopy = _fromPosition != classElementPosition(_element); + bool haveCopy = m_classPositions.at(_element).size() > 1; if (m_finalClasses.count(_element)) // It is part of the target stack. It can be removed if it is a copy that is not in the target position. - return isCopy && (!m_targetStack.count(_fromPosition) || m_targetStack[_fromPosition] != _element); - else if (isCopy) - // It is only a copy, can be removed. - return true; - - // Can be removed unless it is needed by a class that has not been computed yet. - // Note that m_classPositions also includes classes that were deleted in the meantime. - auto range = m_neededBy.equal_range(_element); - for (auto it = range.first; it != range.second; ++it) - if (it->second != _result && !m_classPositions.count(it->second)) - return false; + return haveCopy && (!m_targetStack.count(_fromPosition) || m_targetStack[_fromPosition] != _element); + else if (!haveCopy) + { + // Can be removed unless it is needed by a class that has not been computed yet. + // Note that m_classPositions also includes classes that were deleted in the meantime. + auto range = m_neededBy.equal_range(_element); + for (auto it = range.first; it != range.second; ++it) + if (it->second != _result && !m_classPositions.count(it->second)) + return false; + } return true; } @@ -414,9 +415,9 @@ bool CSECodeGenerator::removeStackTopIfPossible() Id top = m_stack[m_stackHeight]; if (!canBeRemoved(top, Id(-1), m_stackHeight)) return false; - m_generatedItems.push_back(AssemblyItem(Instruction::POP)); + m_classPositions[m_stack[m_stackHeight]].erase(m_stackHeight); m_stack.erase(m_stackHeight); - m_stackHeight--; + appendItem(AssemblyItem(Instruction::POP)); return true; } @@ -428,6 +429,7 @@ void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _locat assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); appendItem(AssemblyItem(dupInstruction(instructionNum), _location)); m_stack[m_stackHeight] = m_stack[_fromPosition]; + m_classPositions[m_stack[m_stackHeight]].insert(m_stackHeight); } void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation const& _location) @@ -439,13 +441,15 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation cons assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); appendItem(AssemblyItem(swapInstruction(instructionNum), _location)); - // The value of a class can be present in multiple locations on the stack. We only update the - // "canonical" one that is tracked by m_classPositions - if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) - m_classPositions[m_stack[m_stackHeight]] = _fromPosition; - if (m_classPositions[m_stack[_fromPosition]] == _fromPosition) - m_classPositions[m_stack[_fromPosition]] = m_stackHeight; - swap(m_stack[m_stackHeight], m_stack[_fromPosition]); + + if (m_stack[m_stackHeight] != m_stack[_fromPosition]) + { + m_classPositions[m_stack[m_stackHeight]].erase(m_stackHeight); + m_classPositions[m_stack[m_stackHeight]].insert(_fromPosition); + m_classPositions[m_stack[_fromPosition]].erase(_fromPosition); + m_classPositions[m_stack[_fromPosition]].insert(m_stackHeight); + swap(m_stack[m_stackHeight], m_stack[_fromPosition]); + } if (m_generatedItems.size() >= 2 && SemanticInformation::isSwapInstruction(m_generatedItems.back()) && *(m_generatedItems.end() - 2) == m_generatedItems.back()) diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index 2a9a31255..a35e31d90 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -119,10 +119,8 @@ private: void addDependencies(Id _c); /// Produce code that generates the given element if it is not yet present. - /// @returns the stack position of the element or c_invalidPosition if it does not actually - /// generate a value on the stack. /// @param _allowSequenced indicates that sequence-constrained operations are allowed - int generateClassElement(Id _c, bool _allowSequenced = false); + void generateClassElement(Id _c, bool _allowSequenced = false); /// @returns the position of the representative of the given id on the stack. /// @note throws an exception if it is not on the stack. int classElementPosition(Id _id) const; @@ -151,8 +149,8 @@ private: std::multimap m_neededBy; /// Current content of the stack. std::map m_stack; - /// Current positions of equivalence classes, equal to c_invalidPosition if already deleted. - std::map m_classPositions; + /// Current positions of equivalence classes, equal to the empty set if already deleted. + std::map> m_classPositions; /// The actual eqivalence class items and how to compute them. ExpressionClasses& m_expressionClasses; From 12f3446bf9f87b1e3450dfb551b3039fe578e638 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 8 May 2015 12:40:11 +0200 Subject: [PATCH 13/35] Tests. --- test/libsolidity/SolidityOptimizer.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 3cb6a536a..4986b1469 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -251,6 +251,27 @@ BOOST_AUTO_TEST_CASE(function_calls) compareVersions("f(uint256)", 36); } +BOOST_AUTO_TEST_CASE(storage_write_in_loops) +{ + char const* sourceCode = R"( + contract test { + uint d; + function f(uint a) returns (uint r) { + var x = d; + for (uint i = 1; i < a * a; i++) { + r = d; + d = i; + } + + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f(uint256)", 0); + compareVersions("f(uint256)", 10); + compareVersions("f(uint256)", 36); +} + BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { eth::KnownState state; From 579d6f4e32263c7da41931a4b2aec36aee21d683 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 11 May 2015 16:40:28 +0200 Subject: [PATCH 14/35] Compute state intersection. --- libevmasm/KnownState.cpp | 47 +++++++++++++++++++------- test/libsolidity/SolidityOptimizer.cpp | 36 ++++++++++++++++++++ 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 41ac4802b..d6fbde2d9 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -160,23 +160,46 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool return op; } -void KnownState::reduceToCommonKnowledge(KnownState const& /*_other*/) +/// Helper function for KnownState::reduceToCommonKnowledge, removes everything from +/// _this which is not in or not equal to the value in _other. +template void intersect( + _Mapping& _this, + _Mapping const& _other, + function<_KeyType(_KeyType)> const& _keyTrans = [](_KeyType _k) { return _k; } +) +{ + for (auto it = _this.begin(); it != _this.end();) + if (_other.count(_keyTrans(it->first)) && _other.at(_keyTrans(it->first)) == it->second) + ++it; + else + it = _this.erase(it); +} + +void KnownState::reduceToCommonKnowledge(KnownState const& _other) { - //@todo - *this = KnownState(m_expressionClasses); + int stackDiff = m_stackHeight - _other.m_stackHeight; + function stackKeyTransform = [=](int _key) -> int { return _key - stackDiff; }; + intersect(m_stackElements, _other.m_stackElements, stackKeyTransform); + // Use the smaller stack height. Essential to terminate in case of loops. + if (m_stackHeight > _other.m_stackHeight) + { + map shiftedStack; + for (auto const& stackElement: m_stackElements) + shiftedStack[stackElement.first - stackDiff] = stackElement.second; + m_stackElements = move(shiftedStack); + m_stackHeight = _other.m_stackHeight; + } + + intersect(m_storageContent, _other.m_storageContent); + intersect(m_memoryContent, _other.m_memoryContent); } bool KnownState::operator==(const KnownState& _other) const { - //@todo - return ( - m_stackElements.empty() && - _other.m_stackElements.empty() && - m_storageContent.empty() && - _other.m_storageContent.empty() && - m_memoryContent.empty() && - _other.m_memoryContent.empty() - ); + return m_storageContent == _other.m_storageContent && + m_memoryContent == _other.m_memoryContent && + m_stackHeight == _other.m_stackHeight && + m_stackElements == _other.m_stackElements; } ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation const& _location) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 4986b1469..e50469dd6 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -272,6 +272,42 @@ BOOST_AUTO_TEST_CASE(storage_write_in_loops) compareVersions("f(uint256)", 36); } +BOOST_AUTO_TEST_CASE(retain_information_in_branches) +{ + // This tests that the optimizer knows that we already have "z == sha3(y)" inside both branches. + char const* sourceCode = R"( + contract c { + bytes32 d; + uint a; + function f(uint x, bytes32 y) returns (uint r_a, bytes32 r_d) { + bytes32 z = sha3(y); + if (x > 8) { + z = sha3(y); + a = x; + } else { + z = sha3(y); + a = x; + } + r_a = a; + r_d = d; + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f(uint256,bytes32)", 0, "abc"); + compareVersions("f(uint256,bytes32)", 8, "def"); + compareVersions("f(uint256,bytes32)", 10, "ghi"); + + m_optimize = true; + bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c"); + size_t numSHA3s = 0; + eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) { + if (_instr == eth::Instruction::SHA3) + numSHA3s++; + }); + BOOST_CHECK_EQUAL(1, numSHA3s); +} + BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { eth::KnownState state; From 30ca78aa0b2f73999731c15d153ec629e20eea7e Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 11 May 2015 17:16:07 +0200 Subject: [PATCH 15/35] small changes --- mix/ClientModel.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 87230664f..5f6a6c26c 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -256,9 +256,7 @@ void ClientModel::setupState(QVariantMap _state) u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); QString sender = transaction.value("sender").toString(); bool isStdContract = transaction.value("stdContract").toBool(); - bool isContractCreation; - if (!transaction.value("isContractCreation").isNull()) - isContractCreation = transaction.value("isContractCreation").toBool(); + bool isContractCreation = transaction.value("isContractCreation").toBool(); if (isStdContract) { if (contractId.isEmpty()) //TODO: This is to support old project files, remove later From 23d4ac1adb11e6b1689117fe7ddf432d57b2f836 Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Mon, 11 May 2015 17:27:11 +0200 Subject: [PATCH 16/35] Coding Standards fix --- test/libp2p/capability.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/libp2p/capability.cpp b/test/libp2p/capability.cpp index 2f1509e80..e4d0d910c 100644 --- a/test/libp2p/capability.cpp +++ b/test/libp2p/capability.cpp @@ -40,9 +40,10 @@ struct P2PFixture struct VerbosityHolder { - int oldLogVerbosity; VerbosityHolder(): oldLogVerbosity(g_logVerbosity) { g_logVerbosity = 10; } ~VerbosityHolder() { g_logVerbosity = oldLogVerbosity; } + + int oldLogVerbosity; }; class TestCapability: public Capability @@ -122,16 +123,14 @@ BOOST_AUTO_TEST_CASE(capability) int const step = 10; - for (int i = 0; i < 3000; i += step) - if (!host1.isStarted() || !host2.isStarted()) - this_thread::sleep_for(chrono::milliseconds(step)); + for (int i = 0; i < 3000 && (!host1.isStarted() || !host2.isStarted()); i += step) + this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE(host1.isStarted() && host2.isStarted()); host1.requirePeer(host2.id(), NodeIPEndpoint(bi::address::from_string(localhost), prefs2.listenPort, prefs2.listenPort)); - for (int i = 0; i < 3000; i += step) - if (!host1.peerCount() || !host2.peerCount()) - this_thread::sleep_for(chrono::milliseconds(step)); + for (int i = 0; i < 3000 && (!host1.peerCount() || !host2.peerCount()); i += step) + this_thread::sleep_for(chrono::milliseconds(step)); BOOST_REQUIRE(host1.peerCount() > 0 && host2.peerCount() > 0); From 3323ebbd1dcbbb38ba6a86aa5b07c0aed8d6d7d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 11 May 2015 17:40:00 +0200 Subject: [PATCH 17/35] Create symlink to old testeth location to make bildbot happy --- test/CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 39a235c58..bedbe42f3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -95,6 +95,12 @@ if (JSONRPC) target_link_libraries(testeth ${JSON_RPC_CPP_CLIENT_LIBRARIES}) endif() +if (UNIX) # Create symlink to old testeth location to make bildbot happy + add_custom_command(TARGET testeth POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_BINARY_DIR}/bin/testeth ${CMAKE_BINARY_DIR}/test/testeth + ) +endif() + enable_testing() set(CTEST_OUTPUT_ON_FAILURE TRUE) @@ -110,7 +116,6 @@ eth_add_test(ClientBase ) eth_add_test(JsonRpc - ARGS --eth_testfile=BlockTests/bcJS_API_Test + ARGS --eth_testfile=BlockTests/bcJS_API_Test ARGS --eth_testfile=BlockTests/bcValidBlockTest ) - From 6f198955dac792873cc427885ee424c9a4ecb646 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 11 May 2015 19:44:45 +0200 Subject: [PATCH 18/35] Use returning erase variant. --- libevmasm/ControlFlowGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmasm/ControlFlowGraph.cpp b/libevmasm/ControlFlowGraph.cpp index 7ed56ff1a..cc68b2af8 100644 --- a/libevmasm/ControlFlowGraph.cpp +++ b/libevmasm/ControlFlowGraph.cpp @@ -289,7 +289,7 @@ void ControlFlowGraph::gatherKnowledge() // Note that this invalidates some contents of pushedTags for (auto it = m_blocks.begin(); it != m_blocks.end();) if (!it->second.startState) - m_blocks.erase(it++); + it = m_blocks.erase(it); else it++; } From c47fe49f80924e3fd54de006189ad7a515d37859 Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Mon, 11 May 2015 22:54:12 +0200 Subject: [PATCH 19/35] Fixed warning: unused parameter --- test/libp2p/capability.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/libp2p/capability.cpp b/test/libp2p/capability.cpp index e4d0d910c..2c158f4d8 100644 --- a/test/libp2p/capability.cpp +++ b/test/libp2p/capability.cpp @@ -68,10 +68,10 @@ protected: bool TestCapability::interpret(unsigned _id, RLP const& _r) { cnote << "Capability::interpret(): custom message received"; - BOOST_ASSERT(_id == UserPacket); ++m_cntReceivedMessages; m_testSum += _r[0].toInt(); - return true; + BOOST_ASSERT(_id == UserPacket); + return (_id == UserPacket); } class TestHostCapability: public HostCapability, public Worker From 03a82e401c8cfe3c16139e6f5830ac74b5cd7ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 May 2015 09:37:39 +0200 Subject: [PATCH 20/35] testeth: support for --singletest option with only test name param. --- test/TestHelper.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index e0aad310f..1d7734e35 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -552,6 +552,9 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e void userDefinedTest(std::function doTests) { if (!Options::get().singleTest) + return; + + if (Options::get().singleTestFile.empty() || Options::get().singleTestName.empty()) { cnote << "Missing user test specification\nUsage: testeth --singletest \n"; return; @@ -732,11 +735,23 @@ Options::Options() inputLimits = true; bigData = true; } - else if (arg == "--singletest" && i + 2 < argc) + else if (arg == "--singletest" && i + 1 < argc) { singleTest = true; - singleTestFile = argv[i + 1]; - singleTestName = argv[i + 2]; + auto name1 = std::string{argv[i + 1]}; + if (i + 1 < argc) // two params + { + auto name2 = std::string{argv[i + 2]}; + if (name2[0] == '-') // not param, another option + singleTestName = std::move(name1); + else + { + singleTestFile = std::move(name1); + singleTestName = std::move(name2); + } + } + else + singleTestName = std::move(name1); } } } From 294a73a4e9f85ae3220c644cd520d9933ebf3966 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 12 May 2015 09:47:55 +0200 Subject: [PATCH 21/35] fixed RUNTIME_OUTPUT_DIRECTORY on osx --- cmake/EthExecutableHelper.cmake | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake index d971a5f92..3dd7fa798 100644 --- a/cmake/EthExecutableHelper.cmake +++ b/cmake/EthExecutableHelper.cmake @@ -88,20 +88,15 @@ macro(eth_install_executable EXECUTABLE) if (APPLE) # First have qt5 install plugins and frameworks add_custom_command(TARGET ${EXECUTABLE} POST_BUILD - COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app -executable=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents/MacOS/${EXECUTABLE} ${eth_qml_dir} - WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents + COMMAND ${MACDEPLOYQT_APP} ${EXECUTABLE}.app -executable=${EXECUTABLE}.app/Contents/MacOS/${EXECUTABLE} ${eth_qml_dir} + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR} + COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${EXECUTABLE}.app/Contents ) - get_target_property(TARGET_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE} RUNTIME_OUTPUT_DIRECTORY) - # This tool and next will inspect linked libraries in order to determine which dependencies are required - if (${CMAKE_CFG_INTDIR} STREQUAL ".") - # TODO: This should only happen for GUI application - set(APP_BUNDLE_PATH "${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${EXECUTABLE}.app") - else () - set(APP_BUNDLE_PATH "${TARGET_RUNTIME_OUTPUT_DIRECTORY}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app") - endif () + # TODO: This should only happen for GUI application + set(APP_BUNDLE_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INDIR}/${EXECUTABLE}.app") + # This tool and next will inspect linked libraries in order to determine which dependencies are required install(CODE " include(BundleUtilities) set(BU_CHMOD_BUNDLE_ITEMS 1) From 9ea0bf5ab1d8c6146377b9fd3d3642c5d176f698 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 12 May 2015 10:46:04 +0200 Subject: [PATCH 22/35] std::pair instead of QString manipulation --- mix/ClientModel.cpp | 78 +++++++++++++++++---------------- mix/ClientModel.h | 16 ++++--- mix/qml/StateListModel.qml | 9 +++- mix/qml/TransactionDialog.qml | 11 +++-- mix/qml/js/TransactionHelper.js | 3 +- 5 files changed, 67 insertions(+), 50 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 5f6a6c26c..bbb9b109b 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -193,7 +193,7 @@ QVariantMap ClientModel::contractAddresses() const { QVariantMap res; for (auto const& c: m_contractAddresses) - res.insert(c.first, QString::fromStdString(toJS(c.second))); + res.insert(c.first.first, QString::fromStdString(toJS(c.second))); return res; } @@ -257,6 +257,7 @@ void ClientModel::setupState(QVariantMap _state) QString sender = transaction.value("sender").toString(); bool isStdContract = transaction.value("stdContract").toBool(); bool isContractCreation = transaction.value("isContractCreation").toBool(); + bool isFunctionCall = transaction.value("isFunctionCall").toBool(); if (isStdContract) { if (contractId.isEmpty()) //TODO: This is to support old project files, remove later @@ -272,7 +273,7 @@ void ClientModel::setupState(QVariantMap _state) { if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later contractId = m_codeModel->contracts().keys()[0]; - TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation); + TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall); transactionSettings.parameterValues = transaction.value("parameters").toMap(); if (contractId == functionId || functionId == "Constructor") @@ -308,9 +309,9 @@ void ClientModel::executeSequence(vector const& _sequence, m_gasCosts.clear(); for (TransactionSettings const& transaction: _sequence) { - QString contractName = resolveContractName(transaction.contractId); - QString address = resolveToken(transaction.contractId, deployedContracts); - if (transaction.functionId == "(transfert)") + std::pair ctrInstance = resolvePair(transaction.contractId); + QString address = resolveToken(ctrInstance, deployedContracts); + if (transaction.isFunctionCall) { callAddress(Address(address.toStdString()), bytes(), transaction); onNewTransaction(); @@ -331,7 +332,7 @@ void ClientModel::executeSequence(vector const& _sequence, else { //encode data - CompiledContract const& compilerRes = m_codeModel->contract(contractName); + CompiledContract const& compilerRes = m_codeModel->contract(ctrInstance.first); QFunctionDefinition const* f = nullptr; bytes contractCode = compilerRes.bytes(); shared_ptr contractDef = compilerRes.sharedContract(); @@ -358,29 +359,28 @@ void ClientModel::executeSequence(vector const& _sequence, QSolidityType const* type = p->type(); QVariant value = transaction.parameterValues.value(p->name()); if (type->type().type == SolidityType::Type::Address) - value = QVariant(resolveToken(value.toString(), deployedContracts)); + { + std::pair ctrParamInstance = resolvePair(value.toString()); + value = QVariant(resolveToken(ctrParamInstance, deployedContracts)); + } encoder.encode(value, type->type()); } - if (transaction.functionId.isEmpty() || transaction.functionId == contractName) + if (transaction.functionId.isEmpty() || transaction.functionId == ctrInstance.first) { bytes param = encoder.encodedData(); contractCode.insert(contractCode.end(), param.begin(), param.end()); Address newAddress = deployContract(contractCode, transaction); deployedContracts.push_back(newAddress); - auto contractAddressIter = m_contractAddresses.find(transaction.contractId); - if (contractAddressIter == m_contractAddresses.end() || newAddress != contractAddressIter->second) - { - QString contractToken = retrieveToken(transaction.contractId, deployedContracts); - m_contractAddresses[contractToken] = newAddress; - m_contractNames[newAddress] = contractToken; - contractAddressesChanged(); - } + std::pair contractToken = retrieveToken(transaction.contractId, deployedContracts); + m_contractAddresses[contractToken] = newAddress; + m_contractNames[newAddress] = contractToken.first; + contractAddressesChanged(); gasCostsChanged(); } else { - auto contractAddressIter = m_contractAddresses.find(retrieveToken(transaction.contractId, deployedContracts)); + auto contractAddressIter = m_contractAddresses.find(ctrInstance); if (contractAddressIter == m_contractAddresses.end()) { emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); @@ -412,31 +412,35 @@ void ClientModel::executeSequence(vector const& _sequence, }); } -QString ClientModel::resolveToken(QString const& _value, vector
const& _contracts) + +std::pair ClientModel::resolvePair(QString const& _contractId) { - QString ret = _value; - if (_value.startsWith("<") && _value.endsWith(">")) - { - QStringList nb = ret.remove("<").remove(">").split(" - "); - ret = QString::fromStdString("0x" + dev::toHex(_contracts.at(nb.back().toInt()).ref())); - } - return ret; + std::pair ret; + ret.first = _contractId; + ret.second = -1; + if (_contractId.startsWith("<") && _contractId.endsWith(">")) + { + QStringList values = ret.first.remove("<").remove(">").split(" - "); + ret.first = values[0]; + ret.second = values[1].toUInt(); + } + return ret; } -QString ClientModel::retrieveToken(QString const& _value, vector
const& _contracts) +QString ClientModel::resolveToken(std::pair const& _value, vector
const& _contracts) { - QString ret = _value; - if (!_value.startsWith("<") && !_value.endsWith(">")) - return "<" + _value + " - " + QString::number(_contracts.size() - 1) + ">"; - return ret; + if (_value.second != -1) + return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref())); + else + return _value.first; } -QString ClientModel::resolveContractName(QString const& _value) +std::pair ClientModel::retrieveToken(QString const& _value, vector
const& _contracts) { - QString ret = _value; - if (_value.startsWith("<") && _value.endsWith(">")) - ret = ret.remove("<").remove(">").split(" - ").first(); - return ret; + std::pair ret; + ret.first = _value; + ret.second = _contracts.size() - 1; + return ret; } void ClientModel::showDebugger() @@ -462,7 +466,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) //try to resolve contract for source level debugging auto nameIter = m_contractNames.find(code.address); CompiledContract const* compilerRes = nullptr; - if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(resolveContractName(nameIter->second)))) //returned object is guaranteed to live till the end of event handler in main thread + if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(nameIter->second))) //returned object is guaranteed to live till the end of event handler in main thread { eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes->assemblyItems() : compilerRes->constructorAssemblyItems(); codes.back()->setDocument(compilerRes->documentId()); @@ -723,7 +727,7 @@ void ClientModel::onNewTransaction() auto contractAddressIter = m_contractNames.find(contractAddress); if (contractAddressIter != m_contractNames.end()) { - CompiledContract const& compilerRes = m_codeModel->contract(resolveContractName(contractAddressIter->second)); + CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); const QContractDefinition* def = compilerRes.contract(); contract = def->name(); if (abi) diff --git a/mix/ClientModel.h b/mix/ClientModel.h index a9cf89018..9d4168995 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -53,10 +53,10 @@ struct SolidityType; struct TransactionSettings { TransactionSettings() {} - TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender, int _isContractCreation): - contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCreation(_isContractCreation) {} + TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender, bool _isContractCreation, bool _isFunctionCall): + contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCreation(_isContractCreation), isFunctionCall(_isFunctionCall) {} TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl): - contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true) {} + contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true), isFunctionCall(false) {} /// Contract name QString contractId; @@ -78,6 +78,8 @@ struct TransactionSettings Secret sender; /// Tr deploys a contract bool isContractCreation; + /// Tr call a ctr function + bool isFunctionCall; }; @@ -229,9 +231,9 @@ private: void onStateReset(); void showDebuggerForTransaction(ExecutionResult const& _t); QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); - QString resolveToken(QString const& _value, std::vector
const& _contracts); - QString resolveContractName(QString const& _value); - QString retrieveToken(QString const& _value, std::vector
const& _contracts); + QString resolveToken(std::pair const& _value, std::vector
const& _contracts); + std::pair retrieveToken(QString const& _value, std::vector
const& _contracts); + std::pair resolvePair(QString const& _contractId); QVariant formatStorageValue(SolidityType const& _type, std::unordered_map const& _storage, unsigned _offset, dev::u256 const& _slot); std::atomic m_running; @@ -241,7 +243,7 @@ private: std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; QList m_gasCosts; - std::map m_contractAddresses; + std::map, Address> m_contractAddresses; std::map m_contractNames; std::map m_stdContractAddresses; std::map m_stdContractNames; diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index b4eed525d..b4d9b6bc6 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -58,9 +58,13 @@ Item { parameters: {}, sender: t.sender, isContractCreation: t.isContractCreation, - label: t.label + label: t.label, + isFunctionCall: t.isFunctionCall }; + if (r.isFunctionCall === undefined) + r.isFunctionCall = true; + if (!r.label) r.label = r.contractId + " - " + r.functionId; @@ -122,7 +126,8 @@ Item { sender: t.sender, parameters: {}, isContractCreation: t.isContractCreation, - label: t.label + label: t.label, + isFunctionCall: t.isFunctionCall }; for (var key in t.parameters) r.parameters[key] = t.parameters[key]; diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index d26fbaf5a..7c9b28aa7 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -87,8 +87,6 @@ Dialog { else loadParameters(); - - visible = true; valueField.focus = true; } @@ -212,12 +210,19 @@ Dialog { } item.isContractCreation = trType.checked; + + if (item.functionId === "(transfert)") + item.isFunctionCall = false; + if (!item.isContractCreation) { item.contractId = recipients.currentText; item.label = item.contractId + " " + item.functionId; if (recipients.current().type === "address") - item.functionId = "(transfert)"; + { + item.functionId = ""; + item.isFunctionCall = false; + } } else { diff --git a/mix/qml/js/TransactionHelper.js b/mix/qml/js/TransactionHelper.js index 33072fdba..b9a011b66 100644 --- a/mix/qml/js/TransactionHelper.js +++ b/mix/qml/js/TransactionHelper.js @@ -11,7 +11,8 @@ function defaultTransaction() parameters: {}, stdContract: false, isContractCreation: true, - label: "" + label: "", + isFunctionCall: true }; } From d969e109bb92f1f4829f2d7a39c80137ef1c30e7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 12 May 2015 12:21:26 +0300 Subject: [PATCH 23/35] Fix braces. --- libethcore/EthashAux.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index c90ee048e..8788acc83 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -24,7 +24,8 @@ namespace dev { -namespace eth{ +namespace eth +{ class EthashAux { From e8f7c26479d4f5d633012a82a53afd3cfa772f7b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 May 2015 11:25:34 +0200 Subject: [PATCH 24/35] Fixed template problem. --- libevmasm/KnownState.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index d6fbde2d9..5a70a74fb 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -162,7 +162,7 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool /// Helper function for KnownState::reduceToCommonKnowledge, removes everything from /// _this which is not in or not equal to the value in _other. -template void intersect( +template void intersect( _Mapping& _this, _Mapping const& _other, function<_KeyType(_KeyType)> const& _keyTrans = [](_KeyType _k) { return _k; } @@ -175,6 +175,11 @@ template void intersect it = _this.erase(it); } +template void intersect(_Mapping& _this, _Mapping const& _other) +{ + intersect<_Mapping, ExpressionClasses::Id>(_this, _other, [](ExpressionClasses::Id _k) { return _k; }); +} + void KnownState::reduceToCommonKnowledge(KnownState const& _other) { int stackDiff = m_stackHeight - _other.m_stackHeight; From 40de803f66f35ecd5a4636effd04b5d1662a3f3d Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 12 May 2015 11:39:31 +0200 Subject: [PATCH 25/35] bug fix --- mix/ClientModel.cpp | 2 +- mix/ClientModel.h | 2 +- mix/qml/TransactionDialog.qml | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index dc9346d3e..fc8ce4c33 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -312,7 +312,7 @@ void ClientModel::executeSequence(vector const& _sequence, { std::pair ctrInstance = resolvePair(transaction.contractId); QString address = resolveToken(ctrInstance, deployedContracts); - if (transaction.isFunctionCall) + if (!transaction.isFunctionCall) { callAddress(Address(address.toStdString()), bytes(), transaction); onNewTransaction(); diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 2ca3d2c69..e1648b78d 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -56,7 +56,7 @@ struct TransactionSettings TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender, bool _isContractCreation, bool _isFunctionCall): contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCreation(_isContractCreation), isFunctionCall(_isFunctionCall) {} TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl): - contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true), isFunctionCall(false) {} + contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true), isFunctionCall(true) {} /// Contract name QString contractId; diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 7c9b28aa7..d9c811704 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -210,9 +210,7 @@ Dialog { } item.isContractCreation = trType.checked; - - if (item.functionId === "(transfert)") - item.isFunctionCall = false; + item.isFunctionCall = item.functionId !== "(transfert)"; if (!item.isContractCreation) { From 2eed2ca5e0910a2d1412fb9e938b9c8778db025b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 12 May 2015 12:55:40 +0300 Subject: [PATCH 26/35] Refactored much EthashAux., especially the DAG generation callback stuff. Made m_epochs active again. --- libethcore/EthashAux.cpp | 49 ++++++++++++++++++++++++++++++---------- libethcore/EthashAux.h | 11 +++++---- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index 210f70c78..7bcb976d6 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -41,6 +41,8 @@ using namespace chrono; using namespace dev; using namespace eth; +const char* DAGChannel::name() { return EthGreen "DAG"; } + EthashAux* dev::eth::EthashAux::s_this = nullptr; EthashAux::~EthashAux() @@ -76,9 +78,30 @@ h256 EthashAux::seedHash(unsigned _number) return get()->m_seedHashes[epoch]; } +uint64_t EthashAux::number(h256 const& _seedHash) +{ + Guard l(get()->x_epochs); + unsigned epoch = 0; + auto epochIter = get()->m_epochs.find(_seedHash); + if (epochIter == get()->m_epochs.end()) + { + // cdebug << "Searching for seedHash " << _seedHash; + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {} + if (epoch == 2048) + { + std::ostringstream error; + error << "apparent block number for " << _seedHash << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); + throw std::invalid_argument(error.str()); + } + } + else + epoch = epochIter->second; + return epoch * ETHASH_EPOCH_LENGTH; +} + void EthashAux::killCache(h256 const& _s) { - RecursiveGuard l(x_this); + RecursiveGuard l(x_lights); m_lights.erase(_s); } @@ -89,7 +112,7 @@ EthashAux::LightType EthashAux::light(BlockInfo const& _header) EthashAux::LightType EthashAux::light(uint64_t _blockNumber) { - RecursiveGuard l(get()->x_this); + RecursiveGuard l(get()->x_lights); h256 seedHash = EthashAux::seedHash(_blockNumber); LightType ret = get()->m_lights[seedHash]; return ret ? ret : (get()->m_lights[seedHash] = make_shared(_blockNumber)); @@ -126,30 +149,32 @@ bytesConstRef EthashAux::FullAllocation::data() const return bytesConstRef((byte const*)ethash_full_dag(full), size()); } -EthashAux::FullType EthashAux::full(BlockInfo const& _header) +EthashAux::FullType EthashAux::full(BlockInfo const& _header, function const& _f) { - return full((uint64_t) _header.number); + return full((uint64_t)_header.number, _f); } -struct DAGChannel: public LogChannel { static const char* name(); static const int verbosity = 0; }; -const char* DAGChannel::name() { return EthGreen "DAG"; } -static int ethash_callback(unsigned int _progress) +static std::function s_dagCallback; +static int dagCallbackShim(unsigned _p) { - clog(DAGChannel) << "Generating DAG file. Progress: " << toString(_progress) << "%"; - return 0; + clog(DAGChannel) << "Generating DAG file. Progress: " << toString(_p) << "%"; + return s_dagCallback ? s_dagCallback(_p) : 0; } -EthashAux::FullType EthashAux::full(uint64_t _blockNumber) +EthashAux::FullType EthashAux::full(uint64_t _blockNumber, function const& _f) { - RecursiveGuard l(get()->x_this); + auto l = light(_blockNumber); h256 seedHash = EthashAux::seedHash(_blockNumber); FullType ret; + + Guard lock(get()->x_fulls); if ((ret = get()->m_fulls[seedHash].lock())) { get()->m_lastUsedFull = ret; return ret; } - ret = get()->m_lastUsedFull = make_shared(light(_blockNumber)->light, ethash_callback); + s_dagCallback = _f; + ret = get()->m_lastUsedFull = make_shared(l->light, dagCallbackShim); get()->m_fulls[seedHash] = ret; return ret; } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index bc9361db7..dbfe54d43 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -27,6 +27,7 @@ namespace dev namespace eth { +struct DAGChannel: public LogChannel { static const char* name(); static const int verbosity = 1; }; class EthashAux { @@ -59,27 +60,29 @@ public: using FullType = std::shared_ptr; static h256 seedHash(unsigned _number); + static uint64_t number(h256 const& _seedHash); static uint64_t cacheSize(BlockInfo const& _header); static LightType light(BlockInfo const& _header); static LightType light(uint64_t _blockNumber); - static FullType full(BlockInfo const& _header); - static FullType full(uint64_t _blockNumber); + static FullType full(BlockInfo const& _header, std::function const& _f = std::function()); + static FullType full(uint64_t _blockNumber, std::function const& _f = std::function()); static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce); static Ethash::Result eval(uint64_t _blockNumber, h256 const& _headerHash, Nonce const& _nonce); - private: EthashAux() {} void killCache(h256 const& _s); static EthashAux* s_this; - RecursiveMutex x_this; + RecursiveMutex x_lights; std::unordered_map> m_lights; + + Mutex x_fulls; std::unordered_map> m_fulls; FullType m_lastUsedFull; From 3a81c1059846a93e1d46d843b319260c6b19b95e Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 12 May 2015 12:14:26 +0200 Subject: [PATCH 27/35] use - instead of "(transfer)" --- mix/qml/TransactionDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index d9c811704..fe4bfc82e 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -107,6 +107,7 @@ Dialog { function loadFunctions(contractId) { functionsModel.clear(); + functionsModel.append({ text: " - " }); var contract = codeModel.contracts[contractId]; if (contract) { var functions = codeModel.contracts[contractId].contract.functions; @@ -114,7 +115,6 @@ Dialog { functionsModel.append({ text: functions[f].name }); } } - functionsModel.append({ text: "(transfert)" }); } function selectContract(contractName) @@ -210,7 +210,7 @@ Dialog { } item.isContractCreation = trType.checked; - item.isFunctionCall = item.functionId !== "(transfert)"; + item.isFunctionCall = item.functionId !== " - "; if (!item.isContractCreation) { From da1fcbb19b4e07e9d40f1d29de0b190100f7e9d1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 12 May 2015 16:46:42 +0300 Subject: [PATCH 28/35] DAG generation improvements. Generate next DAG in AZ! Cleanups to Ethash & Aux. DAG Progress in AZ. --- alethzero/Main.ui | 6 +++ alethzero/MainWin.cpp | 39 +++++++++----- alethzero/MainWin.h | 1 + alethzero/Transact.cpp | 5 +- eth/main.cpp | 107 ++++++++++++++++++++++----------------- libdevcore/Common.h | 8 +-- libdevcore/Worker.cpp | 10 ++-- libethcore/Ethash.cpp | 6 ++- libethcore/Ethash.h | 2 +- libethcore/EthashAux.cpp | 46 ++++++++++++----- libethcore/EthashAux.h | 14 ++++- libethereum/ClientBase.h | 4 +- libethereum/State.cpp | 4 +- 13 files changed, 161 insertions(+), 91 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 5b0ad7582..e0852142a 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -151,6 +151,7 @@ + @@ -1727,6 +1728,11 @@ font-size: 14pt In&ject Block + + + Prepare Next &DAG + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 87c9c2dc9..7551eaa35 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -204,7 +204,7 @@ Main::Main(QWidget *parent) : QSettings s("ethereum", "alethzero"); m_networkConfig = s.value("peers").toByteArray(); bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size()); - m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth", "shh"}, p2p::NetworkPreferences(), network)); + m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), WithExisting::Trust, {"eth"/*, "shh"*/}, p2p::NetworkPreferences(), network)); m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), this)); @@ -943,22 +943,30 @@ void Main::on_preview_triggered() refreshAll(); } +void Main::on_prepNextDAG_triggered() +{ + EthashAux::computeFull(ethereum()->blockChain().number() + ETHASH_EPOCH_LENGTH); +} + void Main::refreshMining() { + pair gp = EthashAux::fullGeneratingProgress(); + QString t; + if (gp.first != EthashAux::NotGenerating) + t = QString("DAG for #%1-#%2: %3% complete; ").arg(gp.first).arg(gp.first + ETHASH_EPOCH_LENGTH - 1).arg(gp.second); MiningProgress p = ethereum()->miningProgress(); - ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); - if (!ui->miningView->isVisible()) - return; - list l = ethereum()->miningHistory(); - static unsigned lh = 0; - if (p.hashes < lh) - ui->miningView->resetStats(); - lh = p.hashes; - ui->miningView->appendStats(l, p); -/* if (p.ms) - for (MineInfo const& i: l) - cnote << i.hashes * 10 << "h/sec, need:" << i.requirement << " best:" << i.best << " best-so-far:" << p.best << " avg-speed:" << (p.hashes * 1000 / p.ms) << "h/sec"; -*/ + ui->mineStatus->setText(t + (ethereum()->isMining() ? p.hashes > 0 ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Awaiting DAG" : "Not mining")); + if (ethereum()->isMining() && p.hashes > 0) + { + if (!ui->miningView->isVisible()) + return; + list l = ethereum()->miningHistory(); + static unsigned lh = 0; + if (p.hashes < lh) + ui->miningView->resetStats(); + lh = p.hashes; + ui->miningView->appendStats(l, p); + } } void Main::setBeneficiary(Address const& _b) @@ -1878,6 +1886,7 @@ void Main::on_mine_triggered() { if (ui->mine->isChecked()) { +// EthashAux::computeFull(ethereum()->blockChain().number()); ethereum()->setAddress(m_beneficiary); ethereum()->startMining(); } @@ -2027,6 +2036,7 @@ std::string Main::prettyU256(dev::u256 const& _n) const void Main::on_post_clicked() { + return; shh::Message m; m.setTo(stringToPublic(ui->shhTo->currentText())); m.setPayload(parseData(ui->shhData->toPlainText().toStdString())); @@ -2051,6 +2061,7 @@ int Main::authenticate(QString _title, QString _text) void Main::refreshWhispers() { + return; ui->whispers->clear(); for (auto const& w: whisper()->all()) { diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 51d4cc8f4..d9075d178 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -124,6 +124,7 @@ private slots: // Mining void on_mine_triggered(); + void on_prepNextDAG_triggered(); // View void on_refresh_triggered(); diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index bc37db8ef..f2a8ef0d5 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -299,8 +299,9 @@ void Transact::rejigData() return; // Determine how much balance we have to play with... - auto s = findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice()); - auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock); + //findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice()); + auto s = fromAccount(); + auto b = ethereum()->balanceAt(s, PendingBlock); m_allGood = true; QString htmlInfo; diff --git a/eth/main.cpp b/eth/main.cpp index d6582e4d3..abb78c20d 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -158,6 +158,11 @@ void help() << " --port Connect to remote port (default: 30303)." << endl << " --network-id Only connect to other hosts with this network id (default:0)." << endl << " --upnp Use UPnP for NAT (default: on)." << endl + << endl + << "Client structured logging:" << endl + << " --structured-logging Enable structured logging (default output to stdout)." << endl + << " --structured-logging-format Set the structured logging time format." << endl + << " --structured-logging-url Set the structured logging destination (currently only file:// supported)." << endl #if ETH_JSONRPC || !ETH_TRUE << endl << "Work farming mode:" << endl @@ -806,8 +811,11 @@ int main(int argc, char** argv) structuredLoggingFormat = string(argv[++i]); else if (arg == "--structured-logging") structuredLogging = true; - else if (arg == "--structured-logging-destination" && i + 1 < argc) + else if (arg == "--structured-logging-url" && i + 1 < argc) + { + structuredLogging = true; structuredLoggingURL = argv[++i]; + } else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) dbPath = argv[++i]; else if ((arg == "-D" || arg == "--create-dag") && i + 1 < argc) @@ -1033,6 +1041,28 @@ int main(int argc, char** argv) if (!clientName.empty()) clientName += "/"; + string logbuf; + bool silence = false; + std::string additional; + g_logPost = [&](std::string const& a, char const*){ + if (silence) + logbuf += a + "\n"; + else + cout << "\r \r" << a << endl << additional << flush; + }; + + auto getPassword = [&](string const& prompt){ + auto s = silence; + silence = true; + cout << endl; + string ret = dev::getPassword(prompt); + silence = s; + return ret; + }; + auto getAccountPassword = [&](Address const& a){ + return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); + }; + StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); @@ -1042,13 +1072,38 @@ int main(int argc, char** argv) clientImplString, dbPath, killChain, - nodeMode == NodeMode::Full ? set{"eth", "shh"} : set(), + nodeMode == NodeMode::Full ? set{"eth"/*, "shh"*/} : set(), netPrefs, &nodesState); if (mode == OperationMode::DAGInit) doInitDAG(web3.ethereum()->blockChain().number() + (initDAG == PendingBlock ? 30000 : 0)); + if (keyManager.exists()) + while (masterPassword.empty()) + { + masterPassword = getPassword("Please enter your MASTER password: "); + if (!keyManager.load(masterPassword)) + { + cout << "Password invalid. Try again." << endl; + masterPassword.clear(); + } + } + else + { + while (masterPassword.empty()) + { + masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): "); + string confirm = getPassword("Please confirm the password by entering it again: "); + if (masterPassword != confirm) + { + cout << "Passwords were different. Try again." << endl; + masterPassword.clear(); + } + } + keyManager.create(masterPassword); + } + auto toNumber = [&](string const& s) -> unsigned { if (s == "latest") return web3.ethereum()->number(); @@ -1137,53 +1192,13 @@ int main(int argc, char** argv) if (remoteHost.size()) web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); - if (keyManager.exists()) - while (masterPassword.empty()) - { - masterPassword = getPassword("Please enter your MASTER password: "); - if (!keyManager.load(masterPassword)) - { - cout << "Password invalid. Try again." << endl; - masterPassword.clear(); - } - } - else - { - while (masterPassword.empty()) - { - masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): "); - string confirm = getPassword("Please confirm the password by entering it again: "); - if (masterPassword != confirm) - { - cout << "Passwords were different. Try again." << endl; - masterPassword.clear(); - } - } - keyManager.create(masterPassword); - } - - string logbuf; - bool silence = false; - std::string additional; - g_logPost = [&](std::string const& a, char const*) { if (silence) logbuf += a + "\n"; else cout << "\r \r" << a << endl << additional << flush; }; - - // TODO: give hints &c. - auto getPassword = [&](Address const& a){ - auto s = silence; - silence = true; - cout << endl; - string ret = dev::getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); - silence = s; - return ret; - }; - #if ETH_JSONRPC || !ETH_TRUE shared_ptr jsonrpcServer; unique_ptr jsonrpcConnector; if (jsonrpc > -1) { jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getPassword, keyManager), vector())); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector())); jsonrpcServer->StartListening(); } #endif @@ -1327,7 +1342,7 @@ int main(int argc, char** argv) if (jsonrpc < 0) jsonrpc = SensibleHttpPort; jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getPassword, keyManager), vector())); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector())); jsonrpcServer->StartListening(); } else if (cmd == "jsonstop") @@ -1479,7 +1494,7 @@ int main(int argc, char** argv) try { Address dest = h160(fromHex(hexAddr, WhenError::Throw)); - c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), amount, dest, bytes(), minGas); + c->submitTransaction(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), amount, dest, bytes(), minGas); } catch (BadHexCharacter& _e) { @@ -1548,7 +1563,7 @@ int main(int argc, char** argv) else if (gas < minGas) cwarn << "Minimum gas amount is" << minGas; else - c->submitTransaction(keyManager.secret(signingKey, [&](){ return getPassword(signingKey); }), endowment, init, gas, gasPrice); + c->submitTransaction(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), endowment, init, gas, gasPrice); } else cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX"; diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 41f1b1d49..95817e41c 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -199,12 +199,12 @@ private: #define DEV_TIMED_FUNCTION DEV_TIMED_SCOPE(__PRETTY_FUNCTION__) #endif -#define DEV_TIMED_IF(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(#S, MS), true); __eth_t.second; __eth_t.second = false) -#define DEV_TIMED_SCOPE_IF(S) ::dev::TimerHelper __eth_t(S, MS) +#define DEV_TIMED_ABOVE(S, MS) for (::std::pair<::dev::TimerHelper, bool> __eth_t(::dev::TimerHelper(#S, MS), true); __eth_t.second; __eth_t.second = false) +#define DEV_TIMED_SCOPE_ABOVE(S) ::dev::TimerHelper __eth_t(S, MS) #if WIN32 -#define DEV_TIMED_FUNCTION_IF(MS) DEV_TIMED_SCOPE_IF(__FUNCSIG__, MS) +#define DEV_TIMED_FUNCTION_ABOVE(MS) DEV_TIMED_SCOPE_ABOVE(__FUNCSIG__, MS) #else -#define DEV_TIMED_FUNCTION_IF(MS) DEV_TIMED_SCOPE_IF(__PRETTY_FUNCTION__, MS) +#define DEV_TIMED_FUNCTION_ABOVE(MS) DEV_TIMED_SCOPE_ABOVE(__PRETTY_FUNCTION__, MS) #endif enum class WithExisting: int diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index 7d790ccf6..ab19b2f74 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -65,15 +65,15 @@ void Worker::startWorking() m_state.exchange(ex); // cnote << "Waiting until not Stopped..."; - DEV_TIMED_IF(Worker stopping, 100) + DEV_TIMED_ABOVE(Worker stopping, 100) while (m_state == WorkerState::Stopped) this_thread::sleep_for(chrono::milliseconds(20)); } })); // cnote << "Spawning" << m_name; } - DEV_TIMED_IF(Start worker, 100) - while (m_state != WorkerState::Started) + DEV_TIMED_ABOVE(Start worker, 100) + while (m_state == WorkerState::Starting) this_thread::sleep_for(chrono::microseconds(20)); } @@ -85,7 +85,7 @@ void Worker::stopWorking() WorkerState ex = WorkerState::Started; m_state.compare_exchange_strong(ex, WorkerState::Stopping); - DEV_TIMED_IF(Stop worker, 100) + DEV_TIMED_ABOVE(Stop worker, 100) while (m_state != WorkerState::Stopped) this_thread::sleep_for(chrono::microseconds(20)); } @@ -99,7 +99,7 @@ void Worker::terminate() { m_state.exchange(WorkerState::Killing); - DEV_TIMED_IF(Terminate worker, 100) + DEV_TIMED_ABOVE(Terminate worker, 100) m_work->join(); m_work.reset(); diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index db1a17b0a..3724d8255 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -76,9 +76,9 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) return ret; } -void Ethash::prep(BlockInfo const& _header) +void Ethash::prep(BlockInfo const& _header, std::function const& _f) { - EthashAux::full(_header); + EthashAux::full((unsigned)_header.number, _f); } bool Ethash::preVerify(BlockInfo const& _header) @@ -310,6 +310,8 @@ void Ethash::GPUMiner::workLoop() unsigned device = instances() > 1 ? index() : s_deviceId; + if (!EthashAux::computeFull(w.blockNumber)) + return; EthashAux::FullType dag = EthashAux::full(w.blockNumber); bytesConstRef dagData = dag->data(); m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device); diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 1fcab55de..82db15e87 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -74,7 +74,7 @@ public: static std::string name(); static unsigned revision(); - static void prep(BlockInfo const& _header); + static void prep(BlockInfo const& _header, std::function const& _f = std::function()); static bool verify(BlockInfo const& _header); static bool preVerify(BlockInfo const& _header); static WorkPackage package(BlockInfo const& _header); diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index 7bcb976d6..f7a0ac41d 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -149,11 +149,6 @@ bytesConstRef EthashAux::FullAllocation::data() const return bytesConstRef((byte const*)ethash_full_dag(full), size()); } -EthashAux::FullType EthashAux::full(BlockInfo const& _header, function const& _f) -{ - return full((uint64_t)_header.number, _f); -} - static std::function s_dagCallback; static int dagCallbackShim(unsigned _p) { @@ -167,16 +162,43 @@ EthashAux::FullType EthashAux::full(uint64_t _blockNumber, functionx_fulls); - if ((ret = get()->m_fulls[seedHash].lock())) + DEV_GUARDED(get()->x_fulls) + if ((ret = get()->m_fulls[seedHash].lock())) + { + get()->m_lastUsedFull = ret; + return ret; + } + + s_dagCallback = _f; + ret = make_shared(l->light, dagCallbackShim); + + DEV_GUARDED(get()->x_fulls) + get()->m_fulls[seedHash] = get()->m_lastUsedFull = ret; + return ret; +} + +unsigned EthashAux::computeFull(uint64_t _blockNumber) +{ + Guard l(get()->x_fulls); + h256 seedHash = EthashAux::seedHash(_blockNumber); + if (FullType ret = get()->m_fulls[seedHash].lock()) { get()->m_lastUsedFull = ret; - return ret; + return 100; } - s_dagCallback = _f; - ret = get()->m_lastUsedFull = make_shared(l->light, dagCallbackShim); - get()->m_fulls[seedHash] = ret; - return ret; + + if (!get()->m_fullGenerator || !get()->m_fullGenerator->joinable()) + { + get()->m_fullProgress = 0; + get()->m_generatingFullNumber = _blockNumber / ETHASH_EPOCH_LENGTH * ETHASH_EPOCH_LENGTH; + get()->m_fullGenerator = unique_ptr(new thread([=](){ + get()->full(_blockNumber, [](unsigned p){ get()->m_fullProgress = p; return 0; }); + get()->m_fullProgress = 0; + get()->m_generatingFullNumber = NotGenerating; + })); + } + + return (get()->m_generatingFullNumber == _blockNumber) ? get()->m_fullProgress : 0; } Ethash::Result EthashAux::FullAllocation::compute(h256 const& _headerHash, Nonce const& _nonce) const diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index dbfe54d43..f57f7a4d3 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -19,7 +19,9 @@ * @date 2014 */ +#include #include +#include #include "Ethash.h" namespace dev @@ -65,7 +67,13 @@ public: static LightType light(BlockInfo const& _header); static LightType light(uint64_t _blockNumber); - static FullType full(BlockInfo const& _header, std::function const& _f = std::function()); + + static const uint64_t NotGenerating = (uint64_t)-1; + /// Kicks off generation of DAG for @a _blocknumber and @returns false or @returns true if ready. + static unsigned computeFull(uint64_t _blockNumber); + /// Information on the generation progress. + static std::pair fullGeneratingProgress() { return std::make_pair(get()->m_generatingFullNumber, get()->m_fullProgress); } + /// Kicks off generation of DAG for @a _blocknumber and blocks until ready; @returns result. static FullType full(uint64_t _blockNumber, std::function const& _f = std::function()); static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } @@ -83,8 +91,12 @@ private: std::unordered_map> m_lights; Mutex x_fulls; + std::condition_variable m_fullsChanged; std::unordered_map> m_fulls; FullType m_lastUsedFull; + std::unique_ptr m_fullGenerator; + uint64_t m_generatingFullNumber = NotGenerating; + unsigned m_fullProgress; Mutex x_epochs; std::unordered_map m_epochs; diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 235a6f540..2b271b1df 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -84,11 +84,11 @@ public: using Interface::submitTransaction; /// Makes the given call. Nothing is recorded into the state. - virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override; + virtual ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) override; using Interface::call; /// Makes the given create. Nothing is recorded into the state. - virtual ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock, FudgeFactor _ff = FudgeFactor::Strict) override; + virtual ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, FudgeFactor _ff = FudgeFactor::Strict) override; using Interface::create; using Interface::balanceAt; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 50e4b26ab..584de461b 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -515,10 +515,10 @@ pair State::sync(BlockChain const& _bc, TransactionQu cnote << i.first << "Dropping old transaction (nonce too low)"; _tq.drop(i.first); } - else if (got > req + 5) + else if (got > req + 25) { // too new - cnote << i.first << "Dropping new transaction (> 5 nonces ahead)"; + cnote << i.first << "Dropping new transaction (> 25 nonces ahead)"; _tq.drop(i.first); } else From 0315689bb37f63c714abc6194a29f8312e6f9e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 May 2015 16:14:18 +0200 Subject: [PATCH 29/35] Ping buildbot --- test/libevm/vm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libevm/vm.cpp b/test/libevm/vm.cpp index 2d67d7670..f9aac2eb2 100644 --- a/test/libevm/vm.cpp +++ b/test/libevm/vm.cpp @@ -432,7 +432,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) } } -} } // Namespace Close +} } // namespace close BOOST_AUTO_TEST_SUITE(VMTests) From 212a01136bd28d0f53cab2e07c018ae888977377 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 May 2015 16:16:44 +0200 Subject: [PATCH 30/35] Unify blocks with shared code. --- libevmasm/Assembly.cpp | 10 +++ libevmasm/AssemblyItem.h | 2 + libevmasm/BlockDeduplicator.cpp | 93 ++++++++++++++++++++++++++ libevmasm/BlockDeduplicator.h | 69 +++++++++++++++++++ test/libsolidity/SolidityOptimizer.cpp | 38 ++++++++++- 5 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 libevmasm/BlockDeduplicator.cpp create mode 100644 libevmasm/BlockDeduplicator.h diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index abcd44516..1011392b9 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include using namespace std; using namespace dev; @@ -348,8 +349,17 @@ Assembly& Assembly::optimise(bool _enable) copy(orig, iter, back_inserter(optimisedItems)); } } + if (optimisedItems.size() < m_items.size()) + { m_items = move(optimisedItems); + count++; + } + + // This only modifies PushTags, we have to run again to actually remove code. + BlockDeduplicator dedup(m_items); + if (dedup.deduplicate()) + count++; } } diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 6f2a65de9..b3012a7ea 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -68,6 +68,8 @@ public: /// @returns true iff the type and data of the items are equal. bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; } bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); } + /// Less-than operator compatible with operator==. + bool operator<(AssemblyItem const& _other) const { return std::tie(m_type, m_data) < std::tie(_other.m_type, _other.m_data); } /// @returns an upper bound for the number of bytes required by this item, assuming that /// the value of a jump tag takes @a _addressLength bytes. diff --git a/libevmasm/BlockDeduplicator.cpp b/libevmasm/BlockDeduplicator.cpp new file mode 100644 index 000000000..ca4f7e21a --- /dev/null +++ b/libevmasm/BlockDeduplicator.cpp @@ -0,0 +1,93 @@ +/* + 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 BlockDeduplicator.cpp + * @author Christian + * @date 2015 + * Unifies basic blocks that share content. + */ + +#include +#include +#include +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; + + +bool BlockDeduplicator::deduplicate() +{ + // Compares indices based on the suffix that starts there, ignoring tags and stopping at + // opcodes that stop the control flow. + function comparator = [&](size_t _i, size_t _j) + { + if (_i == _j) + return false; + + BlockIterator first(m_items.begin() + _i, m_items.end()); + BlockIterator second(m_items.begin() + _j, m_items.end()); + BlockIterator end(m_items.end(), m_items.end()); + + if (first != end && (*first).type() == Tag) + ++first; + if (second != end && (*second).type() == Tag) + ++second; + + return std::lexicographical_compare(first, end, second, end); + }; + + set> blocksSeen(comparator); + map tagReplacement; + for (size_t i = 0; i < m_items.size(); ++i) + { + if (m_items.at(i).type() != Tag) + continue; + auto it = blocksSeen.find(i); + if (it == blocksSeen.end()) + blocksSeen.insert(i); + else + tagReplacement[m_items.at(i).data()] = m_items.at(*it).data(); + } + + bool ret = false; + for (AssemblyItem& item: m_items) + if (item.type() == PushTag && tagReplacement.count(item.data())) + { + ret = true; + item.setData(tagReplacement.at(item.data())); + } + return ret; +} + +BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++() +{ + if (it == end) + return *this; + if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(eth::Instruction::JUMPI)) + it = end; + else + { + ++it; + while (it != end && it->type() == Tag) + ++it; + } + return *this; +} diff --git a/libevmasm/BlockDeduplicator.h b/libevmasm/BlockDeduplicator.h new file mode 100644 index 000000000..8a82a1ed7 --- /dev/null +++ b/libevmasm/BlockDeduplicator.h @@ -0,0 +1,69 @@ +/* + 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 BlockDeduplicator.h + * @author Christian + * @date 2015 + * Unifies basic blocks that share content. + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +class AssemblyItem; +using AssemblyItems = std::vector; + +/** + * Optimizer class to be used to unify blocks that share content. + * Modifies the passed vector in place. + */ +class BlockDeduplicator +{ +public: + BlockDeduplicator(AssemblyItems& _items): m_items(_items) {} + /// @returns true if something was changed + bool deduplicate(); + +private: + /// Iterator that skips tags skips to the end if (all branches of) the control + /// flow does not continue to the next instruction. + struct BlockIterator: std::iterator + { + public: + BlockIterator(AssemblyItems::const_iterator _it, AssemblyItems::const_iterator _end): + it(_it), end(_end) { } + BlockIterator& operator++(); + bool operator==(BlockIterator const& _other) const { return it == _other.it; } + bool operator!=(BlockIterator const& _other) const { return it != _other.it; } + AssemblyItem const& operator*() const { return *it; } + AssemblyItems::const_iterator it; + AssemblyItems::const_iterator end; + }; + + AssemblyItems& m_items; +}; + +} +} diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index e50469dd6..efc9316b0 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace std; using namespace dev::eth; @@ -125,7 +126,7 @@ public: BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); } - void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation) + AssemblyItems getCFG(AssemblyItems const& _input) { AssemblyItems output = _input; // Running it four times should be enough for these tests. @@ -138,6 +139,12 @@ public: back_inserter(optItems)); output = move(optItems); } + return output; + } + + void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation) + { + AssemblyItems output = getCFG(_input); BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); } @@ -925,6 +932,35 @@ BOOST_AUTO_TEST_CASE(control_flow_graph_do_not_remove_returned_to) checkCFG(input, {u256(2)}); } +BOOST_AUTO_TEST_CASE(block_deduplicator) +{ + AssemblyItems input{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + AssemblyItem(PushTag, 3), + u256(6), + eth::Instruction::SWAP3, + eth::Instruction::JUMP, + AssemblyItem(Tag, 1), + u256(6), + eth::Instruction::SWAP3, + eth::Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(6), + eth::Instruction::SWAP3, + eth::Instruction::JUMP, + AssemblyItem(Tag, 3) + }; + BlockDeduplicator dedup(input); + dedup.deduplicate(); + + set pushTags; + for (AssemblyItem const& item: input) + if (item.type() == PushTag) + pushTags.insert(item.data()); + BOOST_CHECK_EQUAL(pushTags.size(), 2); +} + BOOST_AUTO_TEST_SUITE_END() } From bf51bfef0e59c3be4e1ddcadc6b0c44a15b5d272 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 May 2015 17:00:23 +0200 Subject: [PATCH 31/35] Removed unnecessary include. --- libevmasm/BlockDeduplicator.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libevmasm/BlockDeduplicator.cpp b/libevmasm/BlockDeduplicator.cpp index ca4f7e21a..eadbe1b40 100644 --- a/libevmasm/BlockDeduplicator.cpp +++ b/libevmasm/BlockDeduplicator.cpp @@ -26,8 +26,6 @@ #include #include -#include - using namespace std; using namespace dev; using namespace dev::eth; From 18e223e9456195dbc56528927d211805de57202a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 May 2015 17:50:41 +0200 Subject: [PATCH 32/35] Reverse if and else body. --- libsolidity/Compiler.cpp | 12 ++++-- test/libsolidity/SolidityCompiler.cpp | 59 +++++++++++++-------------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index bcd4f9d68..66c503172 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -377,12 +377,16 @@ bool Compiler::visit(IfStatement const& _ifStatement) StackHeightChecker checker(m_context); CompilerContext::LocationSetter locationSetter(m_context, _ifStatement); compileExpression(_ifStatement.getCondition()); - eth::AssemblyItem trueTag = m_context.appendConditionalJump(); + m_context << eth::Instruction::ISZERO; + eth::AssemblyItem falseTag = m_context.appendConditionalJump(); + eth::AssemblyItem endTag = falseTag; + _ifStatement.getTrueStatement().accept(*this); if (_ifStatement.getFalseStatement()) + { + endTag = m_context.appendJumpToNew(); + m_context << falseTag; _ifStatement.getFalseStatement()->accept(*this); - eth::AssemblyItem endTag = m_context.appendJumpToNew(); - m_context << trueTag; - _ifStatement.getTrueStatement().accept(*this); + } m_context << endTag; checker.check(); diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index aa83c4650..dda7847ed 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -116,36 +116,35 @@ BOOST_AUTO_TEST_CASE(ifStatement) bytes code = compileContract(sourceCode); unsigned shift = 60; unsigned boilerplateSize = 73; - bytes expectation({byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x0, - byte(Instruction::DUP1), - byte(Instruction::PUSH1), byte(0x1b + shift), // "true" target - byte(Instruction::JUMPI), - // new check "else if" condition - byte(Instruction::DUP1), - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), byte(0x13 + shift), - byte(Instruction::JUMPI), - // "else" body - byte(Instruction::PUSH1), 0x4f, - byte(Instruction::POP), - byte(Instruction::PUSH1), byte(0x17 + shift), // exit path of second part - byte(Instruction::JUMP), - // "else if" body - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x4e, - byte(Instruction::POP), - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), byte(0x1f + shift), - byte(Instruction::JUMP), - // "if" body - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x4d, - byte(Instruction::POP), - byte(Instruction::JUMPDEST), - byte(Instruction::JUMPDEST), - byte(Instruction::POP), - byte(Instruction::JUMP)}); + bytes expectation({ + byte(Instruction::JUMPDEST), + byte(Instruction::PUSH1), 0x0, + byte(Instruction::DUP1), + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), byte(0x0f + shift), // "false" target + byte(Instruction::JUMPI), + // "if" body + byte(Instruction::PUSH1), 0x4d, + byte(Instruction::POP), + byte(Instruction::PUSH1), byte(0x21 + shift), + byte(Instruction::JUMP), + // new check "else if" condition + byte(Instruction::JUMPDEST), + byte(Instruction::DUP1), + byte(Instruction::ISZERO), + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), byte(0x1c + shift), + byte(Instruction::JUMPI), + // "else if" body + byte(Instruction::PUSH1), 0x4e, + byte(Instruction::POP), + byte(Instruction::PUSH1), byte(0x20 + shift), + byte(Instruction::JUMP), + // "else" body + byte(Instruction::JUMPDEST), + byte(Instruction::PUSH1), 0x4f, + byte(Instruction::POP), + }); checkCodePresentAt(code, expectation, boilerplateSize); } From 368532c107f383c909dde8e951693c9f42f00b0d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 13 May 2015 00:26:43 +0300 Subject: [PATCH 33/35] Extra diagnostics, assertions and fail-safes for tracking BadRoot bug. --- libdevcrypto/MemoryDB.cpp | 34 +++++++++++++++++------ libdevcrypto/MemoryDB.h | 11 ++++++-- libdevcrypto/OverlayDB.cpp | 57 +++++++++++++++++++++++++------------- libdevcrypto/OverlayDB.h | 8 +++--- libdevcrypto/TrieDB.cpp | 2 +- libdevcrypto/TrieDB.h | 36 ++++++++++++++---------- libethereum/BlockChain.cpp | 9 +++++- libethereum/Client.cpp | 2 +- libethereum/State.cpp | 12 +++++++- test/libdevcrypto/trie.cpp | 10 +++++++ 10 files changed, 128 insertions(+), 53 deletions(-) diff --git a/libdevcrypto/MemoryDB.cpp b/libdevcrypto/MemoryDB.cpp index 4b08db083..2cf56475b 100644 --- a/libdevcrypto/MemoryDB.cpp +++ b/libdevcrypto/MemoryDB.cpp @@ -32,6 +32,7 @@ const char* DBWarn::name() { return "TDB"; } std::unordered_map MemoryDB::get() const { + ReadGuard l(x_this); std::unordered_map ret; for (auto const& i: m_main) if (!m_enforceRefs || i.second.second > 0) @@ -39,21 +40,34 @@ std::unordered_map MemoryDB::get() const return ret; } +MemoryDB& MemoryDB::operator=(MemoryDB const& _c) +{ + if (this == &_c) + return *this; + ReadGuard l(_c.x_this); + WriteGuard l2(x_this); + m_main = _c.m_main; + m_aux = _c.m_aux; + return *this; +} + std::string MemoryDB::lookup(h256 const& _h) const { + ReadGuard l(x_this); auto it = m_main.find(_h); if (it != m_main.end()) { if (!m_enforceRefs || it->second.second > 0) return it->second.first; -// else if (m_enforceRefs && m_refCount.count(it->first) && !m_refCount.at(it->first)) -// cnote << "Lookup required for value with no refs. Let's hope it's in the DB." << _h; + else + cwarn << "Lookup required for value with refcount == 0. This is probably a critical trie issue" << _h; } return std::string(); } bool MemoryDB::exists(h256 const& _h) const { + ReadGuard l(x_this); auto it = m_main.find(_h); if (it != m_main.end() && (!m_enforceRefs || it->second.second > 0)) return true; @@ -62,6 +76,7 @@ bool MemoryDB::exists(h256 const& _h) const void MemoryDB::insert(h256 const& _h, bytesConstRef _v) { + WriteGuard l(x_this); auto it = m_main.find(_h); if (it != m_main.end()) { @@ -77,34 +92,34 @@ void MemoryDB::insert(h256 const& _h, bytesConstRef _v) bool MemoryDB::kill(h256 const& _h) { + ReadGuard l(x_this); if (m_main.count(_h)) { if (m_main[_h].second > 0) + { m_main[_h].second--; + return true; + } #if ETH_PARANOIA else { // If we get to this point, then there was probably a node in the level DB which we need to remove and which we have previously // used as part of the memory-based MemoryDB. Nothing to be worried about *as long as the node exists in the DB*. dbdebug << "NOKILL-WAS" << _h; - return false; } dbdebug << "KILL" << _h << "=>" << m_main[_h].second; - return true; } else { dbdebug << "NOKILL" << _h; - return false; - } -#else - } - return true; #endif + } + return false; } void MemoryDB::purge() { + WriteGuard l(x_this); for (auto it = m_main.begin(); it != m_main.end(); ) if (it->second.second) ++it; @@ -114,6 +129,7 @@ void MemoryDB::purge() h256Hash MemoryDB::keys() const { + ReadGuard l(x_this); h256Hash ret; for (auto const& i: m_main) if (i.second.second) diff --git a/libdevcrypto/MemoryDB.h b/libdevcrypto/MemoryDB.h index 3169d8fbc..169682815 100644 --- a/libdevcrypto/MemoryDB.h +++ b/libdevcrypto/MemoryDB.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,9 @@ class MemoryDB public: MemoryDB() {} + MemoryDB(MemoryDB const& _c) { operator=(_c); } + + MemoryDB& operator=(MemoryDB const& _c); void clear() { m_main.clear(); } // WARNING !!!! didn't originally clear m_refCount!!! std::unordered_map get() const; @@ -53,13 +57,14 @@ public: bool kill(h256 const& _h); void purge(); - bytes lookupAux(h256 const& _h) const { try { return m_aux.at(_h).first; } catch (...) { return bytes(); } } - void removeAux(h256 const& _h) { m_aux[_h].second = false; } - void insertAux(h256 const& _h, bytesConstRef _v) { m_aux[_h] = make_pair(_v.toBytes(), true); } + bytes lookupAux(h256 const& _h) const { ReadGuard l(x_this); auto it = m_aux.find(_h); if (it != m_aux.end() && (!m_enforceRefs || it->second.second)) return it->second.first; return bytes(); } + void removeAux(h256 const& _h) { WriteGuard l(x_this); m_aux[_h].second = false; } + void insertAux(h256 const& _h, bytesConstRef _v) { WriteGuard l(x_this); m_aux[_h] = make_pair(_v.toBytes(), true); } h256Hash keys() const; protected: + mutable SharedMutex x_this; std::unordered_map> m_main; std::unordered_map> m_aux; diff --git a/libdevcrypto/OverlayDB.cpp b/libdevcrypto/OverlayDB.cpp index 87c8a0927..957c7f0e3 100644 --- a/libdevcrypto/OverlayDB.cpp +++ b/libdevcrypto/OverlayDB.cpp @@ -19,6 +19,7 @@ * @date 2014 */ +#include #include #include #include @@ -29,6 +30,8 @@ using namespace dev; namespace dev { +h256 const EmptyTrie = sha3(rlp("")); + OverlayDB::~OverlayDB() { if (m_db.use_count() == 1 && m_db.get()) @@ -41,30 +44,41 @@ void OverlayDB::commit() { ldb::WriteBatch batch; // cnote << "Committing nodes to disk DB:"; - for (auto const& i: m_main) + DEV_READ_GUARDED(x_this) { -// cnote << i.first << "#" << m_main[i.first].second; - if (i.second.second) - batch.Put(ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.first.data(), i.second.first.size())); - } - for (auto const& i: m_aux) - if (i.second.second) + for (auto const& i: m_main) { - bytes b = i.first.asBytes(); - b.push_back(255); // for aux - batch.Put(bytesConstRef(&b), bytesConstRef(&i.second.first)); + if (i.second.second) + batch.Put(ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.first.data(), i.second.first.size())); +// cnote << i.first << "#" << m_main[i.first].second; } - m_db->Write(m_writeOptions, &batch); + for (auto const& i: m_aux) + if (i.second.second) + { + bytes b = i.first.asBytes(); + b.push_back(255); // for aux + batch.Put(bytesConstRef(&b), bytesConstRef(&i.second.first)); + } + } + + while (true) + { + ldb::Status o = m_db->Write(m_writeOptions, &batch); + if (o.ok()) + break; + cwarn << "Error writing to database. Sleeping a while then retrying. If it keeps saying this, free up some space!"; + this_thread::sleep_for(chrono::milliseconds(500)); + } m_aux.clear(); m_main.clear(); } } -bytes OverlayDB::lookupAux(h256 _h) const +bytes OverlayDB::lookupAux(h256 const& _h) const { bytes ret = MemoryDB::lookupAux(_h); if (!ret.empty()) - return ret; + return move(ret); std::string v; bytes b = _h.asBytes(); b.push_back(255); // for aux @@ -76,18 +90,19 @@ bytes OverlayDB::lookupAux(h256 _h) const void OverlayDB::rollback() { + WriteGuard l(x_this); m_main.clear(); } -std::string OverlayDB::lookup(h256 _h) const +std::string OverlayDB::lookup(h256 const& _h) const { std::string ret = MemoryDB::lookup(_h); if (ret.empty() && m_db) m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret); - return ret; + return move(ret); } -bool OverlayDB::exists(h256 _h) const +bool OverlayDB::exists(h256 const& _h) const { if (MemoryDB::exists(_h)) return true; @@ -97,16 +112,20 @@ bool OverlayDB::exists(h256 _h) const return !ret.empty(); } -void OverlayDB::kill(h256 _h) +void OverlayDB::kill(h256 const& _h) { -#if ETH_PARANOIA +#if ETH_PARANOIA || 1 if (!MemoryDB::kill(_h)) { std::string ret; if (m_db) m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret); - if (ret.empty()) + // No point node ref decreasing for EmptyTrie since we never bother incrementing it in the first place for + // empty storage tries. + if (ret.empty() && _h != EmptyTrie) cnote << "Decreasing DB node ref count below zero with no DB node. Probably have a corrupt Trie." << _h; + + // TODO: for 1.1: ref-counted triedb. } #else MemoryDB::kill(_h); diff --git a/libdevcrypto/OverlayDB.h b/libdevcrypto/OverlayDB.h index 7f7736ac1..2e5428bdf 100644 --- a/libdevcrypto/OverlayDB.h +++ b/libdevcrypto/OverlayDB.h @@ -46,11 +46,11 @@ public: void commit(); void rollback(); - std::string lookup(h256 _h) const; - bool exists(h256 _h) const; - void kill(h256 _h); + std::string lookup(h256 const& _h) const; + bool exists(h256 const& _h) const; + void kill(h256 const& _h); - bytes lookupAux(h256 _h) const; + bytes lookupAux(h256 const& _h) const; private: using MemoryDB::clear; diff --git a/libdevcrypto/TrieDB.cpp b/libdevcrypto/TrieDB.cpp index 6f84a3e29..719bd74ad 100644 --- a/libdevcrypto/TrieDB.cpp +++ b/libdevcrypto/TrieDB.cpp @@ -25,6 +25,6 @@ using namespace std; using namespace dev; h256 const dev::c_shaNull = sha3(rlp("")); -h256 const dev::EmptyTrie = c_shaNull; +h256 const dev::EmptyTrie = sha3(rlp("")); const char* TrieDBChannel::name() { return "-T-"; } diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index ff2bcc589..29b412bab 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -79,7 +79,7 @@ public: void open(DB* _db) { m_db = _db; } void open(DB* _db, h256 const& _root, Verification _v = Verification::Normal) { m_db = _db; setRoot(_root, _v); } - void init() { setRoot(insertNode(&RLPNull)); assert(node(m_root).size()); } + void init() { setRoot(forceInsertNode(&RLPNull)); assert(node(m_root).size()); } void setRoot(h256 const& _root, Verification _v = Verification::Normal) { @@ -88,11 +88,13 @@ public: { if (m_root == c_shaNull && !m_db->exists(m_root)) init(); - - /*std::cout << "Setting root to " << _root << " (patched to " << m_root << ")" << std::endl;*/ + } + /*std::cout << "Setting root to " << _root << " (patched to " << m_root << ")" << std::endl;*/ +#if ETH_DEBUG + if (_v == Verification::Normal) +#endif if (!node(m_root).size()) BOOST_THROW_EXCEPTION(RootNotFound()); - } } /// True if the trie is uninitialised (i.e. that the DB doesn't contain the root node). @@ -282,11 +284,17 @@ private: std::string deref(RLP const& _n) const; std::string node(h256 _h) const { return m_db->lookup(_h); } - void insertNode(h256 _h, bytesConstRef _v) { m_db->insert(_h, _v); } - void killNode(h256 _h) { m_db->kill(_h); } - h256 insertNode(bytesConstRef _v) { auto h = sha3(_v); insertNode(h, _v); return h; } - void killNode(RLP const& _d) { if (_d.data().size() >= 32) killNode(sha3(_d.data())); } + // These are low-level node insertion functions that just go straight through into the DB. + h256 forceInsertNode(bytesConstRef _v) { auto h = sha3(_v); forceInsertNode(h, _v); return h; } + void forceInsertNode(h256 _h, bytesConstRef _v) { m_db->insert(_h, _v); } + void forceKillNode(h256 _h) { m_db->kill(_h); } + + // This are semantically-aware node insertion functions that only kills when the node's + // data is < 32 bytes. It can safely be used when pruning the trie but won't work correctly + // for the special case of the root (which is always looked up via a hash). In that case, + // use forceKillNode(). + void killNode(RLP const& _d) { if (_d.data().size() >= 32) forceKillNode(sha3(_d.data())); } h256 m_root; DB* m_db = nullptr; @@ -743,8 +751,8 @@ template void GenericTrieDB::insert(bytesConstRef _key, bytesCons // However, we know it's the root node and thus always hashed. // So, if it's less than 32 (and thus should have been deleted but wasn't) then we delete it here. if (rv.size() < 32) - killNode(m_root); - m_root = insertNode(&b); + forceKillNode(m_root); + m_root = forceInsertNode(&b); } template std::string GenericTrieDB::at(bytesConstRef _key) const @@ -890,8 +898,8 @@ template void GenericTrieDB::remove(bytesConstRef _key) if (b.size()) { if (rv.size() < 32) - killNode(m_root); - m_root = insertNode(&b); + forceKillNode(m_root); + m_root = forceInsertNode(&b); } } @@ -1081,7 +1089,7 @@ template RLPStream& GenericTrieDB::streamNode(RLPStream& _s, byte if (_b.size() < 32) _s.appendRaw(_b); else - _s.append(insertNode(&_b)); + _s.append(forceInsertNode(&_b)); return _s; } @@ -1122,7 +1130,7 @@ template bytes GenericTrieDB::graft(RLP const& _orig) // remove second item from the trie after derefrencing it into s & n. auto lh = _orig[1].toHash(); s = node(lh); - killNode(lh); + forceKillNode(lh); n = RLP(s); } assert(n.itemCount() == 2); diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index f13f83588..32a11ee53 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -466,7 +466,14 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import blb.blooms.push_back(s.receipt(i).bloom()); br.receipts.push_back(s.receipt(i)); } - s.cleanup(true); + try { + s.cleanup(true); + } + catch (BadRoot) + { + cwarn << "BadRoot error. Retrying import later."; + BOOST_THROW_EXCEPTION(FutureTime()); + } td = pd.totalDifficulty + tdIncrease; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index a43c98aa2..6d934f3ad 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -164,7 +164,7 @@ const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; } #endif Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): - Worker("eth"), + Worker("eth", 0), m_vc(_dbPath), m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), m_gp(new TrivialGasPricer), diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 584de461b..167d6236e 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -707,11 +707,21 @@ void State::cleanup(bool _fullCommit) { if (_fullCommit) { - paranoia("immediately before database commit", true); // Commit the new trie to disk. clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash()))); + + try { + EnforceRefs er(m_db, true); + rootHash(); + } + catch (BadRoot const&) + { + clog(StateChat) << "Trie corrupt! :-("; + throw; + } + m_db.commit(); clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash()))); diff --git a/test/libdevcrypto/trie.cpp b/test/libdevcrypto/trie.cpp index 3b3fa1dd2..d41739a01 100644 --- a/test/libdevcrypto/trie.cpp +++ b/test/libdevcrypto/trie.cpp @@ -102,10 +102,13 @@ BOOST_AUTO_TEST_CASE(hex_encoded_securetrie_test) { next_permutation(ss.begin(), ss.end()); MemoryDB m; + EnforceRefs r(m, true); GenericTrieDB t(&m); MemoryDB hm; + EnforceRefs hr(hm, true); HashedGenericTrieDB ht(&hm); MemoryDB fm; + EnforceRefs fr(fm, true); FatGenericTrieDB ft(&fm); t.init(); ht.init(); @@ -164,10 +167,13 @@ BOOST_AUTO_TEST_CASE(trie_test_anyorder) { next_permutation(ss.begin(), ss.end()); MemoryDB m; + EnforceRefs r(m, true); GenericTrieDB t(&m); MemoryDB hm; + EnforceRefs hr(hm, true); HashedGenericTrieDB ht(&hm); MemoryDB fm; + EnforceRefs fr(fm, true); FatGenericTrieDB ft(&fm); t.init(); ht.init(); @@ -244,10 +250,13 @@ BOOST_AUTO_TEST_CASE(trie_tests_ordered) } MemoryDB m; + EnforceRefs r(m, true); GenericTrieDB t(&m); MemoryDB hm; + EnforceRefs hr(hm, true); HashedGenericTrieDB ht(&hm); MemoryDB fm; + EnforceRefs fr(fm, true); FatGenericTrieDB ft(&fm); t.init(); ht.init(); @@ -360,6 +369,7 @@ BOOST_AUTO_TEST_CASE(moreTrieTests) #endif { MemoryDB m; + EnforceRefs r(m, true); GenericTrieDB d(&m); d.init(); // initialise as empty tree. MemTrie t; From 6978731367a1fe1caec11abaaaeea92349125c8a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 13 May 2015 00:36:13 +0300 Subject: [PATCH 34/35] Bump. --- libdevcore/Common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 06950cbee..6723ab76d 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -28,7 +28,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.18"; +char const* Version = "0.9.19"; const u256 UndefinedU256 = ~(u256)0; From d638df83ae76519c506839ea827482074be11fb0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 13 May 2015 11:45:18 +0300 Subject: [PATCH 35/35] Revert "CMake: set default RUNTIME_OUTPUT_DIRECTORY property to "bin"" --- CMakeLists.txt | 1 - cmake/EthExecutableHelper.cmake | 44 ++++++++++++++++++--------------- cmake/scripts/copydlls.cmake | 3 ++- libevmasm/CMakeLists.txt | 7 +++++- test/CMakeLists.txt | 9 ++----- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac37454d8..289cecad8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,7 +295,6 @@ message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}" message("------------------------------------------------------------------------") message("") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") set(CMAKE_THREAD_LIBS_INIT pthread) include(EthCompilerSettings) diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake index 3dd7fa798..1d1cb887b 100644 --- a/cmake/EthExecutableHelper.cmake +++ b/cmake/EthExecutableHelper.cmake @@ -45,26 +45,25 @@ endmacro() macro(eth_copy_dlls EXECUTABLE DLLS) # dlls must be unsubstitud list variable (without ${}) in format - # optimized;path_to_dll.dll;debug;path_to_dlld.dll + # optimized;path_to_dll.dll;debug;path_to_dlld.dll list(GET ${DLLS} 1 DLL_RELEASE) list(GET ${DLLS} 3 DLL_DEBUG) - get_target_property(TARGET_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE} RUNTIME_OUTPUT_DIRECTORY) add_custom_command(TARGET ${EXECUTABLE} - POST_BUILD - COMMAND ${CMAKE_COMMAND} ARGS - -DDLL_RELEASE="${DLL_RELEASE}" - -DDLL_DEBUG="${DLL_DEBUG}" + POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS + -DDLL_RELEASE="${DLL_RELEASE}" + -DDLL_DEBUG="${DLL_DEBUG}" -DCONF="$" - -DDESTINATION="${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}" + -DDESTINATION="${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" -P "${ETH_SCRIPTS_DIR}/copydlls.cmake" ) endmacro() -# +# # this function requires the following variables to be specified: # ETH_DEPENDENCY_INSTALL_DIR # -# params: +# params: # QMLDIR # @@ -75,7 +74,7 @@ macro(eth_install_executable EXECUTABLE) set (one_value_args QMLDIR) set (multi_value_args DLLS) cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") - + if (ETH_INSTALL_EXECUTABLE_QMLDIR) if (APPLE) set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}") @@ -88,15 +87,19 @@ macro(eth_install_executable EXECUTABLE) if (APPLE) # First have qt5 install plugins and frameworks add_custom_command(TARGET ${EXECUTABLE} POST_BUILD - COMMAND ${MACDEPLOYQT_APP} ${EXECUTABLE}.app -executable=${EXECUTABLE}.app/Contents/MacOS/${EXECUTABLE} ${eth_qml_dir} - WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR} - COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${EXECUTABLE}.app/Contents + COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app -executable=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents/MacOS/${EXECUTABLE} ${eth_qml_dir} + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents ) - - # TODO: This should only happen for GUI application - set(APP_BUNDLE_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INDIR}/${EXECUTABLE}.app") - + # This tool and next will inspect linked libraries in order to determine which dependencies are required + if (${CMAKE_CFG_INTDIR} STREQUAL ".") + # TODO: This should only happen for GUI application + set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXECUTABLE}.app") + else () + set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app") + endif () + install(CODE " include(BundleUtilities) set(BU_CHMOD_BUNDLE_ITEMS 1) @@ -108,15 +111,14 @@ macro(eth_install_executable EXECUTABLE) get_target_property(TARGET_LIBS ${EXECUTABLE} INTERFACE_LINK_LIBRARIES) string(REGEX MATCH "Qt5::Core" HAVE_QT ${TARGET_LIBS}) if ("${HAVE_QT}" STREQUAL "Qt5::Core") - get_target_property(TARGET_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE} RUNTIME_OUTPUT_DIRECTORY) add_custom_command(TARGET ${EXECUTABLE} POST_BUILD - COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}" + COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}" WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) #workaround for https://bugreports.qt.io/browse/QTBUG-42083 add_custom_command(TARGET ${EXECUTABLE} POST_BUILD COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf" - WORKING_DIRECTORY ${TARGET_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR} VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM ) endif() @@ -142,3 +144,5 @@ macro(eth_install_executable EXECUTABLE) endif () endmacro() + + diff --git a/cmake/scripts/copydlls.cmake b/cmake/scripts/copydlls.cmake index 57eb0ffd4..6d86b8e4e 100644 --- a/cmake/scripts/copydlls.cmake +++ b/cmake/scripts/copydlls.cmake @@ -14,4 +14,5 @@ else () # Debug set(DLL ${DLL_DEBUG}) endif() -execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${DLL}" "${DESTINATION}") +execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${DLL}" "${DESTINATION}") + diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt index eb8fea95c..f8150806f 100644 --- a/libevmasm/CMakeLists.txt +++ b/libevmasm/CMakeLists.txt @@ -19,10 +19,15 @@ set(EXECUTABLE evmasm) file(GLOB HEADERS "*.h") -add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) +if (ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) +endif() target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcrypto) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bedbe42f3..39a235c58 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -95,12 +95,6 @@ if (JSONRPC) target_link_libraries(testeth ${JSON_RPC_CPP_CLIENT_LIBRARIES}) endif() -if (UNIX) # Create symlink to old testeth location to make bildbot happy - add_custom_command(TARGET testeth POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_BINARY_DIR}/bin/testeth ${CMAKE_BINARY_DIR}/test/testeth - ) -endif() - enable_testing() set(CTEST_OUTPUT_ON_FAILURE TRUE) @@ -116,6 +110,7 @@ eth_add_test(ClientBase ) eth_add_test(JsonRpc - ARGS --eth_testfile=BlockTests/bcJS_API_Test + ARGS --eth_testfile=BlockTests/bcJS_API_Test ARGS --eth_testfile=BlockTests/bcValidBlockTest ) +