Browse Source

- Catch events for each incoming transaction.

cl-refactor
yann300 10 years ago
parent
commit
799ff95c28
  1. 302
      mix/ClientModel.cpp
  2. 7
      mix/ClientModel.h
  3. 12
      mix/ContractCallDataEncoder.cpp
  4. 4
      mix/ContractCallDataEncoder.h
  5. 1
      mix/MachineStates.h
  6. 547
      mix/MixClient.cpp
  7. 2
      mix/MixClient.h
  8. 17
      mix/QContractDefinition.cpp
  9. 8
      mix/QContractDefinition.h
  10. 32
      mix/QFunctionDefinition.cpp
  11. 7
      mix/QFunctionDefinition.h
  12. 13
      mix/QVariableDeclaration.cpp
  13. 9
      mix/QVariableDeclaration.h
  14. 28
      mix/qml/Block.qml
  15. 11
      mix/qml/BlockChain.qml

302
mix/ClientModel.cpp

@ -30,6 +30,7 @@
#include <jsonrpccpp/server.h> #include <jsonrpccpp/server.h>
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include <libdevcore/FixedHash.h>
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "Exceptions.h" #include "Exceptions.h"
#include "QContractDefinition.h" #include "QContractDefinition.h"
@ -82,7 +83,7 @@ ClientModel::ClientModel():
qRegisterMetaType<QCallData*>("QCallData"); qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<RecordLogEntry*>("RecordLogEntry*"); qRegisterMetaType<RecordLogEntry*>("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_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>()); m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>());
@ -113,7 +114,7 @@ QString ClientModel::apiCall(QString const& _message)
void ClientModel::mine() void ClientModel::mine()
{ {
if (m_mining) if (m_mining)
BOOST_THROW_EXCEPTION(ExecutionStateException()); BOOST_THROW_EXCEPTION(ExecutionStateException());
m_mining = true; m_mining = true;
emit miningStarted(); emit miningStarted();
@ -210,108 +211,108 @@ QVariantList ClientModel::gasCosts() const
void ClientModel::setupScenario(QVariantMap _scenario) void ClientModel::setupScenario(QVariantMap _scenario)
{ {
m_queueTransactions.clear(); m_queueTransactions.clear();
m_running = true; m_running = true;
m_currentScenario = _scenario; m_currentScenario = _scenario;
QVariantList blocks = _scenario.value("blocks").toList(); QVariantList blocks = _scenario.value("blocks").toList();
QVariantList stateAccounts = _scenario.value("accounts").toList(); QVariantList stateAccounts = _scenario.value("accounts").toList();
m_accounts.clear(); m_accounts.clear();
std::vector<KeyPair> userAccounts; std::vector<KeyPair> userAccounts;
for (auto const& b: stateAccounts) for (auto const& b: stateAccounts)
{ {
QVariantMap account = b.toMap(); QVariantMap account = b.toMap();
Address address = {}; Address address = {};
if (account.contains("secret")) if (account.contains("secret"))
{ {
KeyPair key(Secret(account.value("secret").toString().toStdString())); KeyPair key(Secret(account.value("secret").toString().toStdString()));
userAccounts.push_back(key); userAccounts.push_back(key);
address = key.address(); address = key.address();
} }
else if (account.contains("address")) else if (account.contains("address"))
address = Address(fromHex(account.value("address").toString().toStdString())); address = Address(fromHex(account.value("address").toString().toStdString()));
if (!address) if (!address)
continue; continue;
m_accounts[address] = Account(qvariant_cast<QEther*>(account.value("balance"))->toU256Wei(), Account::NormalCreation); m_accounts[address] = Account(qvariant_cast<QEther*>(account.value("balance"))->toU256Wei(), Account::NormalCreation);
} }
m_ethAccounts->setAccounts(userAccounts); m_ethAccounts->setAccounts(userAccounts);
for (auto const& b: blocks) for (auto const& b: blocks)
{ {
QVariantList transactions = b.toMap().value("transactions").toList(); QVariantList transactions = b.toMap().value("transactions").toList();
m_queueTransactions.push_back(transactions); m_queueTransactions.push_back(transactions);
} }
m_client->resetState(m_accounts, Secret(m_currentScenario.value("miner").toMap().value("secret").toString().toStdString())); 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::newBlock, this, &ClientModel::processNextBlock, Qt::QueuedConnection);
connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection);
connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection);
processNextBlock(); processNextBlock();
} }
void ClientModel::stopExecution() void ClientModel::stopExecution()
{ {
disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock);
disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock);
disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution);
m_running = false; m_running = false;
} }
void ClientModel::finalizeBlock() void ClientModel::finalizeBlock()
{ {
if (m_queueTransactions.size() > 0) if (m_queueTransactions.size() > 0)
mine(); mine();
else else
{ {
disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock); disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextBlock);
disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock);
disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution);
m_running = false; m_running = false;
emit runComplete(); emit runComplete();
} }
} }
void ClientModel::processNextBlock() void ClientModel::processNextBlock()
{ {
processNextTransactions(); processNextTransactions();
} }
void ClientModel::processNextTransactions() void ClientModel::processNextTransactions()
{ {
vector<TransactionSettings> transactionSequence; vector<TransactionSettings> transactionSequence;
for (auto const& t: m_queueTransactions.front()) for (auto const& t: m_queueTransactions.front())
{ {
QVariantMap transaction = t.toMap(); QVariantMap transaction = t.toMap();
QString contractId = transaction.value("contractId").toString(); QString contractId = transaction.value("contractId").toString();
QString functionId = transaction.value("functionId").toString(); QString functionId = transaction.value("functionId").toString();
bool gasAuto = transaction.value("gasAuto").toBool(); bool gasAuto = transaction.value("gasAuto").toBool();
u256 gas = 0; u256 gas = 0;
if (transaction.value("gas").data()) if (transaction.value("gas").data())
gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue()); gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue());
else else
gasAuto = true; gasAuto = true;
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei(); u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei(); u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QString sender = transaction.value("sender").toString(); QString sender = transaction.value("sender").toString();
bool isContractCreation = transaction.value("isContractCreation").toBool(); bool isContractCreation = transaction.value("isContractCreation").toBool();
bool isFunctionCall = transaction.value("isFunctionCall").toBool(); bool isFunctionCall = transaction.value("isFunctionCall").toBool();
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_codeModel->contracts().keys()[0]; contractId = m_codeModel->contracts().keys()[0];
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall); TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall);
transactionSettings.parameterValues = transaction.value("parameters").toMap(); transactionSettings.parameterValues = transaction.value("parameters").toMap();
if (contractId == functionId || functionId == "Constructor") if (contractId == functionId || functionId == "Constructor")
transactionSettings.functionId.clear(); transactionSettings.functionId.clear();
transactionSequence.push_back(transactionSettings); transactionSequence.push_back(transactionSettings);
} }
m_queueTransactions.pop_front(); m_queueTransactions.pop_front();
executeSequence(transactionSequence); executeSequence(transactionSequence);
} }
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence) void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
@ -323,7 +324,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
} }
emit runStarted(); emit runStarted();
//emit runStateChanged(); //emit runStateChanged();
//run sequence //run sequence
@ -332,7 +333,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
try try
{ {
vector<Address> deployedContracts; vector<Address> deployedContracts;
//onStateReset(); //onStateReset();
m_gasCosts.clear(); m_gasCosts.clear();
for (TransactionSettings const& transaction: _sequence) for (TransactionSettings const& transaction: _sequence)
{ {
@ -360,7 +361,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
break; break;
} }
if (!f) 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()) if (!transaction.functionId.isEmpty())
encoder.encode(f); encoder.encode(f);
for (QVariableDeclaration const* p: f->parametersList()) for (QVariableDeclaration const* p: f->parametersList())
@ -391,62 +392,62 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence)
{ {
auto contractAddressIter = m_contractAddresses.find(ctrInstance); auto contractAddressIter = m_contractAddresses.find(ctrInstance);
if (contractAddressIter == m_contractAddresses.end()) if (contractAddressIter == m_contractAddresses.end())
emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId);
callAddress(contractAddressIter->second, encoder.encodedData(), transaction); callAddress(contractAddressIter->second, encoder.encodedData(), transaction);
} }
m_gasCosts.append(m_client->lastExecution().gasUsed); m_gasCosts.append(m_client->lastExecution().gasUsed);
onNewTransaction(); onNewTransaction();
emit runComplete(); emit runComplete();
} }
} }
catch(boost::exception const&) catch(boost::exception const&)
{ {
cerr << boost::current_exception_diagnostic_information(); cerr << boost::current_exception_diagnostic_information();
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
} }
catch(exception const& e) catch(exception const& e)
{ {
cerr << boost::current_exception_diagnostic_information(); cerr << boost::current_exception_diagnostic_information();
emit runFailed(e.what()); emit runFailed(e.what());
} }
emit runStateChanged(); emit runStateChanged();
}); });
} }
void ClientModel::executeTr(QVariantMap _tr) void ClientModel::executeTr(QVariantMap _tr)
{ {
QVariantList trs; QVariantList trs;
trs.push_back(_tr); trs.push_back(_tr);
m_queueTransactions.push_back(trs); m_queueTransactions.push_back(trs);
processNextTransactions(); processNextTransactions();
} }
std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId) std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId)
{ {
std::pair<QString, int> ret = std::make_pair(_contractId, 0); std::pair<QString, int> ret = std::make_pair(_contractId, 0);
if (_contractId.startsWith("<") && _contractId.endsWith(">")) if (_contractId.startsWith("<") && _contractId.endsWith(">"))
{ {
QStringList values = ret.first.remove("<").remove(">").split(" - "); QStringList values = ret.first.remove("<").remove(">").split(" - ");
ret = std::make_pair(values[0], values[1].toUInt()); ret = std::make_pair(values[0], values[1].toUInt());
} }
return ret; return ret;
} }
QString ClientModel::resolveToken(std::pair<QString, int> const& _value, vector<Address> const& _contracts) QString ClientModel::resolveToken(std::pair<QString, int> const& _value, vector<Address> const& _contracts)
{ {
if (_contracts.size() > 0) 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 else
return _value.first; return _value.first;
} }
std::pair<QString, int> ClientModel::retrieveToken(QString const& _value, vector<Address> const& _contracts) std::pair<QString, int> ClientModel::retrieveToken(QString const& _value, vector<Address> const& _contracts)
{ {
std::pair<QString, int> ret; std::pair<QString, int> ret;
ret.first = _value; ret.first = _value;
ret.second = _contracts.size() - 1; ret.second = _contracts.size() - 1;
return ret; return ret;
} }
void ClientModel::showDebugger() void ClientModel::showDebugger()
@ -647,7 +648,7 @@ void ClientModel::emptyRecord()
void ClientModel::debugRecord(unsigned _index) void ClientModel::debugRecord(unsigned _index)
{ {
ExecutionResult e = m_client->execution(_index); ExecutionResult e = m_client->execution(_index);
showDebuggerForTransaction(e); showDebuggerForTransaction(e);
} }
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction) Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
@ -668,7 +669,7 @@ RecordLogEntry* ClientModel::lastBlock() const
strGas << blockInfo.gasUsed; strGas << blockInfo.gasUsed;
stringstream strNumber; stringstream strNumber;
strNumber << blockInfo.number; 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); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
return record; return record;
} }
@ -685,7 +686,7 @@ void ClientModel::onStateReset()
void ClientModel::onNewTransaction() void ClientModel::onNewTransaction()
{ {
ExecutionResult const& tr = m_client->lastExecution(); ExecutionResult const& tr = m_client->lastExecution();
unsigned block = m_client->number() + 1; unsigned block = m_client->number() + 1;
unsigned recordIndex = tr.executonIndex; unsigned recordIndex = tr.executonIndex;
QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex); QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex);
QString address = QString::fromStdString(toJS(tr.address)); QString address = QString::fromStdString(toJS(tr.address));
@ -727,9 +728,11 @@ void ClientModel::onNewTransaction()
Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress;
auto contractAddressIter = m_contractNames.find(contractAddress); auto contractAddressIter = m_contractNames.find(contractAddress);
QVariantMap inputParameters; QVariantMap inputParameters;
QVariantList logs;
if (contractAddressIter != m_contractNames.end()) if (contractAddressIter != m_contractNames.end())
{ {
ContractCallDataEncoder encoder;
CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second);
const QContractDefinition* def = compilerRes.contract(); const QContractDefinition* def = compilerRes.contract();
contract = def->name(); contract = def->name();
@ -739,25 +742,64 @@ void ClientModel::onNewTransaction()
if (funcDef) if (funcDef)
{ {
function = funcDef->name(); function = funcDef->name();
ContractCallDataEncoder encoder;
QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output); QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output);
returned += "("; returned += "(";
returned += returnValues.join(", "); returned += returnValues.join(", ");
returned += ")"; returned += ")";
bytes data = tr.inputParameters; bytes data = tr.inputParameters;
data.erase(data.begin(), data.begin() + 4); data.erase(data.begin(), data.begin() + 4);
QStringList parameters = encoder.decode(funcDef->parametersList(), data); QStringList parameters = encoder.decode(funcDef->parametersList(), data);
for (int k = 0; k < parameters.length(); ++k) for (int k = 0; k < parameters.length(); ++k)
inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k)); inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k));
} }
} }
}
LocalisedLogEntries logs = m_client->logs(); // Fill generated logs and decode parameters
QString sender = QString::fromStdString(dev::toHex(tr.sender.ref())); for (auto const& log: tr.logs)
QString label = contract + "." + function + "()"; {
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, QVariantMap l;
gasUsed, sender, label, inputParameters); 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); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newRecord(log); emit newRecord(log);
} }

