Browse Source

Merge branch 'develop' into p2p

cl-refactor
subtly 10 years ago
parent
commit
2cc5d2b3d6
  1. 10
      CMakeLists.txt
  2. 2
      cmake/EthDependencies.cmake
  3. 2
      evmjit/libevmjit/ExecutionEngine.cpp
  4. 12
      libdevcore/Exceptions.h
  5. 8
      libdevcore/RLP.h
  6. 6
      libdevcore/vector_ref.h
  7. 2
      libdevcrypto/ECDHE.h
  8. 60
      libethcore/CommonEth.cpp
  9. 8
      libethcore/Exceptions.h
  10. 6
      libethereum/BlockDetails.h
  11. 2
      libevm/VM.cpp
  12. 2
      libevmcore/Assembly.cpp
  13. 4
      libevmcore/Assembly.h
  14. 4
      liblll/Parser.cpp
  15. 6
      libp2p/Common.h
  16. 2
      libsolidity/Exceptions.h
  17. 11
      libweb3jsonrpc/CMakeLists.txt
  18. 25
      mix/ClientModel.cpp
  19. 32
      mix/ClientModel.h
  20. 2
      mix/CodeHighlighter.h
  21. 8
      mix/Exceptions.h
  22. 8
      mix/MachineStates.h
  23. 73
      mix/MixClient.cpp
  24. 8
      mix/MixClient.h
  25. 31
      mix/qml/CallStack.qml
  26. 1
      mix/qml/DebugInfoList.qml
  27. 41
      mix/qml/TransactionLog.qml
  28. 10
      mix/qml/main.qml
  29. 1
      mix/res.qrc
  30. 7
      test/CMakeLists.txt
  31. 4
      test/natspec.cpp
  32. 36
      test/webthreestubclient.h

10
CMakeLists.txt

@ -43,6 +43,10 @@ function(configureProject)
if (EVMJIT) if (EVMJIT)
add_definitions(-DETH_EVMJIT) add_definitions(-DETH_EVMJIT)
endif() endif()
if (HEADLESS)
add_definitions(-DETH_HEADLESS)
endif()
endfunction() endfunction()
@ -65,13 +69,13 @@ function(createBuildInfo)
endif () endif ()
if (EVMJIT) if (EVMJIT)
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/JIT") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/JIT")
else () else ()
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/int") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/int")
endif () endif ()
if (PARANOIA) if (PARANOIA)
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/PARA") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/PARA")
endif () endif ()
#cmake build type may be not specified when using msvc #cmake build type may be not specified when using msvc

2
cmake/EthDependencies.cmake

@ -122,6 +122,8 @@ if (NOT HEADLESS)
string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM})
message(" - npm location : ${ETH_NPM}") message(" - npm location : ${ETH_NPM}")
find_program(ETH_JSON_RPC_STUB jsonrpcstub)
endif() #HEADLESS endif() #HEADLESS
# use multithreaded boost libraries, with -mt suffix # use multithreaded boost libraries, with -mt suffix

2
evmjit/libevmjit/ExecutionEngine.cpp

@ -29,7 +29,7 @@ namespace jit
namespace namespace
{ {
typedef ReturnCode(*EntryFuncPtr)(Runtime*); using EntryFuncPtr = ReturnCode(*)(Runtime*);
ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime) ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime)
{ {

12
libdevcore/Exceptions.h

@ -44,10 +44,10 @@ struct FileError: virtual Exception {};
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; }; struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; };
// error information to be added to exceptions // error information to be added to exceptions
typedef boost::error_info<struct tag_invalidSymbol, char> errinfo_invalidSymbol; using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>;
typedef boost::error_info<struct tag_address, std::string> errinfo_wrongAddress; using errinfo_wrongAddress = boost::error_info<struct tag_address, std::string>;
typedef boost::error_info<struct tag_comment, std::string> errinfo_comment; using errinfo_comment = boost::error_info<struct tag_comment, std::string>;
typedef boost::error_info<struct tag_required, bigint> errinfo_required; using errinfo_required = boost::error_info<struct tag_required, bigint>;
typedef boost::error_info<struct tag_got, bigint> errinfo_got; using errinfo_got = boost::error_info<struct tag_got, bigint>;
typedef boost::tuple<errinfo_required, errinfo_got> RequirementError; using RequirementError = boost::tuple<errinfo_required, errinfo_got>;
} }

8
libdevcore/RLP.h

