diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 3cd401e23..156a5285f 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "DebuggingStateWrapper.h" #include "Exceptions.h" #include "QContractDefinition.h" @@ -82,7 +83,7 @@ ClientModel::ClientModel(): qRegisterMetaType("QCallData"); qRegisterMetaType("RecordLogEntry*"); - //connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); + //connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_ethAccounts = make_shared([=](){return m_client.get();}, std::vector()); @@ -113,7 +114,7 @@ QString ClientModel::apiCall(QString const& _message) void ClientModel::mine() { - if (m_mining) + if (m_mining) BOOST_THROW_EXCEPTION(ExecutionStateException()); m_mining = true; emit miningStarted(); @@ -210,108 +211,108 @@ QVariantList ClientModel::gasCosts() const void ClientModel::setupScenario(QVariantMap _scenario) { - m_queueTransactions.clear(); - m_running = true; - - m_currentScenario = _scenario; - QVariantList blocks = _scenario.value("blocks").toList(); - QVariantList stateAccounts = _scenario.value("accounts").toList(); - - m_accounts.clear(); - std::vector userAccounts; - - for (auto const& b: stateAccounts) - { - QVariantMap account = b.toMap(); - Address address = {}; - if (account.contains("secret")) - { - KeyPair key(Secret(account.value("secret").toString().toStdString())); - userAccounts.push_back(key); - address = key.address(); - } - else if (account.contains("address")) - address = Address(fromHex(account.value("address").toString().toStdString())); - if (!address) - continue; - - m_accounts[address] = Account(qvariant_cast(account.value("balance"))->toU256Wei(), Account::NormalCreation); - } - m_ethAccounts->setAccounts(userAccounts); - for (auto const& b: blocks) - { - QVariantList transactions = b.toMap().value("transactions").toList(); - m_queueTransactions.push_back(transactions); - } - - m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); - - connect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock, Qt::QueuedConnection); - connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); - connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); - processNextBlock(); + m_queueTransactions.clear(); + m_running = true; + + m_currentScenario = _scenario; + QVariantList blocks = _scenario.value("blocks").toList(); + QVariantList stateAccounts = _scenario.value("accounts").toList(); + + m_accounts.clear(); + std::vector userAccounts; + + for (auto const& b: stateAccounts) + { + QVariantMap account = b.toMap(); + Address address = {}; + if (account.contains("secret")) + { + KeyPair key(Secret(account.value("secret").toString().toStdString())); + userAccounts.push_back(key); + address = key.address(); + } + else if (account.contains("address")) + address = Address(fromHex(account.value("address").toString().toStdString())); + if (!address) + continue; + + m_accounts[address] = Account(qvariant_cast(account.value("balance"))->toU256Wei(), Account::NormalCreation); + } + m_ethAccounts->setAccounts(userAccounts); + for (auto const& b: blocks) + { + QVariantList transactions = b.toMap().value("transactions").toList(); + m_queueTransactions.push_back(transactions); + } + + m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); + + connect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock, Qt::QueuedConnection); + connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); + connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); + processNextBlock(); } void ClientModel::stopExecution() { - disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); - disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); - disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); - m_running = false; + disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); + disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); + disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); + m_running = false; } void ClientModel::finalizeBlock() { - if (m_queueTransactions.size() > 0) - mine(); - else - { - disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); - disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); - disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); - m_running = false; - emit runComplete(); - } + if (m_queueTransactions.size() > 0) + mine(); + else + { + disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); + disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); + disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); + m_running = false; + emit runComplete(); + } } void ClientModel::processNextBlock() { - processNextTransactions(); + processNextTransactions(); } void ClientModel::processNextTransactions() { - vector transactionSequence; - for (auto const& t: m_queueTransactions.front()) - { - QVariantMap transaction = t.toMap(); - QString contractId = transaction.value("contractId").toString(); - QString functionId = transaction.value("functionId").toString(); - bool gasAuto = transaction.value("gasAuto").toBool(); - u256 gas = 0; - if (transaction.value("gas").data()) - gas = boost::get(qvariant_cast(transaction.value("gas"))->internalValue()); - else - gasAuto = true; - - u256 value = (qvariant_cast(transaction.value("value")))->toU256Wei(); - u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); - QString sender = transaction.value("sender").toString(); - bool isContractCreation = transaction.value("isContractCreation").toBool(); - bool isFunctionCall = transaction.value("isFunctionCall").toBool(); - 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, isFunctionCall); - transactionSettings.parameterValues = transaction.value("parameters").toMap(); - - if (contractId == functionId || functionId == "Constructor") - transactionSettings.functionId.clear(); - - transactionSequence.push_back(transactionSettings); - } - m_queueTransactions.pop_front(); - executeSequence(transactionSequence); + vector transactionSequence; + for (auto const& t: m_queueTransactions.front()) + { + QVariantMap transaction = t.toMap(); + QString contractId = transaction.value("contractId").toString(); + QString functionId = transaction.value("functionId").toString(); + bool gasAuto = transaction.value("gasAuto").toBool(); + u256 gas = 0; + if (transaction.value("gas").data()) + gas = boost::get(qvariant_cast(transaction.value("gas"))->internalValue()); + else + gasAuto = true; + + u256 value = (qvariant_cast(transaction.value("value")))->toU256Wei(); + u256 gasPrice = (qvariant_cast(transaction.value("gasPrice")))->toU256Wei(); + QString sender = transaction.value("sender").toString(); + bool isContractCreation = transaction.value("isContractCreation").toBool(); + bool isFunctionCall = transaction.value("isFunctionCall").toBool(); + 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, isFunctionCall); + transactionSettings.parameterValues = transaction.value("parameters").toMap(); + + if (contractId == functionId || functionId == "Constructor") + transactionSettings.functionId.clear(); + + transactionSequence.push_back(transactionSettings); + } + m_queueTransactions.pop_front(); + executeSequence(transactionSequence); } void ClientModel::executeSequence(vector const& _sequence) @@ -323,7 +324,7 @@ void ClientModel::executeSequence(vector const& _sequence) } emit runStarted(); - //emit runStateChanged(); + //emit runStateChanged(); //run sequence @@ -332,7 +333,7 @@ void ClientModel::executeSequence(vector const& _sequence) try { vector
deployedContracts; - //onStateReset(); + //onStateReset(); m_gasCosts.clear(); for (TransactionSettings const& transaction: _sequence) { @@ -360,7 +361,7 @@ void ClientModel::executeSequence(vector const& _sequence) break; } if (!f) - emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); + emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); if (!transaction.functionId.isEmpty()) encoder.encode(f); for (QVariableDeclaration const* p: f->parametersList()) @@ -391,62 +392,62 @@ void ClientModel::executeSequence(vector const& _sequence) { auto contractAddressIter = m_contractAddresses.find(ctrInstance); if (contractAddressIter == m_contractAddresses.end()) - emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); - callAddress(contractAddressIter->second, encoder.encodedData(), transaction); + emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); + callAddress(contractAddressIter->second, encoder.encodedData(), transaction); } m_gasCosts.append(m_client->lastExecution().gasUsed); onNewTransaction(); - emit runComplete(); - } + emit runComplete(); + } } catch(boost::exception const&) { cerr << boost::current_exception_diagnostic_information(); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); - } + } catch(exception const& e) { cerr << boost::current_exception_diagnostic_information(); emit runFailed(e.what()); - } + } emit runStateChanged(); - }); + }); } void ClientModel::executeTr(QVariantMap _tr) { - QVariantList trs; - trs.push_back(_tr); - m_queueTransactions.push_back(trs); - processNextTransactions(); + QVariantList trs; + trs.push_back(_tr); + m_queueTransactions.push_back(trs); + processNextTransactions(); } std::pair ClientModel::resolvePair(QString const& _contractId) { - std::pair ret = std::make_pair(_contractId, 0); - if (_contractId.startsWith("<") && _contractId.endsWith(">")) - { - QStringList values = ret.first.remove("<").remove(">").split(" - "); - ret = std::make_pair(values[0], values[1].toUInt()); - } - return ret; + std::pair ret = std::make_pair(_contractId, 0); + if (_contractId.startsWith("<") && _contractId.endsWith(">")) + { + QStringList values = ret.first.remove("<").remove(">").split(" - "); + ret = std::make_pair(values[0], values[1].toUInt()); + } + return ret; } QString ClientModel::resolveToken(std::pair const& _value, vector
const& _contracts) { if (_contracts.size() > 0) - return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); //dev::toHex(_contracts.at(_value.second).ref())); + return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); //dev::toHex(_contracts.at(_value.second).ref())); else return _value.first; } std::pair ClientModel::retrieveToken(QString const& _value, vector
const& _contracts) { - std::pair ret; - ret.first = _value; - ret.second = _contracts.size() - 1; - return ret; + std::pair ret; + ret.first = _value; + ret.second = _contracts.size() - 1; + return ret; } void ClientModel::showDebugger() @@ -647,7 +648,7 @@ void ClientModel::emptyRecord() void ClientModel::debugRecord(unsigned _index) { ExecutionResult e = m_client->execution(_index); - showDebuggerForTransaction(e); + showDebuggerForTransaction(e); } Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction) @@ -668,7 +669,7 @@ RecordLogEntry* ClientModel::lastBlock() const strGas << blockInfo.gasUsed; stringstream strNumber; strNumber << blockInfo.number; - RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap()); + RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantList()); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); return record; } @@ -685,7 +686,7 @@ void ClientModel::onStateReset() void ClientModel::onNewTransaction() { ExecutionResult const& tr = m_client->lastExecution(); - unsigned block = m_client->number() + 1; + unsigned block = m_client->number() + 1; unsigned recordIndex = tr.executonIndex; QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex); QString address = QString::fromStdString(toJS(tr.address)); @@ -727,9 +728,11 @@ void ClientModel::onNewTransaction() Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; auto contractAddressIter = m_contractNames.find(contractAddress); - QVariantMap inputParameters; + QVariantMap inputParameters; + QVariantList logs; if (contractAddressIter != m_contractNames.end()) { + ContractCallDataEncoder encoder; CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); const QContractDefinition* def = compilerRes.contract(); contract = def->name(); @@ -739,25 +742,64 @@ void ClientModel::onNewTransaction() if (funcDef) { function = funcDef->name(); - ContractCallDataEncoder encoder; QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output); returned += "("; returned += returnValues.join(", "); returned += ")"; - bytes data = tr.inputParameters; - data.erase(data.begin(), data.begin() + 4); - QStringList parameters = encoder.decode(funcDef->parametersList(), data); - for (int k = 0; k < parameters.length(); ++k) - inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k)); - } + bytes data = tr.inputParameters; + data.erase(data.begin(), data.begin() + 4); + QStringList parameters = encoder.decode(funcDef->parametersList(), data); + for (int k = 0; k < parameters.length(); ++k) + inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k)); + } } - } - LocalisedLogEntries logs = m_client->logs(); - QString sender = QString::fromStdString(dev::toHex(tr.sender.ref())); - QString label = contract + "." + function + "()"; - RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, - gasUsed, sender, label, inputParameters); + // Fill generated logs and decode parameters + for (auto const& log: tr.logs) + { + QVariantMap l; + l.insert("address", QString::fromStdString(log.address.hex())); + auto const& sign = log.topics.front(); // first hash supposed to be the event signature. To check + auto dataIterator = log.data.begin(); + int topicDataIndex = 1; + for (auto const& event: def->eventsList()) + { + if (sign == event->fullHash()) + { + QVariantList paramsList; + l.insert("name", event->name()); + for (auto const& e: event->parametersList()) + { + bytes data; + QString param; + if (!e->isIndexed()) + { + data = bytes(dataIterator, dataIterator + 32); + dataIterator = dataIterator + 32; + } + else + { + data = log.topics.at(topicDataIndex).asBytes(); + topicDataIndex++; + } + param = encoder.decode(e, data); + QVariantMap p; + p.insert("indexed", e->isIndexed()); + p.insert("value", param); + p.insert("name", e->name()); + paramsList.push_back(p); + } + l.insert("param", paramsList); + break; + } + } + logs.push_back(l); + } + } + QString sender = QString::fromStdString(dev::toHex(tr.sender.ref())); + QString label = contract + "." + function + "()"; + RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, + gasUsed, sender, label, inputParameters, logs); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); emit newRecord(log); } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 503b4189f..7cc7b03f6 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -115,6 +115,8 @@ class RecordLogEntry: public QObject Q_PROPERTY(QString label MEMBER m_label CONSTANT) /// input parameters Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) + /// logs + Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT) public: enum RecordType @@ -126,9 +128,9 @@ public: RecordLogEntry(): m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed, - QString _sender, QString _label, QVariantMap _inputParameters): + QString _sender, QString _label, QVariantMap _inputParameters, QVariantList _logs): m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed), - m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters) {} + m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_logs(_logs) {} private: unsigned m_recordIndex; @@ -144,6 +146,7 @@ private: QString m_sender; QString m_label; QVariantMap m_inputParameters; + QVariantList m_logs; }; /** diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 70c1e6068..553c5e075 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -228,16 +228,10 @@ QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found")); } -QStringList ContractCallDataEncoder::decode(QList const& _returnParameters, vector _value) +QString ContractCallDataEncoder::decode(QVariableDeclaration* const& _param, bytes _value) { - QStringList r; - for (int k = 0; k <_returnParameters.length(); k++) - { - QVariableDeclaration* dec = static_cast(_returnParameters.at(k)); - SolidityType const& type = dec->type()->type(); - r.append(decode(type, _value.at(k)).toString()); - } - return r; + SolidityType const& type = _param->type()->type(); + return decode(type, _value).toString(); } QStringList ContractCallDataEncoder::decode(QList const& _returnParameters, bytes _value) diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 57b364b49..748239c5b 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -48,8 +48,8 @@ public: void encode(QVariant const& _data, SolidityType const& _type); /// Decode variable in order to be sent to QML view. QStringList decode(QList const& _dec, bytes _value); - /// Decode @param _parameters - QStringList decode(QList const& _parameters, std::vector _value); + /// Decode @param _parameter + QString decode(QVariableDeclaration* const& _param, bytes _value); /// Decode single variable QVariant decode(SolidityType const& _type, bytes const& _value); /// Get all encoded data encoded by encode function. diff --git a/mix/MachineStates.h b/mix/MachineStates.h index ae9804f7f..1b50c062b 100644 --- a/mix/MachineStates.h +++ b/mix/MachineStates.h @@ -84,6 +84,7 @@ namespace mix unsigned transactionIndex; unsigned executonIndex = 0; bytes inputParameters; + eth::LocalisedLogEntries logs; bool isCall() const { return transactionIndex == std::numeric_limits::max(); } bool isConstructor() const { return !isCall() && !address; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index fcc72db9d..fc2a153c5 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file MixClient.cpp * @author Arkadiy Paronyan arkadiy@ethdev.com @@ -48,27 +48,27 @@ namespace struct MixPow //dummy POW { - typedef int Solution; - static void assignResult(int, BlockInfo const&) {} - static bool verify(BlockInfo const&) { return true; } + typedef int Solution; + static void assignResult(int, BlockInfo const&) {} + static bool verify(BlockInfo const&) { return true; } }; } bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) { - RLPStream block(3); - block.appendList(15) - << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie - << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 - << std::string() << h256() << h64(u64(42)); - block.appendRaw(RLPEmptyList); - block.appendRaw(RLPEmptyList); - return block.out(); + RLPStream block(3); + block.appendList(15) + << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie + << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 + << std::string() << h256() << h64(u64(42)); + block.appendRaw(RLPEmptyList); + block.appendRaw(RLPEmptyList); + return block.out(); } MixClient::MixClient(std::string const& _dbPath): - m_dbPath(_dbPath) + m_dbPath(_dbPath) { resetState(std::unordered_map()); } @@ -85,335 +85,350 @@ LocalisedLogEntries MixClient::logs() void MixClient::resetState(std::unordered_map const& _accounts, Secret const& _miner) { - WriteGuard l(x_state); - Guard fl(x_filtersWatches); - m_filters.clear(); - m_watches.clear(); - LogFilter filter; - m_filters.insert(std::make_pair(filter.sha3(), filter)); - m_watches.insert(std::make_pair(0, ClientWatch(filter.sha3(), Reaping::Automatic))); - - m_stateDB = OverlayDB(); - SecureTrieDB accountState(&m_stateDB); - accountState.init(); - - dev::eth::commit(_accounts, static_cast(m_stateDB), accountState); - h256 stateRoot = accountState.root(); - m_bc.reset(); - m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); - m_state = eth::State(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address()); - m_state.sync(bc()); - m_startState = m_state; - WriteGuard lx(x_executions); - m_executions.clear(); + WriteGuard l(x_state); + Guard fl(x_filtersWatches); + m_filters.clear(); + m_watches.clear(); + //LogFilter filter; + //m_filters.insert(std::make_pair(filter.sha3(), filter)); + //m_watches.insert(std::make_pair(0, ClientWatch(filter.sha3(), Reaping::Automatic))); + + m_stateDB = OverlayDB(); + SecureTrieDB accountState(&m_stateDB); + accountState.init(); + + dev::eth::commit(_accounts, static_cast(m_stateDB), accountState); + h256 stateRoot = accountState.root(); + m_bc.reset(); + m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); + m_state = eth::State(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address()); + m_state.sync(bc()); + m_startState = m_state; + WriteGuard lx(x_executions); + m_executions.clear(); } Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secret const& _secret) { - Transaction ret; - if (_secret) - { - if (_t.isCreation()) - ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); - else - ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); - } - else - { - if (_t.isCreation()) - ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce()); - else - ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce()); - ret.forceSender(_t.safeSender()); - } - return ret; + Transaction ret; + if (_secret) + { + if (_t.isCreation()) + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); + else + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); + } + else + { + if (_t.isCreation()) + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce()); + else + ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce()); + ret.forceSender(_t.safeSender()); + } + return ret; } void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret) { - Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t; + Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t; // do debugging run first - LastHashes lastHashes(256); - lastHashes[0] = bc().numberHash(bc().number()); - for (unsigned i = 1; i < 256; ++i) - lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256(); - - State execState = _state; - execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation - Executive execution(execState, lastHashes, 0); - execution.initialize(t); - execution.execute(); - std::vector machineStates; - std::vector levels; - std::vector codes; - std::map codeIndexes; - std::vector data; - std::map dataIndexes; - bytes const* lastCode = nullptr; - bytesConstRef const* lastData = nullptr; - unsigned codeIndex = 0; - unsigned dataIndex = 0; - auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) - { - VM& vm = *static_cast(voidVM); - ExtVM const& ext = *static_cast(voidExt); - if (lastCode == nullptr || lastCode != &ext.code) - { - auto const& iter = codeIndexes.find(&ext.code); - if (iter != codeIndexes.end()) - codeIndex = iter->second; - else - { - codeIndex = codes.size(); - codes.push_back(MachineCode({ext.myAddress, ext.code})); - codeIndexes[&ext.code] = codeIndex; - } - lastCode = &ext.code; - } - - if (lastData == nullptr || lastData != &ext.data) - { - auto const& iter = dataIndexes.find(&ext.data); - if (iter != dataIndexes.end()) - dataIndex = iter->second; - else - { - dataIndex = data.size(); - data.push_back(ext.data.toBytes()); - dataIndexes[&ext.data] = dataIndex; - } - lastData = &ext.data; - } - - if (levels.size() < ext.depth) - levels.push_back(machineStates.size() - 1); - else - levels.resize(ext.depth); - - machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(), - vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex})); - }; - - execution.go(onOp); - execution.finalize(); - dev::eth::ExecutionResult er = execution.executionResult(); - - switch (er.excepted) - { - case TransactionException::None: - break; - case TransactionException::NotEnoughCash: - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Insufficient balance for contract deployment")); - case TransactionException::OutOfGasBase: - case TransactionException::OutOfGas: - BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); - case TransactionException::BlockGasLimitReached: - BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); - case TransactionException::OutOfStack: - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); - case TransactionException::StackUnderflow: - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow")); - //these should not happen in mix - case TransactionException::Unknown: - case TransactionException::BadInstruction: - case TransactionException::BadJumpDestination: - case TransactionException::InvalidSignature: - case TransactionException::InvalidNonce: - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error")); - }; - - ExecutionResult d; + LastHashes lastHashes(256); + lastHashes[0] = bc().numberHash(bc().number()); + for (unsigned i = 1; i < 256; ++i) + lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256(); + + State execState = _state; + execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation + Executive execution(execState, lastHashes, 0); + execution.initialize(t); + execution.execute(); + std::vector machineStates; + std::vector levels; + std::vector codes; + std::map codeIndexes; + std::vector data; + std::map dataIndexes; + bytes const* lastCode = nullptr; + bytesConstRef const* lastData = nullptr; + unsigned codeIndex = 0; + unsigned dataIndex = 0; + auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) + { + VM& vm = *static_cast(voidVM); + ExtVM const& ext = *static_cast(voidExt); + if (lastCode == nullptr || lastCode != &ext.code) + { + auto const& iter = codeIndexes.find(&ext.code); + if (iter != codeIndexes.end()) + codeIndex = iter->second; + else + { + codeIndex = codes.size(); + codes.push_back(MachineCode({ext.myAddress, ext.code})); + codeIndexes[&ext.code] = codeIndex; + } + lastCode = &ext.code; + } + + if (lastData == nullptr || lastData != &ext.data) + { + auto const& iter = dataIndexes.find(&ext.data); + if (iter != dataIndexes.end()) + dataIndex = iter->second; + else + { + dataIndex = data.size(); + data.push_back(ext.data.toBytes()); + dataIndexes[&ext.data] = dataIndex; + } + lastData = &ext.data; + } + + if (levels.size() < ext.depth) + levels.push_back(machineStates.size() - 1); + else + levels.resize(ext.depth); + + machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(), + vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex})); + }; + + execution.go(onOp); + execution.finalize(); + dev::eth::ExecutionResult er = execution.executionResult(); + + switch (er.excepted) + { + case TransactionException::None: + break; + case TransactionException::NotEnoughCash: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Insufficient balance for contract deployment")); + case TransactionException::OutOfGasBase: + case TransactionException::OutOfGas: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); + case TransactionException::BlockGasLimitReached: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); + case TransactionException::OutOfStack: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); + case TransactionException::StackUnderflow: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow")); + //these should not happen in mix + case TransactionException::Unknown: + case TransactionException::BadInstruction: + case TransactionException::BadJumpDestination: + case TransactionException::InvalidSignature: + case TransactionException::InvalidNonce: + BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error")); + }; + + ExecutionResult d; d.inputParameters = t.data(); - d.result = execution.executionResult(); - d.machineStates = machineStates; - d.executionCode = std::move(codes); - d.transactionData = std::move(data); - d.address = _t.receiveAddress(); - d.sender = _t.sender(); - d.value = _t.value(); - d.gasUsed = er.gasUsed + er.gasRefunded + c_callStipend; - if (_t.isCreation()) - d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); - if (!_call) - d.transactionIndex = m_state.pending().size(); - d.executonIndex = m_executions.size(); + d.result = execution.executionResult(); + d.machineStates = machineStates; + d.executionCode = std::move(codes); + d.transactionData = std::move(data); + d.address = _t.receiveAddress(); + d.sender = _t.sender(); + d.value = _t.value(); + d.gasUsed = er.gasUsed + er.gasRefunded + c_callStipend; + if (_t.isCreation()) + d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); + if (!_call) + d.transactionIndex = m_state.pending().size(); + d.executonIndex = m_executions.size(); // execute on a state - if (!_call) - { - t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t; - er =_state.execute(lastHashes, t); - if (t.isCreation() && _state.code(d.contractAddress).empty()) - BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); - d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend; - // collect watches - h256Set changed; - Guard l(x_filtersWatches); - for (std::pair& i: m_filters) - if ((unsigned)i.second.filter.latest() > bc().number()) - { - // acceptable number. - auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); - if (m.size()) - { - // filter catches them - for (LogEntry const& l: m) - i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); - changed.insert(i.first); - } - } - changed.insert(dev::eth::PendingChangedFilter); - noteChanged(changed); - } - WriteGuard l(x_executions); - m_executions.emplace_back(std::move(d)); + if (!_call) + { + t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t; + er = _state.execute(lastHashes, t); + if (t.isCreation() && _state.code(d.contractAddress).empty()) + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); + d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend; + // collect watches + h256Set changed; + Guard l(x_filtersWatches); + LocalisedLogEntries logs; + //for (unsigned i = 0; i < _state.pending().size(); ++i) + //{ + TransactionReceipt const& tr = _state.receipt(_state.pending().size() - 1); + + auto trHash = _state.pending().at(_state.pending().size() - 1).sha3(); + //for (std::pair& installedFilter: m_filters) + //{ + LogEntries le = tr.log(); // installedFilter.second.filter.matches(tr); + if (le.size()) + for (unsigned j = 0; j < le.size(); ++j) + logs.insert(logs.begin(), LocalisedLogEntry(le[j], bc().number() + 1, trHash)); + //} + //} + + /*if ((unsigned)i.second.filter.latest() > bc().number()) + { + // acceptable number. + auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); + if (m.size()) + { + // filter catches them + for (LogEntry const& l: m) + i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); + changed.insert(i.first); + } + }*/ + changed.insert(dev::eth::PendingChangedFilter); + d.logs = logs; + //noteChanged(changed); + } + WriteGuard l(x_executions); + m_executions.emplace_back(std::move(d)); } void MixClient::mine() { - WriteGuard l(x_state); - m_state.commitToMine(bc()); - m_state.completeMine(0); - bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); - m_state.sync(bc()); - m_startState = m_state; - h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; - noteChanged(changed); + WriteGuard l(x_state); + m_state.commitToMine(bc()); + m_state.completeMine(0); + bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); + m_state.sync(bc()); + m_startState = m_state; + h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; + //noteChanged(changed); } ExecutionResult MixClient::lastExecution() const { - ReadGuard l(x_executions); - return m_executions.empty() ? ExecutionResult() : m_executions.back(); + ReadGuard l(x_executions); + return m_executions.empty() ? ExecutionResult() : m_executions.back(); } ExecutionResult MixClient::execution(unsigned _index) const { - ReadGuard l(x_executions); - return m_executions.at(_index); + ReadGuard l(x_executions); + return m_executions.at(_index); } State MixClient::asOf(h256 const& _block) const { - ReadGuard l(x_state); - return State(m_stateDB, bc(), _block); + ReadGuard l(x_state); + return State(m_stateDB, bc(), _block); } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto) { - WriteGuard l(x_state); - u256 n = m_state.transactionsFrom(toAddress(_secret)); - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - executeTransaction(t, m_state, false, _gasAuto, _secret); + WriteGuard l(x_state); + u256 n = m_state.transactionsFrom(toAddress(_secret)); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + executeTransaction(t, m_state, false, _gasAuto, _secret); } Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto) { - WriteGuard l(x_state); - u256 n = m_state.transactionsFrom(toAddress(_secret)); - eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); - executeTransaction(t, m_state, false, _gasAuto, _secret); - Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); - return address; + WriteGuard l(x_state); + u256 n = m_state.transactionsFrom(toAddress(_secret)); + eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); + executeTransaction(t, m_state, false, _gasAuto, _secret); + Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); + return address; } dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff) { - (void)_blockNumber; - State temp = asOf(eth::PendingBlock); - u256 n = temp.transactionsFrom(_from); - Transaction t(_value, _gasPrice, _gas, _dest, _data, n); - t.forceSender(_from); - if (_ff == FudgeFactor::Lenient) - temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); - WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp, true, _gasAuto); - return lastExecution().result; + (void)_blockNumber; + State temp = asOf(eth::PendingBlock); + u256 n = temp.transactionsFrom(_from); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n); + t.forceSender(_from); + if (_ff == FudgeFactor::Lenient) + temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); + WriteGuard lw(x_state); //TODO: lock is required only for last execution state + executeTransaction(t, temp, true, _gasAuto); + return lastExecution().result; } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { - submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, false); + submitTransaction(_secret, _value, _dest, _data, _gas, _gasPrice, false); } Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) { - return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false); + return submitTransaction(_secret, _endowment, _init, _gas, _gasPrice, false); } dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) { - return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff); + return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff); } dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) { - (void)_blockNumber; - u256 n; - State temp; - { - ReadGuard lr(x_state); - temp = asOf(eth::PendingBlock); - n = temp.transactionsFrom(_from); - } - Transaction t(_value, _gasPrice, _gas, _data, n); - t.forceSender(_from); - if (_ff == FudgeFactor::Lenient) - temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); - WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp, true, false); - return lastExecution().result; + (void)_blockNumber; + u256 n; + State temp; + { + ReadGuard lr(x_state); + temp = asOf(eth::PendingBlock); + n = temp.transactionsFrom(_from); + } + Transaction t(_value, _gasPrice, _gas, _data, n); + t.forceSender(_from); + if (_ff == FudgeFactor::Lenient) + temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); + WriteGuard lw(x_state); //TODO: lock is required only for last execution state + executeTransaction(t, temp, true, false); + return lastExecution().result; } -void MixClient::noteChanged(h256Set const& _filters) +/*void MixClient::noteChanged(h256Set const& _filters) { - for (auto& i: m_watches) - if (_filters.count(i.second.id)) - { - if (m_filters.count(i.second.id)) - i.second.changes += m_filters.at(i.second.id).changes; - else - i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); - } - for (auto& i: m_filters) - i.second.changes.clear(); -} + for (auto& i: m_watches) + if (_filters.count(i.second.id)) + { + if (m_filters.count(i.second.id)) + i.second.changes += m_filters.at(i.second.id).changes; + else + i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); + } + for (auto& i: m_filters) + i.second.changes.clear(); +}*/ eth::BlockInfo MixClient::blockInfo() const { - ReadGuard l(x_state); - return BlockInfo(bc().block()); + ReadGuard l(x_state); + return BlockInfo(bc().block()); } void MixClient::setAddress(Address _us) { - WriteGuard l(x_state); - m_state.setAddress(_us); + WriteGuard l(x_state); + m_state.setAddress(_us); } void MixClient::startMining() { - //no-op + //no-op } void MixClient::stopMining() { - //no-op + //no-op } bool MixClient::isMining() const { - return false; + return false; } uint64_t MixClient::hashrate() const { - return 0; + return 0; } eth::MiningProgress MixClient::miningProgress() const { - return eth::MiningProgress(); + return eth::MiningProgress(); } } diff --git a/mix/MixClient.h b/mix/MixClient.h index 852937634..ece603942 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -91,7 +91,7 @@ protected: private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret()); - void noteChanged(h256Set const& _filters); + //void noteChanged(h256Set const& _filters); dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret()); eth::State m_state; diff --git a/mix/QContractDefinition.cpp b/mix/QContractDefinition.cpp index 51b37e399..fe05e80be 100644 --- a/mix/QContractDefinition.cpp +++ b/mix/QContractDefinition.cpp @@ -39,8 +39,23 @@ QContractDefinition::QContractDefinition(QObject* _parent, dev::solidity::Contra else m_constructor = new QFunctionDefinition(parent); + std::vector found; + for (auto const& f: _contract->getDefinedFunctions()) + { + m_functions.append(new QFunctionDefinition(parent, f)); + found.push_back(f->getName()); + } + for (auto const& it: _contract->getInterfaceFunctions()) - m_functions.append(new QFunctionDefinition(parent, it.second)); + { + if (std::find(found.begin(), found.end(), it.second->getDeclaration().getName()) == found.end()) + m_functions.append(new QFunctionDefinition(parent, it.second)); + } + + + for (auto const& it: _contract->getEvents()) + m_events.append(new QFunctionDefinition(parent, it)); + } QFunctionDefinition const* QContractDefinition::getFunction(dev::FixedHash<4> _hash) const diff --git a/mix/QContractDefinition.h b/mix/QContractDefinition.h index ff0df1f15..f68133ab7 100644 --- a/mix/QContractDefinition.h +++ b/mix/QContractDefinition.h @@ -37,6 +37,7 @@ class QContractDefinition: public QBasicNodeDefinition Q_OBJECT Q_PROPERTY(QQmlListProperty functions READ functions CONSTANT) Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT) + Q_PROPERTY(QQmlListProperty events READ events CONSTANT) public: QContractDefinition(QObject* _parent, solidity::ContractDefinition const* _contract); @@ -44,12 +45,19 @@ public: QQmlListProperty functions() const { return QQmlListProperty(const_cast(this), const_cast(this)->m_functions); } /// Get the constructor of the contract. QFunctionDefinition* constructor() const { return m_constructor; } + /// Get all the functions of the contract. QList const& functionsList() const { return m_functions; } /// Find function by hash, returns nullptr if not found QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const; + /// Get events + QQmlListProperty events() const { return QQmlListProperty(const_cast(this), const_cast(this)->m_events); } + /// Get events + QList const& eventsList() const { return m_events; } + private: QList m_functions; QFunctionDefinition* m_constructor; + QList m_events; }; } diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index e6764d712..52d8ad30c 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -28,15 +28,41 @@ using namespace dev::solidity; using namespace dev::mix; -QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())) +QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())), + m_fullHash(dev::sha3(_f->externalSignature())) +{ + init(_f); +} + +QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer const& _f): QBasicNodeDefinition(_parent, _f.get()), m_hash(dev::sha3(_f->externalSignature())), + m_fullHash(dev::sha3(_f->externalSignature())) +{ + + for (unsigned i = 0; i < _f->getParameters().size(); ++i) + m_parameters.append(new QVariableDeclaration(parent(), _f->getParameters().at(i))); + + for (unsigned i = 0; i < _f->getReturnParameters().size(); ++i) + m_returnParameters.append(new QVariableDeclaration(parent(), _f->getReturnParameters().at(i))); +} + +QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer const& _e): QBasicNodeDefinition(_parent, _e.get()) +{ + for (unsigned i = 0; i < _e->getParameters().size(); ++i) + m_parameters.append(new QVariableDeclaration(parent(), _e->getParameters().at(i))); + FunctionTypePointer _f = std::make_shared(*_e); + m_hash = (FixedHash<4>)dev::sha3(_f->externalSignature(_e->getName())); + m_fullHash = dev::sha3(_f->externalSignature(_e->getName())); +} + +void QFunctionDefinition::init(dev::solidity::FunctionTypePointer _f) { auto paramNames = _f->getParameterNames(); auto paramTypes = _f->getParameterTypes(); auto returnNames = _f->getReturnParameterNames(); auto returnTypes = _f->getReturnParameterTypes(); for (unsigned i = 0; i < paramNames.size(); ++i) - m_parameters.append(new QVariableDeclaration(_parent, paramNames[i], paramTypes[i].get())); + m_parameters.append(new QVariableDeclaration(parent(), paramNames[i], paramTypes[i].get())); for (unsigned i = 0; i < returnNames.size(); ++i) - m_returnParameters.append(new QVariableDeclaration(_parent, returnNames[i], returnTypes[i].get())); + m_returnParameters.append(new QVariableDeclaration(parent(), returnNames[i], returnTypes[i].get())); } diff --git a/mix/QFunctionDefinition.h b/mix/QFunctionDefinition.h index 18f2d911b..a9c45ffcd 100644 --- a/mix/QFunctionDefinition.h +++ b/mix/QFunctionDefinition.h @@ -41,6 +41,10 @@ public: QFunctionDefinition(){} QFunctionDefinition(QObject* _parent): QBasicNodeDefinition(_parent) {} QFunctionDefinition(QObject* _parent, solidity::FunctionTypePointer const& _f); + QFunctionDefinition(QObject* _parent, solidity::ASTPointer const& _f); + QFunctionDefinition(QObject* _parent, solidity::ASTPointer const& _f); + /// Init members + void init(dev::solidity::FunctionTypePointer _f); /// Get all input parameters of this function. QList const& parametersList() const { return m_parameters; } /// Get all input parameters of this function as QML property. @@ -49,10 +53,13 @@ public: QList returnParameters() const { return m_returnParameters; } /// Get the hash of this function declaration on the contract ABI. FixedHash<4> hash() const { return m_hash; } + /// Get the full hash of this function declaration on the contract ABI. + FixedHash<32> fullHash() const { return m_fullHash; } private: int m_index; FixedHash<4> m_hash; + FixedHash<32> m_fullHash; QList m_parameters; QList m_returnParameters; void initQParameters(); diff --git a/mix/QVariableDeclaration.cpp b/mix/QVariableDeclaration.cpp index 3bbb0d523..4299e34ee 100644 --- a/mix/QVariableDeclaration.cpp +++ b/mix/QVariableDeclaration.cpp @@ -24,27 +24,32 @@ #include #include "CodeModel.h" +using namespace solidity; + namespace dev { namespace mix { -QVariableDeclaration::QVariableDeclaration(QObject* _parent, solidity::VariableDeclaration const* _v): - QBasicNodeDefinition(_parent, _v), +QVariableDeclaration::QVariableDeclaration(QObject* _parent, ASTPointer const _v): + QBasicNodeDefinition(_parent, _v.get()), m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().get()))) { + m_isIndexed = _v->isIndexed(); } -QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type): +QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed): QBasicNodeDefinition(_parent, _name), m_type(new QSolidityType(_parent, _type)) { + m_isIndexed = _isIndexed; } -QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type): +QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed): QBasicNodeDefinition(_parent, _name), m_type(new QSolidityType(this, CodeModel::nodeType(_type))) { + m_isIndexed = _isIndexed; } QSolidityType::QSolidityType(QObject* _parent, SolidityType const& _type): diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index 4309550b2..85c719987 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -21,6 +21,7 @@ #include #include +#include #include "QBasicNodeDefinition.h" #include "SolidityType.h" @@ -82,14 +83,16 @@ class QVariableDeclaration: public QBasicNodeDefinition public: QVariableDeclaration() {} - QVariableDeclaration(QObject* _parent, solidity::VariableDeclaration const* _v); - QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type); - QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type); + QVariableDeclaration(QObject* _parent, solidity::ASTPointer const _v); + QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed = false); + QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed = false); QSolidityType* type() const { return m_type; } void setType(QSolidityType* _type) { m_type = _type; } + bool isIndexed() { return m_isIndexed; } private: QSolidityType* m_type; + bool m_isIndexed; }; diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index e331dc6a6..c3ce7865a 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -121,22 +121,36 @@ ColumnLayout clip: true } + Label + { + id: logs + width: logsWidth + text: { + if (index >= 0 && transactions.get(index).logs) + { + for (var k in transactions.get(index).logs) + { + console.log("_________________________") + console.log(JSON.stringify(transactions.get(index).logs[k])) + console.log("_________________________") + } + return transactions.get(index).logs.length + } + else + return 0 + } + } + Button { id: debug - width: logsWidth + width: debugActionWidth text: "debug" onClicked: { clientModel.debugRecord(transactions.get(index).recordIndex); } } - - Label - { - id: logs - width: logsWidth - } } } } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 6e87ed52d..6098ec035 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -30,8 +30,9 @@ Column { property int statusWidth: 50 property int fromWidth: 100 property int toWidth: 250 - property int valueWidth: 100 + property int valueWidth: 50 property int logsWidth: 50 + property int debugActionWidth: 50 Row { @@ -62,6 +63,11 @@ Column { text: "Logs" width: logsWidth } + Label + { + text: "Action" + width: debugActionWidth + } } Rectangle @@ -253,6 +259,8 @@ Column { blockModel.getTransaction(blockIndex, trIndex).returned = _r.returned; tr.recordIndex = _r.recordIndex; blockModel.getTransaction(blockIndex, trIndex).recordIndex = _r.recordIndex; + tr.logs = _r.logs; + blockModel.getTransaction(blockIndex, trIndex).logs = _r.logs; return; } } @@ -270,6 +278,7 @@ Column { itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) itemTr.sender = _r.sender itemTr.recordIndex = _r.recordIndex + itemTr.logs = _r.logs model.blocks[model.blocks.length - 1].transactions.push(itemTr) blockModel.appendTransaction(itemTr)