7
mix/ClientModel.h

@ -115,6 +115,8 @@ class RecordLogEntry: public QObject
Q_PROPERTY(QString label MEMBER m_label CONSTANT) Q_PROPERTY(QString label MEMBER m_label CONSTANT)
/// input parameters /// input parameters
Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT)
/// logs
Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT)
public: public:
enum RecordType enum RecordType
@ -126,9 +128,9 @@ public:
RecordLogEntry(): RecordLogEntry():
m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} 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, 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_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: private:
unsigned m_recordIndex; unsigned m_recordIndex;
@ -144,6 +146,7 @@ private:
QString m_sender; QString m_sender;
QString m_label; QString m_label;
QVariantMap m_inputParameters; QVariantMap m_inputParameters;
QVariantList m_logs;
}; };
/** /**

12
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")); BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found"));
} }
QStringList ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const& _returnParameters, vector<bytes> _value) QString ContractCallDataEncoder::decode(QVariableDeclaration* const& _param, bytes _value)
{ {
QStringList r; SolidityType const& type = _param->type()->type();
for (int k = 0; k <_returnParameters.length(); k++) return decode(type, _value).toString();
{
QVariableDeclaration* dec = static_cast<QVariableDeclaration*>(_returnParameters.at(k));
SolidityType const& type = dec->type()->type();
r.append(decode(type, _value.at(k)).toString());
}
return r;
} }
QStringList ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const& _returnParameters, bytes _value) QStringList ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const& _returnParameters, bytes _value)

4
mix/ContractCallDataEncoder.h

@ -48,8 +48,8 @@ public:
void encode(QVariant const& _data, SolidityType const& _type); void encode(QVariant const& _data, SolidityType const& _type);
/// Decode variable in order to be sent to QML view. /// Decode variable in order to be sent to QML view.
QStringList decode(QList<QVariableDeclaration*> const& _dec, bytes _value); QStringList decode(QList<QVariableDeclaration*> const& _dec, bytes _value);
/// Decode @param _parameters /// Decode @param _parameter
QStringList decode(QList<QVariableDeclaration*> const& _parameters, std::vector<bytes> _value); QString decode(QVariableDeclaration* const& _param, bytes _value);
/// Decode single variable /// Decode single variable
QVariant decode(SolidityType const& _type, bytes const& _value); QVariant decode(SolidityType const& _type, bytes const& _value);
/// Get all encoded data encoded by encode function. /// Get all encoded data encoded by encode function.

1
mix/MachineStates.h

@ -84,6 +84,7 @@ namespace mix
unsigned transactionIndex; unsigned transactionIndex;
unsigned executonIndex = 0; unsigned executonIndex = 0;
bytes inputParameters; bytes inputParameters;
eth::LocalisedLogEntries logs;
bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); } bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); }
bool isConstructor() const { return !isCall() && !address; } bool isConstructor() const { return !isCall() && !address; }

547
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 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file MixClient.cpp /** @file MixClient.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
@ -48,27 +48,27 @@ namespace
struct MixPow //dummy POW struct MixPow //dummy POW
{ {
typedef int Solution; typedef int Solution;
static void assignResult(int, BlockInfo const&) {} static void assignResult(int, BlockInfo const&) {}
static bool verify(BlockInfo const&) { return true; } static bool verify(BlockInfo const&) { return true; }
}; };
} }
bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) bytes MixBlockChain::createGenesisBlock(h256 _stateRoot)
{ {
RLPStream block(3); RLPStream block(3);
block.appendList(15) block.appendList(15)
<< h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie
<< LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0
<< std::string() << h256() << h64(u64(42)); << std::string() << h256() << h64(u64(42));
block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList);
return block.out(); return block.out();
} }
MixClient::MixClient(std::string const& _dbPath): MixClient::MixClient(std::string const& _dbPath):
m_dbPath(_dbPath) m_dbPath(_dbPath)
{ {
resetState(std::unordered_map<Address, Account>()); resetState(std::unordered_map<Address, Account>());
} }
@ -85,335 +85,350 @@ LocalisedLogEntries MixClient::logs()
void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts, Secret const& _miner) void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts, Secret const& _miner)
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
Guard fl(x_filtersWatches); Guard fl(x_filtersWatches);
m_filters.clear(); m_filters.clear();
m_watches.clear(); m_watches.clear();
LogFilter filter; //LogFilter filter;
m_filters.insert(std::make_pair(filter.sha3(), filter)); //m_filters.insert(std::make_pair(filter.sha3(), filter));
m_watches.insert(std::make_pair(0, ClientWatch(filter.sha3(), Reaping::Automatic))); //m_watches.insert(std::make_pair(0, ClientWatch(filter.sha3(), Reaping::Automatic)));
m_stateDB = OverlayDB(); m_stateDB = OverlayDB();
SecureTrieDB<Address, MemoryDB> accountState(&m_stateDB); SecureTrieDB<Address, MemoryDB> accountState(&m_stateDB);
accountState.init(); accountState.init();
dev::eth::commit(_accounts, static_cast<MemoryDB&>(m_stateDB), accountState); dev::eth::commit(_accounts, static_cast<MemoryDB&>(m_stateDB), accountState);
h256 stateRoot = accountState.root(); h256 stateRoot = accountState.root();
m_bc.reset(); m_bc.reset();
m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); m_bc.reset(new MixBlockChain(m_dbPath, stateRoot));
m_state = eth::State(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address()); m_state = eth::State(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address());
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
WriteGuard lx(x_executions); WriteGuard lx(x_executions);
m_executions.clear(); m_executions.clear();
} }
Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secret const& _secret) Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secret const& _secret)
{ {
Transaction ret; Transaction ret;
if (_secret) if (_secret)
{ {
if (_t.isCreation()) if (_t.isCreation())
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret);
else else
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret);
} }
else else
{ {
if (_t.isCreation()) if (_t.isCreation())
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce()); ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce());
else else
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce()); ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce());
ret.forceSender(_t.safeSender()); ret.forceSender(_t.safeSender());
} }
return ret; return ret;
} }
void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret) 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 // do debugging run first
LastHashes lastHashes(256); LastHashes lastHashes(256);
lastHashes[0] = bc().numberHash(bc().number()); lastHashes[0] = bc().numberHash(bc().number());
for (unsigned i = 1; i < 256; ++i) for (unsigned i = 1; i < 256; ++i)
lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256(); lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256();
State execState = _state; State execState = _state;
execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation
Executive execution(execState, lastHashes, 0); Executive execution(execState, lastHashes, 0);
execution.initialize(t); execution.initialize(t);
execution.execute(); execution.execute();
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
std::vector<unsigned> levels; std::vector<unsigned> levels;
std::vector<MachineCode> codes; std::vector<MachineCode> codes;
std::map<bytes const*, unsigned> codeIndexes; std::map<bytes const*, unsigned> codeIndexes;
std::vector<bytes> data; std::vector<bytes> data;
std::map<bytesConstRef const*, unsigned> dataIndexes; std::map<bytesConstRef const*, unsigned> dataIndexes;
bytes const* lastCode = nullptr; bytes const* lastCode = nullptr;
bytesConstRef const* lastData = nullptr; bytesConstRef const* lastData = nullptr;
unsigned codeIndex = 0; unsigned codeIndex = 0;
unsigned dataIndex = 0; unsigned dataIndex = 0;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
{ {
VM& vm = *static_cast<VM*>(voidVM); VM& vm = *static_cast<VM*>(voidVM);
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt); ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
if (lastCode == nullptr || lastCode != &ext.code) if (lastCode == nullptr || lastCode != &ext.code)
{ {
auto const& iter = codeIndexes.find(&ext.code); auto const& iter = codeIndexes.find(&ext.code);
if (iter != codeIndexes.end()) if (iter != codeIndexes.end())
codeIndex = iter->second; codeIndex = iter->second;
else else
{ {
codeIndex = codes.size(); codeIndex = codes.size();
codes.push_back(MachineCode({ext.myAddress, ext.code})); codes.push_back(MachineCode({ext.myAddress, ext.code}));
codeIndexes[&ext.code] = codeIndex; codeIndexes[&ext.code] = codeIndex;
} }
lastCode = &ext.code; lastCode = &ext.code;
} }
if (lastData == nullptr || lastData != &ext.data) if (lastData == nullptr || lastData != &ext.data)
{ {
auto const& iter = dataIndexes.find(&ext.data); auto const& iter = dataIndexes.find(&ext.data);
if (iter != dataIndexes.end()) if (iter != dataIndexes.end())
dataIndex = iter->second; dataIndex = iter->second;
else else
{ {
dataIndex = data.size(); dataIndex = data.size();
data.push_back(ext.data.toBytes()); data.push_back(ext.data.toBytes());
dataIndexes[&ext.data] = dataIndex; dataIndexes[&ext.data] = dataIndex;
} }
lastData = &ext.data; lastData = &ext.data;
} }
if (levels.size() < ext.depth) if (levels.size() < ext.depth)
levels.push_back(machineStates.size() - 1); levels.push_back(machineStates.size() - 1);
else else
levels.resize(ext.depth); levels.resize(ext.depth);
machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(), machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex})); vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex}));
}; };
execution.go(onOp); execution.go(onOp);
execution.finalize(); execution.finalize();
dev::eth::ExecutionResult er = execution.executionResult(); dev::eth::ExecutionResult er = execution.executionResult();
switch (er.excepted) switch (er.excepted)
{ {
case TransactionException::None: case TransactionException::None:
break; break;
case TransactionException::NotEnoughCash: case TransactionException::NotEnoughCash:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Insufficient balance for contract deployment")); BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Insufficient balance for contract deployment"));
case TransactionException::OutOfGasBase: case TransactionException::OutOfGasBase:
case TransactionException::OutOfGas: case TransactionException::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas"));
case TransactionException::BlockGasLimitReached: case TransactionException::BlockGasLimitReached:
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached"));
case TransactionException::OutOfStack: case TransactionException::OutOfStack:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack"));
case TransactionException::StackUnderflow: case TransactionException::StackUnderflow:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow")); BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Stack underflow"));
//these should not happen in mix //these should not happen in mix
case TransactionException::Unknown: case TransactionException::Unknown:
case TransactionException::BadInstruction: case TransactionException::BadInstruction:
case TransactionException::BadJumpDestination: case TransactionException::BadJumpDestination:
case TransactionException::InvalidSignature: case TransactionException::InvalidSignature:
case TransactionException::InvalidNonce: case TransactionException::InvalidNonce:
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error")); BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Internal execution error"));
}; };
ExecutionResult d; ExecutionResult d;
d.inputParameters = t.data(); d.inputParameters = t.data();
d.result = execution.executionResult(); d.result = execution.executionResult();
d.machineStates = machineStates; d.machineStates = machineStates;
d.executionCode = std::move(codes); d.executionCode = std::move(codes);
d.transactionData = std::move(data); d.transactionData = std::move(data);
d.address = _t.receiveAddress(); d.address = _t.receiveAddress();
d.sender = _t.sender(); d.sender = _t.sender();
d.value = _t.value(); d.value = _t.value();
d.gasUsed = er.gasUsed + er.gasRefunded + c_callStipend; d.gasUsed = er.gasUsed + er.gasRefunded + c_callStipend;
if (_t.isCreation()) if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
if (!_call) if (!_call)
d.transactionIndex = m_state.pending().size(); d.transactionIndex = m_state.pending().size();
d.executonIndex = m_executions.size(); d.executonIndex = m_executions.size();
// execute on a state // execute on a state
if (!_call) if (!_call)
{ {
t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t; t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t;
er =_state.execute(lastHashes, t); er = _state.execute(lastHashes, t);
if (t.isCreation() && _state.code(d.contractAddress).empty()) if (t.isCreation() && _state.code(d.contractAddress).empty())
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment"));
d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend; d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend;
// collect watches // collect watches
h256Set changed; h256Set changed;
Guard l(x_filtersWatches); Guard l(x_filtersWatches);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters) LocalisedLogEntries logs;
if ((unsigned)i.second.filter.latest() > bc().number()) //for (unsigned i = 0; i < _state.pending().size(); ++i)
{ //{
// acceptable number. TransactionReceipt const& tr = _state.receipt(_state.pending().size() - 1);
auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
if (m.size()) auto trHash = _state.pending().at(_state.pending().size() - 1).sha3();
{ //for (std::pair<h256 const, eth::InstalledFilter>& installedFilter: m_filters)
// filter catches them //{
for (LogEntry const& l: m) LogEntries le = tr.log(); // installedFilter.second.filter.matches(tr);
i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); if (le.size())
changed.insert(i.first); for (unsigned j = 0; j < le.size(); ++j)
} logs.insert(logs.begin(), LocalisedLogEntry(le[j], bc().number() + 1, trHash));
} //}
changed.insert(dev::eth::PendingChangedFilter); //}
noteChanged(changed);
} /*if ((unsigned)i.second.filter.latest() > bc().number())
WriteGuard l(x_executions); {
m_executions.emplace_back(std::move(d)); // 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() void MixClient::mine()
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
m_state.commitToMine(bc()); m_state.commitToMine(bc());
m_state.completeMine<MixPow>(0); m_state.completeMine<MixPow>(0);
bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce); bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Default & ~ImportRequirements::ValidNonce);
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter };
noteChanged(changed); //noteChanged(changed);
} }
ExecutionResult MixClient::lastExecution() const ExecutionResult MixClient::lastExecution() const
{ {
ReadGuard l(x_executions); ReadGuard l(x_executions);
return m_executions.empty() ? ExecutionResult() : m_executions.back(); return m_executions.empty() ? ExecutionResult() : m_executions.back();
} }
ExecutionResult MixClient::execution(unsigned _index) const ExecutionResult MixClient::execution(unsigned _index) const
{ {
ReadGuard l(x_executions); ReadGuard l(x_executions);
return m_executions.at(_index); return m_executions.at(_index);
} }
State MixClient::asOf(h256 const& _block) const State MixClient::asOf(h256 const& _block) const
{ {
ReadGuard l(x_state); ReadGuard l(x_state);
return State(m_stateDB, bc(), _block); return State(m_stateDB, bc(), _block);
} }
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto) void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto)
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret)); u256 n = m_state.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
executeTransaction(t, m_state, false, _gasAuto, _secret); executeTransaction(t, m_state, false, _gasAuto, _secret);
} }
Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto) Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice, bool _gasAuto)
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret)); u256 n = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
executeTransaction(t, m_state, false, _gasAuto, _secret); executeTransaction(t, m_state, false, _gasAuto, _secret);
Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address; 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) 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; (void)_blockNumber;
State temp = asOf(eth::PendingBlock); State temp = asOf(eth::PendingBlock);
u256 n = temp.transactionsFrom(_from); u256 n = temp.transactionsFrom(_from);
Transaction t(_value, _gasPrice, _gas, _dest, _data, n); Transaction t(_value, _gasPrice, _gas, _dest, _data, n);
t.forceSender(_from); t.forceSender(_from);
if (_ff == FudgeFactor::Lenient) if (_ff == FudgeFactor::Lenient)
temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
WriteGuard lw(x_state); //TODO: lock is required only for last execution state WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp, true, _gasAuto); executeTransaction(t, temp, true, _gasAuto);
return lastExecution().result; return lastExecution().result;
} }
void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) 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) 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) 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) dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff)
{ {
(void)_blockNumber; (void)_blockNumber;
u256 n; u256 n;
State temp; State temp;
{ {
ReadGuard lr(x_state); ReadGuard lr(x_state);
temp = asOf(eth::PendingBlock); temp = asOf(eth::PendingBlock);
n = temp.transactionsFrom(_from); n = temp.transactionsFrom(_from);
} }
Transaction t(_value, _gasPrice, _gas, _data, n); Transaction t(_value, _gasPrice, _gas, _data, n);
t.forceSender(_from); t.forceSender(_from);
if (_ff == FudgeFactor::Lenient) if (_ff == FudgeFactor::Lenient)
temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
WriteGuard lw(x_state); //TODO: lock is required only for last execution state WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp, true, false); executeTransaction(t, temp, true, false);
return lastExecution().result; return lastExecution().result;
} }
void MixClient::noteChanged(h256Set const& _filters) /*void MixClient::noteChanged(h256Set const& _filters)
{ {
for (auto& i: m_watches) for (auto& i: m_watches)
if (_filters.count(i.second.id)) if (_filters.count(i.second.id))
{ {
if (m_filters.count(i.second.id)) if (m_filters.count(i.second.id))
i.second.changes += m_filters.at(i.second.id).changes; i.second.changes += m_filters.at(i.second.id).changes;
else else
i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
} }
for (auto& i: m_filters) for (auto& i: m_filters)
i.second.changes.clear(); i.second.changes.clear();
} }*/
eth::BlockInfo MixClient::blockInfo() const eth::BlockInfo MixClient::blockInfo() const
{ {
ReadGuard l(x_state); ReadGuard l(x_state);
return BlockInfo(bc().block()); return BlockInfo(bc().block());
} }
void MixClient::setAddress(Address _us) void MixClient::setAddress(Address _us)
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
m_state.setAddress(_us); m_state.setAddress(_us);
} }
void MixClient::startMining() void MixClient::startMining()
{ {
//no-op //no-op
} }
void MixClient::stopMining() void MixClient::stopMining()
{ {
//no-op //no-op
} }
bool MixClient::isMining() const bool MixClient::isMining() const
{ {
return false; return false;
} }
uint64_t MixClient::hashrate() const uint64_t MixClient::hashrate() const
{ {
return 0; return 0;
} }
eth::MiningProgress MixClient::miningProgress() const eth::MiningProgress MixClient::miningProgress() const
{ {
return eth::MiningProgress(); return eth::MiningProgress();
} }
} }