@ -37,7 +37,7 @@ namespace dev
{ {
class RLP; class RLP;
typedef std::vector<RLP> RLPs; using RLPs = std::vector<RLP>;
template <class _T> struct intTraits { static const unsigned maxSize = sizeof(_T); }; template <class _T> struct intTraits { static const unsigned maxSize = sizeof(_T); };
template <> struct intTraits<u160> { static const unsigned maxSize = 20; }; template <> struct intTraits<u160> { static const unsigned maxSize = 20; };
@ -125,7 +125,7 @@ public:
/// @note if used to access items in ascending order, this is efficient. /// @note if used to access items in ascending order, this is efficient.
RLP operator[](unsigned _i) const; RLP operator[](unsigned _i) const;
typedef RLP element_type; using element_type = RLP;
/// @brief Iterator class for iterating through items of RLP list. /// @brief Iterator class for iterating through items of RLP list.
class iterator class iterator
@ -133,8 +133,8 @@ public:
friend class RLP; friend class RLP;
public: public:
typedef RLP value_type; using value_type = RLP;
typedef RLP element_type; using element_type = RLP;
iterator& operator++(); iterator& operator++();
iterator operator++(int) { auto ret = *this; operator++(); return ret; } iterator operator++(int) { auto ret = *this; operator++(); return ret; }

6
libdevcore/vector_ref.h

@ -12,9 +12,9 @@ template <class _T>
class vector_ref class vector_ref
{ {
public: public:
typedef _T value_type; using value_type = _T;
typedef _T element_type; using element_type = _T;
typedef typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type mutable_value_type; using mutable_value_type = typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type;
vector_ref(): m_data(nullptr), m_count(0) {} vector_ref(): m_data(nullptr), m_count(0) {}
vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {}

2
libdevcrypto/ECDHE.h

@ -31,7 +31,7 @@ namespace crypto
{ {
/// Public key of remote and corresponding shared secret. /// Public key of remote and corresponding shared secret.
typedef std::pair<Public,h256> AliasSession; using AliasSession = std::pair<Public,h256>;
/** /**
* @brief An addressable EC key pair. * @brief An addressable EC key pair.

60
libethcore/CommonEth.cpp

@ -35,32 +35,42 @@ namespace eth
const unsigned c_protocolVersion = 53; const unsigned c_protocolVersion = 53;
const unsigned c_databaseVersion = 5; const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units = template <size_t n> constexpr u256 exp10()
{ {
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Uether"}, return exp10<n - 1>() * u256(10);
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Vether"}, }
{((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Dether"},
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Nether"}, template <> constexpr u256 exp10<0>()
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Yether"}, {
{(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Zether"}, return u256(1);
{((u256(1000000000) * 1000000000) * 1000000000) * 1000000000, "Eether"}, }
{((u256(1000000000) * 1000000000) * 1000000000) * 1000000, "Pether"},
{((u256(1000000000) * 1000000000) * 1000000000) * 1000, "Tether"},
{(u256(1000000000) * 1000000000) * 1000000000, "Gether"},
{(u256(1000000000) * 1000000000) * 1000000, "Mether"},
{(u256(1000000000) * 1000000000) * 1000, "grand"},
{u256(1000000000) * 1000000000, "ether"},
{u256(1000000000) * 1000000, "finney"},
{u256(1000000000) * 1000, "szabo"},
{u256(1000000000), "Gwei"},
{u256(1000000), "Mwei"},
{u256(1000), "Kwei"},
{u256(1), "wei"}
};
vector<pair<u256, string>> const& units() vector<pair<u256, string>> const& units()
{ {
return g_units; static const vector<pair<u256, string>> s_units =
{
{exp10<54>(), "Uether"},
{exp10<51>(), "Vether"},
{exp10<48>(), "Dether"},
{exp10<45>(), "Nether"},
{exp10<42>(), "Yether"},
{exp10<39>(), "Zether"},
{exp10<36>(), "Eether"},
{exp10<33>(), "Pether"},
{exp10<30>(), "Tether"},
{exp10<27>(), "Gether"},
{exp10<24>(), "Mether"},
{exp10<21>(), "grand"},
{exp10<18>(), "ether"},
{exp10<15>(), "finney"},
{exp10<12>(), "szabo"},
{exp10<9>(), "Gwei"},
{exp10<6>(), "Mwei"},
{exp10<3>(), "Kwei"},
{exp10<0>(), "wei"}
};
return s_units;
} }
std::string formatBalance(bigint const& _b) std::string formatBalance(bigint const& _b)
@ -75,13 +85,13 @@ std::string formatBalance(bigint const& _b)
else else
b = (u256)_b; b = (u256)_b;
if (b > g_units[0].first * 10000) if (b > units()[0].first * 10000)
{ {
ret << (b / g_units[0].first) << " " << g_units[0].second; ret << (b / units()[0].first) << " " << units()[0].second;
return ret.str(); return ret.str();
} }
ret << setprecision(5); ret << setprecision(5);
for (auto const& i: g_units) for (auto const& i: units())
if (i.first != 1 && b >= i.first * 100) if (i.first != 1 && b >= i.first * 100)
{ {
ret << (double(b / (i.first / 1000)) / 1000.0) << " " << i.second; ret << (double(b / (i.first / 1000)) / 1000.0) << " " << i.second;

8
libethcore/Exceptions.h

@ -29,10 +29,10 @@ namespace eth
{ {
// information to add to exceptions // information to add to exceptions
typedef boost::error_info<struct tag_field, std::string> errinfo_name; using errinfo_name = boost::error_info<struct tag_field, std::string>;
typedef boost::error_info<struct tag_field, int> errinfo_field; using errinfo_field = boost::error_info<struct tag_field, int>;
typedef boost::error_info<struct tag_data, std::string> errinfo_data; using errinfo_data = boost::error_info<struct tag_data, std::string>;
typedef boost::tuple<errinfo_field, errinfo_data> BadFieldError; using BadFieldError = boost::tuple<errinfo_field, errinfo_data>;
struct DatabaseAlreadyOpen: virtual dev::Exception {}; struct DatabaseAlreadyOpen: virtual dev::Exception {};
struct NotEnoughCash: virtual dev::Exception {}; struct NotEnoughCash: virtual dev::Exception {};

6
libethereum/BlockDetails.h

@ -70,9 +70,9 @@ struct BlockReceipts
TransactionReceipts receipts; TransactionReceipts receipts;
}; };
typedef std::map<h256, BlockDetails> BlockDetailsHash; using BlockDetailsHash = std::map<h256, BlockDetails>;
typedef std::map<h256, BlockLogBlooms> BlockLogBloomsHash; using BlockLogBloomsHash = std::map<h256, BlockLogBlooms>;
typedef std::map<h256, BlockReceipts> BlockReceiptsHash; using BlockReceiptsHash = std::map<h256, BlockReceipts>;
static const BlockDetails NullBlockDetails; static const BlockDetails NullBlockDetails;
static const BlockLogBlooms NullBlockLogBlooms; static const BlockLogBlooms NullBlockLogBlooms;

2
libevm/VM.cpp

@ -354,7 +354,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
auto base = m_stack.back(); auto base = m_stack.back();
auto expon = m_stack[m_stack.size() - 2]; auto expon = m_stack[m_stack.size() - 2];
m_stack.pop_back(); m_stack.pop_back();
m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256); m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(1) << 256);
break; break;
} }
case Instruction::NOT: case Instruction::NOT:

2
libevmcore/Assembly.cpp

@ -291,7 +291,7 @@ Assembly& Assembly::optimise(bool _enable)
{ Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} }, { Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} },
{ Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} }, { Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} },
{ Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} }, { Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} },
{ Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(2) << 256);} }, { Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(1) << 256);} },
{ Instruction::SIGNEXTEND, signextend }, { Instruction::SIGNEXTEND, signextend },
{ Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} }, { Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} },
{ Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} }, { Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} },

4
libevmcore/Assembly.h

@ -63,8 +63,8 @@ private:
u256 m_data; u256 m_data;
}; };
typedef std::vector<AssemblyItem> AssemblyItems; using AssemblyItems = std::vector<AssemblyItem>;
typedef vector_ref<AssemblyItem const> AssemblyItemsConstRef; using AssemblyItemsConstRef = vector_ref<AssemblyItem const>;
std::ostream& operator<<(std::ostream& _out, AssemblyItemsConstRef _i); std::ostream& operator<<(std::ostream& _out, AssemblyItemsConstRef _i);
inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { return operator<<(_out, AssemblyItemsConstRef(&_i)); } inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { return operator<<(_out, AssemblyItemsConstRef(&_i)); }

4
liblll/Parser.cpp

@ -89,8 +89,8 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out)
using qi::standard::space; using qi::standard::space;
using qi::standard::space_type; using qi::standard::space_type;
using dev::eth::parseTreeLLL_::tagNode; using dev::eth::parseTreeLLL_::tagNode;
typedef sp::basic_string<std::string, sp::utree_type::symbol_type> symbol_type; using symbol_type = sp::basic_string<std::string, sp::utree_type::symbol_type>;
typedef string::const_iterator it; using it = string::const_iterator;
static const u256 ether = u256(1000000000) * 1000000000; static const u256 ether = u256(1000000000) * 1000000000;
static const u256 finney = u256(1000000000) * 1000000; static const u256 finney = u256(1000000000) * 1000000;

6
libp2p/Common.h

@ -115,9 +115,9 @@ inline bool isPermanentProblem(DisconnectReason _r)
/// @returns the string form of the given disconnection reason. /// @returns the string form of the given disconnection reason.
std::string reasonOf(DisconnectReason _r); std::string reasonOf(DisconnectReason _r);
typedef std::pair<std::string, u256> CapDesc; using CapDesc = std::pair<std::string, u256>;
typedef std::set<CapDesc> CapDescSet; using CapDescSet = std::set<CapDesc>;
typedef std::vector<CapDesc> CapDescs; using CapDescs = std::vector<CapDesc>;
/* /*
* Used by Host to pass negotiated information about a connection to a * Used by Host to pass negotiated information about a connection to a

2
libsolidity/Exceptions.h

@ -38,7 +38,7 @@ struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {};
struct DocstringParsingError: virtual Exception {}; struct DocstringParsingError: virtual Exception {};
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation; using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, Location>;
} }
} }

11
libweb3jsonrpc/CMakeLists.txt

@ -32,6 +32,17 @@ target_link_libraries(${EXECUTABLE} secp256k1)
target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} serpent) target_link_libraries(${EXECUTABLE} serpent)
if (ETH_JSON_RPC_STUB)
add_custom_target(jsonrpcstub)
add_custom_command(TARGET jsonrpcstub
POST_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${ETH_JSON_RPC_STUB} spec.json --cpp-server=AbstractWebThreeStubServer --cpp-client=WebThreeStubClient
COMMAND cmake -E rename webthreestubclient.h ../test/webthreestubclient.h
)
add_dependencies(${EXECUTABLE} jsonrpcstub)
endif()
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

25
mix/ClientModel.cpp

@ -82,7 +82,7 @@ ClientModel::ClientModel(AppContext* _context):
qRegisterMetaType<QInstruction*>("QInstruction"); qRegisterMetaType<QInstruction*>("QInstruction");
qRegisterMetaType<QCode*>("QCode"); qRegisterMetaType<QCode*>("QCode");
qRegisterMetaType<QCallData*>("QCallData"); qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry"); 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()));
@ -313,10 +313,10 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
} }
void ClientModel::debugTransaction(unsigned _block, unsigned _index) void ClientModel::debugRecord(unsigned _index)
{ {
auto const& t = m_client->execution(_block, _index); ExecutionResult const& e = m_client->executions().at(_index);
showDebuggerForTransaction(t); showDebuggerForTransaction(e);
} }
void ClientModel::showDebugError(QString const& _error) void ClientModel::showDebugError(QString const& _error)
@ -346,9 +346,10 @@ void ClientModel::onStateReset()
void ClientModel::onNewTransaction() void ClientModel::onNewTransaction()
{ {
unsigned block = m_client->number() + 1;
unsigned index = m_client->pendingExecutions().size() - 1;
ExecutionResult const& tr = m_client->lastExecution(); ExecutionResult const& tr = m_client->lastExecution();
unsigned block = m_client->number() + 1;
unsigned recordIndex = m_client->executions().size() - 1;
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));
QString value = QString::fromStdString(dev::toString(tr.value)); QString value = QString::fromStdString(dev::toString(tr.value));
QString contract = address; QString contract = address;
@ -359,7 +360,7 @@ void ClientModel::onNewTransaction()
//TODO: handle value transfer //TODO: handle value transfer
FixedHash<4> functionHash; FixedHash<4> functionHash;
bool call = false; bool abi = false;
if (creation) if (creation)
{ {
//contract creation //contract creation
@ -374,12 +375,12 @@ void ClientModel::onNewTransaction()
} }
else else
{ {
//call //transaction/call
if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4) if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4)
{ {
functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer); functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer);
function = QString::fromStdString(toJS(functionHash)); function = QString::fromStdString(toJS(functionHash));
call = true; abi = true;
} }
else else
function = QObject::tr("<none>"); function = QObject::tr("<none>");
@ -393,7 +394,7 @@ void ClientModel::onNewTransaction()
auto compilerRes = m_context->codeModel()->code(); auto compilerRes = m_context->codeModel()->code();
QContractDefinition* def = compilerRes->contract(); QContractDefinition* def = compilerRes->contract();
contract = def->name(); contract = def->name();
if (call) if (abi)
{ {
QFunctionDefinition* funcDef = def->getFunction(functionHash); QFunctionDefinition* funcDef = def->getFunction(functionHash);
if (funcDef) if (funcDef)
@ -407,9 +408,9 @@ void ClientModel::onNewTransaction()
} }
} }
TransactionLogEntry* log = new TransactionLogEntry(block, index, contract, function, value, address, returned); RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall());
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership);
emit newTransaction(log); emit newRecord(log);
} }
} }

32
mix/ClientModel.h

@ -69,13 +69,13 @@ struct TransactionSettings
/// UI Transaction log record /// UI Transaction log record
class TransactionLogEntry: public QObject class RecordLogEntry: public QObject
{ {
Q_OBJECT Q_OBJECT
/// Transaction block number /// Recording index
Q_PROPERTY(unsigned block MEMBER m_block CONSTANT) Q_PROPERTY(unsigned recordIndex MEMBER m_recordIndex CONSTANT)
/// Transaction index within the block /// Human readable transaction bloack and transaction index
Q_PROPERTY(unsigned tindex MEMBER m_index CONSTANT) Q_PROPERTY(QString transactionIndex MEMBER m_transactionIndex CONSTANT)
/// Contract name if any /// Contract name if any
Q_PROPERTY(QString contract MEMBER m_contract CONSTANT) Q_PROPERTY(QString contract MEMBER m_contract CONSTANT)
/// Function name if any /// Function name if any
@ -86,21 +86,25 @@ class TransactionLogEntry: public QObject
Q_PROPERTY(QString address MEMBER m_address CONSTANT) Q_PROPERTY(QString address MEMBER m_address CONSTANT)
/// Returned value or transaction address in case of creation /// Returned value or transaction address in case of creation
Q_PROPERTY(QString returned MEMBER m_returned CONSTANT) Q_PROPERTY(QString returned MEMBER m_returned CONSTANT)
/// true if call, false if transaction
Q_PROPERTY(bool call MEMBER m_call CONSTANT)
public: public:
TransactionLogEntry(): RecordLogEntry():
m_block(0), m_index(0) {} m_recordIndex(0), m_call(false) {}
TransactionLogEntry(int _block, int _index, QString _contract, QString _function, QString _value, QString _address, QString _returned): RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call):
m_block(_block), m_index(_index), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned) {} m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call) {}
private: private:
unsigned m_block; unsigned m_recordIndex;
unsigned m_index; QString m_transactionIndex;
QString m_contract; QString m_contract;
QString m_function; QString m_function;
QString m_value; QString m_value;
QString m_address; QString m_address;
QString m_returned; QString m_returned;
bool m_call;
}; };
/** /**
@ -133,8 +137,8 @@ public slots:
/// Setup state, run transaction sequence, show debugger for the last transaction /// Setup state, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration /// @param _state JS object with state configuration
void setupState(QVariantMap _state); void setupState(QVariantMap _state);
/// Show the debugger for a specified transaction /// Show the debugger for a specified record
Q_INVOKABLE void debugTransaction(unsigned _block, unsigned _index); Q_INVOKABLE void debugRecord(unsigned _index);
private slots: private slots:
/// Update UI with machine states result. Display a modal dialog. /// Update UI with machine states result. Display a modal dialog.
@ -168,7 +172,7 @@ signals:
/// @param _message RPC response in Json format /// @param _message RPC response in Json format
void apiResponse(QString const& _message); void apiResponse(QString const& _message);
/// New transaction log entry /// New transaction log entry
void newTransaction(TransactionLogEntry* _tr); void newRecord(RecordLogEntry* _r);
/// State (transaction log) cleared /// State (transaction log) cleared
void stateCleared(); void stateCleared();

2
mix/CodeHighlighter.h

@ -81,7 +81,7 @@ public:
int start; int start;
int length; int length;
}; };
typedef std::vector<FormatRange> Formats; // Sorted by start position using Formats = std::vector<FormatRange>; // Sorted by start position
public: public:
/// Collect highligting information by lexing the source /// Collect highligting information by lexing the source

8
mix/Exceptions.h

@ -40,10 +40,10 @@ struct FunctionNotFoundException: virtual Exception {};
struct ExecutionStateException: virtual Exception {}; struct ExecutionStateException: virtual Exception {};
struct ParameterChangedException: virtual Exception {}; struct ParameterChangedException: virtual Exception {};
typedef boost::error_info<struct tagQmlError, QQmlError> QmlErrorInfo; using QmlErrorInfo = boost::error_info<struct tagQmlError, QQmlError>;
typedef boost::error_info<struct tagFileError, std::string> FileError; using FileError = boost::error_info<struct tagFileError, std::string>;
typedef boost::error_info<struct tagBlockIndex, unsigned> BlockIndex; using BlockIndex = boost::error_info<struct tagBlockIndex, unsigned>;
typedef boost::error_info<struct tagFunctionName, std::string> FunctionName; using FunctionName = boost::error_info<struct tagFunctionName, std::string>;
} }
} }

8
mix/MachineStates.h

@ -61,7 +61,7 @@ namespace mix
*/ */
struct ExecutionResult struct ExecutionResult
{ {
ExecutionResult() : receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {} ExecutionResult(): transactionIndex(std::numeric_limits<unsigned>::max()) {}
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
std::vector<bytes> transactionData; std::vector<bytes> transactionData;
@ -71,9 +71,11 @@ namespace mix
dev::Address sender; dev::Address sender;
dev::Address contractAddress; dev::Address contractAddress;
dev::u256 value; dev::u256 value;
dev::eth::TransactionReceipt receipt; unsigned transactionIndex;
bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); }
}; };
using ExecutionResults = std::vector<ExecutionResult>; using ExecutionResults = std::vector<ExecutionResult>;
} }
} }

