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. 12
      libp2p/Host.cpp
  14. 7
      libp2p/Host.h
  15. 1
      libsolidity/CMakeLists.txt
  16. 2
      libwebthree/WebThree.cpp
  17. 2
      libwebthree/WebThree.h
  18. 120
      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. 22
      mix/qml/DeploymentDialog.qml
  34. 53
      mix/qml/LogsPane.qml
  35. 10
      mix/qml/NewProjectDialog.qml
  36. 34
      mix/qml/StateDialog.qml
  37. 32
      mix/qml/StateList.qml
  38. 28
      mix/qml/StatusPane.qml
  39. 1
      mix/qml/StepActionImage.qml
  40. 9
      mix/qml/StructView.qml
  41. 29
      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 <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h> #include <jsonrpccpp/server/connectors/httpserver.h>
#endif #endif
#include <libethcore/Ethasher.h>
#include "BuildInfo.h" #include "BuildInfo.h"
using namespace std; using namespace std;
using namespace dev; 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 << " -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 << " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/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 << " -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 << " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl
<< " -h,--help Show this help message and exit." << endl << " -h,--help Show this help message and exit." << endl
@ -200,6 +202,7 @@ enum class NodeMode
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
bool initDAG = false;
string listenIP; string listenIP;
unsigned short listenPort = 30303; unsigned short listenPort = 30303;
string publicIP; string publicIP;
@ -304,6 +307,8 @@ int main(int argc, char** argv)
structuredLogging = true; structuredLogging = true;
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i]; dbPath = argv[++i];
else if (arg == "-D" || arg == "--initdag")
initDAG = true;
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{ {
try try
@ -436,6 +441,14 @@ int main(int argc, char** argv)
&nodesState, &nodesState,
miners miners
); );
if (initDAG)
{
cout << "Initializing DAG. (This will take awhile)" << endl;
Ethasher::get()->full(web3.ethereum()->blockChain().info());
return 0;
}
web3.setIdealPeerCount(peers); web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); 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; 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_sloadGas = 50;
u256 const c_sstoreSetGas = 20000; u256 const c_sstoreSetGas = 20000;
u256 const c_sstoreResetGas = 5000; u256 const c_sstoreResetGas = 5000;
u256 const c_sstoreClearGas = 5000;
u256 const c_sstoreRefundGas = 15000; u256 const c_sstoreRefundGas = 15000;
u256 const c_jumpdestGas = 1; u256 const c_jumpdestGas = 1;
u256 const c_logGas = 375; 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_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_sloadGas; ///< Once per SLOAD operation.
extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero. 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_sstoreResetGas; ///< Once per SSTORE operation if the zeroness does not change from zero. NOTE: when c_sstoreSetGas does not apply.
extern u256 const c_sstoreClearGas; ///< Once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero. 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_jumpdestGas; ///< Once per JUMPDEST operation.
extern u256 const c_logGas; ///< Per LOG* 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; runGas = c_sstoreSetGas;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
{ {
runGas = c_sstoreClearGas; runGas = c_sstoreResetGas;
_ext.sub.refunds += c_sstoreRefundGas; _ext.sub.refunds += c_sstoreRefundGas;
} }
else else

1
libevmcore/CMakeLists.txt

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

142
libevmcore/CommonSubexpressionEliminator.cpp