2
mix/MixClient.h

@ -91,7 +91,7 @@ protected:
private: private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret()); 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()); dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret());
eth::State m_state; eth::State m_state;

17
mix/QContractDefinition.cpp

@ -39,8 +39,23 @@ QContractDefinition::QContractDefinition(QObject* _parent, dev::solidity::Contra
else else
m_constructor = new QFunctionDefinition(parent); m_constructor = new QFunctionDefinition(parent);
std::vector<std::string> 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()) 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 QFunctionDefinition const* QContractDefinition::getFunction(dev::FixedHash<4> _hash) const

8
mix/QContractDefinition.h

@ -37,6 +37,7 @@ class QContractDefinition: public QBasicNodeDefinition
Q_OBJECT Q_OBJECT
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT) Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT)
Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT) Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT)
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> events READ events CONSTANT)
public: public:
QContractDefinition(QObject* _parent, solidity::ContractDefinition const* _contract); QContractDefinition(QObject* _parent, solidity::ContractDefinition const* _contract);
@ -44,12 +45,19 @@ public:
QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); } QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); }
/// Get the constructor of the contract. /// Get the constructor of the contract.
QFunctionDefinition* constructor() const { return m_constructor; } QFunctionDefinition* constructor() const { return m_constructor; }
/// Get all the functions of the contract.
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; } QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
/// Find function by hash, returns nullptr if not found /// Find function by hash, returns nullptr if not found
QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const; QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const;
/// Get events
QQmlListProperty<QFunctionDefinition> events() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_events); }
/// Get events
QList<QFunctionDefinition*> const& eventsList() const { return m_events; }
private: private:
QList<QFunctionDefinition*> m_functions; QList<QFunctionDefinition*> m_functions;
QFunctionDefinition* m_constructor; QFunctionDefinition* m_constructor;
QList<QFunctionDefinition*> m_events;
}; };
} }