73
mix/MixClient.cpp

@ -15,7 +15,6 @@
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 Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015 * @date 2015
* Ethereum IDE client. * Ethereum IDE client.
@ -90,11 +89,10 @@ void MixClient::resetState(u256 _balance)
m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Empty); m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Empty);
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
m_pendingExecutions.clear();
m_executions.clear(); m_executions.clear();
} }
void MixClient::executeTransaction(Transaction const& _t, State& _state) void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call)
{ {
bytes rlp = _t.rlp(); bytes rlp = _t.rlp();
@ -171,30 +169,33 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
d.value = _t.value(); d.value = _t.value();
if (_t.isCreation()) if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
d.receipt = TransactionReceipt(execState.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage if (!_call)
m_pendingExecutions.emplace_back(std::move(d)); d.transactionIndex = m_state.pending().size();
m_executions.emplace_back(std::move(d));
// execute on a state // execute on a state
_state.execute(lastHashes, rlp, nullptr, true); if (!_call)
{
// collect watches _state.execute(lastHashes, rlp, nullptr, true);
h256Set changed; // collect watches
Guard l(m_filterLock); h256Set changed;
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters) Guard l(m_filterLock);
if ((unsigned)i.second.filter.latest() > bc().number()) for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
{ if ((unsigned)i.second.filter.latest() > bc().number())
// acceptable number.
auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
if (m.size())
{ {
// filter catches them // acceptable number.
for (LogEntry const& l: m) auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); if (m.size())
changed.insert(i.first); {
// 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);
changed.insert(dev::eth::PendingChangedFilter); noteChanged(changed);
noteChanged(changed); }
} }
void MixClient::mine() void MixClient::mine()
@ -206,28 +207,18 @@ void MixClient::mine()
bc().import(m_state.blockData(), m_stateDB); bc().import(m_state.blockData(), m_stateDB);
m_state.sync(bc()); m_state.sync(bc());
m_startState = m_state; m_startState = m_state;
m_executions.emplace_back(std::move(m_pendingExecutions));
h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter };
noteChanged(changed); noteChanged(changed);
} }
ExecutionResult const& MixClient::execution(unsigned _block, unsigned _transaction) const
{
if (_block == bc().number() + 1)
return m_pendingExecutions.at(_transaction);
return m_executions.at(_block - 1).at(_transaction);
}
ExecutionResult const& MixClient::lastExecution() const ExecutionResult const& MixClient::lastExecution() const
{ {
if (m_pendingExecutions.size() > 0) return m_executions.back();
return m_pendingExecutions.back();
return m_executions.back().back();
} }
ExecutionResults const& MixClient::pendingExecutions() const ExecutionResults const& MixClient::executions() const
{ {
return m_pendingExecutions; return m_executions;
} }
State MixClient::asOf(int _block) const State MixClient::asOf(int _block) const
@ -246,7 +237,7 @@ void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const
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); executeTransaction(t, m_state, false);
} }
Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
@ -254,7 +245,7 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init,
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); executeTransaction(t, m_state, false);
Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
return address; return address;
} }
@ -263,7 +254,7 @@ void MixClient::inject(bytesConstRef _rlp)
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
eth::Transaction t(_rlp, CheckSignature::None); eth::Transaction t(_rlp, CheckSignature::None);
executeTransaction(t, m_state); executeTransaction(t, m_state, false);
} }
void MixClient::flushTransactions() void MixClient::flushTransactions()
@ -282,8 +273,8 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp(); bytes rlp = t.rlp();
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); executeTransaction(t, temp, true);
return m_pendingExecutions.back().returnValue; return lastExecution().returnValue;
} }
u256 MixClient::balanceAt(Address _a, int _block) const u256 MixClient::balanceAt(Address _a, int _block) const