@ -23,6 +23,7 @@
#include <functional> #include <functional>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <libdevcrypto/SHA3.h>
#include <libevmcore/CommonSubexpressionEliminator.h> #include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/AssemblyItem.h> #include <libevmcore/AssemblyItem.h>
@ -34,8 +35,8 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{ {
optimizeBreakingItem(); optimizeBreakingItem();
map<int, ExpressionClasses::Id> initialStackContents; map<int, Id> initialStackContents;
map<int, ExpressionClasses::Id> targetStackContents; map<int, Id> targetStackContents;
int minHeight = m_stackHeight + 1; int minHeight = m_stackHeight + 1;
if (!m_stackElements.empty()) if (!m_stackElements.empty())
minHeight = min(minHeight, m_stackElements.begin()->first); minHeight = min(minHeight, m_stackElements.begin()->first);
@ -58,18 +59,18 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
ostream& CommonSubexpressionEliminator::stream( ostream& CommonSubexpressionEliminator::stream(
ostream& _out, ostream& _out,
map<int, ExpressionClasses::Id> _initialStack, map<int, Id> _initialStack,
map<int, ExpressionClasses::Id> _targetStack map<int, Id> _targetStack
) const ) const
{ {
auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id) auto streamExpressionClass = [this](ostream& _out, Id _id)
{ {
auto const& expr = m_expressionClasses.representative(_id); auto const& expr = m_expressionClasses.representative(_id);
_out << " " << dec << _id << ": " << *expr.item; _out << " " << dec << _id << ": " << *expr.item;
if (expr.sequenceNumber) if (expr.sequenceNumber)
_out << "@" << dec << expr.sequenceNumber; _out << "@" << dec << expr.sequenceNumber;
_out << "("; _out << "(";
for (ExpressionClasses::Id arg: expr.arguments) for (Id arg: expr.arguments)
_out << dec << arg << ","; _out << dec << arg << ",";
_out << ")" << endl; _out << ")" << endl;
}; };
@ -77,7 +78,7 @@ ostream& CommonSubexpressionEliminator::stream(
_out << "Optimizer analysis:" << endl; _out << "Optimizer analysis:" << endl;
_out << "Final stack height: " << dec << m_stackHeight << endl; _out << "Final stack height: " << dec << m_stackHeight << endl;
_out << "Equivalence classes: " << 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); streamExpressionClass(_out, eqClass);
_out << "Initial stack: " << endl; _out << "Initial stack: " << endl;
@ -119,7 +120,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
); );
else if (instruction != Instruction::POP) else if (instruction != Instruction::POP)
{ {
vector<ExpressionClasses::Id> arguments(info.args); vector<Id> arguments(info.args);
for (int i = 0; i < info.args; ++i) for (int i = 0; i < info.args; ++i)
arguments[i] = stackElement(m_stackHeight - i); arguments[i] = stackElement(m_stackHeight - i);
if (_item.instruction() == Instruction::SSTORE) if (_item.instruction() == Instruction::SSTORE)
@ -130,6 +131,8 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
storeInMemory(arguments[0], arguments[1]); storeInMemory(arguments[0], arguments[1]);
else if (_item.instruction() == Instruction::MLOAD) else if (_item.instruction() == Instruction::MLOAD)
setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0])); 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 else
setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem)); 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)) if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI))
return; return;
using Id = ExpressionClasses::Id;
static AssemblyItem s_jump = Instruction::JUMP; static AssemblyItem s_jump = Instruction::JUMP;
Id condition = stackElement(m_stackHeight - 1); 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; m_stackElements[_stackHeight] = _class;
} }
@ -194,26 +196,28 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); 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) if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value)
// do not execute the storage if we know that the value is already there // do not execute the storage if we know that the value is already there
return; return;
m_sequenceNumber++; m_sequenceNumber++;
decltype(m_storageContent) storageContents; 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) 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); storageContents.insert(storageItem);
m_storageContent = move(storageContents); 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_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id));
m_storageContent[_slot] = _value; m_storageContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes // increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++; m_sequenceNumber++;
} }
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot) ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(Id _slot)
{ {
if (m_storageContent.count(_slot)) if (m_storageContent.count(_slot))
return m_storageContent.at(_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); 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) if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value)
// do not execute the store if we know that the value is already there // 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)) if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot))
memoryContents.insert(memoryItem); memoryContents.insert(memoryItem);
m_memoryContent = move(memoryContents); 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_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
m_memoryContent[_slot] = _value; m_memoryContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes // increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++; m_sequenceNumber++;
} }
ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot) ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(Id _slot)
{ {
if (m_memoryContent.count(_slot)) if (m_memoryContent.count(_slot))
return m_memoryContent.at(_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); 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( CSECodeGenerator::CSECodeGenerator(
ExpressionClasses& _expressionClasses, ExpressionClasses& _expressionClasses,
vector<CSECodeGenerator::StoreOperation> const& _storeOperations vector<CSECodeGenerator::StoreOperation> const& _storeOperations
@ -259,8 +294,8 @@ CSECodeGenerator::CSECodeGenerator(
} }
AssemblyItems CSECodeGenerator::generateCode( AssemblyItems CSECodeGenerator::generateCode(
map<int, ExpressionClasses::Id> const& _initialStack, map<int, Id> const& _initialStack,
map<int, ExpressionClasses::Id> const& _targetStackContents map<int, Id> const& _targetStackContents
) )
{ {
m_stack = _initialStack; m_stack = _initialStack;
@ -280,7 +315,7 @@ AssemblyItems CSECodeGenerator::generateCode(
} }
// store all needed sequenced expressions // store all needed sequenced expressions
set<pair<unsigned, ExpressionClasses::Id>> sequencedExpressions; set<pair<unsigned, Id>> sequencedExpressions;
for (auto const& p: m_neededBy) for (auto const& p: m_neededBy)
for (auto id: {p.first, p.second}) for (auto id: {p.first, p.second})
if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber)
@ -327,19 +362,20 @@ AssemblyItems CSECodeGenerator::generateCode(
return m_generatedItems; return m_generatedItems;
} }
void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) void CSECodeGenerator::addDependencies(Id _c)
{ {
if (m_neededBy.count(_c)) if (m_neededBy.count(_c))
return; // we already computed the dependencies for _c return; // we already computed the dependencies for _c
ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); ExpressionClasses::Expression expr = m_expressionClasses.representative(_c);
for (ExpressionClasses::Id argument: expr.arguments) for (Id argument: expr.arguments)
{ {
addDependencies(argument); addDependencies(argument);
m_neededBy.insert(make_pair(argument, _c)); m_neededBy.insert(make_pair(argument, _c));
} }
if (expr.item->type() == Operation && ( if (expr.item->type() == Operation && (
expr.item->instruction() == Instruction::SLOAD || 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 // 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 // they are different that occur before this load
StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ? StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ?
StoreOperation::Storage : StoreOperation::Memory; StoreOperation::Storage : StoreOperation::Memory;
ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0); Id slotToLoadFrom = expr.arguments.at(0);
for (auto const& p: m_storeOperations) for (auto const& p: m_storeOperations)
{ {
if (p.first.first != target) if (p.first.first != target)
continue; continue;
ExpressionClasses::Id slot = p.first.second; Id slot = p.first.second;
StoreOperations const& storeOps = p.second; StoreOperations const& storeOps = p.second;
if (storeOps.front().sequenceNumber > expr.sequenceNumber) if (storeOps.front().sequenceNumber > expr.sequenceNumber)
continue; continue;
if ( bool knownToBeIndependent = false;
(target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) || switch (expr.item->instruction())
(target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom)) {
) 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; continue;
// note that store and load never have the same sequence number // 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) for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it)
if (it->sequenceNumber < expr.sequenceNumber) if (it->sequenceNumber < expr.sequenceNumber)
latestStore = it->expression; 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 // do some cleanup
removeStackTopIfPossible(); removeStackTopIfPossible();
@ -392,8 +458,8 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow
OptimizerException, OptimizerException,
"Sequence constrained operation requested out of sequence." "Sequence constrained operation requested out of sequence."
); );
ExpressionClasses::Ids const& arguments = expr.arguments; vector<Id> const& arguments = expr.arguments;
for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments)) for (Id arg: boost::adaptors::reverse(arguments))
generateClassElement(arg); generateClassElement(arg);
// The arguments are somewhere on the stack now, so it remains to move them at the correct place. // 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( assertThrow(
m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition, 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); 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 // 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. // 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()) if (m_stack.empty())
return false; return false;
assertThrow(m_stack.count(m_stackHeight), OptimizerException, ""); assertThrow(m_stack.count(m_stackHeight) > 0, OptimizerException, "");
ExpressionClasses::Id top = m_stack[m_stackHeight]; Id top = m_stack[m_stackHeight];
if (!canBeRemoved(top)) if (!canBeRemoved(top))
return false; return false;
m_generatedItems.push_back(AssemblyItem(Instruction::POP)); m_generatedItems.push_back(AssemblyItem(Instruction::POP));

61
libevmcore/CommonSubexpressionEliminator.h

@ -57,19 +57,20 @@ using AssemblyItems = std::vector<AssemblyItem>;
class CommonSubexpressionEliminator class CommonSubexpressionEliminator
{ {
public: public:
using Id = ExpressionClasses::Id;
struct StoreOperation struct StoreOperation
{ {
enum Target { Memory, Storage }; enum Target { Memory, Storage };
StoreOperation( StoreOperation(
Target _target, Target _target,
ExpressionClasses::Id _slot, Id _slot,
unsigned _sequenceNumber, unsigned _sequenceNumber,
ExpressionClasses::Id _expression Id _expression
): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {} ): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {}
Target target; Target target;
ExpressionClasses::Id slot; Id slot;
unsigned sequenceNumber; unsigned sequenceNumber;
ExpressionClasses::Id expression; Id expression;
}; };
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
@ -83,8 +84,8 @@ public:
/// Streams debugging information to @a _out. /// Streams debugging information to @a _out.
std::ostream& stream( std::ostream& stream(
std::ostream& _out, std::ostream& _out,
std::map<int, ExpressionClasses::Id> _initialStack = std::map<int, ExpressionClasses::Id>(), std::map<int, Id> _initialStack = std::map<int, Id>(),
std::map<int, ExpressionClasses::Id> _targetStack = std::map<int, ExpressionClasses::Id>() std::map<int, Id> _targetStack = std::map<int, Id>()
) const; ) const;
private: private:
@ -96,39 +97,42 @@ private:
/// Simplifies the given item using /// Simplifies the given item using
/// Assigns a new equivalence class to the next sequence number of the given stack element. /// 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. /// Swaps the given stack elements in their next sequence number.
void swapStackElements(int _stackHeightA, int _stackHeightB); void swapStackElements(int _stackHeightA, int _stackHeightB);
/// Retrieves the current equivalence class fo the given stack element (or generates a new /// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet). /// 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 /// @returns the equivalence class id of the special initial stack element at the given height
/// (must not be positive). /// (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 /// Increments the sequence number, deletes all storage information that might be overwritten
/// and stores the new value at the given slot. /// 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. /// 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 /// Increments the sequence number, deletes all memory information that might be overwritten
/// and stores the new value at the given slot. /// 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. /// 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. /// Current stack height, can be negative.
int m_stackHeight = 0; int m_stackHeight = 0;
/// Current stack layout, mapping stack height -> equivalence class /// 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. /// Current sequence number, this is incremented with each modification to storage or memory.
unsigned m_sequenceNumber = 1; unsigned m_sequenceNumber = 1;
/// Knowledge about storage content. /// 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 /// Knowledge about memory content. Keys are memory addresses, note that the values overlap
/// and are not contained here if they are not completely known. /// 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 /// Keeps information about which storage or memory slots were written to at which sequence
/// number with what instruction. /// number with what instruction.
std::vector<StoreOperation> m_storeOperations; std::vector<StoreOperation> m_storeOperations;
@ -149,6 +153,7 @@ class CSECodeGenerator
public: public:
using StoreOperation = CommonSubexpressionEliminator::StoreOperation; using StoreOperation = CommonSubexpressionEliminator::StoreOperation;
using StoreOperations = std::vector<StoreOperation>; using StoreOperations = std::vector<StoreOperation>;
using Id = ExpressionClasses::Id;
/// Initializes the code generator with the given classes and store operations. /// Initializes the code generator with the given classes and store operations.
/// The store operations have to be sorted by sequence number in ascending order. /// 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 /// @param _targetStackContents final contents of the stack, by stack height relative to initial
/// @note should only be called once on each object. /// @note should only be called once on each object.
AssemblyItems generateCode( AssemblyItems generateCode(
std::map<int, ExpressionClasses::Id> const& _initialStack, std::map<int, Id> const& _initialStack,
std::map<int, ExpressionClasses::Id> const& _targetStackContents std::map<int, Id> const& _targetStackContents
); );
private: private:
/// Recursively discovers all dependencies to @a m_requests. /// 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. /// 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 /// @returns the stack position of the element or c_invalidPosition if it does not actually
/// generate a value on the stack. /// generate a value on the stack.
/// @param _allowSequenced indicates that sequence-constrained operations are allowed /// @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. /// @returns the position of the representative of the given id on the stack.
/// @note throws an exception if it is not 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. /// @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. /// Appends code to remove the topmost stack element if it can be removed.
bool removeStackTopIfPossible(); bool removeStackTopIfPossible();
@ -196,19 +201,19 @@ private:
/// Current height of the stack relative to the start. /// Current height of the stack relative to the start.
int m_stackHeight = 0; int m_stackHeight = 0;
/// If (b, a) is in m_requests then b is needed to compute a. /// 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. /// 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. /// 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. /// The actual eqivalence class items and how to compute them.
ExpressionClasses& m_expressionClasses; ExpressionClasses& m_expressionClasses;
/// Keeps information about which storage or memory slots were written to by which operations. /// Keeps information about which storage or memory slots were written to by which operations.
/// The operations are sorted ascendingly by sequence number. /// 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. /// 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> template <class _AssemblyItemIterator>

31
libevmcore/ExpressionClasses.cpp

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

11
libevmcore/ExpressionClasses.h

@ -78,6 +78,15 @@ public:
bool knownToBeDifferent(Id _a, Id _b); bool knownToBeDifferent(Id _a, Id _b);
/// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32. /// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32.
bool knownToBeDifferentBy32(Id _a, Id _b); 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; 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. /// @returns the id of the matched expression if this pattern is part of a match group.
Id id() const { return matchGroupValue().id; } Id id() const { return matchGroupValue().id; }
/// @returns the data of the matched expression if this pattern is part of a match group. /// @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; std::string toString() const;

42
libevmcore/Instruction.cpp

@ -21,6 +21,7 @@
#include "Instruction.h" #include "Instruction.h"
#include <functional>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/Log.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 } } { 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) for (auto it = _mem.begin(); it != _mem.end(); ++it)
{ {
byte n = *it; Instruction instr = Instruction(*it);
auto iit = c_instructionInfo.find((Instruction)n); size_t additional = 0;
if (numerics || iit == c_instructionInfo.end() || (byte)iit->first != n) // not an instruction or expecting an argument... if (isValidInstruction(instr))
additional = instructionInfo(instr).additional;
u256 data;
for (size_t i = 0; i < additional; ++i)
{ {
if (numerics) data <<= 8;
numerics--; if (it != _mem.end() && ++it != _mem.end())
ret << "0x" << hex << (int)n << " "; 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 else
{ {
auto const& ii = iit->second; InstructionInfo info = instructionInfo(_instr);
ret << ii.name << " "; ret << info.name << " ";
numerics = ii.additional; if (info.additional)
} ret << "0x" << hex << _data << " ";
} }
});
return ret.str(); return ret.str();
} }

4
libevmcore/Instruction.h

@ -21,6 +21,7 @@
#pragma once #pragma once
#include <functional>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Assertions.h> #include <libdevcore/Assertions.h>
#include <libevmcore/Exceptions.h> #include <libevmcore/Exceptions.h>
@ -253,6 +254,9 @@ bool isValidInstruction(Instruction _inst);
/// Convert from string mnemonic to Instruction type. /// Convert from string mnemonic to Instruction type.
extern const std::map<std::string, Instruction> c_instructions; 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. /// Convert from EVM code to simple EVM assembly language.
std::string disassemble(bytes const& _mem); 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 return true; // GAS and PC assume a specific order of opcodes
if (_item.instruction() == Instruction::MSIZE) if (_item.instruction() == Instruction::MSIZE)
return true; // msize is modified already by memory access, avoid that for now 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()); InstructionInfo info = instructionInfo(_item.instruction());
if (_item.instruction() == Instruction::SSTORE) if (_item.instruction() == Instruction::SSTORE)
return false; return false;

12
libp2p/Host.cpp

@ -383,10 +383,11 @@ string Host::pocHost()
void Host::addNode(NodeId const& _node, bi::address const& _addr, unsigned short _udpNodePort, unsigned short _tcpPeerPort) 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) // return if network is stopped while waiting on Host::run() or nodeTable to start
while (isWorking() && !m_run) while (!haveNetwork())
if (isWorking())
this_thread::sleep_for(chrono::milliseconds(50)); this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run) else
return; return;
if (_tcpPeerPort < 30300 || _tcpPeerPort > 30305) if (_tcpPeerPort < 30300 || _tcpPeerPort > 30305)
@ -636,8 +637,9 @@ void Host::startedWorking()
else else
clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "TCP Listen port is invalid or unavailable."; 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())); shared_ptr<NodeTable> nodeTable(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort()));
m_nodeTable->setEventHandler(new HostNodeTableHandler(*this)); nodeTable->setEventHandler(new HostNodeTableHandler(*this));
m_nodeTable = nodeTable;
restoreNetwork(&m_restoreNetwork); restoreNetwork(&m_restoreNetwork);
clog(NetNote) << "p2p.started id:" << id().abridged(); clog(NetNote) << "p2p.started id:" << id().abridged();