32
mix/QFunctionDefinition.cpp

@ -28,15 +28,41 @@
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::mix; 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<FunctionDefinition> 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<dev::solidity::EventDefinition> 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<FunctionType>(*_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 paramNames = _f->getParameterNames();
auto paramTypes = _f->getParameterTypes(); auto paramTypes = _f->getParameterTypes();
auto returnNames = _f->getReturnParameterNames(); auto returnNames = _f->getReturnParameterNames();
auto returnTypes = _f->getReturnParameterTypes(); auto returnTypes = _f->getReturnParameterTypes();
for (unsigned i = 0; i < paramNames.size(); ++i) 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) 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()));
} }

7
mix/QFunctionDefinition.h

@ -41,6 +41,10 @@ public:
QFunctionDefinition(){} QFunctionDefinition(){}
QFunctionDefinition(QObject* _parent): QBasicNodeDefinition(_parent) {} QFunctionDefinition(QObject* _parent): QBasicNodeDefinition(_parent) {}
QFunctionDefinition(QObject* _parent, solidity::FunctionTypePointer const& _f); QFunctionDefinition(QObject* _parent, solidity::FunctionTypePointer const& _f);
QFunctionDefinition(QObject* _parent, solidity::ASTPointer<solidity::EventDefinition> const& _f);
QFunctionDefinition(QObject* _parent, solidity::ASTPointer<solidity::FunctionDefinition> const& _f);
/// Init members
void init(dev::solidity::FunctionTypePointer _f);
/// Get all input parameters of this function. /// Get all input parameters of this function.
QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; } QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; }
/// Get all input parameters of this function as QML property. /// Get all input parameters of this function as QML property.
@ -49,10 +53,13 @@ public:
QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; } QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; }
/// Get the hash of this function declaration on the contract ABI. /// Get the hash of this function declaration on the contract ABI.
FixedHash<4> hash() const { return m_hash; } 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: private:
int m_index; int m_index;
FixedHash<4> m_hash; FixedHash<4> m_hash;
FixedHash<32> m_fullHash;
QList<QVariableDeclaration*> m_parameters; QList<QVariableDeclaration*> m_parameters;
QList<QVariableDeclaration*> m_returnParameters; QList<QVariableDeclaration*> m_returnParameters;
void initQParameters(); void initQParameters();

