Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
e212074d4f
  1. 13
      eth/main.cpp
  2. 1
      libethcore/Params.cpp
  3. 3
      libethcore/Params.h
  4. 2
      libevm/VM.cpp
  5. 1
      libevmcore/CMakeLists.txt
  6. 142
      libevmcore/CommonSubexpressionEliminator.cpp
  7. 61
      libevmcore/CommonSubexpressionEliminator.h
  8. 31
      libevmcore/ExpressionClasses.cpp
  9. 11
      libevmcore/ExpressionClasses.h
  10. 42
      libevmcore/Instruction.cpp
  11. 4
      libevmcore/Instruction.h
  12. 2
      libevmcore/SemanticInformation.cpp
  13. 16
      libp2p/Host.cpp
  14. 7
      libp2p/Host.h
  15. 1
      libsolidity/CMakeLists.txt
  16. 2
      libwebthree/WebThree.cpp
  17. 2
      libwebthree/WebThree.h
  18. 134
      mix/ClientModel.cpp
  19. 1
      mix/ClientModel.h
  20. 61
      mix/CodeModel.cpp
  21. 12
      mix/CodeModel.h
  22. 44
      mix/ContractCallDataEncoder.cpp
  23. 2
      mix/ContractCallDataEncoder.h
  24. 14
      mix/MixApplication.cpp
  25. 1
      mix/MixApplication.h
  26. 2
      mix/MixClient.cpp
  27. 2
      mix/QBasicNodeDefinition.cpp
  28. 3
      mix/QBasicNodeDefinition.h
  29. 6
      mix/SolidityType.h
  30. 2
      mix/qml.qrc
  31. 2
      mix/qml/Application.qml
  32. 4
      mix/qml/Debugger.qml
  33. 410
      mix/qml/DeploymentDialog.qml
  34. 53
      mix/qml/LogsPane.qml
  35. 94
      mix/qml/NewProjectDialog.qml
  36. 564
      mix/qml/StateDialog.qml
  37. 74
      mix/qml/StateList.qml
  38. 28
      mix/qml/StatusPane.qml
  39. 1
      mix/qml/StepActionImage.qml
  40. 9
      mix/qml/StructView.qml
  41. 379
      mix/qml/TransactionDialog.qml
  42. 1
      mix/qml/VariablesView.qml
  43. 13
      mix/qml/WebPreview.qml
  44. 12
      mix/qml/html/WebContainer.html
  45. 90
      mix/qml/js/Printer.js
  46. 2
      mix/qml/js/TransactionHelper.js
  47. 106
      mix/qml/js/ansi2html.js
  48. 14
      mix/test/TestService.cpp
  49. 1
      mix/test/TestService.h
  50. 110
      mix/test/qml/TestMain.qml
  51. 140
      mix/test/qml/js/TestDebugger.js
  52. 71
      mix/test/qml/js/TestTutorial.js
  53. 13
      neth/main.cpp
  54. 168
      test/SolidityOptimizer.cpp
  55. 2
      test/peer.cpp

13
eth/main.cpp

@ -44,6 +44,7 @@
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include <libethcore/Ethasher.h>
#include "BuildInfo.h"
using namespace std;
using namespace dev;
@ -112,6 +113,7 @@ void help()
<< " -c,--client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/Ethereum)." << endl
<< " -D,--initdag Initialize DAG for mining and exit." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl
<< " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl
<< " -h,--help Show this help message and exit." << endl
@ -200,6 +202,7 @@ enum class NodeMode
int main(int argc, char** argv)
{
bool initDAG = false;
string listenIP;
unsigned short listenPort = 30303;
string publicIP;
@ -304,6 +307,8 @@ int main(int argc, char** argv)
structuredLogging = true;
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if (arg == "-D" || arg == "--initdag")
initDAG = true;
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
@ -436,6 +441,14 @@ int main(int argc, char** argv)
&nodesState,
miners
);
if (initDAG)
{
cout << "Initializing DAG. (This will take awhile)" << endl;
Ethasher::get()->full(web3.ethereum()->blockChain().info());
return 0;
}
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;

1
libethcore/Params.cpp

@ -46,7 +46,6 @@ u256 const c_sha3WordGas = 6;
u256 const c_sloadGas = 50;
u256 const c_sstoreSetGas = 20000;
u256 const c_sstoreResetGas = 5000;
u256 const c_sstoreClearGas = 5000;
u256 const c_sstoreRefundGas = 15000;
u256 const c_jumpdestGas = 1;
u256 const c_logGas = 375;

3
libethcore/Params.h

@ -48,8 +48,7 @@ extern u256 const c_sha3WordGas; ///< Once per word of the SHA3 operation's da
extern u256 const c_copyGas; ///< Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
extern u256 const c_sloadGas; ///< Once per SLOAD operation.
extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero.
extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change.
extern u256 const c_sstoreClearGas; ///< Once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness does not change from zero. NOTE: when c_sstoreSetGas does not apply.
extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_jumpdestGas; ///< Once per JUMPDEST operation.
extern u256 const c_logGas; ///< Per LOG* operation.

2
libevm/VM.cpp

@ -108,7 +108,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
runGas = c_sstoreSetGas;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
{
runGas = c_sstoreClearGas;
runGas = c_sstoreResetGas;
_ext.sub.refunds += c_sstoreRefundGas;
}
else

1
libevmcore/CMakeLists.txt

@ -25,6 +25,7 @@ else()
endif()
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} devcrypto)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

142
libevmcore/CommonSubexpressionEliminator.cpp

