From bfc037f68858b03d02cafc182a3ca45d7ce2c308 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 15 Feb 2015 18:00:49 +0100 Subject: [PATCH] Transact dialog supports all the NatSpec stuff. --- alethzero/CMakeLists.txt | 3 +- alethzero/Context.cpp | 33 +++ alethzero/Context.h | 27 +++ alethzero/Debugger.ui | 34 ++- alethzero/Main.ui | 286 +++--------------------- alethzero/MainWin.cpp | 320 +-------------------------- alethzero/MainWin.h | 45 +--- alethzero/NatspecHandler.h | 5 +- alethzero/OurWebThreeStubServer.cpp | 2 +- alethzero/Transact.cpp | 325 ++++++++++++++++++++++++++++ alethzero/Transact.h | 82 +++++++ alethzero/Transact.ui | 244 +++++++++++++++++++++ libethcore/CommonEth.cpp | 10 - libethcore/CommonEth.h | 33 ++- 14 files changed, 810 insertions(+), 639 deletions(-) create mode 100644 alethzero/Transact.cpp create mode 100644 alethzero/Transact.h create mode 100644 alethzero/Transact.ui diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index eba34ca75..9e41260fe 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -16,6 +16,7 @@ include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) qt5_wrap_ui(ui_Main.h Main.ui) qt5_wrap_ui(ui_Debugger.h Debugger.ui) +qt5_wrap_ui(ui_Transact.h Transact.ui) file(GLOB HEADERS "*.h") @@ -28,7 +29,7 @@ endif () # eth_add_executable is defined in cmake/EthExecutableHelper.cmake eth_add_executable(${EXECUTABLE} ICON alethzero - UI_RESOURCES alethzero.icns Main.ui Debugger.ui + UI_RESOURCES alethzero.icns Main.ui Debugger.ui Transact.ui WIN_RESOURCES alethzero.rc ) diff --git a/alethzero/Context.cpp b/alethzero/Context.cpp index 0505a03e0..d187e6d47 100644 --- a/alethzero/Context.cpp +++ b/alethzero/Context.cpp @@ -20,7 +20,40 @@ */ #include "Context.h" +#include +#include +using namespace std; +using namespace dev; +using namespace dev::eth; + +NatSpecFace::~NatSpecFace() +{ +} Context::~Context() { } + +void initUnits(QComboBox* _b) +{ + for (auto n = (unsigned)units().size(); n-- != 0; ) + _b->addItem(QString::fromStdString(units()[n].second), n); +} + +vector keysAsVector(QList const& keys) +{ + auto list = keys.toStdList(); + return {begin(list), end(list)}; +} + +bool sourceIsSolidity(string const& _source) +{ + // TODO: Improve this heuristic + return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol"); +} + +bool sourceIsSerpent(string const& _source) +{ + // TODO: Improve this heuristic + return (_source.substr(0, 5) == "//ser"); +} diff --git a/alethzero/Context.h b/alethzero/Context.h index 2a41988a3..a2fb1a130 100644 --- a/alethzero/Context.h +++ b/alethzero/Context.h @@ -22,11 +22,38 @@ #pragma once #include +#include #include +#include #include +class QComboBox; + namespace dev { namespace eth { class StateDiff; } } +#define Small "font-size: small; " +#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; " +#define Div(S) "
" +#define Span(S) "" + +void initUnits(QComboBox* _b); + +std::vector keysAsVector(QList const& _keys); + +bool sourceIsSolidity(std::string const& _source); +bool sourceIsSerpent(std::string const& _source); + +class NatSpecFace +{ +public: + virtual ~NatSpecFace(); + + virtual void add(dev::h256 const& _contractHash, std::string const& _doc) = 0; + virtual std::string retrieve(dev::h256 const& _contractHash) const = 0; + virtual std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData) = 0; + virtual std::string getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionDacta) = 0; +}; + class Context { public: diff --git a/alethzero/Debugger.ui b/alethzero/Debugger.ui index 59b706fab..6751655ae 100644 --- a/alethzero/Debugger.ui +++ b/alethzero/Debugger.ui @@ -17,12 +17,24 @@ - QFrame::StyledPanel + QFrame::NoFrame QFrame::Raised + + 0 + + + 0 + + + 0 + + + 0 + @@ -83,6 +95,12 @@ + + + 0 + 0 + + Qt::Horizontal @@ -213,12 +231,24 @@ - QFrame::StyledPanel + QFrame::NoFrame QFrame::Raised + + 0 + + + 0 + + + 0 + + + 0 + diff --git a/alethzero/Main.ui b/alethzero/Main.ui index fe475bae9..b7c4f6c96 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -144,6 +144,7 @@ + @@ -168,7 +169,6 @@ - @@ -516,237 +516,11 @@ false + - - - - 0 - 0 - - - - - 510 - 386 - - - - QDockWidget::DockWidgetFeatureMask - - - Transact - - - 1 - - - - - - - - 0 - 0 - - - - &Data - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - data - - - - - - - Qt::Vertical - - - - QFrame::NoFrame - - - 0 - - - - - Qt::ClickFocus - - - QFrame::NoFrame - - - 0 - - - true - - - - - - - - &Gas - - - gas - - - - - - - - 0 - 0 - - - - &To - - - destination - - - - - - - gas - - - 1 - - - 430000000 - - - 10000 - - - - - - - - - - - - - @ - - - 1 - - - 430000000 - - - - - - - false - - - true - - - - - - - - - - - - - 430000000 - - - 0 - - - - - - - &Amount - - - value - - - - - - - - 0 - 0 - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - - (Create Contract) - - - - - - - - &Execute - - - - - - - - 0 - 0 - - - - - - - - - - - De&bug - - - - - - QDockWidget::DockWidgetFeatureMask @@ -1384,9 +1158,6 @@ font-size: 14pt Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - data - @@ -1439,9 +1210,6 @@ font-size: 14pt TTL - - destination - @@ -1455,9 +1223,6 @@ font-size: 14pt From - - destination - @@ -1471,9 +1236,6 @@ font-size: 14pt To - - destination - @@ -1504,9 +1266,6 @@ font-size: 14pt Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - data - @@ -1533,9 +1292,6 @@ font-size: 14pt Work to Prove - - destination - @@ -1620,7 +1376,7 @@ font-size: 14pt - &New Address + &New Address... @@ -1891,6 +1647,11 @@ font-size: 14pt &Kill Account + + + New &Transaction... + + @@ -1913,23 +1674,30 @@ font-size: 14pt - destination - calculatedName - value - valueUnits - gas - gasPrice - gasPriceUnits - data - send - idealPeers - port - clientName + shhTo + shhFrom + shhTtl + shhTopic + shhWork + shhData + log + post verbosity + jsConsole tabWidget urlEdit webView + idealPeers + forceAddress + port + clientName + transactionQueue + pendingInfo + blockChainFilter nameReg + nodes + whispers + jsInput diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index f794f6758..1d497cc0b 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -56,6 +56,8 @@ #include "MiningView.h" #include "BuildInfo.h" #include "OurWebThreeStubServer.h" +#include "Transact.h" +#include "Debugger.h" #include "ui_Main.h" using namespace std; using namespace dev; @@ -63,17 +65,6 @@ using namespace dev::p2p; using namespace dev::eth; namespace js = json_spirit; -#define Small "font-size: small; " -#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; " -#define Div(S) "
" -#define Span(S) "" - -static void initUnits(QComboBox* _b) -{ - for (auto n = (unsigned)units().size(); n-- != 0; ) - _b->addItem(QString::fromStdString(units()[n].second), n); -} - QString Main::fromRaw(h256 _n, unsigned* _inc) { if (_n) @@ -99,12 +90,6 @@ QString Main::fromRaw(h256 _n, unsigned* _inc) return QString(); } -static vector keysAsVector(QList const& keys) -{ - auto list = keys.toStdList(); - return {begin(list), end(list)}; -} - QString contentsOfQResource(string const& res) { QFile file(QString::fromStdString(res)); @@ -120,7 +105,8 @@ Address c_newConfig = Address("c6d9d2cd449a754c494264e1809c50e34d64562b"); Main::Main(QWidget *parent) : QMainWindow(parent), - ui(new Ui::Main) + ui(new Ui::Main), + m_transact(this, this) { setWindowFlags(Qt::Window); ui->setupUi(this); @@ -149,12 +135,6 @@ Main::Main(QWidget *parent) : ui->configDock->close(); on_verbosity_valueChanged(); - initUnits(ui->gasPriceUnits); - initUnits(ui->valueUnits); - ui->valueUnits->setCurrentIndex(6); - ui->gasPriceUnits->setCurrentIndex(4); - ui->gasPrice->setValue(10); - on_destination_currentTextChanged(); statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->peerCount); @@ -373,12 +353,6 @@ void Main::on_forceMining_triggered() ethereum()->setForceMining(ui->forceMining->isChecked()); } -void Main::on_enableOptimizer_triggered() -{ - m_enableOptimizer = ui->enableOptimizer->isChecked(); - on_data_textChanged(); -} - QString Main::contents(QString _s) { return QString::fromStdString(dev::asString(dev::contents(_s.toStdString()))); @@ -412,6 +386,12 @@ void Main::load(QString _s) }*/ } +void Main::on_newTransaction_triggered() +{ + m_transact.setEnvironment(m_myKeys, ethereum(), &m_natSpecDB); + m_transact.exec(); +} + void Main::on_loadJS_triggered() { QString f = QFileDialog::getOpenFileName(this, "Load Javascript", QString(), "Javascript (*.js);;All files (*)"); @@ -673,7 +653,6 @@ void Main::writeSettings() s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("showAll", ui->showAll->isChecked()); s.setValue("showAllAccounts", ui->showAllAccounts->isChecked()); - s.setValue("enableOptimizer", m_enableOptimizer); s.setValue("clientName", ui->clientName->text()); s.setValue("idealPeers", ui->idealPeers->value()); s.setValue("port", ui->port->value()); @@ -743,8 +722,6 @@ void Main::readSettings(bool _skipGeometry) ui->paranoia->setChecked(s.value("paranoia", false).toBool()); ui->showAll->setChecked(s.value("showAll", false).toBool()); ui->showAllAccounts->setChecked(s.value("showAllAccounts", false).toBool()); - m_enableOptimizer = s.value("enableOptimizer", true).toBool(); - ui->enableOptimizer->setChecked(m_enableOptimizer); ui->clientName->setText(s.value("clientName", "").toString()); if (ui->clientName->text().isEmpty()) ui->clientName->setText(QInputDialog::getText(nullptr, "Enter identity", "Enter a name that will identify you on the peer network")); @@ -991,7 +968,6 @@ void Main::refreshNetwork() void Main::refreshAll() { - refreshDestination(); refreshBlockChain(); refreshBlockCount(); refreshPending(); @@ -1042,20 +1018,6 @@ void Main::refreshAccounts() } } -void Main::refreshDestination() -{ - cwatch << "refreshDestination()"; - QString s; - for (auto i: ethereum()->addresses()) - if ((s = pretty(i)).size()) - // A namereg address - if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1) - ui->destination->addItem(s); - for (int i = 0; i < ui->destination->count(); ++i) - if (ui->destination->itemText(i) != "(Create Contract)" && !fromString(ui->destination->itemText(i))) - ui->destination->removeItem(i--); -} - void Main::refreshBlockCount() { cwatch << "refreshBlockCount()"; @@ -1573,19 +1535,6 @@ void Main::on_contracts_doubleClicked() qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); } -void Main::on_destination_currentTextChanged() -{ - if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)") - if (Address a = fromString(ui->destination->currentText())) - ui->calculatedName->setText(render(a)); - else - ui->calculatedName->setText("Unknown Address"); - else - ui->calculatedName->setText("Create Contract"); - on_data_textChanged(); -// updateFee(); -} - static shh::FullTopic topicFromText(QString _s) { shh::BuildTopic ret; @@ -1640,133 +1589,6 @@ static shh::FullTopic topicFromText(QString _s) return ret; } -bool Main::sourceIsSolidity(string const& _source) -{ - // TODO: Improve this heuristic - return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol"); -} - -static bool sourceIsSerpent(string const& _source) -{ - // TODO: Improve this heuristic - return (_source.substr(0, 5) == "//ser"); -} - -string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compiler, - string const& _contractName) -{ - string ret = ""; - auto const& contract = _compiler.getContractDefinition(_contractName); - auto interfaceFunctions = contract.getInterfaceFunctions(); - - for (auto const& it: interfaceFunctions) - { - ret += it.first.abridged(); - ret += " :"; - ret += it.second->getDeclaration().getName() + "\n"; - } - return ret; -} - -void Main::on_data_textChanged() -{ - if (isCreation()) - { - string src = ui->data->toPlainText().toStdString(); - vector errors; - QString lll; - QString solidity; - if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0) - { - m_data = fromHex(src); - } - else if (sourceIsSolidity(src)) - { - dev::solidity::CompilerStack compiler; - try - { -// compiler.addSources(dev::solidity::StandardSources); - m_data = compiler.compile(src, m_enableOptimizer); - solidity = "