13
mix/QVariableDeclaration.cpp

@ -24,27 +24,32 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include "CodeModel.h" #include "CodeModel.h"
using namespace solidity;
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
QVariableDeclaration::QVariableDeclaration(QObject* _parent, solidity::VariableDeclaration const* _v): QVariableDeclaration::QVariableDeclaration(QObject* _parent, ASTPointer<VariableDeclaration> const _v):
QBasicNodeDefinition(_parent, _v), QBasicNodeDefinition(_parent, _v.get()),
m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().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), QBasicNodeDefinition(_parent, _name),
m_type(new QSolidityType(_parent, _type)) 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), QBasicNodeDefinition(_parent, _name),
m_type(new QSolidityType(this, CodeModel::nodeType(_type))) m_type(new QSolidityType(this, CodeModel::nodeType(_type)))
{ {
m_isIndexed = _isIndexed;
} }
QSolidityType::QSolidityType(QObject* _parent, SolidityType const& _type): QSolidityType::QSolidityType(QObject* _parent, SolidityType const& _type):

9
mix/QVariableDeclaration.h

@ -21,6 +21,7 @@
#include <QDebug> #include <QDebug>
#include <QVariantList> #include <QVariantList>
#include <libsolidity/AST.h>
#include "QBasicNodeDefinition.h" #include "QBasicNodeDefinition.h"
#include "SolidityType.h" #include "SolidityType.h"
@ -82,14 +83,16 @@ class QVariableDeclaration: public QBasicNodeDefinition
public: public:
QVariableDeclaration() {} QVariableDeclaration() {}
QVariableDeclaration(QObject* _parent, solidity::VariableDeclaration const* _v); QVariableDeclaration(QObject* _parent, solidity::ASTPointer<solidity::VariableDeclaration> const _v);
QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type); QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed = false);
QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type); QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed = false);
QSolidityType* type() const { return m_type; } QSolidityType* type() const { return m_type; }
void setType(QSolidityType* _type) { m_type = _type; } void setType(QSolidityType* _type) { m_type = _type; }
bool isIndexed() { return m_isIndexed; }
private: private:
QSolidityType* m_type; QSolidityType* m_type;
bool m_isIndexed;
}; };