7
libp2p/Host.h

@ -141,8 +141,11 @@ public:
/// Resets acceptor, socket, and IO service. Called by deallocator. /// Resets acceptor, socket, and IO service. Called by deallocator.
void stop(); void stop();
/// @returns if network is running. /// @returns if network has been started.
bool isStarted() const { return m_run; } 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(); } NodeId id() const { return m_alias.pub(); }

1
libsolidity/CMakeLists.txt

@ -27,7 +27,6 @@ endif()
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} devcrypto)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) 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) void WebThreeDirect::setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers)
{ {
auto had = haveNetwork(); auto had = isNetworkStarted();
if (had) if (had)
stopNetwork(); stopNetwork();
m_net.setNetworkPreferences(_n, _dropPeers); m_net.setNetworkPreferences(_n, _dropPeers);

2
libwebthree/WebThree.h

@ -163,7 +163,7 @@ public:
/// Sets the ideal number of peers. /// Sets the ideal number of peers.
void setIdealPeerCount(size_t _n) override; 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; void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers = false) override;

120
mix/ClientModel.cpp

@ -43,6 +43,7 @@
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace std;
namespace dev namespace dev
{ {
@ -54,7 +55,7 @@ class RpcConnector: public jsonrpc::AbstractServerConnector
public: public:
virtual bool StartListening() override { return true; } virtual bool StartListening() override { return true; }
virtual bool StopListening() 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); m_response = QString::fromStdString(_response);
return true; return true;
@ -102,7 +103,7 @@ QString ClientModel::apiCall(QString const& _message)
} }
catch (...) catch (...)
{ {
std::cerr << boost::current_exception_diagnostic_information(); cerr << boost::current_exception_diagnostic_information();
return QString(); return QString();
} }
} }
@ -126,7 +127,7 @@ void ClientModel::mine()
catch (...) catch (...)
{ {
m_mining = false; 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 runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
} }
emit miningStateChanged(); emit miningStateChanged();
@ -149,7 +150,7 @@ QVariantMap ClientModel::contractAddresses() const
{ {
QVariantMap res; QVariantMap res;
for (auto const& c: m_contractAddresses) 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; return res;
} }
@ -158,14 +159,14 @@ void ClientModel::setupState(QVariantMap _state)
QVariantList balances = _state.value("accounts").toList(); QVariantList balances = _state.value("accounts").toList();
QVariantList transactions = _state.value("transactions").toList(); QVariantList transactions = _state.value("transactions").toList();
std::map<Secret, u256> accounts; map<Secret, u256> accounts;
for (auto const& b: balances) for (auto const& b: balances)
{ {
QVariantMap address = b.toMap(); 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) for (auto const& t: transactions)
{ {
QVariantMap transaction = t.toMap(); QVariantMap transaction = t.toMap();
@ -203,7 +204,7 @@ void ClientModel::setupState(QVariantMap _state)
executeSequence(transactionSequence, accounts); 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) if (m_running)
BOOST_THROW_EXCEPTION(ExecutionStateException()); BOOST_THROW_EXCEPTION(ExecutionStateException());
@ -225,7 +226,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
if (!transaction.stdContractUrl.isEmpty()) if (!transaction.stdContractUrl.isEmpty())
{ {
//std contract //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; TransactionSettings stdTransaction = transaction;
stdTransaction.gas = 500000;// TODO: get this from std contracts library stdTransaction.gas = 500000;// TODO: get this from std contracts library
Address address = deployContract(stdContractCode, stdTransaction); 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); CompiledContract const& compilerRes = m_codeModel->contract(transaction.contractId);
QFunctionDefinition const* f = nullptr; QFunctionDefinition const* f = nullptr;
bytes contractCode = compilerRes.bytes(); bytes contractCode = compilerRes.bytes();
std::shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract(); shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
if (transaction.functionId.isEmpty()) if (transaction.functionId.isEmpty())
f = contractDef->constructor(); f = contractDef->constructor();
else else
@ -297,12 +298,12 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
} }
catch(boost::exception const&) 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())); 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()); emit runFailed(e.what());
} }
m_running = false; m_running = false;
@ -329,16 +330,16 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
{ {
QHash<int, int> codeMap; QHash<int, int> codeMap;
codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code, 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 //try to resolve contract for source level debugging
auto nameIter = m_contractNames.find(code.address); 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();
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems(); codes.back()->setDocument(compilerRes->documentId());
codes.back()->setDocument(compilerRes.documentId()); codeItems.push_back(move(assemblyItems));
codeItems.push_back(std::move(assemblyItems)); contracts.push_back(compilerRes);
contracts.push_back(&compilerRes);
} }
else else
{ {
@ -353,8 +354,8 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
QVariantList states; QVariantList states;
QVariantList solCallStack; QVariantList solCallStack;
std::map<int, QVariableDeclaration*> solLocals; //<stack pos, decl> map<int, QVariableDeclaration*> solLocals; //<stack pos, decl>
std::map<QString, QVariableDeclaration*> storageDeclarations; //<name, decl> map<QString, QVariableDeclaration*> storageDeclarations; //<name, decl>
unsigned prevInstructionIndex = 0; unsigned prevInstructionIndex = 0;
for (MachineState const& s: _t.machineStates) for (MachineState const& s: _t.machineStates)
@ -366,7 +367,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
CompiledContract const* contract = contracts[s.codeIndex]; CompiledContract const* contract = contracts[s.codeIndex];
AssemblyItem const& instruction = codeItems[s.codeIndex][instructionIndex]; 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 //register new local variable initialization
auto localIter = contract->locals().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end)); 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); 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 //track calls into functions
AssemblyItem const& prevInstruction = codeItems[s.codeIndex][prevInstructionIndex]; AssemblyItem const& prevInstruction = codeItems[s.codeIndex][prevInstructionIndex];
@ -404,41 +405,49 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
QVariantList storageDeclarationList; QVariantList storageDeclarationList;
QVariantMap storageValues; QVariantMap storageValues;
for (auto st: s.storage) for (auto st: s.storage)
if (st.first < std::numeric_limits<unsigned>::max()) if (st.first < numeric_limits<unsigned>::max())
{ {
auto storageIter = contract->storage().find(static_cast<unsigned>(st.first)); auto storageIter = contract->storage().find(static_cast<unsigned>(st.first));
if (storageIter != contract->storage().end()) if (storageIter != contract->storage().end())
{ {
QVariableDeclaration* storageDec = nullptr; QVariableDeclaration* storageDec = nullptr;
auto decIter = storageDeclarations.find(storageIter.value().name); for (SolidityDeclaration const& codeDec : storageIter.value())
{
if (codeDec.type.name.startsWith("mapping"))
continue; //mapping type not yet managed
auto decIter = storageDeclarations.find(codeDec.name);
if (decIter != storageDeclarations.end()) if (decIter != storageDeclarations.end())
storageDec = decIter->second; storageDec = decIter->second;
else else
{ {
storageDec = new QVariableDeclaration(debugData, storageIter.value().name.toStdString(), storageIter.value().type); storageDec = new QVariableDeclaration(debugData, codeDec.name.toStdString(), codeDec.type);
storageDeclarations[storageDec->name()] = storageDec; storageDeclarations[storageDec->name()] = storageDec;
} }
if (storageDec->type()->name().startsWith("mapping"))
break; //mapping type not yet managed
storageDeclarationList.push_back(QVariant::fromValue(storageDec)); storageDeclarationList.push_back(QVariant::fromValue(storageDec));
storageValues[storageDec->name()] = formatValue(storageDec->type()->type(), st.second); storageValues[storageDec->name()] = formatStorageValue(storageDec->type()->type(), s.storage, codeDec.offset, codeDec.slot);
}
} }
} }
storage["variables"] = storageDeclarationList; storage["variables"] = storageDeclarationList;
storage["values"] = storageValues; storage["values"] = storageValues;
prevInstructionIndex = instructionIndex; 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))); 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); debugDataReady(debugData);
} }
QVariant ClientModel::formatValue(SolidityType const& _type, dev::u256 const& _value) QVariant ClientModel::formatValue(SolidityType const& _type, u256 const& _value)
{ {
ContractCallDataEncoder decoder; ContractCallDataEncoder decoder;
bytes val = toBigEndian(_value); bytes val = toBigEndian(_value);
@ -446,6 +455,45 @@ QVariant ClientModel::formatValue(SolidityType const& _type, dev::u256 const& _v
return res; 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() void ClientModel::emptyRecord()
{ {
debugDataReady(new QDebugData()); debugDataReady(new QDebugData());
@ -471,9 +519,9 @@ void ClientModel::callContract(Address const& _contract, bytes const& _data, Tra
RecordLogEntry* ClientModel::lastBlock() const RecordLogEntry* ClientModel::lastBlock() const
{ {
eth::BlockInfo blockInfo = m_client->blockInfo(); eth::BlockInfo blockInfo = m_client->blockInfo();
std::stringstream strGas; stringstream strGas;
strGas << blockInfo.gasUsed; strGas << blockInfo.gasUsed;
std::stringstream strNumber; stringstream strNumber;
strNumber << blockInfo.number; strNumber << blockInfo.number;
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(toHex(blockInfo.hash().ref()))), tr("Gas Used: ") + QString::fromStdString(strGas.str()), QString(), QString(), false, RecordLogEntry::RecordType::Block); 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); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership);
@ -496,7 +544,7 @@ void ClientModel::onNewTransaction()
unsigned recordIndex = tr.executonIndex; unsigned recordIndex = tr.executonIndex;
QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex); QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex);
QString address = QString::fromStdString(toJS(tr.address)); QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(dev::toString(tr.value)); QString value = QString::fromStdString(toString(tr.value));
QString contract = address; QString contract = address;
QString function; QString function;
QString returned; QString returned;

1
mix/ClientModel.h

@ -204,6 +204,7 @@ private:
void onStateReset(); void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t); void showDebuggerForTransaction(ExecutionResult const& _t);
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); 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_running;
std::atomic<bool> m_mining; std::atomic<bool> m_mining;