@ -23,6 +23,7 @@
#include <functional>
#include <boost/range/adaptor/reversed.hpp>
#include <libdevcrypto/SHA3.h>
#include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/AssemblyItem.h>
@ -34,8 +35,8 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{
optimizeBreakingItem();
map<int, ExpressionClasses::Id> initialStackContents;
map<int, ExpressionClasses::Id> targetStackContents;
map<int, Id> initialStackContents;
map<int, Id> targetStackContents;
int minHeight = m_stackHeight + 1;
if (!m_stackElements.empty())
minHeight = min(minHeight, m_stackElements.begin()->first);
@ -58,18 +59,18 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
ostream& CommonSubexpressionEliminator::stream(
ostream& _out,
map<int, ExpressionClasses::Id> _initialStack,
map<int, ExpressionClasses::Id> _targetStack
map<int, Id> _initialStack,
map<int, Id> _targetStack
) const
{
auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id)
auto streamExpressionClass = [this](ostream& _out, Id _id)
{
auto const& expr = m_expressionClasses.representative(_id);
_out << " " << dec << _id << ": " << *expr.item;
if (expr.sequenceNumber)
_out << "@" << dec << expr.sequenceNumber;
_out << "(";
for (ExpressionClasses::Id arg: expr.arguments)
for (Id arg: expr.arguments)
_out << dec << arg << ",";
_out << ")" << endl;
};
@ -77,7 +78,7 @@ ostream& CommonSubexpressionEliminator::stream(
_out << "Optimizer analysis:" << endl;
_out << "Final stack height: " << dec << m_stackHeight << endl;
_out << "Equivalence classes: " << endl;
for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
for (Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass)
streamExpressionClass(_out, eqClass);
_out << "Initial stack: " << endl;
@ -119,7 +120,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
);
else if (instruction != Instruction::POP)
{
vector<ExpressionClasses::Id> arguments(info.args);
vector<Id> arguments(info.args);
for (int i = 0; i < info.args; ++i)
arguments[i] = stackElement(m_stackHeight - i);
if (_item.instruction() == Instruction::SSTORE)
@ -130,6 +131,8 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
storeInMemory(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::MLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0]));
else if (_item.instruction() == Instruction::SHA3)
setStackElement(m_stackHeight + _item.deposit(), applySha3(arguments.at(0), arguments.at(1)));
else
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem));
}
@ -142,7 +145,6 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI))
return;
using Id = ExpressionClasses::Id;
static AssemblyItem s_jump = Instruction::JUMP;
Id condition = stackElement(m_stackHeight - 1);
@ -163,7 +165,7 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
}
}
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class)
void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Id _class)
{
m_stackElements[_stackHeight] = _class;
}
@ -194,26 +196,28 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight)));
}
void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
void CommonSubexpressionEliminator::storeInStorage(Id _slot, Id _value)
{
if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value)
// do not execute the storage if we know that the value is already there
return;
m_sequenceNumber++;
decltype(m_storageContent) storageContents;
// copy over values at points where we know that they are different from _slot
// Copy over all values (i.e. retain knowledge about them) where we know that this store
// operation will not destroy the knowledge. Specifically, we copy storage locations we know
// are different from _slot or locations where we know that the stored value is equal to _value.
for (auto const& storageItem: m_storageContent)
if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot))
if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value)
storageContents.insert(storageItem);
m_storageContent = move(storageContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber);
Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id));
m_storageContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot)
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(Id _slot)
{
if (m_storageContent.count(_slot))
return m_storageContent.at(_slot);
@ -221,7 +225,7 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionC
return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber);
}
void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value)
void CommonSubexpressionEliminator::storeInMemory(Id _slot, Id _value)
{
if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value)
// do not execute the store if we know that the value is already there
@ -233,14 +237,14 @@ void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, E
if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot))
memoryContents.insert(memoryItem);
m_memoryContent = move(memoryContents);
ExpressionClasses::Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber);
Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber);
m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
m_memoryContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
}
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot)
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(Id _slot)
{
if (m_memoryContent.count(_slot))
return m_memoryContent.at(_slot);
@ -248,6 +252,37 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionCl
return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber);
}
CommonSubexpressionEliminator::Id CommonSubexpressionEliminator::applySha3(Id _start, Id _length)
{
// Special logic if length is a short constant, otherwise we cannot tell.
u256 const* l = m_expressionClasses.knownConstant(_length);
// unknown or too large length
if (!l || *l > 128)
return m_expressionClasses.find(Instruction::SHA3, {_start, _length}, true, m_sequenceNumber);
vector<Id> arguments;
for (u256 i = 0; i < *l; i += 32)
{
Id slot = m_expressionClasses.find(Instruction::ADD, {_start, m_expressionClasses.find(i)});
arguments.push_back(loadFromMemory(slot));
}
if (m_knownSha3Hashes.count(arguments))
return m_knownSha3Hashes.at(arguments);
Id v;
// If all arguments are known constants, compute the sha3 here
if (all_of(arguments.begin(), arguments.end(), [this](Id _a) { return !!m_expressionClasses.knownConstant(_a); }))
{
bytes data;
for (Id a: arguments)
data += toBigEndian(*m_expressionClasses.knownConstant(a));
data.resize(size_t(*l));
v = m_expressionClasses.find(u256(sha3(data)));
}
else
v = m_expressionClasses.find(Instruction::SHA3, {_start, _length}, true, m_sequenceNumber);
return m_knownSha3Hashes[arguments] = v;
}
CSECodeGenerator::CSECodeGenerator(
ExpressionClasses& _expressionClasses,
vector<CSECodeGenerator::StoreOperation> const& _storeOperations
@ -259,8 +294,8 @@ CSECodeGenerator::CSECodeGenerator(
}
AssemblyItems CSECodeGenerator::generateCode(
map<int, ExpressionClasses::Id> const& _initialStack,
map<int, ExpressionClasses::Id> const& _targetStackContents
map<int, Id> const& _initialStack,
map<int, Id> const& _targetStackContents
)
{
m_stack = _initialStack;
@ -280,7 +315,7 @@ AssemblyItems CSECodeGenerator::generateCode(
}
// store all needed sequenced expressions
set<pair<unsigned, ExpressionClasses::Id>> sequencedExpressions;
set<pair<unsigned, Id>> sequencedExpressions;
for (auto const& p: m_neededBy)
for (auto id: {p.first, p.second})
if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
@ -327,19 +362,20 @@ AssemblyItems CSECodeGenerator::generateCode(
return m_generatedItems;
}
void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
void CSECodeGenerator::addDependencies(Id _c)
{
if (m_neededBy.count(_c))
return; // we already computed the dependencies for _c
ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
for (ExpressionClasses::Id argument: expr.arguments)
for (Id argument: expr.arguments)
{
addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c));
}
if (expr.item->type() == Operation && (
expr.item->instruction() == Instruction::SLOAD ||
expr.item->instruction() == Instruction::MLOAD
expr.item->instruction() == Instruction::MLOAD ||
expr.item->instruction() == Instruction::SHA3
))
{
// this loads an unknown value from storage or memory and thus, in addition to its
@ -347,22 +383,52 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
// they are different that occur before this load
StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ?
StoreOperation::Storage : StoreOperation::Memory;
ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0);
Id slotToLoadFrom = expr.arguments.at(0);
for (auto const& p: m_storeOperations)
{
if (p.first.first != target)
continue;
ExpressionClasses::Id slot = p.first.second;
Id slot = p.first.second;
StoreOperations const& storeOps = p.second;
if (storeOps.front().sequenceNumber > expr.sequenceNumber)
continue;
if (
(target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) ||
(target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom))
)
bool knownToBeIndependent = false;
switch (expr.item->instruction())
{
case Instruction::SLOAD:
knownToBeIndependent = m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom);
break;
case Instruction::MLOAD:
knownToBeIndependent = m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom);
break;
case Instruction::SHA3:
{
Id length = expr.arguments.at(1);
Id offsetToStart = m_expressionClasses.find(Instruction::SUB, {slot, slotToLoadFrom});
u256 const* o = m_expressionClasses.knownConstant(offsetToStart);
u256 const* l = m_expressionClasses.knownConstant(length);
if (l && *l == 0)
knownToBeIndependent = true;
else if (o)
{
// We could get problems here if both *o and *l are larger than 2**254
// but it is probably ok for the optimizer to produce wrong code for such cases
// which cannot be executed anyway because of the non-payable price.
if (u2s(*o) <= -32)
knownToBeIndependent = true;
else if (l && u2s(*o) >= 0 && *o >= *l)
knownToBeIndependent = true;
}
break;
}
default:
break;
}
if (knownToBeIndependent)
continue;
// note that store and load never have the same sequence number
ExpressionClasses::Id latestStore = storeOps.front().expression;
Id latestStore = storeOps.front().expression;
for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it)
if (it->sequenceNumber < expr.sequenceNumber)
latestStore = it->expression;
@ -372,7 +438,7 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
}
}
int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced)
int CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
{
// do some cleanup
removeStackTopIfPossible();
@ -392,8 +458,8 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow
OptimizerException,
"Sequence constrained operation requested out of sequence."
);
ExpressionClasses::Ids const& arguments = expr.arguments;
for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments))
vector<Id> const& arguments = expr.arguments;
for (Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg);
// The arguments are somewhere on the stack now, so it remains to move them at the correct place.
@ -478,7 +544,7 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow
}
}
int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
int CSECodeGenerator::classElementPosition(Id _id) const
{
assertThrow(
m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition,
@ -488,7 +554,7 @@ int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
return m_classPositions.at(_id);
}
bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result)
bool CSECodeGenerator::canBeRemoved(Id _element, Id _result)
{
// Returns false if _element is finally needed or is needed by a class that has not been
// computed yet. Note that m_classPositions also includes classes that were deleted in the meantime.
@ -506,8 +572,8 @@ bool CSECodeGenerator::removeStackTopIfPossible()
{
if (m_stack.empty())
return false;
assertThrow(m_stack.count(m_stackHeight), OptimizerException, "");
ExpressionClasses::Id top = m_stack[m_stackHeight];
assertThrow(m_stack.count(m_stackHeight) > 0, OptimizerException, "");
Id top = m_stack[m_stackHeight];
if (!canBeRemoved(top))
return false;
m_generatedItems.push_back(AssemblyItem(Instruction::POP));

61
libevmcore/CommonSubexpressionEliminator.h

@ -57,19 +57,20 @@ using AssemblyItems = std::vector<AssemblyItem>;
class CommonSubexpressionEliminator
{
public:
using Id = ExpressionClasses::Id;
struct StoreOperation
{
enum Target { Memory, Storage };
StoreOperation(
Target _target,
ExpressionClasses::Id _slot,
Id _slot,
unsigned _sequenceNumber,
ExpressionClasses::Id _expression
Id _expression
): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {}
Target target;
ExpressionClasses::Id slot;
Id slot;
unsigned sequenceNumber;
ExpressionClasses::Id expression;
Id expression;
};
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
@ -83,8 +84,8 @@ public:
/// Streams debugging information to @a _out.
std::ostream& stream(
std::ostream& _out,
std::map<int, ExpressionClasses::Id> _initialStack = std::map<int, ExpressionClasses::Id>(),
std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>()
std::map<int, Id> _initialStack = std::map<int, Id>(),
std::map<int, Id> _targetStack = std::map<int, Id>()
) const;
private:
@ -96,39 +97,42 @@ private:
/// Simplifies the given item using
/// Assigns a new equivalence class to the next sequence number of the given stack element.
void setStackElement(int _stackHeight, ExpressionClasses::Id _class);
void setStackElement(int _stackHeight, Id _class);
/// Swaps the given stack elements in their next sequence number.
void swapStackElements(int _stackHeightA, int _stackHeightB);
/// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet).
ExpressionClasses::Id stackElement(int _stackHeight);
Id stackElement(int _stackHeight);
/// @returns the equivalence class id of the special initial stack element at the given height
/// (must not be positive).
ExpressionClasses::Id initialStackElement(int _stackHeight);
Id initialStackElement(int _stackHeight);
/// Increments the sequence number, deletes all storage information that might be overwritten
/// and stores the new value at the given slot.
void storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
void storeInStorage(Id _slot, Id _value);
/// Retrieves the current value at the given slot in storage or creates a new special sload class.
ExpressionClasses::Id loadFromStorage(ExpressionClasses::Id _slot);
Id loadFromStorage(Id _slot);
/// Increments the sequence number, deletes all memory information that might be overwritten
/// and stores the new value at the given slot.
void storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value);
void storeInMemory(Id _slot, Id _value);
/// Retrieves the current value at the given slot in memory or creates a new special mload class.
ExpressionClasses::Id loadFromMemory(ExpressionClasses::Id _slot);
Id loadFromMemory(Id _slot);
/// Finds or creates a new expression that applies the sha3 hash function to the contents in memory.
Id applySha3(Id _start, Id _length);
/// Current stack height, can be negative.
int m_stackHeight = 0;
/// Current stack layout, mapping stack height -> equivalence class
std::map<int, ExpressionClasses::Id> m_stackElements;
std::map<int, Id> m_stackElements;
/// Current sequence number, this is incremented with each modification to storage or memory.
unsigned m_sequenceNumber = 1;
/// Knowledge about storage content.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_storageContent;
std::map<Id, Id> m_storageContent;
/// Knowledge about memory content. Keys are memory addresses, note that the values overlap
/// and are not contained here if they are not completely known.
std::map<ExpressionClasses::Id, ExpressionClasses::Id> m_memoryContent;
std::map<Id, Id> m_memoryContent;
/// Keeps record of all sha3 hashes that are computed.
std::map<std::vector<Id>, Id> m_knownSha3Hashes;
/// Keeps information about which storage or memory slots were written to at which sequence
/// number with what instruction.
std::vector<StoreOperation> m_storeOperations;
@ -149,6 +153,7 @@ class CSECodeGenerator
public:
using StoreOperation = CommonSubexpressionEliminator::StoreOperation;
using StoreOperations = std::vector<StoreOperation>;
using Id = ExpressionClasses::Id;
/// Initializes the code generator with the given classes and store operations.
/// The store operations have to be sorted by sequence number in ascending order.
@ -159,25 +164,25 @@ public:
/// @param _targetStackContents final contents of the stack, by stack height relative to initial
/// @note should only be called once on each object.
AssemblyItems generateCode(
std::map<int, ExpressionClasses::Id> const& _initialStack,
std::map<int, ExpressionClasses::Id> const& _targetStackContents
std::map<int, Id> const& _initialStack,
std::map<int, Id> const& _targetStackContents
);
private:
/// Recursively discovers all dependencies to @a m_requests.
void addDependencies(ExpressionClasses::Id _c);
void addDependencies(Id _c);
/// Produce code that generates the given element if it is not yet present.
/// @returns the stack position of the element or c_invalidPosition if it does not actually
/// generate a value on the stack.
/// @param _allowSequenced indicates that sequence-constrained operations are allowed
int generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced = false);
int generateClassElement(Id _c, bool _allowSequenced = false);
/// @returns the position of the representative of the given id on the stack.
/// @note throws an exception if it is not on the stack.
int classElementPosition(ExpressionClasses::Id _id) const;
int classElementPosition(Id _id) const;
/// @returns true if @a _element can be removed - in general or, if given, while computing @a _result.
bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1));
bool canBeRemoved(Id _element, Id _result = Id(-1));
/// Appends code to remove the topmost stack element if it can be removed.
bool removeStackTopIfPossible();
@ -196,19 +201,19 @@ private:
/// Current height of the stack relative to the start.
int m_stackHeight = 0;
/// If (b, a) is in m_requests then b is needed to compute a.
std::multimap<ExpressionClasses::Id, ExpressionClasses::Id> m_neededBy;
std::multimap<Id, Id> m_neededBy;
/// Current content of the stack.
std::map<int, ExpressionClasses::Id> m_stack;
std::map<int, Id> m_stack;
/// Current positions of equivalence classes, equal to c_invalidPosition if already deleted.
std::map<ExpressionClasses::Id, int> m_classPositions;
std::map<Id, int> m_classPositions;
/// The actual eqivalence class items and how to compute them.
ExpressionClasses& m_expressionClasses;
/// Keeps information about which storage or memory slots were written to by which operations.
/// The operations are sorted ascendingly by sequence number.
std::map<std::pair<StoreOperation::Target, ExpressionClasses::Id>, StoreOperations> m_storeOperations;
std::map<std::pair<StoreOperation::Target, Id>, StoreOperations> m_storeOperations;
/// The set of equivalence classes that should be present on the stack at the end.
std::set<ExpressionClasses::Id> m_finalClasses;
std::set<Id> m_finalClasses;
};
template <class _AssemblyItemIterator>

31
libevmcore/ExpressionClasses.cpp

@ -84,24 +84,35 @@ ExpressionClasses::Id ExpressionClasses::find(
bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is a non-zero constant.
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
return constant.matches(representative(difference), *this) && constant.d() != u256(0);
return knownNonZero(find(Instruction::SUB, {_a, _b}));
}
bool ExpressionClasses::knownToBeDifferentBy32(ExpressionClasses::Id _a, ExpressionClasses::Id _b)
{
// Try to simplify "_a - _b" and return true iff the value is at least 32 away from zero.
u256 const* v = knownConstant(find(Instruction::SUB, {_a, _b}));
// forbidden interval is ["-31", 31]
return v && *v + 31 > u256(62);
}
bool ExpressionClasses::knownZero(Id _c)
{
return Pattern(u256(0)).matches(representative(_c), *this);
}
bool ExpressionClasses::knownNonZero(Id _c)
{
return Pattern(u256(0)).matches(representative(find(Instruction::ISZERO, {_c})), *this);
}
u256 const* ExpressionClasses::knownConstant(Id _c)
{
map<unsigned, Expression const*> matchGroups;
Pattern constant(Push);
constant.setMatchGroup(1, matchGroups);
Id difference = find(Instruction::SUB, {_a, _b});
if (!constant.matches(representative(difference), *this))
return false;
// forbidden interval is ["-31", 31]
return constant.d() + 31 > u256(62);
if (!constant.matches(representative(_c), *this))
return nullptr;
return &constant.d();
}
string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const

11
libevmcore/ExpressionClasses.h

@ -78,6 +78,15 @@ public:
bool knownToBeDifferent(Id _a, Id _b);
/// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32.
bool knownToBeDifferentBy32(Id _a, Id _b);
/// @returns true if the value of the given class is known to be zero.
/// @note that this is not the negation of knownNonZero
bool knownZero(Id _c);
/// @returns true if the value of the given class is known to be nonzero.
/// @note that this is not the negation of knownZero
bool knownNonZero(Id _c);
/// @returns a pointer to the value if the given class is known to be a constant,
/// and a nullptr otherwise.
u256 const* knownConstant(Id _c);
std::string fullDAGToString(Id _id) const;
@ -131,7 +140,7 @@ public:
/// @returns the id of the matched expression if this pattern is part of a match group.
Id id() const { return matchGroupValue().id; }
/// @returns the data of the matched expression if this pattern is part of a match group.
u256 d() const { return matchGroupValue().item->data(); }
u256 const& d() const { return matchGroupValue().item->data(); }
std::string toString() const;