8
mix/MixClient.h

@ -45,9 +45,8 @@ public:
void resetState(u256 _balance); void resetState(u256 _balance);
KeyPair const& userAccount() const { return m_userAccount; } KeyPair const& userAccount() const { return m_userAccount; }
void mine(); void mine();
ExecutionResult const& execution(unsigned _block, unsigned _transaction) const;
ExecutionResult const& lastExecution() const; ExecutionResult const& lastExecution() const;
ExecutionResults const& pendingExecutions() const; ExecutionResults const& executions() const;
//dev::eth::Interface //dev::eth::Interface
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
@ -90,7 +89,7 @@ public:
bool submitNonce(h256 const&) override { return false; } bool submitNonce(h256 const&) override { return false; }
private: private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state); void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call);
void noteChanged(h256Set const& _filters); void noteChanged(h256Set const& _filters);
dev::eth::State asOf(int _block) const; dev::eth::State asOf(int _block) const;
MixBlockChain& bc() { return *m_bc; } MixBlockChain& bc() { return *m_bc; }
@ -105,8 +104,7 @@ private:
mutable std::mutex m_filterLock; mutable std::mutex m_filterLock;
std::map<h256, dev::eth::InstalledFilter> m_filters; std::map<h256, dev::eth::InstalledFilter> m_filters;
std::map<unsigned, dev::eth::ClientWatch> m_watches; std::map<unsigned, dev::eth::ClientWatch> m_watches;
std::vector<ExecutionResults> m_executions; ExecutionResults m_executions;
ExecutionResults m_pendingExecutions;
std::string m_dbPath; std::string m_dbPath;
unsigned m_minigThreads; unsigned m_minigThreads;
}; };

