diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 6ba7e54f9..2e1ffaf8a 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -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 accounts; + map accounts; for (auto const& b: balances) { QVariantMap address = b.toMap(); - accounts.insert(std::make_pair(Secret(address.value("secret").toString().toStdString()), (qvariant_cast(address.value("balance")))->toU256Wei())); + accounts.insert(make_pair(Secret(address.value("secret").toString().toStdString()), (qvariant_cast(address.value("balance")))->toU256Wei())); } - std::vector transactionSequence; + vector 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 const& _sequence, std::map const& _balances) +void ClientModel::executeSequence(vector const& _sequence, map const& _balances) { if (m_running) BOOST_THROW_EXCEPTION(ExecutionStateException()); @@ -218,7 +219,7 @@ void ClientModel::executeSequence(std::vector 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 const& _seque CompiledContract const& compilerRes = m_codeModel->contract(transaction.contractId); QFunctionDefinition const* f = nullptr; bytes contractCode = compilerRes.bytes(); - std::shared_ptr contractDef = compilerRes.sharedContract(); + shared_ptr contractDef = compilerRes.sharedContract(); if (transaction.functionId.isEmpty()) f = contractDef->constructor(); else @@ -290,12 +291,12 @@ void ClientModel::executeSequence(std::vector 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 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 solLocals; // - std::map storageDeclarations; // + map solLocals; // + map storageDeclarations; // 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::max()) + for (auto st: s.storage) + if (st.first < numeric_limits::max()) { auto storageIter = contract->storage().find(static_cast(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 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(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; diff --git a/mix/ClientModel.h b/mix/ClientModel.h index a5d89d859..8c090f355 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -200,6 +200,7 @@ private: void onStateReset(); void showDebuggerForTransaction(ExecutionResult const& _t); QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); + QVariant formatStorageValue(SolidityType const& _type, std::map const& _storage, unsigned _offset, dev::u256 const& _slot); std::atomic m_running; std::atomic m_mining; diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 53ec34e2b..5685d8ed8 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -54,8 +54,8 @@ using namespace dev::solidity; class CollectDeclarationsVisitor: public ASTConstVisitor { public: - CollectDeclarationsVisitor(QHash* _functions, QHash* _locals, QHash* _storage): - m_functions(_functions), m_locals(_locals), m_storage(_storage), m_functionScope(false), m_storageSlot(0) {} + CollectDeclarationsVisitor(QHash* _functions, QHash* _locals): + m_functions(_functions), m_locals(_locals), m_functionScope(false) {} private: LocationPair nodeLocation(ASTNode const& _node) { @@ -79,19 +79,17 @@ private: SolidityDeclaration decl; decl.type = CodeModel::nodeType(_node.getType().get()); decl.name = QString::fromStdString(_node.getName()); + decl.slot = 0; + decl.offset = 0; if (m_functionScope) m_locals->insert(nodeLocation(_node), decl); - else - m_storage->insert(m_storageSlot++, decl); return true; } private: QHash* m_functions; QHash* m_locals; - QHash* m_storage; bool m_functionScope; - uint m_storageSlot; }; dev::eth::AssemblyItems filterLocations(dev::eth::AssemblyItems const& _locations, dev::solidity::ContractDefinition const& _contract, QHash _functions) @@ -108,6 +106,24 @@ dev::eth::AssemblyItems filterLocations(dev::eth::AssemblyItems const& _location return result; } +QHash collectStorage(dev::solidity::ContractDefinition const& _contract) +{ + QHash result; + dev::solidity::ContractType const* contractType = dynamic_cast(_contract.getType(nullptr).get()); + if (!contractType) + return result; + + for (auto v : contractType->getStateVariables()) + { + dev::solidity::VariableDeclaration const* declaration = std::get<0>(v); + dev::u256 slot = std::get<1>(v); + unsigned offset = std::get<2>(v); + result[static_cast(slot)].push_back(SolidityDeclaration { QString::fromStdString(declaration->getName()), CodeModel::nodeType(declaration->getType().get()), slot, offset }); + } + return result; +} + + } //namespace void BackgroundWorker::queueCodeChange(int _jobId) @@ -133,7 +149,8 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler if (contractDefinition.getLocation().sourceName.get()) m_documentId = QString::fromStdString(*contractDefinition.getLocation().sourceName); - CollectDeclarationsVisitor visitor(&m_functions, &m_locals, &m_storage); + CollectDeclarationsVisitor visitor(&m_functions, &m_locals); + m_storage = collectStorage(contractDefinition); contractDefinition.accept(visitor); m_assemblyItems = filterLocations(_compiler.getRuntimeAssemblyItems(name), contractDefinition, m_functions); m_constructorAssemblyItems = filterLocations(_compiler.getAssemblyItems(name), contractDefinition, m_functions); @@ -335,7 +352,7 @@ dev::bytes const& CodeModel::getStdContractCode(const QString& _contractName, co SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) { - SolidityType r { SolidityType::Type::UnsignedInteger, 32, false, false, QString::fromStdString(_type->toString()), std::vector(), std::vector() }; + SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector(), std::vector() }; if (!_type) return r; r.dynamicSize = _type->isDynamicallySized(); @@ -367,7 +384,12 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) if (array->isByteArray()) r.type = SolidityType::Type::Bytes; else - r = nodeType(array->getBaseType().get()); + { + SolidityType elementType = nodeType(array->getBaseType().get()); + elementType.name = r.name; + r = elementType; + } + r.count = static_cast(array->getLength()); r.array = true; } break; @@ -384,7 +406,10 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) r.type = SolidityType::Type::Struct; StructType const* s = dynamic_cast(_type); for(auto const& structMember: s->getMembers()) - r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.first), nodeType(structMember.second.get()) }); + { + auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.first); + r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.first), nodeType(structMember.second.get()), slotAndOffset.first, slotAndOffset.second }); + } } break; case Type::Category::Function: diff --git a/mix/CodeModel.h b/mix/CodeModel.h index a4ca31e08..8fd0606f4 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -99,7 +99,7 @@ public: QHash const& functions() const { return m_functions; } QHash const& locals() const { return m_locals; } - QHash const& storage() const { return m_storage; } + QHash const& storage() const { return m_storage; } private: uint m_sourceHash; @@ -112,7 +112,7 @@ private: eth::AssemblyItems m_constructorAssemblyItems; QHash m_functions; QHash m_locals; - QHash m_storage; + QHash m_storage; friend class CodeModel; }; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 7729c0ffe..fadc8a45a 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -185,7 +185,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c // execute on a state if (!_call) { - _state.execute(lastHashes, _t); + dev::eth::ExecutionResult 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")); // collect watches diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index cf9345061..4309550b2 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -26,14 +26,15 @@ #pragma once +namespace dev +{ + namespace solidity { class Type; class VariableDeclaration; } -namespace dev -{ namespace mix { diff --git a/mix/SolidityType.h b/mix/SolidityType.h index 9990e2e7f..accdb14b4 100644 --- a/mix/SolidityType.h +++ b/mix/SolidityType.h @@ -25,6 +25,7 @@ along with cpp-ethereum. If not, see . #include #include +#include namespace dev { @@ -49,6 +50,7 @@ struct SolidityType }; Type type; unsigned size; //in bytes, + unsigned count; bool array; bool dynamicSize; QString name; @@ -60,7 +62,11 @@ struct SolidityDeclaration { QString name; SolidityType type; + dev::u256 slot; + unsigned offset; }; +using SolidityDeclarations = std::vector; + } }