42
libevmcore/Instruction.cpp

@ -21,6 +21,7 @@
#include "Instruction.h"
#include <functional>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h>
@ -294,27 +295,42 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, ZeroTier } }
};
string dev::eth::disassemble(bytes const& _mem)
void dev::eth::eachInstruction(
bytes const& _mem,
function<void(Instruction,u256 const&)> const& _onInstruction
)
{
stringstream ret;
unsigned numerics = 0;
for (auto it = _mem.begin(); it != _mem.end(); ++it)
{
byte n = *it;
auto iit = c_instructionInfo.find((Instruction)n);
if (numerics || iit == c_instructionInfo.end() || (byte)iit->first != n) // not an instruction or expecting an argument...
Instruction instr = Instruction(*it);
size_t additional = 0;
if (isValidInstruction(instr))
additional = instructionInfo(instr).additional;
u256 data;
for (size_t i = 0; i < additional; ++i)
{
if (numerics)
numerics--;
ret << "0x" << hex << (int)n << " ";
data <<= 8;
if (it != _mem.end() && ++it != _mem.end())
data |= *it;
}
_onInstruction(instr, data);
}
}
string dev::eth::disassemble(bytes const& _mem)
{
stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
if (!isValidInstruction(_instr))
ret << "0x" << hex << int(_instr) << " ";
else
{
auto const& ii = iit->second;
ret << ii.name << " ";
numerics = ii.additional;
InstructionInfo info = instructionInfo(_instr);
ret << info.name << " ";
if (info.additional)
ret << "0x" << hex << _data << " ";
}
}
});
return ret.str();
}

4
libevmcore/Instruction.h

@ -21,6 +21,7 @@
#pragma once
#include <functional>
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libevmcore/Exceptions.h>
@ -253,6 +254,9 @@ bool isValidInstruction(Instruction _inst);
/// Convert from string mnemonic to Instruction type.
extern const std::map<std::string, Instruction> c_instructions;
/// Iterate through EVM code and call a function on each instruction.
void eachInstruction(bytes const& _mem, std::function<void(Instruction,u256 const&)> const& _onInstruction);
/// Convert from EVM code to simple EVM assembly language.
std::string disassemble(bytes const& _mem);

2
libevmcore/SemanticInformation.cpp

@ -52,8 +52,6 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
return true; // GAS and PC assume a specific order of opcodes
if (_item.instruction() == Instruction::MSIZE)
return true; // msize is modified already by memory access, avoid that for now
if (_item.instruction() == Instruction::SHA3)
return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content.
InstructionInfo info = instructionInfo(_item.instruction());
if (_item.instruction() == Instruction::SSTORE)
return false;

16
libp2p/Host.cpp

@ -383,11 +383,12 @@ string Host::pocHost()
void Host::addNode(NodeId const& _node, bi::address const& _addr, unsigned short _udpNodePort, unsigned short _tcpPeerPort)
{
// TODO: p2p clean this up (bring tested acceptor code over from network branch)
while (isWorking() && !m_run)
this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run)
return;
// return if network is stopped while waiting on Host::run() or nodeTable to start
while (!haveNetwork())
if (isWorking())
this_thread::sleep_for(chrono::milliseconds(50));
else
return;
if (_tcpPeerPort < 30300 || _tcpPeerPort > 30305)
cwarn << "Non-standard port being recorded: " << _tcpPeerPort;
@ -636,8 +637,9 @@ void Host::startedWorking()
else
clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "TCP Listen port is invalid or unavailable.";
m_nodeTable.reset(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort()));
m_nodeTable->setEventHandler(new HostNodeTableHandler(*this));
shared_ptr<NodeTable> nodeTable(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort()));
nodeTable->setEventHandler(new HostNodeTableHandler(*this));
m_nodeTable = nodeTable;
restoreNetwork(&m_restoreNetwork);
clog(NetNote) << "p2p.started id:" << id().abridged();

7
libp2p/Host.h

@ -141,9 +141,12 @@ public:
/// Resets acceptor, socket, and IO service. Called by deallocator.
void stop();
/// @returns if network is running.
bool isStarted() const { return m_run; }
/// @returns if network has been started.
bool isStarted() const { return isWorking(); }
/// @returns if network is started and interactive.
bool haveNetwork() const { return m_run && !!m_nodeTable; }
NodeId id() const { return m_alias.pub(); }
/// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error.

1
libsolidity/CMakeLists.txt

@ -27,7 +27,6 @@ endif()
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} devcrypto)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )

2
libwebthree/WebThree.cpp

@ -74,7 +74,7 @@ WebThreeDirect::~WebThreeDirect()
void WebThreeDirect::setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers)
{
auto had = haveNetwork();
auto had = isNetworkStarted();
if (had)
stopNetwork();
m_net.setNetworkPreferences(_n, _dropPeers);

2
libwebthree/WebThree.h

@ -163,7 +163,7 @@ public:
/// Sets the ideal number of peers.
void setIdealPeerCount(size_t _n) override;
bool haveNetwork() const override { return m_net.isStarted(); }
bool haveNetwork() const override { return m_net.haveNetwork(); }
void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers = false) override;

134
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;
@ -102,7 +103,7 @@ QString ClientModel::apiCall(QString const& _message)
}
catch (...)
{
std::cerr << boost::current_exception_diagnostic_information();
cerr << boost::current_exception_diagnostic_information();
return QString();
}
}
@ -126,7 +127,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();
@ -149,7 +150,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;
}
@ -158,14 +159,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();
@ -203,7 +204,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());
@ -225,7 +226,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);
@ -238,7 +239,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
@ -297,12 +298,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;
@ -329,16 +330,16 @@ 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())
CompiledContract const* compilerRes = nullptr;
if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(nameIter->second)))
{
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));
contracts.push_back(&compilerRes);
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes->assemblyItems() : compilerRes->constructorAssemblyItems();
codes.back()->setDocument(compilerRes->documentId());
codeItems.push_back(move(assemblyItems));
contracts.push_back(compilerRes);
}
else
{
@ -353,8 +354,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)
@ -366,7 +367,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));
@ -374,7 +375,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];
@ -389,7 +390,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"))
@ -403,42 +404,50 @@ 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()));
SourceLocation location = instruction.getLocation();
if (contract->contract()->location() == location || contract->functions().contains(LocationPair(location.start, location.end)))
location = dev::SourceLocation(-1, -1, location.sourceName);
solState = new QSolState(debugData, move(storage), move(solCallStack), move(locals), location.start, location.end, QString::fromUtf8(location.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);
@ -446,6 +455,45 @@ 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--)
{
auto slotIter = _storage.find(slot);
u256 slotValue = slotIter != _storage.end() ? slotIter->second : u256();
bytes slotBytes = toBigEndian(slotValue);
auto start = slotBytes.end() - _type.size - offset;
bytes val(32 - _type.size); //prepend with zeroes
val.insert(val.end(), start, start + _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());
@ -471,9 +519,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);
@ -496,7 +544,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;

1
mix/ClientModel.h

@ -204,6 +204,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<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot);
std::atomic<bool> m_running;
std::atomic<bool> m_mining;

61
mix/CodeModel.cpp

@ -54,8 +54,8 @@ using namespace dev::solidity;
class CollectDeclarationsVisitor: public ASTConstVisitor
{
public:
CollectDeclarationsVisitor(QHash<LocationPair, QString>* _functions, QHash<LocationPair, SolidityDeclaration>* _locals, QHash<unsigned, SolidityDeclaration>* _storage):
m_functions(_functions), m_locals(_locals), m_storage(_storage), m_functionScope(false), m_storageSlot(0) {}
CollectDeclarationsVisitor(QHash<LocationPair, QString>* _functions, QHash<LocationPair, SolidityDeclaration>* _locals):
m_functions(_functions), m_locals(_locals), m_functionScope(false) {}
private:
LocationPair nodeLocation(ASTNode const& _node)
{
@ -79,31 +79,30 @@ 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<LocationPair, QString>* m_functions;
QHash<LocationPair, SolidityDeclaration>* m_locals;
QHash<unsigned, SolidityDeclaration>* m_storage;
bool m_functionScope;
uint m_storageSlot;
};
dev::eth::AssemblyItems filterLocations(dev::eth::AssemblyItems const& _locations, dev::solidity::ContractDefinition const& _contract, QHash<LocationPair, QString> _functions)
QHash<unsigned, SolidityDeclarations> collectStorage(dev::solidity::ContractDefinition const& _contract)
{
dev::eth::AssemblyItems result;
result.reserve(_locations.size());
for (dev::eth::AssemblyItem item : _locations)
QHash<unsigned, SolidityDeclarations> result;
dev::solidity::ContractType contractType(_contract);
for (auto v : contractType.getStateVariables())
{
dev::SourceLocation const& l = item.getLocation();
if (_contract.getLocation() == l || _functions.contains(LocationPair(l.start, l.end)))
item.setLocation(dev::SourceLocation(-1, -1, l.sourceName));
result.push_back(item);
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<unsigned>(slot)].push_back(SolidityDeclaration { QString::fromStdString(declaration->getName()), CodeModel::nodeType(declaration->getType().get()), slot, offset });
}
return result;
}
@ -133,10 +132,11 @@ 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);
m_assemblyItems = _compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = _compiler.getAssemblyItems(name);
}
QString CompiledContract::codeHex() const
@ -220,7 +220,7 @@ QVariantMap CodeModel::contracts() const
return result;
}
CompiledContract* CodeModel::contractByDocumentId(QString _documentId) const
CompiledContract* CodeModel::contractByDocumentId(QString const& _documentId) const
{
Guard l(x_contractMap);
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c)
@ -229,7 +229,7 @@ CompiledContract* CodeModel::contractByDocumentId(QString _documentId) const
return nullptr;
}
CompiledContract const& CodeModel::contract(QString _name) const
CompiledContract const& CodeModel::contract(QString const& _name) const
{
Guard l(x_contractMap);
CompiledContract* res = m_contractMap.value(_name);
@ -238,6 +238,13 @@ CompiledContract const& CodeModel::contract(QString _name) const
return *res;
}
CompiledContract const* CodeModel::tryGetContract(QString const& _name) const
{
Guard l(x_contractMap);
CompiledContract* res = m_contractMap.value(_name);
return res;
}
void CodeModel::releaseContracts()
{
for (ContractMap::iterator c = m_contractMap.begin(); c != m_contractMap.end(); ++c)
@ -335,10 +342,9 @@ 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<SolidityDeclaration>(), std::vector<QString>() };
SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector<SolidityDeclaration>(), std::vector<QString>() };
if (!_type)
return r;
r.dynamicSize = _type->isDynamicallySized();
switch (_type->getCategory())
{
case Type::Category::Integer:
@ -367,7 +373,13 @@ 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<unsigned>(array->getLength());
r.dynamicSize = _type->isDynamicallySized();
r.array = true;
}
break;
@ -384,7 +396,10 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
r.type = SolidityType::Type::Struct;
StructType const* s = dynamic_cast<StructType const*>(_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:

12
mix/CodeModel.h

@ -99,7 +99,7 @@ public:
QHash<LocationPair, QString> const& functions() const { return m_functions; }
QHash<LocationPair, SolidityDeclaration> const& locals() const { return m_locals; }
QHash<unsigned, SolidityDeclaration> const& storage() const { return m_storage; }
QHash<unsigned, SolidityDeclarations> const& storage() const { return m_storage; }
private:
uint m_sourceHash;
@ -112,7 +112,7 @@ private:
eth::AssemblyItems m_constructorAssemblyItems;
QHash<LocationPair, QString> m_functions;
QHash<LocationPair, SolidityDeclaration> m_locals;
QHash<unsigned, SolidityDeclaration> m_storage;
QHash<unsigned, SolidityDeclarations> m_storage;
friend class CodeModel;
};
@ -141,10 +141,14 @@ public:
/// Get contract code by url. Contract is compiled on first access and cached
dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url);
/// Get contract by name
CompiledContract const& contract(QString _name) const;
/// Throws if not found
CompiledContract const& contract(QString const& _name) const;
/// Get contract by name
/// @returns nullptr if not found
CompiledContract const* tryGetContract(QString const& _name) const;
/// Find a contract by document id
/// @returns CompiledContract object or null if not found
Q_INVOKABLE CompiledContract* contractByDocumentId(QString _documentId) const;
Q_INVOKABLE CompiledContract* contractByDocumentId(QString const& _documentId) const;
/// Reset code model
Q_INVOKABLE void reset() { reset(QVariantMap()); }
/// Convert solidity type info to mix type