31
mix/qml/CallStack.qml

@ -1,31 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
Item {
property alias model: callTable.model
signal frameActivated(int index)
ColumnLayout {
anchors.fill: parent
Text {
text: qsTr("Call Stack")
Layout.fillWidth: true
}
TableView {
id: callTable
Layout.fillWidth: true
Layout.fillHeight: true
headerDelegate: null
TableViewColumn {
role: "modelData"
title: qsTr("Address")
width: parent.width
}
onActivated: {
frameActivated(row);
}
}
}
}

1
mix/qml/DebugInfoList.qml

@ -115,6 +115,7 @@ ColumnLayout {
//storageContainer.state = ""; //storageContainer.state = "";
} }
} }
onActivated: rowActivated(row);
TableViewColumn { TableViewColumn {
role: "modelData" role: "modelData"
width: parent.width width: parent.width

41
mix/qml/TransactionLog.qml

@ -5,6 +5,14 @@ import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
Item { Item {
property bool showLogs: true
property ListModel fullModel: ListModel{}
property ListModel transactionModel: ListModel{}
onShowLogsChanged: {
logTable.model = showLogs ? fullModel : transactionModel
}
Action { Action {
id: addStateAction id: addStateAction
text: "Add State" text: "Add State"
@ -70,17 +78,13 @@ Item {
} }
} }
TableView { TableView {
id: logTable
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: logModel model: fullModel
TableViewColumn { TableViewColumn {
role: "block" role: "transactionIndex"
title: qsTr("Block")
width: 40
}
TableViewColumn {
role: "tindex"
title: qsTr("Index") title: qsTr("Index")
width: 40 width: 40
} }
@ -110,30 +114,31 @@ Item {
width: 120 width: 120
} }
onActivated: { onActivated: {
var item = logModel.get(row); var item = logTable.model.get(row);
clientModel.debugTransaction(item.block, item.tindex); clientModel.debugRecord(item.recordIndex);
} }
Keys.onPressed: { Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logModel.count) { if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logModel.get(currentRow); var item = logTable.model.get(currentRow);
appContext.toClipboard(item.returned); appContext.toClipboard(item.returned);
} }
} }
} }
} }
ListModel {
id: logModel
}
Connections { Connections {
target: clientModel target: clientModel
onStateCleared: { onStateCleared: {
logModel.clear(); fullModel.clear();
transactionModel.clear();
} }
onNewTransaction: { onNewRecord: {
if (recording.checked) if (recording.checked)
logModel.append(_tr); {
fullModel.append(_r);
if (!_r.call)
transactionModel.append(_r);
}
} }
} }