28
mix/qml/Block.qml

@ -121,22 +121,36 @@ ColumnLayout
clip: true 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 Button
{ {
id: debug id: debug
width: logsWidth width: debugActionWidth
text: "debug" text: "debug"
onClicked: onClicked:
{ {
clientModel.debugRecord(transactions.get(index).recordIndex); clientModel.debugRecord(transactions.get(index).recordIndex);
} }
} }
Label
{
id: logs
width: logsWidth
}
} }
} }
} }

11
mix/qml/BlockChain.qml

@ -30,8 +30,9 @@ Column {
property int statusWidth: 50 property int statusWidth: 50
property int fromWidth: 100 property int fromWidth: 100
property int toWidth: 250 property int toWidth: 250
property int valueWidth: 100 property int valueWidth: 50
property int logsWidth: 50 property int logsWidth: 50
property int debugActionWidth: 50
Row Row
{ {
@ -62,6 +63,11 @@ Column {
text: "Logs" text: "Logs"
width: logsWidth width: logsWidth
} }
Label
{
text: "Action"
width: debugActionWidth
}
} }
Rectangle Rectangle
@ -253,6 +259,8 @@ Column {
blockModel.getTransaction(blockIndex, trIndex).returned = _r.returned; blockModel.getTransaction(blockIndex, trIndex).returned = _r.returned;
tr.recordIndex = _r.recordIndex; tr.recordIndex = _r.recordIndex;
blockModel.getTransaction(blockIndex, trIndex).recordIndex = _r.recordIndex; blockModel.getTransaction(blockIndex, trIndex).recordIndex = _r.recordIndex;
tr.logs = _r.logs;
blockModel.getTransaction(blockIndex, trIndex).logs = _r.logs;
return; return;
} }
} }
@ -270,6 +278,7 @@ Column {
itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei)
itemTr.sender = _r.sender itemTr.sender = _r.sender
itemTr.recordIndex = _r.recordIndex itemTr.recordIndex = _r.recordIndex
itemTr.logs = _r.logs
model.blocks[model.blocks.length - 1].transactions.push(itemTr) model.blocks[model.blocks.length - 1].transactions.push(itemTr)
blockModel.appendTransaction(itemTr) blockModel.appendTransaction(itemTr)

Loading…
Cancel
Save