44
mix/ContractCallDataEncoder.cpp

@ -48,33 +48,57 @@ void ContractCallDataEncoder::encode(QFunctionDefinition const* _function)
void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type)
{
u256 count = 1;
QStringList strList;
if (_type.array)
{
if (_data.type() == QVariant::String)
strList = _data.toString().split(",", QString::SkipEmptyParts); //TODO: proper parsing
else
strList = _data.toStringList();
count = strList.count();
}
else
strList.append(_data.toString());
if (_type.dynamicSize)
{
u256 count = 0;
if (_type.type == SolidityType::Type::Bytes)
count = encodeSingleItem(_data, _type, m_dynamicData);
count = encodeSingleItem(_data.toString(), _type, m_dynamicData);
else
{
QVariantList list = qvariant_cast<QVariantList>(_data);
for (auto const& item: list)
count = strList.count();
for (auto const& item: strList)
encodeSingleItem(item, _type, m_dynamicData);
count = list.size();
}
bytes sizeEnc(32);
toBigEndian(count, sizeEnc);
m_encodedData.insert(m_encodedData.end(), sizeEnc.begin(), sizeEnc.end());
}
else
encodeSingleItem(_data, _type, m_encodedData);
{
if (_type.array)
count = _type.count;
int c = static_cast<int>(count);
if (strList.size() > c)
strList.erase(strList.begin() + c, strList.end());
else
while (strList.size() < c)
strList.append(QString());
for (auto const& item: strList)
encodeSingleItem(item, _type, m_encodedData);
}
}
unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, SolidityType const& _type, bytes& _dest)
unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest)
{
if (_type.type == SolidityType::Type::Struct)
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Struct parameters are not supported yet"));
unsigned const alignSize = 32;
QString src = _data.toString();
QString src = _data;
bytes result;
if ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'")))
@ -104,9 +128,9 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, Solidi
}
unsigned dataSize = _type.dynamicSize ? result.size() : alignSize;
if (result.size() % alignSize != 0)
result.resize((result.size() & ~(alignSize - 1)) + alignSize);
_dest.insert(_dest.end(), result.begin(), result.end());
if ((_dest.size() - 4) % alignSize != 0)
_dest.resize((_dest.size() & ~(alignSize - 1)) + alignSize);
return dataSize;
}

2
mix/ContractCallDataEncoder.h

@ -58,7 +58,7 @@ public:
dev::bytes decodeBytes(dev::bytes const& _rawValue);
private:
unsigned encodeSingleItem(QVariant const& _data, SolidityType const& _type, bytes& _dest);
unsigned encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest);
bigint decodeInt(dev::bytes const& _rawValue);
dev::bytes encodeInt(QString const& _str);
QString toString(dev::bigint const& _int);

14
mix/MixApplication.cpp

@ -20,6 +20,7 @@
*/
#include "MixApplication.h"
#include <boost/exception/diagnostic_information.hpp>
#include <QQmlApplicationEngine>
#include <QUrl>
#include <QIcon>
@ -101,3 +102,16 @@ void MixApplication::initialize()
MixApplication::~MixApplication()
{
}
bool MixApplication::notify(QObject * receiver, QEvent * event)
{
try
{
return QApplication::notify(receiver, event);
}
catch (...)
{
std::cerr << boost::current_exception_diagnostic_information();
}
return false;
}

1
mix/MixApplication.h

@ -62,6 +62,7 @@ public:
static void initialize();
virtual ~MixApplication();
QQmlApplicationEngine* engine() { return m_engine.get(); }
bool notify(QObject* _receiver, QEvent* _event) override;
private:
std::unique_ptr<QQmlApplicationEngine> m_engine;

2
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

2
mix/QBasicNodeDefinition.cpp

@ -28,7 +28,7 @@ namespace mix
{
QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d):
QObject(_parent), m_name(QString::fromStdString(_d->getName()))
QObject(_parent), m_name(QString::fromStdString(_d->getName())), m_location(_d->getLocation())
{
}

3
mix/QBasicNodeDefinition.h

@ -23,6 +23,7 @@
#include <string>
#include <QObject>
#include <libevmcore/SourceLocation.h>
namespace dev
{
@ -47,9 +48,11 @@ public:
QBasicNodeDefinition(QObject* _parent, std::string const& _name);
/// Get the name of the node.
QString name() const { return m_name; }
dev::SourceLocation const& location() { return m_location; }
private:
QString m_name;
dev::SourceLocation m_location;
};
}

6
mix/SolidityType.h

@ -25,6 +25,7 @@ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
#include <QString>
#include <vector>
#include <libdevcore/Common.h>
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<SolidityDeclaration>;
}
}

2
mix/qml.qrc

@ -61,5 +61,7 @@
<file>qml/js/ProjectModel.js</file>
<file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file>
<file>qml/js/Printer.js</file>
<file>qml/js/ansi2html.js</file>
</qresource>
</RCC>

2
mix/qml/Application.qml