Solidity

"; - solidity += "
var " + QString::fromStdString(compiler.defaultContractName()) + " = web3.eth.contractFromAbi(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + ");
"; - solidity += "
" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "
"; - solidity += "
" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "
"; - } - catch (dev::Exception const& exception) - { - ostringstream error; - solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); - solidity = "

Solidity

" + QString::fromStdString(error.str()).toHtmlEscaped() + "
"; - } - catch (...) - { - solidity = "

Solidity

Uncaught exception.
"; - } - } -#ifndef _MSC_VER - else if (sourceIsSerpent(src)) - { - try - { - m_data = dev::asBytes(::compile(src)); - for (auto& i: errors) - i = "(LLL " + i + ")"; - } - catch (string err) - { - errors.push_back("Serpent " + err); - } - } -#endif - else - { - m_data = compileLLL(src, m_enableOptimizer, &errors); - if (errors.empty()) - { - auto asmcode = compileLLLToAsm(src, false); - lll = "

Pre

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
"; - if (m_enableOptimizer) - { - asmcode = compileLLLToAsm(src, true); - lll = "

Opt

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
" + lll; - } - } - } - QString errs; - if (errors.size()) - { - errs = "

Errors

"; - for (auto const& i: errors) - errs.append("
" + QString::fromStdString(i).toHtmlEscaped() + "
"); - } - ui->code->setHtml(errs + lll + solidity + "

Code

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped() + "

Hex

" Div(Mono) + QString::fromStdString(toHex(m_data)) + "
"); - ui->gas->setMinimum((qint64)Client::txGas(m_data, 0)); - if (!ui->gas->isEnabled()) - ui->gas->setValue(m_backupGas); - ui->gas->setEnabled(true); - } - else - { - m_data = parseData(ui->data->toPlainText().toStdString()); - ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true))); - if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size()) - { - ui->gas->setMinimum((qint64)Client::txGas(m_data, 1)); - if (!ui->gas->isEnabled()) - ui->gas->setValue(m_backupGas); - ui->gas->setEnabled(true); - } - else - { - if (ui->gas->isEnabled()) - m_backupGas = ui->gas->value(); - ui->gas->setValue((qint64)Client::txGas(m_data)); - ui->gas->setEnabled(false); - } - } - updateFee(); -} - void Main::on_clearPending_triggered() { writeSettings(); @@ -1791,54 +1613,6 @@ void Main::on_killBlockchain_triggered() refreshAll(); } -bool Main::isCreation() const -{ - return ui->destination->currentText().isEmpty() || ui->destination->currentText() == "(Create Contract)"; -} - -u256 Main::fee() const -{ - return ui->gas->value() * gasPrice(); -} - -u256 Main::value() const -{ - if (ui->valueUnits->currentIndex() == -1) - return 0; - return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; -} - -u256 Main::gasPrice() const -{ - if (ui->gasPriceUnits->currentIndex() == -1) - return 0; - return ui->gasPrice->value() * units()[units().size() - 1 - ui->gasPriceUnits->currentIndex()].first; -} - -u256 Main::total() const -{ - return value() + fee(); -} - -void Main::updateFee() -{ - ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str())); - auto totalReq = total(); - ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); - - bool ok = false; - for (auto i: m_myKeys) - if (ethereum()->balanceAt(i.address()) >= totalReq) - { - ok = true; - break; - } - ui->send->setEnabled(ok); - QPalette p = ui->total->palette(); - p.setColor(QPalette::WindowText, QColor(ok ? 0x00 : 0x80, 0x00, 0x00)); - ui->total->setPalette(p); -} - void Main::on_net_triggered() { ui->port->setEnabled(!ui->net->isChecked()); @@ -1900,76 +1674,12 @@ void Main::on_mine_triggered() ethereum()->stopMining(); } -void Main::on_send_clicked() -{ - u256 totalReq = value() + fee(); - for (auto i: m_myKeys) - if (ethereum()->balanceAt(i.address(), 0) >= totalReq) - { - Secret s = i.secret(); - if (isCreation()) - { - // If execution is a contract creation, add Natspec to - // a local Natspec LEVELDB - ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice()); - string src = ui->data->toPlainText().toStdString(); - if (sourceIsSolidity(src)) - try - { - dev::solidity::CompilerStack compiler; - m_data = compiler.compile(src, m_enableOptimizer); - for (string& s: compiler.getContractNames()) - { - h256 contractHash = compiler.getContractCodeHash(s); - m_natspecDB.add(contractHash, compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser)); - } - } - catch (...) - { - statusBar()->showMessage("Couldn't compile Solidity Contract."); - } - } - else - ethereum()->transact(s, value(), fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); - return; - } - statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount."); -} - void Main::keysChanged() { onBalancesChange(); m_server->setAccounts(keysAsVector(m_myKeys)); } -void Main::on_debug_clicked() -{ - try - { - u256 totalReq = value() + fee(); - for (auto i: m_myKeys) - if (ethereum()->balanceAt(i.address()) >= totalReq) - { - State st(ethereum()->postState()); - Secret s = i.secret(); - Transaction t = isCreation() ? - Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(dev::toAddress(s)), s) : - Transaction(value(), gasPrice(), ui->gas->value(), fromString(ui->destination->currentText()), m_data, st.transactionsFrom(dev::toAddress(s)), s); - Debugger dw(this, this); - Executive e(st, ethereum()->blockChain(), 0); - dw.populate(e, t); - dw.exec(); - return; - } - statusBar()->showMessage("Couldn't make transaction: no single account contains at least the required amount."); - } - catch (dev::Exception const& _e) - { - statusBar()->showMessage("Error running transaction: " + QString::fromStdString(diagnostic_information(_e))); - // this output is aimed at developers, reconsider using _e.what for more user friendly output. - } -} - bool beginsWith(Address _a, bytes const& _b) { for (unsigned i = 0; i < min(20, _b.size()); ++i) @@ -2095,16 +1805,6 @@ void Main::on_post_clicked() whisper()->inject(m.seal(from, topicFromText(ui->shhTopic->toPlainText()), ui->shhTtl->value(), ui->shhWork->value())); } -string Main::lookupNatSpec(dev::h256 const& _contractHash) const -{ - return m_natspecDB.retrieve(_contractHash); -} - -string Main::lookupNatSpecUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData) -{ - return m_natspecDB.getUserNotice(_contractHash, _transactionData); -} - int Main::authenticate(QString _title, QString _text) { QMessageBox userInput(this); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index d503a16dd..6c4a97301 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -37,7 +37,7 @@ #include #include #include "Context.h" -#include "Debugger.h" +#include "Transact.h" #include "NatspecHandler.h" namespace Ui { @@ -72,11 +72,8 @@ public: dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } std::shared_ptr whisper() const { return m_webThree->whisper(); } - std::string lookupNatSpec(dev::h256 const& _contractHash) const; - std::string lookupNatSpecUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData); + NatSpecFace* natSpec() { return &m_natSpecDB; } - QList owned() const { return m_myIdentities + m_myKeys; } - QVariant evalRaw(QString const& _js); QString pretty(dev::Address _a) const override; @@ -85,6 +82,10 @@ public: dev::Address fromString(QString const& _a) const override; std::string renderDiff(dev::eth::StateDiff const& _d) const override; + QList owned() const { return m_myIdentities + m_myKeys; } + + dev::u256 gasPrice() const { return 10 * dev::eth::szabo; } + public slots: void load(QString _file); void note(QString _entry); @@ -118,17 +119,6 @@ private slots: void on_showAllAccounts_triggered() { refreshAccounts(); } void on_preview_triggered(); - // Transacting - void on_value_valueChanged() { updateFee(); } - void on_gas_valueChanged() { updateFee(); } - void on_valueUnits_currentIndexChanged() { updateFee(); } - void on_gasPriceUnits_currentIndexChanged() { updateFee(); } - void on_gasPrice_valueChanged() { updateFee(); } - void on_destination_currentTextChanged(); - void on_data_textChanged(); - void on_send_clicked(); - void on_debug_clicked(); - // Account management void on_newAccount_triggered(); void on_killAccount_triggered(); @@ -137,6 +127,7 @@ private slots: void on_exportKey_triggered(); // Tools + void on_newTransaction_triggered(); void on_loadJS_triggered(); // Stuff concerning the blocks/transactions/accounts panels @@ -165,7 +156,6 @@ private slots: void on_inject_triggered(); void on_forceMining_triggered(); void on_usePrivate_triggered(); - void on_enableOptimizer_triggered(); void on_turboMining_triggered(); void on_jitvm_triggered(); @@ -196,12 +186,6 @@ private: void readSettings(bool _skipGeometry = false); void writeSettings(); - bool isCreation() const; - dev::u256 fee() const; - dev::u256 total() const; - dev::u256 value() const; - dev::u256 gasPrice() const; - unsigned installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f); unsigned installWatch(dev::h256 _tf, WatchHandler const& _f); void uninstallWatch(unsigned _w); @@ -228,15 +212,9 @@ private: void refreshAll(); void refreshPending(); void refreshAccounts(); - void refreshDestination(); void refreshBlockCount(); void refreshBalances(); - /// Attempts to infer that @c _source contains Solidity code - bool sourceIsSolidity(std::string const& _source); - /// @eturns all method hashes of a Solidity contract in a string - std::string const getFunctionHashes(dev::solidity::CompilerStack const &_compiler, std::string const& _contractName = ""); - std::unique_ptr ui; std::unique_ptr m_webThree; @@ -251,13 +229,8 @@ private: QList m_myKeys; QList m_myIdentities; QString m_privateChain; - dev::bytes m_data; dev::Address m_nameReg; - unsigned m_backupGas; - - bool m_enableOptimizer = true; - QNetworkAccessManager m_webCtrl; QList> m_consoleHistory; @@ -269,5 +242,7 @@ private: std::unique_ptr m_server; static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr); - NatspecHandler m_natspecDB; + NatspecHandler m_natSpecDB; + + Transact m_transact; }; diff --git a/alethzero/NatspecHandler.h b/alethzero/NatspecHandler.h index 98677dbc2..15ceb7b0b 100644 --- a/alethzero/NatspecHandler.h +++ b/alethzero/NatspecHandler.h @@ -28,10 +28,11 @@ #pragma warning(pop) #include #include +#include "Context.h" namespace ldb = leveldb; -class NatspecHandler +class NatspecHandler: public NatSpecFace { public: NatspecHandler(); @@ -40,7 +41,7 @@ class NatspecHandler /// Stores locally in a levelDB a key value pair of contract code hash to natspec documentation void add(dev::h256 const& _contractHash, std::string const& _doc); /// Retrieves the natspec documentation as a string given a contract code hash - std::string retrieve(dev::h256 const& _contractHash) const; + std::string retrieve(dev::h256 const& _contractHash) const override; /// Given a json natspec string and the transaction data return the user notice std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData); diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 89d616756..06f5fc637 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -92,7 +92,7 @@ bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t) } // TODO: include total cost in Ether - string userNotice = m_main->lookupNatSpecUserNotice(contractCodeHash, _t.data); + string userNotice = m_main->natSpec()->getUserNotice(contractCodeHash, _t.data); if (userNotice.empty()) return showUnknownCallNotice(_t); diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp new file mode 100644 index 000000000..4ac0bbcbf --- /dev/null +++ b/alethzero/Transact.cpp @@ -0,0 +1,325 @@ +/* + 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 Transact.cpp + * @author Gav Wood + * @date 2015 + */ + +#include "Transact.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#include +#endif +#include "Debugger.h" +#include "ui_Transact.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +Transact::Transact(Context* _c, QWidget* _parent): + QDialog(_parent), + ui(new Ui::Transact), + m_context(_c) +{ + ui->setupUi(this); + + initUnits(ui->gasPriceUnits); + initUnits(ui->valueUnits); + ui->valueUnits->setCurrentIndex(6); + ui->gasPriceUnits->setCurrentIndex(4); + ui->gasPrice->setValue(10); + on_destination_currentTextChanged(); +} + +Transact::~Transact() +{ + delete ui; +} + +void Transact::setEnvironment(QList _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB) +{ + m_myKeys = _myKeys; + m_ethereum = _eth; + m_natSpecDB = _natSpecDB; +} + +bool Transact::isCreation() const +{ + return ui->destination->currentText().isEmpty() || ui->destination->currentText() == "(Create Contract)"; +} + +u256 Transact::fee() const +{ + return ui->gas->value() * gasPrice(); +} + +u256 Transact::value() const +{ + if (ui->valueUnits->currentIndex() == -1) + return 0; + return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; +} + +u256 Transact::gasPrice() const +{ + if (ui->gasPriceUnits->currentIndex() == -1) + return 0; + return ui->gasPrice->value() * units()[units().size() - 1 - ui->gasPriceUnits->currentIndex()].first; +} + +u256 Transact::total() const +{ + return value() + fee(); +} + +void Transact::updateDestination() +{ + cwatch << "updateDestination()"; + QString s; + for (auto i: ethereum()->addresses()) + if ((s = m_context->pretty(i)).size()) + // A namereg address + if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1) + ui->destination->addItem(s); + for (int i = 0; i < ui->destination->count(); ++i) + if (ui->destination->itemText(i) != "(Create Contract)" && !m_context->fromString(ui->destination->itemText(i))) + ui->destination->removeItem(i--); +} + +void Transact::updateFee() +{ + ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str())); + auto totalReq = total(); + ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); + + bool ok = false; + for (auto i: m_myKeys) + if (ethereum()->balanceAt(i.address()) >= totalReq) + { + ok = true; + break; + } + ui->send->setEnabled(ok); + QPalette p = ui->total->palette(); + p.setColor(QPalette::WindowText, QColor(ok ? 0x00 : 0x80, 0x00, 0x00)); + ui->total->setPalette(p); +} + +string Transact::getFunctionHashes(dev::solidity::CompilerStack const& _compiler, string const& _contractName) +{ + string ret = ""; + auto const& contract = _compiler.getContractDefinition(_contractName); + auto interfaceFunctions = contract.getInterfaceFunctions(); + + for (auto const& it: interfaceFunctions) + { + ret += it.first.abridged(); + ret += " :"; + ret += it.second->getDeclaration().getName() + "\n"; + } + return ret; +} + +void Transact::on_destination_currentTextChanged() +{ + if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)") + if (Address a = m_context->fromString(ui->destination->currentText())) + ui->calculatedName->setText(m_context->render(a)); + else + ui->calculatedName->setText("Unknown Address"); + else + ui->calculatedName->setText("Create Contract"); + rejigData(); +// updateFee(); +} + +void Transact::rejigData() +{ + if (isCreation()) + { + string src = ui->data->toPlainText().toStdString(); + vector errors; + QString lll; + QString solidity; + if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0) + { + m_data = fromHex(src); + } + else if (sourceIsSolidity(src)) + { + dev::solidity::CompilerStack compiler; + try + { +// compiler.addSources(dev::solidity::StandardSources); + m_data = compiler.compile(src, ui->optimize->isChecked()); + solidity = "

Solidity

"; + solidity += "
var " + QString::fromStdString(compiler.defaultContractName()) + " = web3.eth.contractFromAbi(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + ");
"; + solidity += "
" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "
"; + solidity += "
" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "
"; + } + catch (dev::Exception const& exception) + { + ostringstream error; + solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); + solidity = "

Solidity

" + QString::fromStdString(error.str()).toHtmlEscaped() + "
"; + } + catch (...) + { + solidity = "

Solidity

Uncaught exception.
"; + } + } +#ifndef _MSC_VER + else if (sourceIsSerpent(src)) + { + try + { + m_data = dev::asBytes(::compile(src)); + for (auto& i: errors) + i = "(LLL " + i + ")"; + } + catch (string err) + { + errors.push_back("Serpent " + err); + } + } +#endif + else + { + m_data = compileLLL(src, ui->optimize->isChecked(), &errors); + if (errors.empty()) + { + auto asmcode = compileLLLToAsm(src, false); + lll = "

Pre

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
"; + if (ui->optimize->isChecked()) + { + asmcode = compileLLLToAsm(src, true); + lll = "

Opt

" + QString::fromStdString(asmcode).toHtmlEscaped() + "
" + lll; + } + } + } + QString errs; + if (errors.size()) + { + errs = "

Errors

"; + for (auto const& i: errors) + errs.append("
" + QString::fromStdString(i).toHtmlEscaped() + "
"); + } + ui->code->setHtml(errs + lll + solidity + "

Code

" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped() + "

Hex

" Div(Mono) + QString::fromStdString(toHex(m_data)) + "
"); + ui->gas->setMinimum((qint64)Interface::txGas(m_data, 0)); + if (!ui->gas->isEnabled()) + ui->gas->setValue(m_backupGas); + ui->gas->setEnabled(true); + } + else + { + m_data = parseData(ui->data->toPlainText().toStdString()); + ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true))); + if (ethereum()->codeAt(m_context->fromString(ui->destination->currentText()), 0).size()) + { + ui->gas->setMinimum((qint64)Interface::txGas(m_data, 1)); + if (!ui->gas->isEnabled()) + ui->gas->setValue(m_backupGas); + ui->gas->setEnabled(true); + } + else + { + if (ui->gas->isEnabled()) + m_backupGas = ui->gas->value(); + ui->gas->setValue((qint64)Interface::txGas(m_data)); + ui->gas->setEnabled(false); + } + } + updateFee(); +} + +void Transact::on_send_clicked() +{ + u256 totalReq = value() + fee(); + for (auto const& i: m_myKeys) + if (ethereum()->balanceAt(i.address(), 0) >= totalReq) + { + Secret s = i.secret(); + if (isCreation()) + { + // If execution is a contract creation, add Natspec to + // a local Natspec LEVELDB + ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice()); + string src = ui->data->toPlainText().toStdString(); + if (sourceIsSolidity(src)) + try + { + dev::solidity::CompilerStack compiler; + m_data = compiler.compile(src, ui->optimize->isChecked()); + for (string const& s: compiler.getContractNames()) + { + h256 contractHash = compiler.getContractCodeHash(s); + m_natSpecDB->add(contractHash, compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser)); + } + } + catch (...) + { + } + close(); + return; + } + else + ethereum()->transact(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); + return; + } + QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); +} + +void Transact::on_debug_clicked() +{ + try + { + u256 totalReq = value() + fee(); + for (auto i: m_myKeys) + if (ethereum()->balanceAt(i.address()) >= totalReq) + { + State st(ethereum()->postState()); + Secret s = i.secret(); + Transaction t = isCreation() ? + Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(dev::toAddress(s)), s) : + Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText()), m_data, st.transactionsFrom(dev::toAddress(s)), s); + Debugger dw(m_context, this); + Executive e(st, ethereum()->blockChain(), 0); + dw.populate(e, t); + dw.exec(); + close(); + return; + } + QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); + } + catch (dev::Exception const& _e) + { + QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction. Low-level error: " + QString::fromStdString(diagnostic_information(_e))); + // this output is aimed at developers, reconsider using _e.what for more user friendly output. + } +} diff --git a/alethzero/Transact.h b/alethzero/Transact.h new file mode 100644 index 000000000..afb45f62d --- /dev/null +++ b/alethzero/Transact.h @@ -0,0 +1,82 @@ +/* + 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 Transact.h + * @author Gav Wood + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "Context.h" + +namespace Ui { class Transact; } +namespace dev { namespace eth { class Client; } } +namespace dev { namespace solidity { class CompilerStack; } } + +class Transact: public QDialog +{ + Q_OBJECT + +public: + explicit Transact(Context* _context, QWidget* _parent = 0); + ~Transact(); + + void setEnvironment(QList _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); + +private slots: + void on_destination_currentTextChanged(); + void on_value_valueChanged() { updateFee(); } + void on_gas_valueChanged() { updateFee(); } + void on_valueUnits_currentIndexChanged() { updateFee(); } + void on_gasPriceUnits_currentIndexChanged() { updateFee(); } + void on_gasPrice_valueChanged() { updateFee(); } + void on_data_textChanged() { rejigData(); } + void on_optimize_clicked() { rejigData(); } + void on_send_clicked(); + void on_debug_clicked(); + void on_cancel_clicked() { close(); } + +private: + dev::eth::Client* ethereum() { return m_ethereum; } + void rejigData(); + + void updateDestination(); + void updateFee(); + bool isCreation() const; + dev::u256 fee() const; + dev::u256 total() const; + dev::u256 value() const; + dev::u256 gasPrice() const; + + std::string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, std::string const& _contractName = std::string()); + + Ui::Transact* ui; + + unsigned m_backupGas; + dev::bytes m_data; + + QList m_myKeys; + dev::eth::Client* m_ethereum; + Context* m_context; + NatSpecFace* m_natSpecDB; +}; diff --git a/alethzero/Transact.ui b/alethzero/Transact.ui new file mode 100644 index 000000000..c66e47aa8 --- /dev/null +++ b/alethzero/Transact.ui @@ -0,0 +1,244 @@ + + + Transact + + + + 0 + 0 + 543 + 695 + + + + Dialog + + + + + + + + + 430000000 + + + 0 + + + + + + + &Amount + + + value + + + + + + + false + + + true + + + + + + + + + + Qt::Vertical + + + + QFrame::NoFrame + + + 0 + + + + + Qt::ClickFocus + + + QFrame::NoFrame + + + 0 + + + true + + + + + + + + + + + + 0 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + &To + + + destination + + + + + + + + + + &Debug + + + + + + + &Execute + + + false + + + + + + + &Gas + + + gas + + + + + + + gas + + + 1 + + + 430000000 + + + 10000 + + + + + + + @ + + + 1 + + + 430000000 + + + + + + + &Optimise + + + true + + + + + + + + 0 + 0 + + + + D&ata + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + data + + + + + + + true + + + + (Create Contract) + + + + + + + + + 0 + 0 + + + + + + + + + + + &Cancel + + + Esc + + + + + + + + diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 47344a156..e07eb04ba 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -35,16 +35,6 @@ namespace eth const unsigned c_protocolVersion = 53; const unsigned c_databaseVersion = 5; -template u256 exp10() -{ - return exp10() * u256(10); -} - -template <> u256 exp10<0>() -{ - return u256(1); -} - vector> const& units() { static const vector> s_units = diff --git a/libethcore/CommonEth.h b/libethcore/CommonEth.h index 966794953..79525082f 100644 --- a/libethcore/CommonEth.h +++ b/libethcore/CommonEth.h @@ -47,26 +47,21 @@ std::vector> const& units(); /// The log bloom's size (512 bit). using LogBloom = h512; +template inline u256 exp10() +{ + return exp10() * u256(10); +} + +template <> inline u256 exp10<0>() +{ + return u256(1); +} + // The various denominations; here for ease of use where needed within code. -/*static const u256 Uether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Vether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Dether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Nether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Yether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Zether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Eether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Pether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Tether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Gether = (u256(1000000000) * 1000000000) * 1000000000; -static const u256 Mether = (u256(1000000000) * 1000000000) * 1000000; -static const u256 grand = (u256(1000000000) * 1000000000) * 1000;*/ -static const u256 ether = u256(1000000000) * 1000000000; -static const u256 finney = u256(1000000000) * 1000000; -static const u256 szabo = u256(1000000000) * 1000; -/*static const u256 Gwei = u256(1000000000); -static const u256 Mwei = u256(1000000); -static const u256 Kwei = u256(1000);*/ -static const u256 wei = u256(1); +static const u256 ether = exp10<18>(); +static const u256 finney = exp10<15>(); +static const u256 szabo = exp10<12>(); +static const u256 wei = exp10<0>(); } }