61
mix/CodeModel.cpp

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

12
mix/CodeModel.h

@ -99,7 +99,7 @@ public:
QHash<LocationPair, QString> const& functions() const { return m_functions; } QHash<LocationPair, QString> const& functions() const { return m_functions; }
QHash<LocationPair, SolidityDeclaration> const& locals() const { return m_locals; } 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: private:
uint m_sourceHash; uint m_sourceHash;
@ -112,7 +112,7 @@ private:
eth::AssemblyItems m_constructorAssemblyItems; eth::AssemblyItems m_constructorAssemblyItems;
QHash<LocationPair, QString> m_functions; QHash<LocationPair, QString> m_functions;
QHash<LocationPair, SolidityDeclaration> m_locals; QHash<LocationPair, SolidityDeclaration> m_locals;
QHash<unsigned, SolidityDeclaration> m_storage; QHash<unsigned, SolidityDeclarations> m_storage;
friend class CodeModel; friend class CodeModel;
}; };
@ -141,10 +141,14 @@ public:
/// Get contract code by url. Contract is compiled on first access and cached /// Get contract code by url. Contract is compiled on first access and cached
dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url); dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url);
/// Get contract by name /// 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 /// Find a contract by document id
/// @returns CompiledContract object or null if not found /// @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 /// Reset code model
Q_INVOKABLE void reset() { reset(QVariantMap()); } Q_INVOKABLE void reset() { reset(QVariantMap()); }
/// Convert solidity type info to mix type /// 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) 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) if (_type.dynamicSize)
{ {
u256 count = 0;
if (_type.type == SolidityType::Type::Bytes) if (_type.type == SolidityType::Type::Bytes)
count = encodeSingleItem(_data, _type, m_dynamicData); count = encodeSingleItem(_data.toString(), _type, m_dynamicData);
else else
{ {
QVariantList list = qvariant_cast<QVariantList>(_data); count = strList.count();
for (auto const& item: list) for (auto const& item: strList)
encodeSingleItem(item, _type, m_dynamicData); encodeSingleItem(item, _type, m_dynamicData);
count = list.size();
} }
bytes sizeEnc(32); bytes sizeEnc(32);
toBigEndian(count, sizeEnc); toBigEndian(count, sizeEnc);
m_encodedData.insert(m_encodedData.end(), sizeEnc.begin(), sizeEnc.end()); m_encodedData.insert(m_encodedData.end(), sizeEnc.begin(), sizeEnc.end());
} }
else 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) if (_type.type == SolidityType::Type::Struct)
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Struct parameters are not supported yet")); BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Struct parameters are not supported yet"));
unsigned const alignSize = 32; unsigned const alignSize = 32;
QString src = _data.toString(); QString src = _data;
bytes result; bytes result;
if ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'"))) 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; 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()); _dest.insert(_dest.end(), result.begin(), result.end());
if ((_dest.size() - 4) % alignSize != 0)
_dest.resize((_dest.size() & ~(alignSize - 1)) + alignSize);
return dataSize; return dataSize;
} }