@ -178,7 +178,7 @@ ApplicationWindow {
id: editStatesAction
text: qsTr("Edit States")
shortcut: "Ctrl+Alt+E"
onTriggered: stateList.show();
onTriggered: stateList.open();
}
Connections {

4
mix/qml/Debugger.qml

@ -12,6 +12,10 @@ Rectangle {
id: debugPanel
property alias transactionLog: transactionLog
property alias debugSlider: statesSlider
property alias solLocals: solLocals
property alias solStorage: solStorage
property alias solCallStack: solCallStack
signal debugExecuteLocation(string documentId, var location)
property string compilationErrorMessage
property bool assemblyMode: false

410
mix/qml/DeploymentDialog.qml

@ -2,7 +2,7 @@ import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
@ -11,16 +11,11 @@ import "js/QEtherHelper.js" as QEtherHelper
import "."
Window {
Dialog {
id: modalDeploymentDialog
modality: Qt.ApplicationModal
width: 735
height: 320
maximumWidth: width
minimumWidth: width
maximumHeight: height
minimumHeight: height
height: 400
visible: false
property alias applicationUrlEth: applicationUrlEth.text
property alias applicationUrlHttp: applicationUrlHttp.text
@ -32,8 +27,6 @@ Window {
property string currentAccount
property alias gasToUse: gasToUseInput.text
color: appStyle.generic.layout.backgroundColor
function close()
{
visible = false;
@ -41,10 +34,7 @@ Window {
function open()
{
modalDeploymentDialog.setX((Screen.width - width) / 2);
modalDeploymentDialog.setY((Screen.height - height) / 2);
visible = true;
var requests = [{
//accounts
jsonrpc: "2.0",
@ -160,136 +150,140 @@ Window {
id: lightFont
}
Column
{
spacing: 5
contentItem: Rectangle {
color: appStyle.generic.layout.backgroundColor
anchors.fill: parent
anchors.margins: 10
ColumnLayout
Column
{
id: containerDeploy
Layout.fillWidth: true
Layout.preferredHeight: 500
RowLayout
spacing: 5
anchors.fill: parent
anchors.margins: 10
ColumnLayout
{
Rectangle
id: containerDeploy
Layout.fillWidth: true
Layout.preferredHeight: 500
RowLayout
{
Layout.preferredWidth: 357
DefaultLabel
Rectangle
{
text: qsTr("Deployment")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
Layout.preferredWidth: 357
DefaultLabel
{
text: qsTr("Deployment")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
}
}
}
Button
{
action: displayHelpAction
iconSource: "qrc:/qml/img/help.png"
}
Action {
id: displayHelpAction
tooltip: qsTr("Help")
onTriggered: {
Qt.openUrlExternally("https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network")
Button
{
action: displayHelpAction
iconSource: "qrc:/qml/img/help.png"
}
}
Button
{
action: openFolderAction
iconSource: "qrc:/qml/img/openedfolder.png"
}
Action {
id: openFolderAction
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Open Package Folder")
onTriggered: {
fileIo.openFileBrowser(projectModel.deploymentDir);
Action {
id: displayHelpAction
tooltip: qsTr("Help")
onTriggered: {
Qt.openUrlExternally("https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network")
}
}
}
Button
{
action: b64Action
iconSource: "qrc:/qml/img/b64.png"
}
Button
{
action: openFolderAction
iconSource: "qrc:/qml/img/openedfolder.png"
}
Action {
id: b64Action
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Copy Base64 conversion to ClipBoard")
onTriggered: {
clipboard.text = deploymentDialog.packageBase64;
Action {
id: openFolderAction
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Open Package Folder")
onTriggered: {
fileIo.openFileBrowser(projectModel.deploymentDir);
}
}
}
Button
{
action: exitAction
iconSource: "qrc:/qml/img/exit.png"
}
Button
{
action: b64Action
iconSource: "qrc:/qml/img/b64.png"
}
Action {
id: exitAction
tooltip: qsTr("Exit")
onTriggered: {
close()
Action {
id: b64Action
enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Copy Base64 conversion to ClipBoard")
onTriggered: {
clipboard.text = deploymentDialog.packageBase64;
}
}
}
}
GridLayout
{
columns: 2
width: parent.width
Button
{
action: exitAction
iconSource: "qrc:/qml/img/exit.png"
}
DefaultLabel
{
text: qsTr("Root Registrar address:")
Action {
id: exitAction
tooltip: qsTr("Exit")
onTriggered: {
close()
}
}
}
DefaultTextField
GridLayout
{
Layout.preferredWidth: 350
id: registrarAddr
}
columns: 2
width: parent.width
DefaultLabel
{
text: qsTr("Account used to deploy:")
}
DefaultLabel
{
text: qsTr("Root Registrar address:")
}
Rectangle
{
width: 300
height: 25
color: "transparent"
ComboBox {
id: comboAccounts
property var balances: []
onCurrentIndexChanged : {
if (modelAccounts.count > 0)
{
currentAccount = modelAccounts.get(currentIndex).id;
balance.text = balances[currentIndex];
}
}
model: ListModel {
id: modelAccounts
}
DefaultTextField
{
Layout.preferredWidth: 350
id: registrarAddr
}
DefaultLabel
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: comboAccounts.right
anchors.leftMargin: 20
id: balance;
text: qsTr("Account used to deploy:")
}
Rectangle
{
width: 300
height: 25
color: "transparent"
ComboBox {
id: comboAccounts
property var balances: []
onCurrentIndexChanged : {
if (modelAccounts.count > 0)
{
currentAccount = modelAccounts.get(currentIndex).id;
balance.text = balances[currentIndex];
}
}
model: ListModel {
id: modelAccounts
}
}
DefaultLabel
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: comboAccounts.right
anchors.leftMargin: 20
id: balance;
}
}
}
@ -382,120 +376,120 @@ Window {
anchors.verticalCenter: parent.verticalCenter
}
}
}
Rectangle
{
width: parent.width
height: 1
color: "#5891d3"
}
ColumnLayout
{
id: containerRegister
Layout.fillWidth: true
Layout.preferredHeight: 500
RowLayout
Rectangle
{
Layout.preferredHeight: 25
Rectangle
{
Layout.preferredWidth: 356
DefaultLabel
{
text: qsTr("Registration")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
}
}
width: parent.width
height: 1
color: "#5891d3"
}
GridLayout
ColumnLayout
{
columns: 2
id: containerRegister
Layout.fillWidth: true
DefaultLabel
Layout.preferredHeight: 500
RowLayout
{
Layout.preferredWidth: 355
text: qsTr("Local package URL")
Layout.preferredHeight: 25
Rectangle
{
Layout.preferredWidth: 356
DefaultLabel
{
text: qsTr("Registration")
font.family: lightFont.name
font.underline: true
anchors.centerIn: parent
}
}
}
DefaultTextField
GridLayout
{
Layout.preferredWidth: 350
id: localPackageUrl
readOnly: true
enabled: rowRegister.isOkToRegister()
}
columns: 2
Layout.fillWidth: true
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("URL Hint contract address:")
}
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("Local package URL")
}
DefaultTextField
{
Layout.preferredWidth: 350
id: urlHintAddr
enabled: rowRegister.isOkToRegister()
}
DefaultTextField
{
Layout.preferredWidth: 350
id: localPackageUrl
readOnly: true
enabled: rowRegister.isOkToRegister()
}
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("Web Application Resources URL: ")
}
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("URL Hint contract address:")
}
DefaultTextField
{
Layout.preferredWidth: 350
id: applicationUrlHttp
enabled: rowRegister.isOkToRegister()
}
}
DefaultTextField
{
Layout.preferredWidth: 350
id: urlHintAddr
enabled: rowRegister.isOkToRegister()
}
RowLayout
{
id: rowRegister
Layout.fillWidth: true
DefaultLabel
{
Layout.preferredWidth: 355
text: qsTr("Web Application Resources URL: ")
}
Rectangle
{
Layout.preferredWidth: 357
color: "transparent"
DefaultTextField
{
Layout.preferredWidth: 350
id: applicationUrlHttp
enabled: rowRegister.isOkToRegister()
}
}
function isOkToRegister()
RowLayout
{
return Object.keys(projectModel.deploymentAddresses).length > 0 && deploymentDialog.packageHash !== "";
}
id: rowRegister
Layout.fillWidth: true
Button {
action: registerAction
iconSource: "qrc:/qml/img/note.png"
}
Rectangle
{
Layout.preferredWidth: 357
color: "transparent"
}
Action {
id: registerAction
enabled: rowRegister.isOkToRegister()
tooltip: qsTr("Register hosted Web Application.")
onTriggered: {
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "")
{
deployDialog.title = text;
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step.")
deployDialog.open();
return;
function isOkToRegister()
{
return Object.keys(projectModel.deploymentAddresses).length > 0 && deploymentDialog.packageHash !== "";
}
Button {
action: registerAction
iconSource: "qrc:/qml/img/note.png"
}
Action {
id: registerAction
enabled: rowRegister.isOkToRegister()
tooltip: qsTr("Register hosted Web Application.")
onTriggered: {
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "")
{
deployDialog.title = text;
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step.")
deployDialog.open();
return;
}
var inError = [];
if (applicationUrlHttp.text.length > 32)
inError.push(qsTr(applicationUrlHttp.text));
if (!stopForInputError(inError))
ProjectModelCode.registerToUrlHint();
}
var inError = [];
if (applicationUrlHttp.text.length > 32)
inError.push(qsTr(applicationUrlHttp.text));
if (!stopForInputError(inError))
ProjectModelCode.registerToUrlHint();
}
}
}

53
mix/qml/LogsPane.qml

@ -7,6 +7,11 @@ import org.ethereum.qml.SortFilterProxyModel 1.0
Rectangle
{
property variant currentStatus;
function clear()
{
logsModel.clear();
}
function push(_level, _type, _content)
{
_content = _content.replace(/\n/g, " ")
@ -57,7 +62,7 @@ Rectangle
model: SortFilterProxyModel {
id: proxyModel
source: logsModel
property var roles: ["-", "javascript", "run", "state", "comp"]
property var roles: ["-", "javascript", "run", "state", "comp", "deployment"]
Component.onCompleted: {
filterType = regEx(proxyModel.roles);
@ -90,7 +95,7 @@ Rectangle
return "(?:" + roles.join('|') + ")";
}
filterType: "(?:javascript|run|state|comp)"
filterType: "(?:javascript|run|state|comp|deployment)"
filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive
@ -384,6 +389,50 @@ Rectangle
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
ToolButton {
id: deloyButton
checkable: true
height: logStyle.generic.layout.headerButtonHeight
width: 50
anchors.verticalCenter: parent.verticalCenter
checked: true
onCheckedChanged: {
proxyModel.toogleFilter("deployment")
}
tooltip: qsTr("Deployment")
style:
ButtonStyle {
label:
Item {
DefaultLabel {
font.family: logStyle.generic.layout.logLabelFont
font.pointSize: appStyle.absoluteSize(-3)
color: logStyle.generic.layout.logLabelColor
anchors.centerIn: parent
text: qsTr("Deploy.")
}
}
background:
Rectangle {
color: deloyButton.checked ? logStyle.generic.layout.buttonSelected : "transparent"
}
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor1
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 2;
height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2
}
}
Row

94
mix/qml/NewProjectDialog.qml

@ -1,10 +1,11 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
Window {
Dialog {
id: newProjectWin
modality: Qt.ApplicationModal
@ -33,62 +34,65 @@ Window {
close();
accepted();
}
GridLayout {
id: dialogContent
columns: 2
contentItem: Rectangle {
anchors.fill: parent
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
GridLayout
{
id: dialogContent
columns: 2
anchors.fill: parent
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
Label {
text: qsTr("Title")
}
TextField {
id: titleField
focus: true
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
Label {
text: qsTr("Title")
}
}
Label {
text: qsTr("Path")
}
RowLayout {
TextField {
id: pathField
id: titleField
focus: true
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
}
}
Button {
text: qsTr("Browse")
onClicked: createProjectFileDialog.open()
Label {
text: qsTr("Path")
}
RowLayout {
TextField {
id: pathField
Layout.fillWidth: true
Keys.onReturnPressed: {
if (okButton.enabled)
acceptAndClose();
}
}
Button {
text: qsTr("Browse")
onClicked: createProjectFileDialog.open()
}
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
id: okButton;
enabled: titleField.text != "" && pathField.text != ""
text: qsTr("OK");
onClicked: {
acceptAndClose();
Button {
id: okButton;
enabled: titleField.text != "" && pathField.text != ""
text: qsTr("OK");
onClicked: {
acceptAndClose();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
}
@ -102,8 +106,8 @@ Window {
var u = createProjectFileDialog.fileUrl.toString();
if (u.indexOf("file://") == 0)
u = u.substring(7, u.length)
if (Qt.platform.os == "windows" && u.indexOf("/") == 0)
u = u.substring(1, u.length);
if (Qt.platform.os == "windows" && u.indexOf("/") == 0)
u = u.substring(1, u.length);
pathField.text = u;
}
}

564
mix/qml/StateDialog.qml

@ -1,6 +1,6 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Controls.Styles 1.3
@ -9,7 +9,7 @@ import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
import "."
Window {
Dialog {
id: modalStateDialog
modality: Qt.ApplicationModal
@ -17,7 +17,6 @@ Window {
height: 480
title: qsTr("Edit State")
visible: false
color: stateDialogStyle.generic.backgroundColor
property alias stateTitle: titleField.text
property alias isDefault: defaultCheckBox.checked
@ -28,6 +27,10 @@ Window {
property var stateAccounts: []
signal accepted
StateDialogStyle {
id: stateDialogStyle
}
function open(index, item, setDefault) {
stateIndex = index;
stateTitle = item.title;
@ -48,9 +51,6 @@ Window {
stateAccounts.push(item.accounts[k]);
}
modalStateDialog.setX((Screen.width - width) / 2);
modalStateDialog.setY((Screen.height - height) / 2);
visible = true;
isDefault = setDefault;
titleField.focus = true;
@ -77,338 +77,334 @@ Window {
item.accounts = stateAccounts;
return item;
}
StateDialogStyle {
id: stateDialogStyle
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
contentItem: Rectangle {
color: stateDialogStyle.generic.backgroundColor
ColumnLayout {
id: dialogContent
anchors.top: parent.top
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Title")
}
DefaultTextField
{
id: titleField
Layout.fillWidth: true
}
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
Layout.fillWidth: true
Rectangle
{
Layout.preferredWidth: 75
DefaultLabel {
id: accountsLabel
Layout.preferredWidth: 75
text: qsTr("Accounts")
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ColumnLayout {
id: dialogContent
anchors.top: parent.top
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Title")
}
DefaultTextField
{
id: titleField
Layout.fillWidth: true
}
}
Button
CommonSeparator
{
anchors.top: accountsLabel.bottom
anchors.topMargin: 10
iconSource: "qrc:/qml/img/plus.png"
action: newAccountAction
Layout.fillWidth: true
}
Action {
id: newAccountAction
tooltip: qsTr("Add new Account")
onTriggered:
{
var account = stateListModel.newAccount("1000000", QEther.Ether);
stateAccounts.push(account);
accountsModel.append(account);
}
}
}
RowLayout
{
Layout.fillWidth: true
MessageDialog
{
id: alertAlreadyUsed
text: qsTr("This account is in use. You cannot remove it. The first account is used to deploy config contract and cannot be removed.")
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok
}
Rectangle
{
Layout.preferredWidth: 75
DefaultLabel {
id: accountsLabel
Layout.preferredWidth: 75
text: qsTr("Accounts")
}
TableView
{
id: accountsView
Layout.fillWidth: true
model: accountsModel
headerVisible: false
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 150
delegate: Item {
RowLayout
Button
{
height: 25
width: parent.width
Button
anchors.top: accountsLabel.bottom
anchors.topMargin: 10
iconSource: "qrc:/qml/img/plus.png"
action: newAccountAction
}
Action {
id: newAccountAction
tooltip: qsTr("Add new Account")
onTriggered:
{
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAccountAction
var account = stateListModel.newAccount("1000000", QEther.Ether);
stateAccounts.push(account);
accountsModel.append(account);
}
}
}
Action {
id: deleteAccountAction
tooltip: qsTr("Delete Account")
onTriggered:
MessageDialog
{
id: alertAlreadyUsed
text: qsTr("This account is in use. You cannot remove it. The first account is used to deploy config contract and cannot be removed.")
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok
}
TableView
{
id: accountsView
Layout.fillWidth: true
model: accountsModel
headerVisible: false
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 150
delegate: Item {
RowLayout
{
if (transactionsModel.isUsed(stateAccounts[styleData.row].secret))
alertAlreadyUsed.open();
else
height: 25
width: parent.width
Button
{
stateAccounts.splice(styleData.row, 1);
accountsModel.remove(styleData.row);
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAccountAction
}
Action {
id: deleteAccountAction
tooltip: qsTr("Delete Account")
onTriggered:
{
if (transactionsModel.isUsed(stateAccounts[styleData.row].secret))
alertAlreadyUsed.open();
else
{
stateAccounts.splice(styleData.row, 1);
accountsModel.remove(styleData.row);
}
}
}
DefaultTextField {
anchors.verticalCenter: parent.verticalCenter
onTextChanged: {
if (styleData.row > -1)
stateAccounts[styleData.row].name = text;
}
text: {
return styleData.value
}
}
}
}
}
DefaultTextField {
anchors.verticalCenter: parent.verticalCenter
onTextChanged: {
if (styleData.row > -1)
stateAccounts[styleData.row].name = text;
}
text: {
return styleData.value
TableViewColumn {
role: "balance"
title: qsTr("Balance")
width: 200
delegate: Item {
Ether {
id: balanceField
edit: true
displayFormattedValue: false
value: styleData.value
}
}
}
rowDelegate:
Rectangle {
color: styleData.alternate ? "transparent" : "#f0f0f0"
height: 30;
}
}
}
TableViewColumn {
role: "balance"
title: qsTr("Balance")
width: 200
delegate: Item {
Ether {
id: balanceField
edit: true
displayFormattedValue: false
value: styleData.value
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Default")
}
CheckBox {
id: defaultCheckBox
Layout.fillWidth: true
}
}
rowDelegate:
Rectangle {
color: styleData.alternate ? "transparent" : "#f0f0f0"
height: 30;
CommonSeparator
{
Layout.fillWidth: true
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
ColumnLayout {
anchors.top: dialogContent.bottom
anchors.topMargin: 5
spacing: 0
RowLayout
{
Layout.preferredWidth: 150
DefaultLabel {
text: qsTr("Transactions: ")
}
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Default")
}
CheckBox {
id: defaultCheckBox
Layout.fillWidth: true
}
}
Button
{
iconSource: "qrc:/qml/img/plus.png"
action: newTrAction
width: 10
height: 10
anchors.right: parent.right
}
CommonSeparator
{
Layout.fillWidth: true
}
}
Action {
id: newTrAction
tooltip: qsTr("Create a new transaction")
onTriggered: transactionsModel.addTransaction()
}
}
ColumnLayout {
anchors.top: dialogContent.bottom
anchors.topMargin: 5
spacing: 0
RowLayout
{
Layout.preferredWidth: 150
DefaultLabel {
text: qsTr("Transactions: ")
ScrollView
{
Layout.fillHeight: true
Layout.preferredWidth: 300
Column
{
Layout.fillHeight: true
Repeater
{
id: trRepeater
model: transactionsModel
delegate: transactionRenderDelegate
visible: transactionsModel.count > 0
height: 20 * transactionsModel.count
}
}
}
}
Button
RowLayout
{
iconSource: "qrc:/qml/img/plus.png"
action: newTrAction
width: 10
height: 10
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
close();
accepted();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
Action {
id: newTrAction
tooltip: qsTr("Create a new transaction")
onTriggered: transactionsModel.addTransaction()
}
}
ListModel {
id: accountsModel
ScrollView
{
Layout.fillHeight: true
Layout.preferredWidth: 300
Column
{
Layout.fillHeight: true
Repeater
function removeAccount(_i)
{
id: trRepeater
model: transactionsModel
delegate: transactionRenderDelegate
visible: transactionsModel.count > 0
height: 20 * transactionsModel.count
accountsModel.remove(_i);
stateAccounts.splice(_i, 1);
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
acceptAndClose();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
}
ListModel {
id: accountsModel
function removeAccount(_i)
{
accountsModel.remove(_i);
stateAccounts.splice(_i, 1);
}
}
ListModel {
id: transactionsModel
ListModel {
id: transactionsModel
function editTransaction(index) {
transactionDialog.stateAccounts = stateAccounts;
transactionDialog.open(index, transactionsModel.get(index));
}
function editTransaction(index) {
transactionDialog.stateAccounts = stateAccounts;
transactionDialog.open(index, transactionsModel.get(index));
}
function addTransaction() {
function addTransaction() {
// Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handler would just edit the item that was just created, no harm done
var item = TransactionHelper.defaultTransaction();
transactionDialog.stateAccounts = stateAccounts;
transactionDialog.open(transactionsModel.count, item);
}
// Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handler would just edit the item that was just created, no harm done
var item = TransactionHelper.defaultTransaction();
transactionDialog.stateAccounts = stateAccounts;
transactionDialog.open(transactionsModel.count, item);
}
function deleteTransaction(index) {
stateTransactions.splice(index, 1);
transactionsModel.remove(index);
}
function deleteTransaction(index) {
stateTransactions.splice(index, 1);
transactionsModel.remove(index);
}
function isUsed(secret)
{
for (var i in stateTransactions)
{
if (stateTransactions[i].sender === secret)
return true;
}
return false;
}
}
function isUsed(secret)
{
for (var i in stateTransactions)
{
if (stateTransactions[i].sender === secret)
return true;
}
return false;
}
}
Component {
id: transactionRenderDelegate
RowLayout {
DefaultLabel {
Layout.preferredWidth: 150
text: functionId
}
Component {
id: transactionRenderDelegate
RowLayout {
DefaultLabel {
Layout.preferredWidth: 150
text: functionId
}
Button
{
id: deleteBtn
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAction
width: 10
height: 10
Action {
id: deleteAction
tooltip: qsTr("Delete")
onTriggered: transactionsModel.deleteTransaction(index)
}
}
Button
{
id: deleteBtn
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAction
width: 10
height: 10
Action {
id: deleteAction
tooltip: qsTr("Delete")
onTriggered: transactionsModel.deleteTransaction(index)
Button
{
iconSource: "qrc:/qml/img/edit.png"
action: editAction
visible: stdContract === false
width: 10
height: 10
Action {
id: editAction
tooltip: qsTr("Edit")
onTriggered: transactionsModel.editTransaction(index)
}
}
}
}
}
Button
{
iconSource: "qrc:/qml/img/edit.png"
action: editAction
visible: stdContract === false
width: 10
height: 10
Action {
id: editAction
tooltip: qsTr("Edit")
onTriggered: transactionsModel.editTransaction(index)
TransactionDialog
{
id: transactionDialog
onAccepted:
{
var item = transactionDialog.getItem();
if (transactionDialog.transactionIndex < transactionsModel.count) {
transactionsModel.set(transactionDialog.transactionIndex, item);
stateTransactions[transactionDialog.transactionIndex] = item;
} else {
transactionsModel.append(item);
stateTransactions.push(item);
}
}
}
}
}
}
TransactionDialog
{
id: transactionDialog
onAccepted:
{
var item = transactionDialog.getItem();
if (transactionDialog.transactionIndex < transactionsModel.count) {
transactionsModel.set(transactionDialog.transactionIndex, item);
stateTransactions[transactionDialog.transactionIndex] = item;
} else {
transactionsModel.append(item);
stateTransactions.push(item);
}
}
}
}

74
mix/qml/StateList.qml

@ -1,39 +1,50 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import "."
Window {
Dialog {
id: stateListContainer
modality: Qt.WindowModal
width: 640
height: 480
visible: false
ColumnLayout
{
contentItem: Rectangle {
anchors.fill: parent
TableView {
id: list
Layout.fillHeight: true
Layout.fillWidth: true
model: projectModel.stateListModel
itemDelegate: renderDelegate
headerDelegate: null
TableViewColumn {
role: "title"
title: qsTr("State")
width: list.width
ColumnLayout
{
anchors.fill: parent
TableView {
id: list
Layout.fillHeight: true
Layout.fillWidth: true
model: projectModel.stateListModel
itemDelegate: renderDelegate
headerDelegate: null
frameVisible: false
TableViewColumn {
role: "title"
title: qsTr("State")
width: list.width
}
}
}
Button {
anchors.bottom: parent.bottom
action: addStateAction
Row{
spacing: 5
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 10
Button {
action: addStateAction
}
Button {
action: closeAction
}
}
}
}
@ -69,12 +80,21 @@ Window {
}
}
Action {
id: addStateAction
text: "&Add State"
shortcut: "Ctrl+T"
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: list.model.addState();
Row
{
Action {
id: addStateAction
text: qsTr("Add State")
shortcut: "Ctrl+T"
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: list.model.addState();
}
Action {
id: closeAction
text: qsTr("Close")
onTriggered: stateListContainer.close();
}
}
}

28
mix/qml/StatusPane.qml

@ -27,7 +27,6 @@ Rectangle {
debugImg.state = "";
currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "error" }
}
debugRunActionIcon.enabled = codeModel.hasContract;
}
function infoMessage(text, type)
@ -77,7 +76,11 @@ Rectangle {
Connections {
target:clientModel
onRunStarted: infoMessage(qsTr("Running transactions..."), "Run");
onRunStarted:
{
logPane.clear()
infoMessage(qsTr("Running transactions..."), "Run");
}
onRunFailed: errorMessage(format(_message), "Run");
onRunComplete: infoMessage(qsTr("Run complete"), "Run");
onNewBlock: infoMessage(qsTr("New block created"), "State");
@ -281,24 +284,9 @@ Rectangle {
anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter
id: debugImg
iconSource: "qrc:/qml/img/bugiconinactive.png"
action: debugRunActionIcon
states: [
State{
name: "active"
PropertyChanges { target: debugImg; iconSource: "qrc:/qml/img/bugiconactive.png"}
}
]
}
Action {
id: debugRunActionIcon
onTriggered: {
if (mainContent.rightViewIsVisible())
mainContent.hideRightView()
else
mainContent.startQuickDebugging();
}
enabled: false
text: ""
iconSource: "qrc:/qml/img/bugiconactive.png"
action: showHideRightPanelAction
}
}
}

1
mix/qml/StepActionImage.qml

@ -6,6 +6,7 @@ import QtQuick.Controls.Styles 1.1
Rectangle {
id: buttonActionContainer
color: "transparent"
property string disableStateImg
property string enabledStateImg
property string buttonTooltip

9
mix/qml/StructView.qml

@ -79,11 +79,14 @@ Column
function getValue()
{
var r = "";
if (value && value[modelData.name] !== undefined)
return value[modelData.name];
r = value[modelData.name];
else if (modelData.type.category === QSolidityType.Struct)
return {};
return "";
r = {};
if (Array.isArray(r))
r = r.join(", ");
return r;
}
}
}

379
mix/qml/TransactionDialog.qml

@ -1,19 +1,19 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "."
Window {
Dialog {
id: modalTransactionDialog
modality: Qt.ApplicationModal
width: 520
height: 500;
height: 500
visible: false
color: transactionDialogStyle.generic.backgroundColor
title: qsTr("Edit Transaction")
property int transactionIndex
property alias gas: gasValueEdit.gasValue;
@ -27,6 +27,10 @@ Window {
property alias stateAccounts: senderComboBox.model
signal accepted;
StateDialogStyle {
id: transactionDialogStyle
}
function open(index, item) {
rowFunction.visible = !useTransactionDefaultValue;
rowValue.visible = !useTransactionDefaultValue;
@ -73,8 +77,6 @@ Window {
}
}
initTypeLoader();
modalTransactionDialog.setX((Screen.width - width) / 2);
modalTransactionDialog.setY((Screen.height - height) / 2);
visible = true;
valueField.focus = true;
@ -178,214 +180,219 @@ Window {
item.parameters = paramValues;
return item;
}
StateDialogStyle {
id: transactionDialogStyle
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
contentItem: Rectangle {
color: transactionDialogStyle.generic.backgroundColor
ColumnLayout {
id: dialogContent
anchors.top: parent.top
spacing: 10
RowLayout
{
id: rowSender
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Sender")
}
ComboBox {
function select(secret)
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ColumnLayout {
id: dialogContent
anchors.top: parent.top
spacing: 10
RowLayout
{
for (var i in model)
if (model[i].secret === secret)
id: rowSender
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Sender")
}
ComboBox {
function select(secret)
{
currentIndex = i;
break;
for (var i in model)
if (model[i].secret === secret)
{
currentIndex = i;
break;
}
}
}
id: senderComboBox
Layout.preferredWidth: 350
currentIndex: 0
textRole: "name"
editable: false
}
}
id: senderComboBox
Layout.preferredWidth: 350
currentIndex: 0
textRole: "name"
editable: false
}
}
RowLayout
{
id: rowContract
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Contract")
}
ComboBox {
id: contractComboBox
function currentValue() {
return (currentIndex >=0 && currentIndex < contractsModel.count) ? contractsModel.get(currentIndex).cid : "";
RowLayout
{
id: rowContract
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Contract")
}
ComboBox {
id: contractComboBox
function currentValue() {
return (currentIndex >=0 && currentIndex < contractsModel.count) ? contractsModel.get(currentIndex).cid : "";
}
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: contractsModel
}
onCurrentIndexChanged: {
loadFunctions(currentValue());
}
}
}
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: contractsModel
RowLayout
{
id: rowFunction
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Function")
}
ComboBox {
id: functionComboBox
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
}
onCurrentIndexChanged: {
loadParameters();
}
}
}
onCurrentIndexChanged: {
loadFunctions(currentValue());
CommonSeparator
{
Layout.fillWidth: true
}
}
}
RowLayout
{
id: rowFunction
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Function")
}
ComboBox {
id: functionComboBox
Layout.preferredWidth: 350
currentIndex: -1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
RowLayout
{
id: rowValue
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Value")
}
Ether {
id: valueField
edit: true
displayFormattedValue: true
}
}
onCurrentIndexChanged: {
loadParameters();
CommonSeparator
{
Layout.fillWidth: true
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
id: rowGas
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Gas")
}
DefaultTextField
{
property variant gasValue
onGasValueChanged: text = gasValue.value();
onTextChanged: gasValue.setValue(text);
implicitWidth: 200
id: gasValueEdit;
}
}
RowLayout
{
id: rowValue
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Value")
}
Ether {
id: valueField
edit: true
displayFormattedValue: true
}
}
CommonSeparator
{
Layout.fillWidth: true
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
id: rowGasPrice
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Gas Price")
}
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
}
}
RowLayout
{
id: rowGas
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Gas")
}
CommonSeparator
{
Layout.fillWidth: true
}
DefaultTextField
{
property variant gasValue
onGasValueChanged: text = gasValue.value();
onTextChanged: gasValue.setValue(text);
implicitWidth: 200
id: gasValueEdit;
}
}
DefaultLabel {
id: paramLabel
text: qsTr("Parameters:")
Layout.preferredWidth: 75
}
CommonSeparator
{
Layout.fillWidth: true
}
ScrollView
{
id: paramScroll
anchors.top: paramLabel.bottom
anchors.topMargin: 10
Layout.fillWidth: true
Layout.fillHeight: true
StructView
{
id: typeLoader
Layout.preferredWidth: 150
members: paramsModel;
}
}
RowLayout
{
id: rowGasPrice
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Gas Price")
}
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
CommonSeparator
{
Layout.fillWidth: true
visible: paramsModel.length > 0
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
DefaultLabel {
id: paramLabel
text: qsTr("Parameters:")
Layout.preferredWidth: 75
}
ScrollView
RowLayout
{
id: paramScroll
anchors.top: paramLabel.bottom
anchors.topMargin: 10
Layout.fillWidth: true
Layout.fillHeight: true
StructView
{
id: typeLoader
Layout.preferredWidth: 150
members: paramsModel;
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
close();
accepted();
}
}
}
CommonSeparator
{
Layout.fillWidth: true
visible: paramsModel.length > 0
}
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("OK");
onClicked: {
acceptAndClose();
Button {
text: qsTr("Cancel");
onClicked: close();
Layout.fillWidth: true
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
}
}

1
mix/qml/VariablesView.qml

@ -2,7 +2,6 @@ import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import "."
DebugInfoList
{

13
mix/qml/WebPreview.qml

@ -13,7 +13,10 @@ Item {
property string pendingPageUrl: ""
property bool initialized: false
property alias urlInput: urlInput
property alias webView: webView
property string webContent; //for testing
signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content)
signal webContentReady
function setPreviewUrl(url) {
if (!initialized)
@ -57,6 +60,13 @@ Item {
action(i);
}
function getContent() {
webView.runJavaScript("getContent()", function(result) {
webContent = result;
webContentReady();
});
}
function changePage() {
setPreviewUrl(urlInput.text);
}
@ -345,7 +355,7 @@ Item {
height: 22
width: 22
action: clearAction
iconSource: "qrc:/qml/img/broom.png"
iconSource: "qrc:/qml/img/cleariconactive.png"
}
Action {
@ -423,6 +433,7 @@ Item {
id: resultTextArea
width: expressionPanel.width
wrapMode: Text.Wrap
textFormat: Text.RichText
font.family: webPreviewStyle.general.fontName
font.pointSize: appStyle.absoluteSize(-3)
backgroundVisible: true

12
mix/qml/html/WebContainer.html

@ -3,6 +3,8 @@
<head>
<script src="qrc:///js/bignumber.min.js"></script>
<script src="qrc:///js/webthree.js"></script>
<script src="qrc:///qml/js/Printer.js"></script>
<script src="qrc:///qml/js/ansi2html.js"></script>
<script>
loadPage = function(url) {
var preview = document.getElementById('preview');
@ -40,7 +42,15 @@ init = function(url) {
executeJavaScript = function(script) {
var preview = document.getElementById('preview');
var obj = preview.contentWindow.eval(script);
return JSON.stringify(obj, null, 2);
var printed = prettyPrint(obj);
return ansi2html(printed);
}
getContent = function() {
var preview = document.getElementById('preview');
var doc = preview.contentDocument? preview.contentDocument: preview.contentWindow.document;
var body = doc.getElementsByTagName('body')[0];
return body.innerHTML;
}
</script>

90
mix/qml/js/Printer.js

@ -0,0 +1,90 @@
var prettyPrint = (function () {
function pp(object, indent) {
try {
JSON.stringify(object, null, 2);
} catch (e) {
return pp(e, indent);
}
var str = "";
if(object instanceof Array) {
str += "[";
for(var i = 0, l = object.length; i < l; i++) {
str += pp(object[i], indent);
if(i < l-1) {
str += ", ";
}
}
str += " ]";
} else if (object instanceof Error) {
str += "\e[31m" + "Error:\e[0m " + object.message;
} else if (isBigNumber(object)) {
str += "\e[32m'" + object.toString(10) + "'";
} else if(typeof(object) === "object") {
str += "{\n";
indent += " ";
var last = getFields(object).pop()
getFields(object).forEach(function (k) {
str += indent + k + ": ";
try {
str += pp(object[k], indent);
} catch (e) {
str += pp(e, indent);
}
if(k !== last) {
str += ",";
}
str += "\n";
});
str += indent.substr(2, indent.length) + "}";
} else if(typeof(object) === "string") {
str += "\e[32m'" + object + "'";
} else if(typeof(object) === "undefined") {
str += "\e[1m\e[30m" + object;
} else if(typeof(object) === "number") {
str += "\e[31m" + object;
} else if(typeof(object) === "function") {
str += "\e[35m[Function]";
} else {
str += object;
}
str += "\e[0m";
return str;
}
var redundantFields = [
'valueOf',
'toString',
'toLocaleString',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
'__proto__'
];
var getFields = function (object) {
var result = Object.getOwnPropertyNames(object);
if (object.constructor && object.constructor.prototype) {
result = result.concat(Object.getOwnPropertyNames(object.constructor.prototype));
}
return result.filter(function (field) {
return redundantFields.indexOf(field) === -1;
});
};
var isBigNumber = function (object) {
return typeof BigNumber !== 'undefined' && object instanceof BigNumber;
};
function prettyPrintI(/* */) {
var args = arguments;
var ret = "";
for (var i = 0, l = args.length; i < l; i++) {
ret += pp(args[i], "") + "\n";
}
return ret;
}
return prettyPrintI;
})();

2
mix/qml/js/TransactionHelper.js

@ -25,7 +25,7 @@ function rpcCall(requests, callBack)
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status !== 200 || httpRequest.responseText === "")
{
var errorText = qsTr("Deployment error: Error while registering Dapp ") + httpRequest.status;
var errorText = qsTr("Unable to initiate request to the live network. Please verify your ethereum node is up.") + qsTr(" Error status: ") + httpRequest.status;
console.log(errorText);
deploymentError(errorText);
}

106
mix/qml/js/ansi2html.js

@ -0,0 +1,106 @@
var ansi2html = (function(){
function ansi2htmlI(str) {
// this lib do not support \e
str = str.replace(/e\[/g, '[');
// nor line breaks
str = '<div>' + str.replace(/\n/g, '</div><div>') + '</div>';
var props = {}
, open = false
var stylemap =
{ bold: "font-weight"
, underline: "text-decoration"
, color: "color"
, background: "background"
}
function style() {
var key, val, style = []
for (var key in props) {
val = props[key]
if (!val) continue
if (val == true) {
style.push(stylemap[key] + ':' + key)
} else {
style.push(stylemap[key] + ':' + val)
}
}
return style.join(';')
}
function tag(code) {
var i
, tag = ''
, n = ansi2htmlI.table[code]
if (open) tag += '</span>'
open = false
if (n) {
for (i in n) props[i] = n[i]
tag += '<span style="' + style() + '">'
open = true
} else {
props = {}
}
return tag
}
return str.replace(/\[(\d+;)?(\d+)*m/g, function(match, b1, b2) {
var i, code, res = ''
if (b2 == '' || b2 == null) b2 = '0'
for (i = 1; i < arguments.length - 2; i++) {
if (!arguments[i]) continue
code = parseInt(arguments[i])
res += tag(code)
}
return res
}) + tag()
}
/* not implemented:
* italic
* blink
* invert
* strikethrough
*/
ansi2htmlI.table =
{ 0: null
, 1: { bold: true }
, 3: { italic: true }
, 4: { underline: true }
, 5: { blink: true }
, 6: { blink: true }
, 7: { invert: true }
, 9: { strikethrough: true }
, 23: { italic: false }
, 24: { underline: false }
, 25: { blink: false }
, 27: { invert: false }
, 29: { strikethrough: false }
, 30: { color: 'black' }
, 31: { color: 'red' }
, 32: { color: 'green' }
, 33: { color: 'yellow' }
, 34: { color: 'blue' }
, 35: { color: 'magenta' }
, 36: { color: 'cyan' }
, 37: { color: 'white' }
, 39: { color: null }
, 40: { background: 'black' }
, 41: { background: 'red' }
, 42: { background: 'green' }
, 43: { background: 'yellow' }
, 44: { background: 'blue' }
, 45: { background: 'magenta' }
, 46: { background: 'cyan' }
, 47: { background: 'white' }
, 49: { background: null }
}
return ansi2htmlI;
})();

14
mix/test/TestService.cpp

@ -20,6 +20,7 @@
* Ethereum IDE client.
*/
#include <iostream>
#include "TestService.h"
#include <QtTest/QSignalSpy>
#include <QElapsedTimer>
@ -111,6 +112,12 @@ bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeou
return spy.size();
}
bool TestService::waitForRendering(QObject* _item, int timeout)
{
QWindow* window = eventWindow(_item);
return waitForSignal(window, "frameSwapped()", timeout);
}
bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay)
{
QWindow* window = eventWindow(_item);
@ -156,6 +163,7 @@ bool TestService::keyClickChar(QObject* _item, QString const& _character, int _m
bool TestService::mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay)
{
QWindow* window = qobject_cast<QWindow*>(_item);
QMetaObject const* mo = _item->metaObject();
if (!window)
window = eventWindow(_item);
mouseEvent(MouseClick, window, _item, Qt::MouseButton(_button), Qt::KeyboardModifiers(_modifiers), QPointF(_x, _y), _delay);
@ -170,18 +178,22 @@ void TestService::setTargetWindow(QObject* _window)
window->requestActivate();
}
QWindow* TestService::eventWindow(QObject* _item)
{
QQuickItem* item = qobject_cast<QQuickItem*>(_item);
if (item && item->window())
return item->window();
QQuickWindow* window = qobject_cast<QQuickWindow*>(_item);
QWindow* window = qobject_cast<QQuickWindow*>(_item);
if (!window && _item->parent())
window = eventWindow(_item->parent());
if (!window)
window = qobject_cast<QQuickWindow*>(m_targetWindow);
if (window)
{
window->requestActivate();
std::cout << window->title().toStdString();
return window;
}
item = qobject_cast<QQuickItem*>(m_targetWindow);

1
mix/test/TestService.h

@ -42,6 +42,7 @@ public:
public slots:
bool waitForSignal(QObject* _item, QString _signalName, int _timeout);
bool waitForRendering(QObject* _item, int timeout);
bool keyPress(QObject* _item, int _key, int _modifiers, int _delay);
bool keyRelease(QObject* _item, int _key, int _modifiers, int _delay);
bool keyClick(QObject* _item, int _key, int _modifiers, int _delay);

110
mix/test/qml/TestMain.qml

@ -2,6 +2,8 @@ import QtQuick 2.2
import QtTest 1.1
import org.ethereum.qml.TestService 1.0
import "../../qml"
import "js/TestDebugger.js" as TestDebugger
import "js/TestTutorial.js" as TestTutorial
TestCase
{
@ -14,6 +16,8 @@ TestCase
{
if (el === undefined)
el = mainApplication;
if (el.contentItem) //for dialgos
el = el.contentItem
for (var c in str)
{
@ -23,6 +27,11 @@ TestCase
}
}
Application
{
id: mainApplication
}
function newProject()
{
waitForRendering(mainApplication.mainContent, 10000);
@ -43,100 +52,25 @@ TestCase
fail("not compiled");
}
function clickElement(el, x, y)
function editHtml(c)
{
ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10)
}
function test_defaultTransactionSequence()
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract() {\r" +
" uint x = 69;\r" +
" uint y = 5;\r" +
" for (uint i = 0; i < y; ++i) {\r" +
" x += 42;\r" +
" z += x;\r" +
" }\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3);
}
function test_transactionWithParameter()
{
newProject();
editContract(
"contract Contract {\r" +
" function setZ(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
transactionDialog.selectFunction("setZ");
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
mainApplication.projectModel.openDocument("index.html");
wait(1);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)");
mainApplication.mainContent.codeEditor.getEditor("index.html").setText(c);
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
}
function test_constructorParameters()
function clickElement(el, x, y)
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
wait(1);
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("not run");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 4);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "returned", "(442)");
if (el.contentItem)
el = el.contentItem;
ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10)
}
Application
{
id: mainApplication
}
function test_tutorial() { TestTutorial.test_tutorial(); }
function test_dbg_defaultTransactionSequence() { TestDebugger.test_defaultTransactionSequence(); }
function test_dbg_transactionWithParameter() { TestDebugger.test_transactionWithParameter(); }
function test_dbg_constructorParameters() { TestDebugger.test_constructorParameters(); }
function test_dbg_arrayParametersAndStorage() { TestDebugger.test_arrayParametersAndStorage(); }
}

140
mix/test/qml/js/TestDebugger.js

@ -0,0 +1,140 @@
function test_defaultTransactionSequence()
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract() {\r" +
" uint x = 69;\r" +
" uint y = 5;\r" +
" for (uint i = 0; i < y; ++i) {\r" +
" x += 42;\r" +
" z += x;\r" +
" }\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("Error running transaction");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3);
}
function test_transactionWithParameter()
{
newProject();
editContract(
"contract Contract {\r" +
" function setZ(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("setZ");
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("Error running transaction");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)");
}
function test_constructorParameters()
{
newProject();
editContract(
"contract Contract {\r" +
" function Contract(uint256 x) {\r" +
" z = x;\r" +
" }\r" +
" function getZ() returns(uint256) {\r" +
" return z;\r" +
" }\r" +
" uint z;\r" +
"}\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
clickElement(transactionDialog, 140, 300);
ts.typeString("442", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("getZ");
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("Error running transaction");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 4);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "returned", "(442)");
}
function test_arrayParametersAndStorage()
{
newProject();
editContract(
" contract ArrayTest {\r" +
" function setM(uint256[] x) external\r" +
" {\r" +
" m = x;\r" +
" s = 5;\r" +
" }\r" +
" \r" +
" function setMV(uint72[5] x) external\r" +
" {\r" +
" mv = x;\r" +
" s = 42;\r" +
" }\r" +
" \r" +
" uint256[] m;\r" +
" uint72[5] mv;\r" +
" uint256 s;\r" +
" }\r");
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("setM");
clickElement(transactionDialog, 140, 300);
ts.typeString("4,5,6,2,10", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.model.addTransaction();
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("setMV");
clickElement(transactionDialog, 140, 300);
ts.typeString("13,35,1,4", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000))
fail("Error running transaction");
//debug setM
mainApplication.clientModel.debugRecord(3);
mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue;
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "m", ["4","5","6","2","10"]);
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "s", "5");
//debug setMV
mainApplication.clientModel.debugRecord(4);
mainApplication.mainContent.rightPane.debugSlider.value = mainApplication.mainContent.rightPane.debugSlider.maximumValue - 1;
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "mv", ["13","35","1","4","0"]);
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "s", "42");
tryCompare(mainApplication.mainContent.rightPane.solCallStack.listModel, 0, "setMV");
}

71
mix/test/qml/js/TestTutorial.js

@ -0,0 +1,71 @@
//Test case to cover Mix tutorial
function test_tutorial()
{
newProject();
editContract(
"contract Rating {\r" +
" function setRating(bytes32 _key, uint256 _value) {\r" +
" ratings[_key] = _value;\r" +
" }\r" +
" mapping (bytes32 => uint256) public ratings;\r" +
"}\r"
);
editHtml(
"<!doctype>\r" +
"<html>\r" +
"<head>\r" +
"<script type='text/javascript'>\r" +
"function getRating() {\r" +
" var param = document.getElementById('query').value;\r" +
" var res = contracts['Rating'].contract.ratings(param);\r" +
" document.getElementById('queryres').innerText = res;\r" +
"}\r" +
"function setRating() {\r" +
" var key = document.getElementById('key').value;\r" +
" var value = parseInt(document.getElementById('value').value);\r" +
" var res = contracts['Rating'].contract.setRating(key, value);\r" +
"}\r" +
"</script>\r" +
"</head>\r" +
"<body bgcolor='#E6E6FA'>\r" +
" <h1>Ratings</h1>\r" +
" <div>\r" +
" Store:\r" +
" <input type='string' id='key'>\r" +
" <input type='number' id='value'>\r" +
" <button onclick='setRating()'>Save</button>\r" +
" </div>\r" +
" <div>\r" +
" Query:\r" +
" <input type='string' id='query' onkeyup='getRating()'>\r" +
" <div id='queryres'></div>\r" +
" </div>\r" +
"</body>\r" +
"</html>\r"
);
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.addTransaction();
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
ts.waitForRendering(transactionDialog, 3000);
transactionDialog.selectFunction("setRating");
clickElement(transactionDialog, 200, 310);
ts.typeString("Titanic", transactionDialog);
clickElement(transactionDialog, 200, 330);
ts.typeString("2", transactionDialog);
transactionDialog.acceptAndClose();
mainApplication.projectModel.stateDialog.acceptAndClose();
mainApplication.mainContent.startQuickDebugging();
if (!ts.waitForSignal(mainApplication.clientModel, "debugDataReady(QObject*)", 5000))
fail("Error running transaction");
wait(1);
clickElement(mainApplication.mainContent.webView.webView, 1, 1);
ts.typeString("\t\t\t\t");
ts.typeString("Titanic");
tryCompare(mainApplication.mainContent.rightPane.transactionLog.callModel, "count", 8); //wait for 8 calls
mainApplication.mainContent.webView.getContent();
ts.waitForSignal(mainApplication.mainContent.webView, "webContentReady()", 5000);
var body = mainApplication.mainContent.webView.webContent;
verify(body.indexOf("<div id=\"queryres\">2</div>") != -1, "Web content not updated")
}

13
neth/main.cpp

@ -40,6 +40,7 @@
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include <libethcore/Ethasher.h>
#include "BuildInfo.h"
#undef KEY_EVENT // from windows.h
@ -76,6 +77,7 @@ void help()
<< " -c,--client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/Ethereum)." << endl
<< " -D,--initdag Initialize DAG for mining and exit." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl
<< " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl
<< " -h,--help Show this help message and exit." << endl
@ -321,6 +323,7 @@ enum class NodeMode
int main(int argc, char** argv)
{
bool initDAG = true;
string listenIP;
unsigned short listenPort = 30303;
string publicIP;
@ -424,6 +427,8 @@ int main(int argc, char** argv)
structuredLogging = true;
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if (arg == "-D" || arg == "--initdag")
initDAG = true;
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
@ -556,6 +561,14 @@ int main(int argc, char** argv)
&nodesState,
miners
);
if (initDAG)
{
cout << "Initialize DAG. (This will take awhile)" << endl;
Ethasher::get()->full(web3.ethereum()->blockChain().info());
return 0;
}
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;

168
test/SolidityOptimizer.cpp

@ -56,8 +56,16 @@ public:
m_nonOptimizedContract = m_contractAddress;
m_optimize = true;
bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
size_t nonOptimizedSize = 0;
eth::eachInstruction(nonOptimizedBytecode, [&](Instruction, u256 const&) {
nonOptimizedSize++;
});
size_t optimizedSize = 0;
eth::eachInstruction(optimizedBytecode, [&](Instruction, u256 const&) {
optimizedSize++;
});
BOOST_CHECK_MESSAGE(
nonOptimizedBytecode.size() > optimizedBytecode.size(),
nonOptimizedSize > optimizedSize,
"Optimizer did not reduce bytecode size."
);
m_optimizedContract = m_contractAddress;
@ -75,11 +83,16 @@ public:
"\nOptimized: " + toHex(optimizedOutput));
}
void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation)
AssemblyItems getCSE(AssemblyItems const& _input)
{
eth::CommonSubexpressionEliminator cse;
BOOST_REQUIRE(cse.feedItems(_input.begin(), _input.end()) == _input.end());
AssemblyItems output = cse.getOptimizedItems();
return cse.getOptimizedItems();
}
void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation)
{
AssemblyItems output = getCSE(_input);
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
@ -569,6 +582,155 @@ BOOST_AUTO_TEST_CASE(cse_jumpi_jump)
});
}
BOOST_AUTO_TEST_CASE(cse_empty_sha3)
{
AssemblyItems input{
u256(0),
Instruction::DUP2,
Instruction::SHA3
};
checkCSE(input, {
u256(sha3(bytesConstRef()))
});
}
BOOST_AUTO_TEST_CASE(cse_partial_sha3)
{
AssemblyItems input{
u256(0xabcd) << (256 - 16),
u256(0),
Instruction::MSTORE,
u256(2),
u256(0),
Instruction::SHA3
};
checkCSE(input, {
u256(0xabcd) << (256 - 16),
u256(0),
Instruction::MSTORE,
u256(sha3(bytes{0xab, 0xcd}))
});
}
BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_location)
{
// sha3 twice from same dynamic location
AssemblyItems input{
Instruction::DUP2,
Instruction::DUP1,
Instruction::MSTORE,
u256(64),
Instruction::DUP2,
Instruction::SHA3,
u256(64),
Instruction::DUP3,
Instruction::SHA3
};
checkCSE(input, {
Instruction::DUP2,
Instruction::DUP1,
Instruction::MSTORE,
u256(64),
Instruction::DUP2,
Instruction::SHA3,
Instruction::DUP1
});
}
BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content)
{
// sha3 twice from different dynamic location but with same content
AssemblyItems input{
Instruction::DUP1,
u256(0x80),
Instruction::MSTORE, // m[128] = DUP1
u256(0x20),
u256(0x80),
Instruction::SHA3, // sha3(m[128..(128+32)])
Instruction::DUP2,
u256(12),
Instruction::MSTORE, // m[12] = DUP1
u256(0x20),
u256(12),
Instruction::SHA3 // sha3(m[12..(12+32)])
};
checkCSE(input, {
u256(0x80),
Instruction::DUP2,
Instruction::DUP2,
Instruction::MSTORE,
u256(0x20),
Instruction::SWAP1,
Instruction::SHA3,
u256(12),
Instruction::DUP3,
Instruction::SWAP1,
Instruction::MSTORE,
Instruction::DUP1
});
}
BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_dynamic_store_in_between)
{
// sha3 twice from different dynamic location but with same content,
// dynamic mstore in between, which forces us to re-calculate the sha3
AssemblyItems input{
u256(0x80),
Instruction::DUP2,
Instruction::DUP2,
Instruction::MSTORE, // m[128] = DUP1
u256(0x20),
Instruction::DUP1,
Instruction::DUP3,
Instruction::SHA3, // sha3(m[128..(128+32)])
u256(12),
Instruction::DUP5,
Instruction::DUP2,
Instruction::MSTORE, // m[12] = DUP1
Instruction::DUP12,
Instruction::DUP14,
Instruction::MSTORE, // destroys memory knowledge
Instruction::SWAP2,
Instruction::SWAP1,
Instruction::SWAP2,
Instruction::SHA3 // sha3(m[12..(12+32)])
};
checkCSE(input, input);
}
BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between)
{
// sha3 twice from different dynamic location but with same content,
// dynamic mstore in between, but does not force us to re-calculate the sha3
AssemblyItems input{
u256(0x80),
Instruction::DUP2,
Instruction::DUP2,
Instruction::MSTORE, // m[128] = DUP1
u256(0x20),
Instruction::DUP1,
Instruction::DUP3,
Instruction::SHA3, // sha3(m[128..(128+32)])
u256(12),
Instruction::DUP5,
Instruction::DUP2,
Instruction::MSTORE, // m[12] = DUP1
Instruction::DUP12,
u256(12 + 32),
Instruction::MSTORE, // does not destoy memory knowledge
Instruction::DUP13,
u256(128 - 32),
Instruction::MSTORE, // does not destoy memory knowledge
u256(0x20),
u256(12),
Instruction::SHA3 // sha3(m[12..(12+32)])
};
// if this changes too often, only count the number of SHA3 and MSTORE instructions
AssemblyItems output = getCSE(input);
BOOST_CHECK_EQUAL(4, count(output.begin(), output.end(), AssemblyItem(Instruction::MSTORE)));
BOOST_CHECK_EQUAL(1, count(output.begin(), output.end(), AssemblyItem(Instruction::SHA3)));
}
BOOST_AUTO_TEST_SUITE_END()
}

2
test/peer.cpp

@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(save_nodes)
h->setIdealPeerCount(10);
// starting host is required so listenport is available
h->start();
while (!h->isStarted())
while (!h->haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2));
hosts.push_back(h);
}

Loading…
Cancel
Save