|
|
@ -43,6 +43,7 @@ |
|
|
|
|
|
|
|
using namespace dev; |
|
|
|
using namespace dev::eth; |
|
|
|
using namespace std; |
|
|
|
|
|
|
|
namespace dev |
|
|
|
{ |
|
|
@ -54,7 +55,7 @@ class RpcConnector: public jsonrpc::AbstractServerConnector |
|
|
|
public: |
|
|
|
virtual bool StartListening() override { return true; } |
|
|
|
virtual bool StopListening() override { return true; } |
|
|
|
virtual bool SendResponse(std::string const& _response, void*) override |
|
|
|
virtual bool SendResponse(string const& _response, void*) override |
|
|
|
{ |
|
|
|
m_response = QString::fromStdString(_response); |
|
|
|
return true; |
|
|
@ -101,7 +102,7 @@ QString ClientModel::apiCall(QString const& _message) |
|
|
|
} |
|
|
|
catch (...) |
|
|
|
{ |
|
|
|
std::cerr << boost::current_exception_diagnostic_information(); |
|
|
|
cerr << boost::current_exception_diagnostic_information(); |
|
|
|
return QString(); |
|
|
|
} |
|
|
|
} |
|
|
@ -125,7 +126,7 @@ void ClientModel::mine() |
|
|
|
catch (...) |
|
|
|
{ |
|
|
|
m_mining = false; |
|
|
|
std::cerr << boost::current_exception_diagnostic_information(); |
|
|
|
cerr << boost::current_exception_diagnostic_information(); |
|
|
|
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); |
|
|
|
} |
|
|
|
emit miningStateChanged(); |
|
|
@ -142,7 +143,7 @@ QVariantMap ClientModel::contractAddresses() const |
|
|
|
{ |
|
|
|
QVariantMap res; |
|
|
|
for (auto const& c: m_contractAddresses) |
|
|
|
res.insert(c.first, QString::fromStdString(dev::toJS(c.second))); |
|
|
|
res.insert(c.first, QString::fromStdString(toJS(c.second))); |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
@ -151,14 +152,14 @@ void ClientModel::setupState(QVariantMap _state) |
|
|
|
QVariantList balances = _state.value("accounts").toList(); |
|
|
|
QVariantList transactions = _state.value("transactions").toList(); |
|
|
|
|
|
|
|
std::map<Secret, u256> accounts; |
|
|
|
map<Secret, u256> accounts; |
|
|
|
for (auto const& b: balances) |
|
|
|
{ |
|
|
|
QVariantMap address = b.toMap(); |
|
|
|
accounts.insert(std::make_pair(Secret(address.value("secret").toString().toStdString()), (qvariant_cast<QEther*>(address.value("balance")))->toU256Wei())); |
|
|
|
accounts.insert(make_pair(Secret(address.value("secret").toString().toStdString()), (qvariant_cast<QEther*>(address.value("balance")))->toU256Wei())); |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<TransactionSettings> transactionSequence; |
|
|
|
vector<TransactionSettings> transactionSequence; |
|
|
|
for (auto const& t: transactions) |
|
|
|
{ |
|
|
|
QVariantMap transaction = t.toMap(); |
|
|
@ -196,7 +197,7 @@ void ClientModel::setupState(QVariantMap _state) |
|
|
|
executeSequence(transactionSequence, accounts); |
|
|
|
} |
|
|
|
|
|
|
|
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, std::map<Secret, u256> const& _balances) |
|
|
|
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence, map<Secret, u256> const& _balances) |
|
|
|
{ |
|
|
|
if (m_running) |
|
|
|
BOOST_THROW_EXCEPTION(ExecutionStateException()); |
|
|
@ -218,7 +219,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque |
|
|
|
if (!transaction.stdContractUrl.isEmpty()) |
|
|
|
{ |
|
|
|
//std contract
|
|
|
|
dev::bytes const& stdContractCode = m_codeModel->getStdContractCode(transaction.contractId, transaction.stdContractUrl); |
|
|
|
bytes const& stdContractCode = m_codeModel->getStdContractCode(transaction.contractId, transaction.stdContractUrl); |
|
|
|
TransactionSettings stdTransaction = transaction; |
|
|
|
stdTransaction.gas = 500000;// TODO: get this from std contracts library
|
|
|
|
Address address = deployContract(stdContractCode, stdTransaction); |
|
|
@ -231,7 +232,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque |
|
|
|
CompiledContract const& compilerRes = m_codeModel->contract(transaction.contractId); |
|
|
|
QFunctionDefinition const* f = nullptr; |
|
|
|
bytes contractCode = compilerRes.bytes(); |
|
|
|
std::shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract(); |
|
|
|
shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract(); |
|
|
|
if (transaction.functionId.isEmpty()) |
|
|
|
f = contractDef->constructor(); |
|
|
|
else |
|
|
@ -290,12 +291,12 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque |
|
|
|
} |
|
|
|
catch(boost::exception const&) |
|
|
|
{ |
|
|
|
std::cerr << boost::current_exception_diagnostic_information(); |
|
|
|
cerr << boost::current_exception_diagnostic_information(); |
|
|
|
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); |
|
|
|
} |
|
|
|
catch(std::exception const& e) |
|
|
|
catch(exception const& e) |
|
|
|
{ |
|
|
|
std::cerr << boost::current_exception_diagnostic_information(); |
|
|
|
cerr << boost::current_exception_diagnostic_information(); |
|
|
|
emit runFailed(e.what()); |
|
|
|
} |
|
|
|
m_running = false; |
|
|
@ -322,7 +323,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) |
|
|
|
{ |
|
|
|
QHash<int, int> codeMap; |
|
|
|
codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code, codeMap)); |
|
|
|
codeMaps.push_back(std::move(codeMap)); |
|
|
|
codeMaps.push_back(move(codeMap)); |
|
|
|
//try to resolve contract for source level debugging
|
|
|
|
auto nameIter = m_contractNames.find(code.address); |
|
|
|
if (nameIter != m_contractNames.end()) |
|
|
@ -330,7 +331,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) |
|
|
|
CompiledContract const& compilerRes = m_codeModel->contract(nameIter->second); |
|
|
|
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems(); |
|
|
|
codes.back()->setDocument(compilerRes.documentId()); |
|
|
|
codeItems.push_back(std::move(assemblyItems)); |
|
|
|
codeItems.push_back(move(assemblyItems)); |
|
|
|
contracts.push_back(&compilerRes); |
|
|
|
} |
|
|
|
else |
|
|
@ -346,8 +347,8 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) |
|
|
|
|
|
|
|
QVariantList states; |
|
|
|
QVariantList solCallStack; |
|
|
|
std::map<int, QVariableDeclaration*> solLocals; //<stack pos, decl>
|
|
|
|
std::map<QString, QVariableDeclaration*> storageDeclarations; //<name, decl>
|
|
|
|
map<int, QVariableDeclaration*> solLocals; //<stack pos, decl>
|
|
|
|
map<QString, QVariableDeclaration*> storageDeclarations; //<name, decl>
|
|
|
|
|
|
|
|
unsigned prevInstructionIndex = 0; |
|
|
|
for (MachineState const& s: _t.machineStates) |
|
|
@ -359,7 +360,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) |
|
|
|
CompiledContract const* contract = contracts[s.codeIndex]; |
|
|
|
AssemblyItem const& instruction = codeItems[s.codeIndex][instructionIndex]; |
|
|
|
|
|
|
|
if (instruction.type() == dev::eth::Push && !instruction.data()) |
|
|
|
if (instruction.type() == eth::Push && !instruction.data()) |
|
|
|
{ |
|
|
|
//register new local variable initialization
|
|
|
|
auto localIter = contract->locals().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end)); |
|
|
@ -367,7 +368,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) |
|
|
|
solLocals[s.stack.size()] = new QVariableDeclaration(debugData, localIter.value().name.toStdString(), localIter.value().type); |
|
|
|
} |
|
|
|
|
|
|
|
if (instruction.type() == dev::eth::Tag) |
|
|
|
if (instruction.type() == eth::Tag) |
|
|
|
{ |
|
|
|
//track calls into functions
|
|
|
|
AssemblyItem const& prevInstruction = codeItems[s.codeIndex][prevInstructionIndex]; |
|
|
@ -382,7 +383,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) |
|
|
|
QVariantMap locals; |
|
|
|
QVariantList localDeclarations; |
|
|
|
QVariantMap localValues; |
|
|
|
for(auto l: solLocals) |
|
|
|
for (auto l: solLocals) |
|
|
|
if (l.first < (int)s.stack.size()) |
|
|
|
{ |
|
|
|
if (l.second->type()->name().startsWith("mapping")) |
|
|
@ -396,42 +397,45 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) |
|
|
|
QVariantMap storage; |
|
|
|
QVariantList storageDeclarationList; |
|
|
|
QVariantMap storageValues; |
|
|
|
for(auto st: s.storage) |
|
|
|
if (st.first < std::numeric_limits<unsigned>::max()) |
|
|
|
for (auto st: s.storage) |
|
|
|
if (st.first < numeric_limits<unsigned>::max()) |
|
|
|
{ |
|
|
|
auto storageIter = contract->storage().find(static_cast<unsigned>(st.first)); |
|
|
|
if (storageIter != contract->storage().end()) |
|
|
|
{ |
|
|
|
QVariableDeclaration* storageDec = nullptr; |
|
|
|
auto decIter = storageDeclarations.find(storageIter.value().name); |
|
|
|
if (decIter != storageDeclarations.end()) |
|
|
|
storageDec = decIter->second; |
|
|
|
else |
|
|
|
for (SolidityDeclaration const& codeDec : storageIter.value()) |
|
|
|
{ |
|
|
|
storageDec = new QVariableDeclaration(debugData, storageIter.value().name.toStdString(), storageIter.value().type); |
|
|
|
storageDeclarations[storageDec->name()] = storageDec; |
|
|
|
if (codeDec.type.name.startsWith("mapping")) |
|
|
|
continue; //mapping type not yet managed
|
|
|
|
auto decIter = storageDeclarations.find(codeDec.name); |
|
|
|
if (decIter != storageDeclarations.end()) |
|
|
|
storageDec = decIter->second; |
|
|
|
else |
|
|
|
{ |
|
|
|
storageDec = new QVariableDeclaration(debugData, codeDec.name.toStdString(), codeDec.type); |
|
|
|
storageDeclarations[storageDec->name()] = storageDec; |
|
|
|
} |
|
|
|
storageDeclarationList.push_back(QVariant::fromValue(storageDec)); |
|
|
|
storageValues[storageDec->name()] = formatStorageValue(storageDec->type()->type(), s.storage, codeDec.offset, codeDec.slot); |
|
|
|
} |
|
|
|
if (storageDec->type()->name().startsWith("mapping")) |
|
|
|
break; //mapping type not yet managed
|
|
|
|
storageDeclarationList.push_back(QVariant::fromValue(storageDec)); |
|
|
|
storageValues[storageDec->name()] = formatValue(storageDec->type()->type(), st.second); |
|
|
|
} |
|
|
|
} |
|
|
|
storage["variables"] = storageDeclarationList; |
|
|
|
storage["values"] = storageValues; |
|
|
|
|
|
|
|
prevInstructionIndex = instructionIndex; |
|
|
|
solState = new QSolState(debugData, std::move(storage), std::move(solCallStack), std::move(locals), instruction.getLocation().start, instruction.getLocation().end, QString::fromUtf8(instruction.getLocation().sourceName->c_str())); |
|
|
|
solState = new QSolState(debugData, move(storage), move(solCallStack), move(locals), instruction.getLocation().start, instruction.getLocation().end, QString::fromUtf8(instruction.getLocation().sourceName->c_str())); |
|
|
|
} |
|
|
|
|
|
|
|
states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState))); |
|
|
|
} |
|
|
|
|
|
|
|
debugData->setStates(std::move(states)); |
|
|
|
debugData->setStates(move(states)); |
|
|
|
debugDataReady(debugData); |
|
|
|
} |
|
|
|
|
|
|
|
QVariant ClientModel::formatValue(SolidityType const& _type, dev::u256 const& _value) |
|
|
|
QVariant ClientModel::formatValue(SolidityType const& _type, u256 const& _value) |
|
|
|
{ |
|
|
|
ContractCallDataEncoder decoder; |
|
|
|
bytes val = toBigEndian(_value); |
|
|
@ -439,6 +443,41 @@ QVariant ClientModel::formatValue(SolidityType const& _type, dev::u256 const& _v |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
QVariant ClientModel::formatStorageValue(SolidityType const& _type, map<u256, u256> const& _storage, unsigned _offset, u256 const& _slot) |
|
|
|
{ |
|
|
|
u256 slot = _slot; |
|
|
|
QVariantList values; |
|
|
|
ContractCallDataEncoder decoder; |
|
|
|
u256 count = 1; |
|
|
|
if (_type.dynamicSize) |
|
|
|
{ |
|
|
|
count = _storage.at(slot); |
|
|
|
slot = fromBigEndian<u256>(sha3(toBigEndian(slot)).asBytes()); |
|
|
|
} |
|
|
|
else if (_type.array) |
|
|
|
count = _type.count; |
|
|
|
|
|
|
|
unsigned offset = _offset; |
|
|
|
while (count--) |
|
|
|
{ |
|
|
|
|
|
|
|
bytes slotBytes = toBigEndian(_storage.at(slot)); |
|
|
|
bytes val(slotBytes.begin() + offset, slotBytes.begin() + offset + _type.size); |
|
|
|
values.append(decoder.decode(_type, val)); |
|
|
|
offset += _type.size; |
|
|
|
if ((offset + _type.size) > 32) |
|
|
|
{ |
|
|
|
slot++; |
|
|
|
offset = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!_type.array) |
|
|
|
return values[0]; |
|
|
|
|
|
|
|
return QVariant::fromValue(values); |
|
|
|
} |
|
|
|
|
|
|
|
void ClientModel::emptyRecord() |
|
|
|
{ |
|
|
|
debugDataReady(new QDebugData()); |
|
|
@ -464,9 +503,9 @@ void ClientModel::callContract(Address const& _contract, bytes const& _data, Tra |
|
|
|
RecordLogEntry* ClientModel::lastBlock() const |
|
|
|
{ |
|
|
|
eth::BlockInfo blockInfo = m_client->blockInfo(); |
|
|
|
std::stringstream strGas; |
|
|
|
stringstream strGas; |
|
|
|
strGas << blockInfo.gasUsed; |
|
|
|
std::stringstream strNumber; |
|
|
|
stringstream strNumber; |
|
|
|
strNumber << blockInfo.number; |
|
|
|
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash.ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block); |
|
|
|
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); |
|
|
@ -489,7 +528,7 @@ void ClientModel::onNewTransaction() |
|
|
|
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)); |
|
|
|
QString value = QString::fromStdString(dev::toString(tr.value)); |
|
|
|
QString value = QString::fromStdString(toString(tr.value)); |
|
|
|
QString contract = address; |
|
|
|
QString function; |
|
|
|
QString returned; |
|
|
|