2
mix/ContractCallDataEncoder.h

@ -58,7 +58,7 @@ public:
dev::bytes decodeBytes(dev::bytes const& _rawValue); dev::bytes decodeBytes(dev::bytes const& _rawValue);
private: 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); bigint decodeInt(dev::bytes const& _rawValue);
dev::bytes encodeInt(QString const& _str); dev::bytes encodeInt(QString const& _str);
QString toString(dev::bigint const& _int); QString toString(dev::bigint const& _int);

14
mix/MixApplication.cpp

@ -20,6 +20,7 @@
*/ */
#include "MixApplication.h" #include "MixApplication.h"
#include <boost/exception/diagnostic_information.hpp>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QUrl> #include <QUrl>
#include <QIcon> #include <QIcon>
@ -101,3 +102,16 @@ void MixApplication::initialize()
MixApplication::~MixApplication() 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(); static void initialize();
virtual ~MixApplication(); virtual ~MixApplication();
QQmlApplicationEngine* engine() { return m_engine.get(); } QQmlApplicationEngine* engine() { return m_engine.get(); }
bool notify(QObject* _receiver, QEvent* _event) override;
private: private:
std::unique_ptr<QQmlApplicationEngine> m_engine; 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 // execute on a state
if (!_call) if (!_call)
{ {
_state.execute(lastHashes, _t); dev::eth::ExecutionResult er =_state.execute(lastHashes, _t);
if (_t.isCreation() && _state.code(d.contractAddress).empty()) if (_t.isCreation() && _state.code(d.contractAddress).empty())
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment"));
// collect watches // collect watches

2
mix/QBasicNodeDefinition.cpp

@ -28,7 +28,7 @@ namespace mix
{ {
QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): 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 <string>
#include <QObject> #include <QObject>
#include <libevmcore/SourceLocation.h>
namespace dev namespace dev
{ {
@ -47,9 +48,11 @@ public:
QBasicNodeDefinition(QObject* _parent, std::string const& _name); QBasicNodeDefinition(QObject* _parent, std::string const& _name);
/// Get the name of the node. /// Get the name of the node.
QString name() const { return m_name; } QString name() const { return m_name; }
dev::SourceLocation const& location() { return m_location; }
private: private:
QString m_name; 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 <QString>
#include <vector> #include <vector>
#include <libdevcore/Common.h>
namespace dev namespace dev
{ {
@ -49,6 +50,7 @@ struct SolidityType
}; };
Type type; Type type;
unsigned size; //in bytes, unsigned size; //in bytes,
unsigned count;
bool array; bool array;
bool dynamicSize; bool dynamicSize;
QString name; QString name;
@ -60,7 +62,11 @@ struct SolidityDeclaration
{ {
QString name; QString name;
SolidityType type; 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/ProjectModel.js</file>
<file>qml/js/QEtherHelper.js</file> <file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file> <file>qml/js/TransactionHelper.js</file>
<file>qml/js/Printer.js</file>
<file>qml/js/ansi2html.js</file>
</qresource> </qresource>
</RCC> </RCC>

2
mix/qml/Application.qml

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

4
mix/qml/Debugger.qml

@ -12,6 +12,10 @@ Rectangle {
id: debugPanel id: debugPanel
property alias transactionLog: transactionLog 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) signal debugExecuteLocation(string documentId, var location)
property string compilationErrorMessage property string compilationErrorMessage
property bool assemblyMode: false property bool assemblyMode: false

22
mix/qml/DeploymentDialog.qml

@ -2,7 +2,7 @@ import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3 import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0 import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper import "js/TransactionHelper.js" as TransactionHelper
@ -11,16 +11,11 @@ import "js/QEtherHelper.js" as QEtherHelper
import "." import "."
Window { Dialog {
id: modalDeploymentDialog id: modalDeploymentDialog
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
width: 735 width: 735
height: 320 height: 400
maximumWidth: width
minimumWidth: width
maximumHeight: height
minimumHeight: height
visible: false visible: false
property alias applicationUrlEth: applicationUrlEth.text property alias applicationUrlEth: applicationUrlEth.text
property alias applicationUrlHttp: applicationUrlHttp.text property alias applicationUrlHttp: applicationUrlHttp.text
@ -32,8 +27,6 @@ Window {
property string currentAccount property string currentAccount
property alias gasToUse: gasToUseInput.text property alias gasToUse: gasToUseInput.text
color: appStyle.generic.layout.backgroundColor
function close() function close()
{ {
visible = false; visible = false;
@ -41,10 +34,7 @@ Window {
function open() function open()
{ {
modalDeploymentDialog.setX((Screen.width - width) / 2);
modalDeploymentDialog.setY((Screen.height - height) / 2);
visible = true; visible = true;
var requests = [{ var requests = [{
//accounts //accounts
jsonrpc: "2.0", jsonrpc: "2.0",
@ -160,6 +150,9 @@ Window {
id: lightFont id: lightFont
} }
contentItem: Rectangle {
color: appStyle.generic.layout.backgroundColor
anchors.fill: parent
Column Column
{ {
spacing: 5 spacing: 5
@ -292,6 +285,7 @@ Window {
id: balance; id: balance;
} }
} }
}
DefaultLabel DefaultLabel
{ {
@ -382,7 +376,6 @@ Window {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
}
Rectangle Rectangle
{ {
@ -501,6 +494,7 @@ Window {
} }
} }
} }
}
MessageDialog { MessageDialog {
id: deployDialog id: deployDialog

53
mix/qml/LogsPane.qml

@ -7,6 +7,11 @@ import org.ethereum.qml.SortFilterProxyModel 1.0
Rectangle Rectangle
{ {
property variant currentStatus; property variant currentStatus;
function clear()
{
logsModel.clear();
}
function push(_level, _type, _content) function push(_level, _type, _content)
{ {
_content = _content.replace(/\n/g, " ") _content = _content.replace(/\n/g, " ")
@ -57,7 +62,7 @@ Rectangle
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxyModel id: proxyModel
source: logsModel source: logsModel
property var roles: ["-", "javascript", "run", "state", "comp"] property var roles: ["-", "javascript", "run", "state", "comp", "deployment"]
Component.onCompleted: { Component.onCompleted: {
filterType = regEx(proxyModel.roles); filterType = regEx(proxyModel.roles);
@ -90,7 +95,7 @@ Rectangle
return "(?:" + roles.join('|') + ")"; return "(?:" + roles.join('|') + ")";
} }
filterType: "(?:javascript|run|state|comp)" filterType: "(?:javascript|run|state|comp|deployment)"
filterContent: "" filterContent: ""
filterSyntax: SortFilterProxyModel.RegExp filterSyntax: SortFilterProxyModel.RegExp
filterCaseSensitivity: Qt.CaseInsensitive filterCaseSensitivity: Qt.CaseInsensitive
@ -384,6 +389,50 @@ Rectangle
height: parent.height height: parent.height
color: logStyle.generic.layout.buttonSeparatorColor2 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 Row

10
mix/qml/NewProjectDialog.qml

@ -1,10 +1,11 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
Window { Dialog {
id: newProjectWin id: newProjectWin
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
@ -33,8 +34,10 @@ Window {
close(); close();
accepted(); accepted();
} }
contentItem: Rectangle {
GridLayout { anchors.fill: parent
GridLayout
{
id: dialogContent id: dialogContent
columns: 2 columns: 2
anchors.fill: parent anchors.fill: parent
@ -92,6 +95,7 @@ Window {
} }
} }
} }
}
FileDialog { FileDialog {
id: createProjectFileDialog id: createProjectFileDialog

34
mix/qml/StateDialog.qml

@ -1,6 +1,6 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import QtQuick.Controls.Styles 1.3 import QtQuick.Controls.Styles 1.3
@ -9,7 +9,7 @@ import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper import "js/TransactionHelper.js" as TransactionHelper
import "." import "."
Window { Dialog {
id: modalStateDialog id: modalStateDialog
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
@ -17,7 +17,6 @@ Window {
height: 480 height: 480
title: qsTr("Edit State") title: qsTr("Edit State")
visible: false visible: false
color: stateDialogStyle.generic.backgroundColor
property alias stateTitle: titleField.text property alias stateTitle: titleField.text
property alias isDefault: defaultCheckBox.checked property alias isDefault: defaultCheckBox.checked
@ -28,6 +27,10 @@ Window {
property var stateAccounts: [] property var stateAccounts: []
signal accepted signal accepted
StateDialogStyle {
id: stateDialogStyle
}
function open(index, item, setDefault) { function open(index, item, setDefault) {
stateIndex = index; stateIndex = index;
stateTitle = item.title; stateTitle = item.title;
@ -48,9 +51,6 @@ Window {
stateAccounts.push(item.accounts[k]); stateAccounts.push(item.accounts[k]);
} }
modalStateDialog.setX((Screen.width - width) / 2);
modalStateDialog.setY((Screen.height - height) / 2);
visible = true; visible = true;
isDefault = setDefault; isDefault = setDefault;
titleField.focus = true; titleField.focus = true;
@ -77,18 +77,16 @@ Window {
item.accounts = stateAccounts; item.accounts = stateAccounts;
return item; return item;
} }
contentItem: Rectangle {
StateDialogStyle { color: stateDialogStyle.generic.backgroundColor
id: stateDialogStyle ColumnLayout {
} anchors.fill: parent
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 10 anchors.margins: 10
ColumnLayout { ColumnLayout {
id: dialogContent id: dialogContent
anchors.top: parent.top anchors.top: parent.top
RowLayout RowLayout
{ {
Layout.fillWidth: true Layout.fillWidth: true
@ -288,11 +286,6 @@ Window {
} }
} }
} }
CommonSeparator
{
Layout.fillWidth: true
}
} }
RowLayout RowLayout
@ -303,7 +296,8 @@ Window {
Button { Button {
text: qsTr("OK"); text: qsTr("OK");
onClicked: { onClicked: {
acceptAndClose(); close();
accepted();
} }
} }
Button { Button {
@ -311,7 +305,6 @@ Window {
onClicked: close(); onClicked: close();
} }
} }
}
ListModel { ListModel {
id: accountsModel id: accountsModel
@ -412,3 +405,6 @@ Window {
} }
} }
} }
}
}
}

32
mix/qml/StateList.qml

@ -1,19 +1,19 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 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.Layouts 1.1
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import "." import "."
Window { Dialog {
id: stateListContainer id: stateListContainer
modality: Qt.WindowModal modality: Qt.WindowModal
width: 640 width: 640
height: 480 height: 480
visible: false visible: false
contentItem: Rectangle {
anchors.fill: parent
ColumnLayout ColumnLayout
{ {
anchors.fill: parent anchors.fill: parent
@ -24,6 +24,7 @@ Window {
model: projectModel.stateListModel model: projectModel.stateListModel
itemDelegate: renderDelegate itemDelegate: renderDelegate
headerDelegate: null headerDelegate: null
frameVisible: false
TableViewColumn { TableViewColumn {
role: "title" role: "title"
title: qsTr("State") title: qsTr("State")
@ -31,10 +32,20 @@ Window {
} }
} }
Button { Row{
spacing: 5
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 10
Button {
action: addStateAction action: addStateAction
} }
Button {
action: closeAction
}
}
}
} }
Component { Component {
@ -69,12 +80,21 @@ Window {
} }
} }
Row
{
Action { Action {
id: addStateAction id: addStateAction
text: "&Add State" text: qsTr("Add State")
shortcut: "Ctrl+T" shortcut: "Ctrl+T"
enabled: codeModel.hasContract && !clientModel.running; enabled: codeModel.hasContract && !clientModel.running;
onTriggered: list.model.addState(); 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 = ""; debugImg.state = "";
currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "error" } currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "error" }
} }
debugRunActionIcon.enabled = codeModel.hasContract;
} }
function infoMessage(text, type) function infoMessage(text, type)
@ -77,7 +76,11 @@ Rectangle {
Connections { Connections {
target:clientModel target:clientModel
onRunStarted: infoMessage(qsTr("Running transactions..."), "Run"); onRunStarted:
{
logPane.clear()
infoMessage(qsTr("Running transactions..."), "Run");
}
onRunFailed: errorMessage(format(_message), "Run"); onRunFailed: errorMessage(format(_message), "Run");
onRunComplete: infoMessage(qsTr("Run complete"), "Run"); onRunComplete: infoMessage(qsTr("Run complete"), "Run");
onNewBlock: infoMessage(qsTr("New block created"), "State"); onNewBlock: infoMessage(qsTr("New block created"), "State");
@ -281,24 +284,9 @@ Rectangle {
anchors.rightMargin: 9 anchors.rightMargin: 9
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
id: debugImg id: debugImg
iconSource: "qrc:/qml/img/bugiconinactive.png" text: ""
action: debugRunActionIcon iconSource: "qrc:/qml/img/bugiconactive.png"
states: [ action: showHideRightPanelAction
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
} }
} }
} }

1
mix/qml/StepActionImage.qml

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

9
mix/qml/StructView.qml

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

29
mix/qml/TransactionDialog.qml

@ -1,19 +1,19 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0 import QtQuick.Window 2.0
import QtQuick.Controls.Styles 1.3 import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0 import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper import "js/TransactionHelper.js" as TransactionHelper
import "." import "."
Window { Dialog {
id: modalTransactionDialog id: modalTransactionDialog
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
width: 520 width: 520
height: 500; height: 500
visible: false visible: false
color: transactionDialogStyle.generic.backgroundColor
title: qsTr("Edit Transaction") title: qsTr("Edit Transaction")
property int transactionIndex property int transactionIndex
property alias gas: gasValueEdit.gasValue; property alias gas: gasValueEdit.gasValue;
@ -27,6 +27,10 @@ Window {
property alias stateAccounts: senderComboBox.model property alias stateAccounts: senderComboBox.model
signal accepted; signal accepted;
StateDialogStyle {
id: transactionDialogStyle
}
function open(index, item) { function open(index, item) {
rowFunction.visible = !useTransactionDefaultValue; rowFunction.visible = !useTransactionDefaultValue;
rowValue.visible = !useTransactionDefaultValue; rowValue.visible = !useTransactionDefaultValue;
@ -73,8 +77,6 @@ Window {
} }
} }
initTypeLoader(); initTypeLoader();
modalTransactionDialog.setX((Screen.width - width) / 2);
modalTransactionDialog.setY((Screen.height - height) / 2);
visible = true; visible = true;
valueField.focus = true; valueField.focus = true;
@ -178,11 +180,10 @@ Window {
item.parameters = paramValues; item.parameters = paramValues;
return item; return item;
} }
contentItem: Rectangle {
StateDialogStyle { color: transactionDialogStyle.generic.backgroundColor
id: transactionDialogStyle ColumnLayout {
} anchors.fill: parent
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 10 anchors.margins: 10
@ -370,6 +371,7 @@ Window {
visible: paramsModel.length > 0 visible: paramsModel.length > 0
} }
} }
}
RowLayout RowLayout
{ {
@ -379,13 +381,18 @@ Window {
Button { Button {
text: qsTr("OK"); text: qsTr("OK");
onClicked: { onClicked: {
acceptAndClose(); close();
accepted();
} }
} }
Button { Button {
text: qsTr("Cancel"); text: qsTr("Cancel");
onClicked: close(); onClicked: close();
Layout.fillWidth: true
} }
} }
} }
} }
}

1
mix/qml/VariablesView.qml

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

13
mix/qml/WebPreview.qml

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

12
mix/qml/html/WebContainer.html

@ -3,6 +3,8 @@
<head> <head>
<script src="qrc:///js/bignumber.min.js"></script> <script src="qrc:///js/bignumber.min.js"></script>
<script src="qrc:///js/webthree.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> <script>
loadPage = function(url) { loadPage = function(url) {
var preview = document.getElementById('preview'); var preview = document.getElementById('preview');
@ -40,7 +42,15 @@ init = function(url) {
executeJavaScript = function(script) { executeJavaScript = function(script) {
var preview = document.getElementById('preview'); var preview = document.getElementById('preview');
var obj = preview.contentWindow.eval(script); 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> </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.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status !== 200 || httpRequest.responseText === "") 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); console.log(errorText);
deploymentError(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. * Ethereum IDE client.
*/ */
#include <iostream>
#include "TestService.h" #include "TestService.h"
#include <QtTest/QSignalSpy> #include <QtTest/QSignalSpy>
#include <QElapsedTimer> #include <QElapsedTimer>
@ -111,6 +112,12 @@ bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeou
return spy.size(); 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) bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay)
{ {
QWindow* window = eventWindow(_item); 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) bool TestService::mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay)
{ {
QWindow* window = qobject_cast<QWindow*>(_item); QWindow* window = qobject_cast<QWindow*>(_item);
QMetaObject const* mo = _item->metaObject();
if (!window) if (!window)
window = eventWindow(_item); window = eventWindow(_item);
mouseEvent(MouseClick, window, _item, Qt::MouseButton(_button), Qt::KeyboardModifiers(_modifiers), QPointF(_x, _y), _delay); 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(); window->requestActivate();
} }
QWindow* TestService::eventWindow(QObject* _item) QWindow* TestService::eventWindow(QObject* _item)
{ {
QQuickItem* item = qobject_cast<QQuickItem*>(_item); QQuickItem* item = qobject_cast<QQuickItem*>(_item);
if (item && item->window()) if (item && item->window())
return 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) if (!window)
window = qobject_cast<QQuickWindow*>(m_targetWindow); window = qobject_cast<QQuickWindow*>(m_targetWindow);
if (window) if (window)
{ {
window->requestActivate(); window->requestActivate();
std::cout << window->title().toStdString();
return window; return window;
} }
item = qobject_cast<QQuickItem*>(m_targetWindow); item = qobject_cast<QQuickItem*>(m_targetWindow);

1
mix/test/TestService.h

@ -42,6 +42,7 @@ public:
public slots: public slots:
bool waitForSignal(QObject* _item, QString _signalName, int _timeout); 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 keyPress(QObject* _item, int _key, int _modifiers, int _delay);
bool keyRelease(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); 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 QtTest 1.1
import org.ethereum.qml.TestService 1.0 import org.ethereum.qml.TestService 1.0
import "../../qml" import "../../qml"
import "js/TestDebugger.js" as TestDebugger
import "js/TestTutorial.js" as TestTutorial
TestCase TestCase
{ {
@ -14,6 +16,8 @@ TestCase
{ {
if (el === undefined) if (el === undefined)
el = mainApplication; el = mainApplication;
if (el.contentItem) //for dialgos
el = el.contentItem
for (var c in str) for (var c in str)
{ {
@ -23,6 +27,11 @@ TestCase
} }
} }
Application
{
id: mainApplication
}
function newProject() function newProject()
{ {
waitForRendering(mainApplication.mainContent, 10000); waitForRendering(mainApplication.mainContent, 10000);
@ -43,100 +52,25 @@ TestCase
fail("not compiled"); fail("not compiled");
} }
function clickElement(el, x, y) function editHtml(c)
{ {
ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10) mainApplication.projectModel.openDocument("index.html");
}
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();
wait(1); wait(1);
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) mainApplication.mainContent.codeEditor.getEditor("index.html").setText(c);
fail("not run"); ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)");
} }
function test_constructorParameters() function clickElement(el, x, y)
{ {
newProject(); if (el.contentItem)
editContract( el = el.contentItem;
"contract Contract {\r" + ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10)
" 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)");
} }
Application function test_tutorial() { TestTutorial.test_tutorial(); }
{ function test_dbg_defaultTransactionSequence() { TestDebugger.test_defaultTransactionSequence(); }
id: mainApplication 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 <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h> #include <jsonrpccpp/server/connectors/httpserver.h>
#endif #endif
#include <libethcore/Ethasher.h>
#include "BuildInfo.h" #include "BuildInfo.h"
#undef KEY_EVENT // from windows.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 << " -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 << " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/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 << " -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 << " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl
<< " -h,--help Show this help message and exit." << endl << " -h,--help Show this help message and exit." << endl
@ -321,6 +323,7 @@ enum class NodeMode
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
bool initDAG = true;
string listenIP; string listenIP;
unsigned short listenPort = 30303; unsigned short listenPort = 30303;
string publicIP; string publicIP;
@ -424,6 +427,8 @@ int main(int argc, char** argv)
structuredLogging = true; structuredLogging = true;
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i]; dbPath = argv[++i];
else if (arg == "-D" || arg == "--initdag")
initDAG = true;
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{ {
try try
@ -556,6 +561,14 @@ int main(int argc, char** argv)
&nodesState, &nodesState,
miners miners
); );
if (initDAG)
{
cout << "Initialize DAG. (This will take awhile)" << endl;
Ethasher::get()->full(web3.ethereum()->blockChain().info());
return 0;
}
web3.setIdealPeerCount(peers); web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); 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; eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;

168
test/SolidityOptimizer.cpp

@ -56,8 +56,16 @@ public:
m_nonOptimizedContract = m_contractAddress; m_nonOptimizedContract = m_contractAddress;
m_optimize = true; m_optimize = true;
bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName); 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( BOOST_CHECK_MESSAGE(
nonOptimizedBytecode.size() > optimizedBytecode.size(), nonOptimizedSize > optimizedSize,
"Optimizer did not reduce bytecode size." "Optimizer did not reduce bytecode size."
); );
m_optimizedContract = m_contractAddress; m_optimizedContract = m_contractAddress;
@ -75,11 +83,16 @@ public:
"\nOptimized: " + toHex(optimizedOutput)); "\nOptimized: " + toHex(optimizedOutput));
} }
void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation) AssemblyItems getCSE(AssemblyItems const& _input)
{ {
eth::CommonSubexpressionEliminator cse; eth::CommonSubexpressionEliminator cse;
BOOST_REQUIRE(cse.feedItems(_input.begin(), _input.end()) == _input.end()); 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()); 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() BOOST_AUTO_TEST_SUITE_END()
} }

2
test/peer.cpp

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

Loading…
Cancel
Save