10
mix/qml/main.qml

@ -54,6 +54,7 @@ ApplicationWindow {
MenuItem { action: toggleTransactionLogAction } MenuItem { action: toggleTransactionLogAction }
MenuItem { action: toggleWebPreviewAction } MenuItem { action: toggleWebPreviewAction }
MenuItem { action: toggleWebPreviewOrientationAction } MenuItem { action: toggleWebPreviewOrientationAction }
MenuItem { action: toggleCallsInLog }
} }
} }
@ -162,6 +163,15 @@ ApplicationWindow {
onTriggered: mainContent.toggleWebPreviewOrientation(); onTriggered: mainContent.toggleWebPreviewOrientation();
} }
Action {
id: toggleCallsInLog
text: qsTr("Show Calls in Transaction Log")
shortcut: ""
checkable: true
checked: mainContent.rightPane.transactionLog.showLogs
onTriggered: mainContent.rightPane.transactionLog.showLogs = !mainContent.rightPane.transactionLog.showLogs
}
Action { Action {
id: toggleRunOnLoadAction id: toggleRunOnLoadAction
text: qsTr("Load State on Startup") text: qsTr("Load State on Startup")

1
mix/res.qrc

@ -61,7 +61,6 @@
<file>stdc/std.sol</file> <file>stdc/std.sol</file>
<file>qml/TransactionLog.qml</file> <file>qml/TransactionLog.qml</file>
<file>res/mix_256x256x32.png</file> <file>res/mix_256x256x32.png</file>
<file>qml/CallStack.qml</file>
<file>qml/QVariableDeclaration.qml</file> <file>qml/QVariableDeclaration.qml</file>
<file>qml/Style.qml</file> <file>qml/Style.qml</file>
<file>qml/qmldir</file> <file>qml/qmldir</file>

7
test/CMakeLists.txt

@ -21,9 +21,10 @@ target_link_libraries(testeth ethereum)
target_link_libraries(testeth ethcore) target_link_libraries(testeth ethcore)
target_link_libraries(testeth secp256k1) target_link_libraries(testeth secp256k1)
target_link_libraries(testeth solidity) target_link_libraries(testeth solidity)
target_link_libraries(testeth webthree) if (NOT HEADLESS)
target_link_libraries(testeth natspec) target_link_libraries(testeth webthree)
target_link_libraries(testeth natspec)
endif()
if (JSONRPC) if (JSONRPC)
target_link_libraries(testeth web3jsonrpc) target_link_libraries(testeth web3jsonrpc)
target_link_libraries(testeth ${JSON_RPC_CPP_CLIENT_LIBRARIES}) target_link_libraries(testeth ${JSON_RPC_CPP_CLIENT_LIBRARIES})

4
test/natspec.cpp

@ -19,6 +19,8 @@
* @date 2015 * @date 2015
*/ */
#if !ETH_HEADLESS
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libnatspec/NatspecExpressionEvaluator.h> #include <libnatspec/NatspecExpressionEvaluator.h>
@ -110,3 +112,5 @@ BOOST_AUTO_TEST_CASE(natspec_js_eval_error)
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
#endif

36
test/webthreestubclient.h

@ -213,6 +213,16 @@ class WebThreeStubClient : public jsonrpc::Client
else else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
} }
bool eth_flush() throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p = Json::nullValue;
Json::Value result = this->CallMethod("eth_flush",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value eth_blockByHash(const std::string& param1) throw (jsonrpc::JsonRpcException) Json::Value eth_blockByHash(const std::string& param1) throw (jsonrpc::JsonRpcException)
{ {
Json::Value p; Json::Value p;
@ -347,13 +357,13 @@ class WebThreeStubClient : public jsonrpc::Client
else else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
} }
bool eth_changed(const int& param1) throw (jsonrpc::JsonRpcException) Json::Value eth_changed(const int& param1) throw (jsonrpc::JsonRpcException)
{ {
Json::Value p; Json::Value p;
p.append(param1); p.append(param1);
Json::Value result = this->CallMethod("eth_changed",p); Json::Value result = this->CallMethod("eth_changed",p);
if (result.isBool()) if (result.isArray())
return result.asBool(); return result;
else else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
} }
@ -377,6 +387,26 @@ class WebThreeStubClient : public jsonrpc::Client
else else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
} }
Json::Value eth_getWork() throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p = Json::nullValue;
Json::Value result = this->CallMethod("eth_getWork",p);
if (result.isArray())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool eth_submitWork(const std::string& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_submitWork",p);
if (result.isBool())
return result.asBool();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{ {
Json::Value p; Json::Value p;

Loading…
Cancel
Save