Browse Source

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

cl-refactor
Gav Wood 10 years ago
parent
commit
0a16af5b8f
  1. 2
      eth/main.cpp
  2. 5
      libethcore/EthashAux.cpp
  3. 42
      libevmasm/Assembly.cpp
  4. 6
      libevmasm/Assembly.h
  5. 2
      libevmasm/AssemblyItem.h
  6. 75
      libevmasm/BlockDeduplicator.cpp
  7. 16
      libevmasm/BlockDeduplicator.h
  8. 8
      libevmasm/ControlFlowGraph.cpp
  9. 2
      libsolidity/AST.cpp
  10. 22
      libsolidity/ArrayUtils.cpp
  11. 2
      libsolidity/Compiler.cpp
  12. 2
      libsolidity/CompilerContext.h
  13. 10
      libsolidity/ExpressionCompiler.cpp
  14. 5
      libsolidity/ExpressionCompiler.h
  15. 2
      libsolidity/Token.h
  16. 20
      libsolidity/Types.cpp
  17. 22
      libsolidity/Types.h
  18. 4
      mix/CodeModel.cpp
  19. 35
      mix/QBigInt.cpp
  20. 3
      mix/QBigInt.h
  21. 1
      mix/SolidityType.h
  22. 1
      mix/qml.qrc
  23. 7
      mix/qml/QIntTypeView.qml
  24. 23
      mix/qml/TransactionDialog.qml
  25. 125
      mix/qml/js/InputValidator.js
  26. 57
      solc/jsonCompiler.cpp
  27. 7
      test/TestHelper.cpp
  28. 1
      test/TestHelper.h
  29. 7
      test/fuzzTesting/CMakeLists.txt
  30. 204
      test/fuzzTesting/createRandomStateTest.cpp
  31. 224
      test/fuzzTesting/fuzzHelper.cpp
  32. 97
      test/fuzzTesting/fuzzHelper.h
  33. 102
      test/libethereum/BlockTestsFiller/bcUncleTestFiller.json
  34. 20
      test/libethereum/blockchain.cpp
  35. 27
      test/libsolidity/SolidityABIJSON.cpp
  36. 192
      test/libsolidity/SolidityCompiler.cpp
  37. 66
      test/libsolidity/SolidityEndToEndTest.cpp
  38. 33
      test/libsolidity/SolidityNameAndTypeResolution.cpp
  39. 32
      test/libsolidity/SolidityOptimizer.cpp
  40. 41
      test/libsolidity/solidityExecutionFramework.h

2
eth/main.cpp

@ -628,7 +628,7 @@ int main(int argc, char** argv)
nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(), nodeMode == NodeMode::Full ? set<string>{"eth"/*, "shh"*/} : set<string>(),
netPrefs, netPrefs,
&nodesState); &nodesState);
auto toNumber = [&](string const& s) -> unsigned { auto toNumber = [&](string const& s) -> unsigned {
if (s == "latest") if (s == "latest")
return web3.ethereum()->number(); return web3.ethereum()->number();

5
libethcore/EthashAux.cpp

@ -240,8 +240,9 @@ Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{ {
if (FullType dag = get()->m_fulls[_seedHash].lock()) DEV_GUARDED(get()->x_fulls)
return dag->compute(_headerHash, _nonce); if (FullType dag = get()->m_fulls[_seedHash].lock())
return dag->compute(_headerHash, _nonce);
DEV_IF_THROWS(return EthashAux::get()->light(_seedHash)->compute(_headerHash, _nonce)) DEV_IF_THROWS(return EthashAux::get()->light(_seedHash)->compute(_headerHash, _nonce))
{ {
return Ethash::Result{ ~h256(), h256() }; return Ethash::Result{ ~h256(), h256() };

42
libevmasm/Assembly.cpp

@ -127,7 +127,10 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
_out << " PUSH \"" << m_strings.at((h256)i.data()) << "\""; _out << " PUSH \"" << m_strings.at((h256)i.data()) << "\"";
break; break;
case PushTag: case PushTag:
_out << " PUSH [tag" << dec << i.data() << "]"; if (i.data() == 0)
_out << " PUSH [ErrorTag]";
else
_out << " PUSH [tag" << dec << i.data() << "]";
break; break;
case PushSub: case PushSub:
_out << " PUSH [$" << h256(i.data()).abridged() << "]"; _out << " PUSH [$" << h256(i.data()).abridged() << "]";
@ -207,8 +210,12 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
createJsonValue("PUSH tag", i.getLocation().start, i.getLocation().end, m_strings.at((h256)i.data()))); createJsonValue("PUSH tag", i.getLocation().start, i.getLocation().end, m_strings.at((h256)i.data())));
break; break;
case PushTag: case PushTag:
collection.append( if (i.data() == 0)
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, string(i.data()))); collection.append(
createJsonValue("PUSH [ErrorTag]", i.getLocation().start, i.getLocation().end, ""));
else
collection.append(
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, string(i.data())));
break; break;
case PushSub: case PushSub:
collection.append( collection.append(
@ -226,7 +233,7 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
collection.append( collection.append(
createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data()))); createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data())));
collection.append( collection.append(
createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end)); createJsonValue("JUMPDEST", i.getLocation().start, i.getLocation().end));
break; break;
case PushData: case PushData:
collection.append(createJsonValue("PUSH data", i.getLocation().start, i.getLocation().end, toStringInHex(i.data()))); collection.append(createJsonValue("PUSH data", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
@ -307,6 +314,11 @@ Assembly& Assembly::optimise(bool _enable)
count = 0; count = 0;
copt << "Performing optimisation..."; copt << "Performing optimisation...";
// This only modifies PushTags, we have to run again to actually remove code.
BlockDeduplicator dedup(m_items);
if (dedup.deduplicate())
count++;
{ {
ControlFlowGraph cfg(m_items); ControlFlowGraph cfg(m_items);
AssemblyItems optimisedItems; AssemblyItems optimisedItems;
@ -349,11 +361,6 @@ Assembly& Assembly::optimise(bool _enable)
m_items = move(optimisedItems); m_items = move(optimisedItems);
count++; count++;
} }
// This only modifies PushTags, we have to run again to actually remove code.
BlockDeduplicator dedup(m_items);
if (dedup.deduplicate())
count++;
} }
} }
@ -387,6 +394,11 @@ bytes Assembly::assemble() const
// m_data must not change from here on // m_data must not change from here on
for (AssemblyItem const& i: m_items) for (AssemblyItem const& i: m_items)
{
// store position of the invalid jump destination
if (i.type() != Tag && tagPos[0] == 0)
tagPos[0] = ret.size();
switch (i.type()) switch (i.type())
{ {
case Operation: case Operation:
@ -448,17 +460,23 @@ bytes Assembly::assemble() const
} }
case Tag: case Tag:
tagPos[(unsigned)i.data()] = ret.size(); tagPos[(unsigned)i.data()] = ret.size();
assertThrow(i.data() != 0, AssemblyException, "");
ret.push_back((byte)Instruction::JUMPDEST); ret.push_back((byte)Instruction::JUMPDEST);
break; break;
default: default:
BOOST_THROW_EXCEPTION(InvalidOpcode()); BOOST_THROW_EXCEPTION(InvalidOpcode());
} }
}
for (auto const& i: tagRef) for (auto const& i: tagRef)
{ {
bytesRef r(ret.data() + i.first, bytesPerTag); bytesRef r(ret.data() + i.first, bytesPerTag);
//@todo in the failure case, we could use the position of the invalid jumpdest auto tag = i.second;
toBigEndian(i.second < tagPos.size() ? tagPos[i.second] : (1 << (8 * bytesPerTag)) - 1, r); if (tag >= tagPos.size())
tag = 0;
if (tag == 0)
assertThrow(tagPos[tag] != 0, AssemblyException, "");
toBigEndian(tagPos[tag], r);
} }
if (!m_data.empty()) if (!m_data.empty())

6
libevmasm/Assembly.h

@ -67,6 +67,8 @@ public:
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; } AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem errorTag() { return AssemblyItem(PushTag, 0); }
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
AssemblyItems const& getItems() const { return m_items; } AssemblyItems const& getItems() const { return m_items; }
AssemblyItem const& back() const { return m_items.back(); } AssemblyItem const& back() const { return m_items.back(); }
@ -97,7 +99,6 @@ public:
const StringMap &_sourceCodes = StringMap(), const StringMap &_sourceCodes = StringMap(),
bool _inJsonFormat = false bool _inJsonFormat = false
) const; ) const;
protected: protected:
std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const; std::string getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); } void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@ -109,7 +110,8 @@ private:
Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const; Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const;
protected: protected:
unsigned m_usedTags = 0; // 0 is reserved for exception
unsigned m_usedTags = 1;
AssemblyItems m_items; AssemblyItems m_items;
mutable std::map<h256, bytes> m_data; mutable std::map<h256, bytes> m_data;
std::vector<Assembly> m_subs; std::vector<Assembly> m_subs;

2
libevmasm/AssemblyItem.h

@ -65,7 +65,7 @@ public:
/// @returns the instruction of this item (only valid if type() == Operation) /// @returns the instruction of this item (only valid if type() == Operation)
Instruction instruction() const { return Instruction(byte(m_data)); } Instruction instruction() const { return Instruction(byte(m_data)); }
/// @returns true iff the type and data of the items are equal. /// @returns true if the type and data of the items are equal.
bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; } bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; }
bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); } bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); }
/// Less-than operator compatible with operator==. /// Less-than operator compatible with operator==.

75
libevmasm/BlockDeduplicator.cpp

@ -35,13 +35,33 @@ bool BlockDeduplicator::deduplicate()
{ {
// Compares indices based on the suffix that starts there, ignoring tags and stopping at // Compares indices based on the suffix that starts there, ignoring tags and stopping at
// opcodes that stop the control flow. // opcodes that stop the control flow.
// Virtual tag that signifies "the current block" and which is used to optimise loops.
// We abort if this virtual tag actually exists.
AssemblyItem pushSelf(PushTag, u256(-4));
if (
std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag())
)
return false;
function<bool(size_t, size_t)> comparator = [&](size_t _i, size_t _j) function<bool(size_t, size_t)> comparator = [&](size_t _i, size_t _j)
{ {
if (_i == _j) if (_i == _j)
return false; return false;
BlockIterator first(m_items.begin() + _i, m_items.end()); // To compare recursive loops, we have to already unify PushTag opcodes of the
BlockIterator second(m_items.begin() + _j, m_items.end()); // block's own tag.
AssemblyItem pushFirstTag(pushSelf);
AssemblyItem pushSecondTag(pushSelf);
if (_i < m_items.size() && m_items.at(_i).type() == Tag)
pushFirstTag = m_items.at(_i).pushTag();
if (_j < m_items.size() && m_items.at(_j).type() == Tag)
pushSecondTag = m_items.at(_j).pushTag();
BlockIterator first(m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf);
BlockIterator second(m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf);
BlockIterator end(m_items.end(), m_items.end()); BlockIterator end(m_items.end(), m_items.end());
if (first != end && (*first).type() == Tag) if (first != end && (*first).type() == Tag)
@ -52,27 +72,34 @@ bool BlockDeduplicator::deduplicate()
return std::lexicographical_compare(first, end, second, end); return std::lexicographical_compare(first, end, second, end);
}; };
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator); size_t iterations = 0;
map<u256, u256> tagReplacement; for (; ; ++iterations)
for (size_t i = 0; i < m_items.size(); ++i)
{ {
if (m_items.at(i).type() != Tag) //@todo this should probably be optimized.
continue; set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
auto it = blocksSeen.find(i); map<u256, u256> tagReplacement;
if (it == blocksSeen.end()) for (size_t i = 0; i < m_items.size(); ++i)
blocksSeen.insert(i);
else
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
}
bool ret = false;
for (AssemblyItem& item: m_items)
if (item.type() == PushTag && tagReplacement.count(item.data()))
{ {
ret = true; if (m_items.at(i).type() != Tag)
item.setData(tagReplacement.at(item.data())); continue;
auto it = blocksSeen.find(i);
if (it == blocksSeen.end())
blocksSeen.insert(i);
else
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
} }
return ret;
bool changed = false;
for (AssemblyItem& item: m_items)
if (item.type() == PushTag && tagReplacement.count(item.data()))
{
changed = true;
item.setData(tagReplacement.at(item.data()));
}
if (!changed)
break;
}
return iterations > 0;
} }
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++() BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
@ -89,3 +116,11 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
} }
return *this; return *this;
} }
AssemblyItem const& BlockDeduplicator::BlockIterator::operator*() const
{
if (replaceItem && replaceWith && *it == *replaceItem)
return *replaceWith;
else
return *it;
}

16
libevmasm/BlockDeduplicator.h

@ -47,19 +47,27 @@ public:
bool deduplicate(); bool deduplicate();
private: private:
/// Iterator that skips tags skips to the end if (all branches of) the control /// Iterator that skips tags and skips to the end if (all branches of) the control
/// flow does not continue to the next instruction. /// flow does not continue to the next instruction.
/// If the arguments are supplied to the constructor, replaces items on the fly.
struct BlockIterator: std::iterator<std::forward_iterator_tag, AssemblyItem const> struct BlockIterator: std::iterator<std::forward_iterator_tag, AssemblyItem const>
{ {
public: public:
BlockIterator(AssemblyItems::const_iterator _it, AssemblyItems::const_iterator _end): BlockIterator(
it(_it), end(_end) { } AssemblyItems::const_iterator _it,
AssemblyItems::const_iterator _end,
AssemblyItem const* _replaceItem = nullptr,
AssemblyItem const* _replaceWith = nullptr
):
it(_it), end(_end), replaceItem(_replaceItem), replaceWith(_replaceWith) {}
BlockIterator& operator++(); BlockIterator& operator++();
bool operator==(BlockIterator const& _other) const { return it == _other.it; } bool operator==(BlockIterator const& _other) const { return it == _other.it; }
bool operator!=(BlockIterator const& _other) const { return it != _other.it; } bool operator!=(BlockIterator const& _other) const { return it != _other.it; }
AssemblyItem const& operator*() const { return *it; } AssemblyItem const& operator*() const;
AssemblyItems::const_iterator it; AssemblyItems::const_iterator it;
AssemblyItems::const_iterator end; AssemblyItems::const_iterator end;
AssemblyItem const* replaceItem;
AssemblyItem const* replaceWith;
}; };
AssemblyItems& m_items; AssemblyItems& m_items;

8
libevmasm/ControlFlowGraph.cpp

@ -226,7 +226,10 @@ void ControlFlowGraph::gatherKnowledge()
//@todo we might have to do something like incrementing the sequence number for each JUMPDEST //@todo we might have to do something like incrementing the sequence number for each JUMPDEST
assertThrow(!!workQueue.back().first, OptimizerException, ""); assertThrow(!!workQueue.back().first, OptimizerException, "");
if (!m_blocks.count(workQueue.back().first)) if (!m_blocks.count(workQueue.back().first))
{
workQueue.pop_back();
continue; // too bad, we do not know the tag, probably an invalid jump continue; // too bad, we do not know the tag, probably an invalid jump
}
BasicBlock& block = m_blocks.at(workQueue.back().first); BasicBlock& block = m_blocks.at(workQueue.back().first);
KnownStatePointer state = workQueue.back().second; KnownStatePointer state = workQueue.back().second;
workQueue.pop_back(); workQueue.pop_back();
@ -257,10 +260,7 @@ void ControlFlowGraph::gatherKnowledge()
); );
state->feedItem(m_items.at(pc++)); state->feedItem(m_items.at(pc++));
if (tags.empty() || std::any_of(tags.begin(), tags.end(), [&](u256 const& _tag) if (tags.empty())
{
return !m_blocks.count(BlockId(_tag));
}))
{ {
if (!unknownJumpEncountered) if (!unknownJumpEncountered)
{ {

2
libsolidity/AST.cpp

@ -890,6 +890,8 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->getType()); ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->getType());
if (!m_index) if (!m_index)
BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted.")); BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
if (type.isString())
BOOST_THROW_EXCEPTION(createTypeError("Index access for string is not possible."));
m_index->expectType(IntegerType(256)); m_index->expectType(IntegerType(256));
if (type.isByteArray()) if (type.isByteArray())
m_type = make_shared<FixedBytesType>(1); m_type = make_shared<FixedBytesType>(1);

22
libsolidity/ArrayUtils.cpp

@ -364,7 +364,13 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
return; return;
} }
// stack: end_pos pos // stack: end_pos pos
eth::AssemblyItem loopStart = m_context.newTag();
// jump to and return from the loop to allow for duplicate code removal
eth::AssemblyItem returnTag = m_context.pushNewTag();
m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
// stack: <return tag> end_pos pos
eth::AssemblyItem loopStart = m_context.appendJumpToNew();
m_context << loopStart; m_context << loopStart;
// check for loop condition // check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
@ -380,7 +386,11 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
m_context.appendJumpTo(loopStart); m_context.appendJumpTo(loopStart);
// cleanup // cleanup
m_context << zeroLoopEnd; m_context << zeroLoopEnd;
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP << eth::Instruction::SWAP1;
// "return"
m_context << eth::Instruction::JUMP;
m_context << returnTag;
solAssert(m_context.getStackHeight() == stackHeightStart - 1, ""); solAssert(m_context.getStackHeight() == stackHeightStart - 1, "");
} }
@ -455,12 +465,10 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::DUP2 << load; m_context << eth::Instruction::DUP2 << load;
// stack: <base_ref> <index> <length> // stack: <base_ref> <index> <length>
// check out-of-bounds access // check out-of-bounds access
m_context << eth::Instruction::DUP2 << eth::Instruction::LT; m_context << eth::Instruction::DUP2 << eth::Instruction::LT << eth::Instruction::ISZERO;
eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); // out-of-bounds access throws exception
// out-of-bounds access throws exception (just STOP for now) m_context.appendConditionalJumpTo(m_context.errorTag());
m_context << eth::Instruction::STOP;
m_context << legalAccess;
// stack: <base_ref> <index> // stack: <base_ref> <index>
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized()) if (_arrayType.isDynamicallySized())

2
libsolidity/Compiler.cpp

@ -194,7 +194,6 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
} }
else else
m_context << eth::Instruction::STOP; // function not found m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions) for (auto const& it: interfaceFunctions)
{ {
FunctionTypePointer const& functionType = it.second; FunctionTypePointer const& functionType = it.second;
@ -289,7 +288,6 @@ bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
m_breakTags.clear(); m_breakTags.clear();
m_continueTags.clear(); m_continueTags.clear();
m_context << m_context.getFunctionEntryLabel(_variableDeclaration);
ExpressionCompiler(m_context, m_optimize).appendStateVariableAccessor(_variableDeclaration); ExpressionCompiler(m_context, m_optimize).appendStateVariableAccessor(_variableDeclaration);
return false; return false;

2
libsolidity/CompilerContext.h

@ -98,6 +98,8 @@ public:
eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); } eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
/// Appends a JUMP to a tag already on the stack /// Appends a JUMP to a tag already on the stack
CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
/// Returns an "ErrorTag"
eth::AssemblyItem errorTag() { return m_asm.errorTag(); }
/// Appends a JUMP to a specific tag /// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; } CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag. /// Appends pushing of a new tag and @returns the new tag.

10
libsolidity/ExpressionCompiler.cpp

@ -824,7 +824,10 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
if (arrayType.getLocation() == ArrayType::Location::Storage) if (arrayType.getLocation() == ArrayType::Location::Storage)
{ {
if (arrayType.isByteArray()) if (arrayType.isByteArray())
{
solAssert(!arrayType.isString(), "Index access to string is not allowed.");
setLValue<StorageByteArrayElement>(_indexAccess); setLValue<StorageByteArrayElement>(_indexAccess);
}
else else
setLValueToStorageItem(_indexAccess); setLValueToStorageItem(_indexAccess);
} }
@ -1103,8 +1106,11 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << eth::Instruction::CALLCODE; m_context << eth::Instruction::CALLCODE;
else else
m_context << eth::Instruction::CALL; m_context << eth::Instruction::CALL;
auto tag = m_context.appendConditionalJump();
m_context << eth::Instruction::STOP << tag; // STOP if CALL leaves 0. //Propagate error condition (if CALL pushes 0 on stack).
m_context << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(m_context.errorTag());
if (_functionType.valueSet()) if (_functionType.valueSet())
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
if (_functionType.gasSet()) if (_functionType.gasSet())

5
libsolidity/ExpressionCompiler.h

@ -98,10 +98,7 @@ private:
void appendHighBitsCleanup(IntegerType const& _typeOnStack); void appendHighBitsCleanup(IntegerType const& _typeOnStack);
/// Appends code to call a function of the given type with the given arguments. /// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall( void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments);
FunctionType const& _functionType,
std::vector<ASTPointer<Expression const>> const& _arguments
);
/// Appends code that evaluates the given arguments and moves the result to memory encoded as /// Appends code that evaluates the given arguments and moves the result to memory encoded as
/// specified by the ABI. The memory offset is expected to be on the stack and is updated by /// specified by the ABI. The memory offset is expected to be on the stack and is updated by
/// this call. If @a _padToWordBoundaries is set to false, all values are concatenated without /// this call. If @a _padToWordBoundaries is set to false, all values are concatenated without

2
libsolidity/Token.h

@ -286,6 +286,7 @@ namespace solidity
K(Bytes32, "bytes32", 0) \ K(Bytes32, "bytes32", 0) \
K(Bytes, "bytes", 0) \ K(Bytes, "bytes", 0) \
K(Byte, "byte", 0) \ K(Byte, "byte", 0) \
K(String, "string", 0) \
K(Address, "address", 0) \ K(Address, "address", 0) \
K(Bool, "bool", 0) \ K(Bool, "bool", 0) \
K(Real, "real", 0) \ K(Real, "real", 0) \
@ -312,7 +313,6 @@ namespace solidity
K(Match, "match", 0) \ K(Match, "match", 0) \
K(Of, "of", 0) \ K(Of, "of", 0) \
K(Relocatable, "relocatable", 0) \ K(Relocatable, "relocatable", 0) \
T(String, "string", 0) \
K(Switch, "switch", 0) \ K(Switch, "switch", 0) \
K(Throw, "throw", 0) \ K(Throw, "throw", 0) \
K(Try, "try", 0) \ K(Try, "try", 0) \

20
libsolidity/Types.cpp

@ -145,6 +145,8 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
return make_shared<BoolType>(); return make_shared<BoolType>();
else if (_typeToken == Token::Bytes) else if (_typeToken == Token::Bytes)
return make_shared<ArrayType>(ArrayType::Location::Storage); return make_shared<ArrayType>(ArrayType::Location::Storage);
else if (_typeToken == Token::String)
return make_shared<ArrayType>(ArrayType::Location::Storage, true);
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); std::string(Token::toString(_typeToken)) + " to type."));
@ -663,7 +665,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
// let us not allow assignment to memory arrays for now // let us not allow assignment to memory arrays for now
if (convertTo.getLocation() != Location::Storage) if (convertTo.getLocation() != Location::Storage)
return false; return false;
if (convertTo.isByteArray() != isByteArray()) if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false; return false;
if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType())) if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType()))
return false; return false;
@ -684,8 +686,12 @@ bool ArrayType::operator==(Type const& _other) const
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
return false; return false;
ArrayType const& other = dynamic_cast<ArrayType const&>(_other); ArrayType const& other = dynamic_cast<ArrayType const&>(_other);
if (other.m_location != m_location || other.isByteArray() != isByteArray() || if (
other.isDynamicallySized() != isDynamicallySized()) other.m_location != m_location ||
other.isByteArray() != isByteArray() ||
other.isString() != isString() ||
other.isDynamicallySized() != isDynamicallySized()
)
return false; return false;
return isDynamicallySized() || getLength() == other.getLength(); return isDynamicallySized() || getLength() == other.getLength();
} }
@ -736,7 +742,9 @@ unsigned ArrayType::getSizeOnStack() const
string ArrayType::toString() const string ArrayType::toString() const
{ {
if (isByteArray()) if (isString())
return "string";
else if (isByteArray())
return "bytes"; return "bytes";
string ret = getBaseType()->toString() + "["; string ret = getBaseType()->toString() + "[";
if (!isDynamicallySized()) if (!isDynamicallySized())
@ -746,7 +754,7 @@ string ArrayType::toString() const
TypePointer ArrayType::externalType() const TypePointer ArrayType::externalType() const
{ {
if (m_isByteArray) if (m_arrayKind != ArrayKind::Ordinary)
return shared_from_this(); return shared_from_this();
if (!m_baseType->externalType()) if (!m_baseType->externalType())
return TypePointer(); return TypePointer();
@ -762,7 +770,7 @@ TypePointer ArrayType::externalType() const
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
{ {
auto copy = make_shared<ArrayType>(_location); auto copy = make_shared<ArrayType>(_location);
copy->m_isByteArray = m_isByteArray; copy->m_arrayKind = m_arrayKind;
if (m_baseType->getCategory() == Type::Category::Array) if (m_baseType->getCategory() == Type::Category::Array)
copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location); copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location);
else else

22
libsolidity/Types.h

@ -367,10 +367,10 @@ public:
virtual Category getCategory() const override { return Category::Array; } virtual Category getCategory() const override { return Category::Array; }
/// Constructor for a byte array ("bytes") /// Constructor for a byte array ("bytes") and string.
explicit ArrayType(Location _location): explicit ArrayType(Location _location, bool _isString = false):
m_location(_location), m_location(_location),
m_isByteArray(true), m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
m_baseType(std::make_shared<FixedBytesType>(1)) m_baseType(std::make_shared<FixedBytesType>(1))
{} {}
/// Constructor for a dynamically sized array type ("type[]") /// Constructor for a dynamically sized array type ("type[]")
@ -394,11 +394,17 @@ public:
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual unsigned getSizeOnStack() const override; virtual unsigned getSizeOnStack() const override;
virtual std::string toString() const override; virtual std::string toString() const override;
virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; } virtual MemberList const& getMembers() const override
{
return isString() ? EmptyMemberList : s_arrayTypeMemberList;
}
virtual TypePointer externalType() const override; virtual TypePointer externalType() const override;
Location getLocation() const { return m_location; } Location getLocation() const { return m_location; }
bool isByteArray() const { return m_isByteArray; } /// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
/// @returns true if this is a string
bool isString() const { return m_arrayKind == ArrayKind::String; }
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; } u256 const& getLength() const { return m_length; }
@ -407,8 +413,12 @@ public:
std::shared_ptr<ArrayType> copyForLocation(Location _location) const; std::shared_ptr<ArrayType> copyForLocation(Location _location) const;
private: private:
/// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String };
Location m_location; Location m_location;
bool m_isByteArray = false; ///< Byte arrays ("bytes") have different semantics from ordinary arrays. ///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
ArrayKind m_arrayKind = ArrayKind::Ordinary;
TypePointer m_baseType; TypePointer m_baseType;
bool m_hasDynamicLength = true; bool m_hasDynamicLength = true;
u256 m_length; u256 m_length;

4
mix/CodeModel.cpp

@ -523,7 +523,9 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
case Type::Category::Array: case Type::Category::Array:
{ {
ArrayType const* array = dynamic_cast<ArrayType const*>(_type); ArrayType const* array = dynamic_cast<ArrayType const*>(_type);
if (array->isByteArray()) if (array->isString())
r.type = SolidityType::Type::String;
else if (array->isByteArray())
r.type = SolidityType::Type::Bytes; r.type = SolidityType::Type::Bytes;
else else
{ {

35
mix/QBigInt.cpp

@ -57,3 +57,38 @@ QBigInt* QBigInt::divide(QBigInt* const& _value) const
BigIntVariant toDivide = _value->internalValue(); BigIntVariant toDivide = _value->internalValue();
return new QBigInt(boost::apply_visitor(mix::divide(), m_internalValue, toDivide)); return new QBigInt(boost::apply_visitor(mix::divide(), m_internalValue, toDivide));
} }
QVariantMap QBigInt::checkAgainst(QString const& _type) const
{
QVariantMap ret;
QString type = _type;
QString capacity = type.replace("uint", "").replace("int", "");
if (capacity.isEmpty())
capacity = "256";
bigint range = 256^(capacity.toInt() / 8);
bigint value = boost::get<bigint>(this->internalValue());
ret.insert("valid", true);
if (_type.startsWith("uint") && value > range - 1)
{
ret.insert("minValue", "0");
std::ostringstream s;
s << range - 1;
ret.insert("maxValue", QString::fromStdString(s.str()));
if (value > range)
ret["valid"] = false;
}
else if (_type.startsWith("int"))
{
range = range / 2;
std::ostringstream s;
s << -range;
ret.insert("minValue", QString::fromStdString(s.str()));
s.str("");
s.clear();
s << range - 1;
ret.insert("maxValue", QString::fromStdString(s.str()));
if (-range > value || value > range - 1)
ret["valid"] = false;
}
return ret;
}

3
mix/QBigInt.h

@ -84,6 +84,7 @@ public:
Q_INVOKABLE QString value() const; Q_INVOKABLE QString value() const;
/// Set the value of the BigInteger used. Will use u256 type. Invokable from QML. /// Set the value of the BigInteger used. Will use u256 type. Invokable from QML.
Q_INVOKABLE void setValue(QString const& _value) { m_internalValue = dev::jsToU256(_value.toStdString()); } Q_INVOKABLE void setValue(QString const& _value) { m_internalValue = dev::jsToU256(_value.toStdString()); }
Q_INVOKABLE void setBigInt(QString const& _value) { m_internalValue = bigint(_value.toStdString()); }
/// Subtract by @a _value. Invokable from QML. /// Subtract by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* subtract(QBigInt* const& _value) const; Q_INVOKABLE QBigInt* subtract(QBigInt* const& _value) const;
/// Add @a _value to the current big integer. Invokable from QML. /// Add @a _value to the current big integer. Invokable from QML.
@ -92,6 +93,8 @@ public:
Q_INVOKABLE QBigInt* multiply(QBigInt* const& _value) const; Q_INVOKABLE QBigInt* multiply(QBigInt* const& _value) const;
/// divide by @a _value. Invokable from QML. /// divide by @a _value. Invokable from QML.
Q_INVOKABLE QBigInt* divide(QBigInt* const& _value) const; Q_INVOKABLE QBigInt* divide(QBigInt* const& _value) const;
/// check if the current value satisfy the given type
Q_INVOKABLE QVariantMap checkAgainst(QString const& _type) const;
protected: protected:
BigIntVariant m_internalValue; BigIntVariant m_internalValue;

1
mix/SolidityType.h

@ -45,6 +45,7 @@ struct SolidityType
Bool, Bool,
Address, Address,
Bytes, Bytes,
String,
Enum, Enum,
Struct Struct
}; };

1
mix/qml.qrc

@ -63,5 +63,6 @@
<file>qml/js/Printer.js</file> <file>qml/js/Printer.js</file>
<file>qml/js/ansi2html.js</file> <file>qml/js/ansi2html.js</file>
<file>qml/js/NetworkDeployment.js</file> <file>qml/js/NetworkDeployment.js</file>
<file>qml/js/InputValidator.js</file>
</qresource> </qresource>
</RCC> </RCC>

7
mix/qml/QIntTypeView.qml

@ -21,8 +21,15 @@ Item
clip: true clip: true
selectByMouse: true selectByMouse: true
text: value text: value
anchors.fill: parent
font.pointSize: dbgStyle.general.basicFontSize font.pointSize: dbgStyle.general.basicFontSize
color: dbgStyle.general.basicColor color: dbgStyle.general.basicColor
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
} }
} }
} }

23
mix/qml/TransactionDialog.qml

@ -6,6 +6,7 @@ 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 "js/InputValidator.js" as InputValidator
import "." import "."
Dialog { Dialog {
@ -503,10 +504,22 @@ Dialog {
anchors.right: parent.right; anchors.right: parent.right;
Button { Button {
text: qsTr("OK"); text: qsTr("OK");
onClicked: { onClicked: {
close(); var invalid = InputValidator.validate(paramsModel, paramValues);
accepted(); if (invalid.length === 0)
{
close();
accepted();
}
else
{
errorDialog.text = qsTr("Some parameters are invalid:\n");
for (var k in invalid)
errorDialog.text += invalid[k].message + "\n";
errorDialog.open();
}
} }
} }
@ -514,6 +527,12 @@ Dialog {
text: qsTr("Cancel"); text: qsTr("Cancel");
onClicked: close(); onClicked: close();
} }
MessageDialog {
id: errorDialog
standardButtons: StandardButton.Ok
icon: StandardIcon.Critical
}
} }
} }
} }

125
mix/qml/js/InputValidator.js

@ -0,0 +1,125 @@
Qt.include("QEtherHelper.js")
var nbRegEx = new RegExp('^[0-9]+$');
function validate(model, values)
{
var inError = [];
for (var k in model)
{
if (values[model[k].name])
{
var type = model[k].type.name;
var res;
if (isContractType(type))
res = validateAddress(type, values[model[k].name]);
else if (type.indexOf("int") !== -1)
res = validateInt(type, values[model[k].name]);
else if (type.indexOf("bytes") !== -1)
res = validateBytes(type, values[model[k].name]);
else if (type.indexOf("bool") !== -1)
res = validateBool(type, values[model[k].name]);
else if (type.indexOf("address") !== -1)
res = validateAddress(type, values[model[k].name]);
else
res.valid = true;
if (!res.valid)
inError.push({ type: type, value: values, message: res.message });
}
}
return inError;
}
function isContractType(_type)
{
for (var k in Object.keys(codeModel.contracts))
{
if ("contract " + Object.keys(codeModel.contracts)[k] === _type)
return true;
}
return false;
}
function validateInt(_type, _value)
{
var ret = { valid: true, message: "" }
if (_value.indexOf("-") === 0)
{
_value = _value.substring(1);
if (_type.indexOf("uint") === -1)
{
ret.valid = false;
ret.message = "uint type cannot represent negative number";
}
}
ret.valid = nbRegEx.test(_value);
if (!ret.valid)
ret.message = _value + " does not represent " + _type + " type.";
else
{
var bigInt = createBigInt(_value);
bigInt.setBigInt(_value);
var result = bigInt.checkAgainst(_type);
if (!result.valid)
{
ret.valid = false;
ret.message = _type + " should be between " + result.minValue + " and " + result.maxValue;
}
}
return ret;
}
function validateAddress(_type, _value)
{
var ret = { valid: true, message: "" }
if (_value.indexOf("<") === 0 && _value.indexOf(">") === _value.length - 1)
{
var v = _value.split(' - ');
if (v.length !== 2 || !nbRegEx.test(v[1].replace(">", ""))) // <Contract - 2>
{
ret.valid = false;
ret.message = _value + " is not a valid token for address type.";
}
}
else if (_value.indexOf("0x") !== 0)
{
ret.valid = false
ret.message = "Address type should start with 0x.";
}
else
{
_value = _value.substring(2);
if (_value.length !== 40)
{
ret.valid = false
ret.message = "Address type should contain 40 characters.";
}
}
return ret;
}
function validateBytes(_type, _value)
{
var ret = { valid: true, message: "" }
if (_value.length > parseInt(_type.replace("bytes", "")) )
{
ret.valid = false;
ret.message = _type + " should not contains more than " + _type.replace("bytes", "") + " characters";
}
return ret;
}
function validateBool(_type, _value)
{
var ret = { valid: true, message: "" }
if (_value !== "1" && _value !== "0")
{
ret.valid = false;
ret.message = _value + " is not in the correct bool format";
}
return ret;
}
function validateEnum(_type, _value)
{
}

57
solc/jsonCompiler.cpp

@ -27,6 +27,7 @@
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevmcore/Params.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h> #include <libsolidity/ASTPrinter.h>
@ -58,6 +59,61 @@ Json::Value functionHashes(ContractDefinition const& _contract)
return functionHashes; return functionHashes;
} }
Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
{
if (_gas.isInfinite || _gas.value > std::numeric_limits<Json::LargestUInt>::max())
return Json::Value(Json::nullValue);
else
return Json::Value(Json::LargestUInt(_gas.value));
}
Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
{
Json::Value gasEstimates(Json::objectValue);
using Gas = GasEstimator::GasConsumption;
if (!_compiler.getAssemblyItems(_contract) && !_compiler.getRuntimeAssemblyItems(_contract))
return gasEstimates;
if (eth::AssemblyItems const* items = _compiler.getAssemblyItems(_contract))
{
Gas gas = GasEstimator::functionalEstimation(*items);
u256 bytecodeSize(_compiler.getRuntimeBytecode(_contract).size());
Json::Value creationGas(Json::arrayValue);
creationGas[0] = gasToJson(gas);
creationGas[1] = gasToJson(bytecodeSize * eth::c_createDataGas);
gasEstimates["creation"] = creationGas;
}
if (eth::AssemblyItems const* items = _compiler.getRuntimeAssemblyItems(_contract))
{
ContractDefinition const& contract = _compiler.getContractDefinition(_contract);
Json::Value externalFunctions(Json::objectValue);
for (auto it: contract.getInterfaceFunctions())
{
string sig = it.second->externalSignature();
externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
}
gasEstimates["external"] = externalFunctions;
Json::Value internalFunctions(Json::objectValue);
for (auto const& it: contract.getDefinedFunctions())
{
if (it->isPartOfExternalInterface() || it->isConstructor())
continue;
size_t entry = _compiler.getFunctionEntryPoint(_contract, *it);
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
if (entry > 0)
gas = GasEstimator::functionalEstimation(*items, entry, *it);
FunctionType type(*it);
string sig = it->getName() + "(";
auto end = type.getParameterTypes().end();
for (auto it = type.getParameterTypes().begin(); it != end; ++it)
sig += (*it)->toString() + (it + 1 == end ? "" : ",");
sig += ")";
internalFunctions[sig] = gasToJson(gas);
}
gasEstimates["internal"] = internalFunctions;
}
return gasEstimates;
}
string compile(string _input, bool _optimize) string compile(string _input, bool _optimize)
{ {
StringMap sources; StringMap sources;
@ -109,6 +165,7 @@ string compile(string _input, bool _optimize)
contractData["bytecode"] = toHex(compiler.getBytecode(contractName)); contractData["bytecode"] = toHex(compiler.getBytecode(contractName));
contractData["opcodes"] = eth::disassemble(compiler.getBytecode(contractName)); contractData["opcodes"] = eth::disassemble(compiler.getBytecode(contractName));
contractData["functionHashes"] = functionHashes(compiler.getContractDefinition(contractName)); contractData["functionHashes"] = functionHashes(compiler.getContractDefinition(contractName));
contractData["gasEstimates"] = estimateGas(compiler, contractName);
ostringstream unused; ostringstream unused;
contractData["assembly"] = compiler.streamAssembly(unused, contractName, sources, true); contractData["assembly"] = compiler.streamAssembly(unused, contractName, sources, true);
output["contracts"][contractName] = contractData; output["contracts"][contractName] = contractData;

7
test/TestHelper.cpp

@ -328,7 +328,7 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost)
{ {
// export output // export output
m_TestObject["out"] = _output.size() > 4096 ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add); m_TestObject["out"] = (_output.size() > 4096 && !Options::get().fulloutput) ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add);
// export logs // export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
@ -588,7 +588,7 @@ void userDefinedTest(std::function<void(json_spirit::mValue&, bool)> doTests)
oSingleTest[pos->first] = pos->second; oSingleTest[pos->first] = pos->second;
json_spirit::mValue v_singleTest(oSingleTest); json_spirit::mValue v_singleTest(oSingleTest);
doTests(v_singleTest, false); doTests(v_singleTest, test::Options::get().fillTests);
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {
@ -760,6 +760,8 @@ Options::Options()
else else
singleTestName = std::move(name1); singleTestName = std::move(name1);
} }
else if (arg == "--fulloutput")
fulloutput = true;
} }
} }
@ -769,7 +771,6 @@ Options const& Options::get()
return instance; return instance;
} }
LastHashes lastHashes(u256 _currentBlockNumber) LastHashes lastHashes(u256 _currentBlockNumber)
{ {
LastHashes ret; LastHashes ret;

1
test/TestHelper.h

@ -184,6 +184,7 @@ public:
bool stats = false; ///< Execution time stats bool stats = false; ///< Execution time stats
std::string statsOutFile; ///< Stats output file. "out" for standard output std::string statsOutFile; ///< Stats output file. "out" for standard output
bool checkState = false;///< Throw error when checking test states bool checkState = false;///< Throw error when checking test states
bool fulloutput = false;///< Replace large output to just it's length
/// Test selection /// Test selection
/// @{ /// @{

7
test/fuzzTesting/CMakeLists.txt

@ -9,10 +9,13 @@ include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp")
add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp")
add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp" )
add_executable(checkRandomStateTest "./checkRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(checkRandomStateTest "./checkRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp")
list(APPEND SRCS "./fuzzHelper.cpp")
add_sources(${SRCS})
target_link_libraries(createRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(createRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomVMTest ethereum) target_link_libraries(createRandomVMTest ethereum)
target_link_libraries(createRandomVMTest ethcore) target_link_libraries(createRandomVMTest ethcore)

204
test/fuzzTesting/createRandomStateTest.cpp

@ -37,24 +37,88 @@
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <test/libevm/vm.h> #include <test/libevm/vm.h>
#include <test/TestHelper.h> #include <test/TestHelper.h>
#include <test/fuzzTesting/fuzzHelper.h>
using namespace std; using namespace std;
using namespace json_spirit; using namespace json_spirit;
using namespace dev; using namespace dev;
void doStateTests(json_spirit::mValue& _v); void doStateTests(json_spirit::mValue& _v);
void doChristophAlgo();
void doRandomCodeAlgo();
string const c_testExample = R"(
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x42",
"gasLimit" : "400000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
}
}
)";
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{
for (auto i = 0; i < argc; ++i)
{
auto arg = std::string{argv[i]};
dev::test::Options& options = const_cast<dev::test::Options&>(dev::test::Options::get());
if (arg == "--fulloutput")
options.fulloutput = true;
}
//doChristophAlgo();
doRandomCodeAlgo();
return 0;
}
void doChristophAlgo()
{ {
g_logVerbosity = 0; g_logVerbosity = 0;
// create random code // create random code
boost::random::mt19937 gen; boost::random::mt19937 gen;
auto now = chrono::steady_clock::now().time_since_epoch(); auto now = chrono::steady_clock::now().time_since_epoch();
auto timeSinceEpoch = chrono::duration_cast<chrono::nanoseconds>(now).count(); auto timeSinceEpoch = chrono::duration_cast<chrono::nanoseconds>(now).count();
gen.seed(static_cast<unsigned int>(timeSinceEpoch)); gen.seed(static_cast<unsigned int>(timeSinceEpoch));
// set min and max length of the random evm code // set min and max length of the random evm code
boost::random::uniform_int_distribution<> lengthOfCodeDist(8, 24); boost::random::uniform_int_distribution<> lengthOfCodeDist(8, 24);
boost::random::uniform_int_distribution<> reasonableInputValuesSize(0, 7); boost::random::uniform_int_distribution<> reasonableInputValuesSize(0, 7);
@ -77,18 +141,16 @@ int main(int argc, char *argv[])
reasonableInputValues.push_back(u256("0x945304eb96065b2a98b57a48a06ae28d285a71b5")); reasonableInputValues.push_back(u256("0x945304eb96065b2a98b57a48a06ae28d285a71b5"));
reasonableInputValues.push_back(randGenUniformInt()); reasonableInputValues.push_back(randGenUniformInt());
int lengthOfCode = lengthOfCodeDist(gen); int lengthOfCode = lengthOfCodeDist(gen);
string randomCode; string randomCode;
for (int i = 0; i < lengthOfCode; ++i) for (int i = 0; i < lengthOfCode; ++i)
{ {
// pre-fill stack to avoid that most of the test fail with a stackunderflow // pre-fill stack to avoid that most of the test fail with a stackunderflow
if (i < 8 && (randGen() < 192)) if (i < 8 && (randGen() < 192))
{ {
randomCode += randGen() < 32 ? toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode())) : "7f" + toHex(reasonableInputValues[randGenInputValue()]); randomCode += randGen() < 32 ? toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode())) : "7f" + toHex(reasonableInputValues[randGenInputValue()]);
continue; continue;
} }
uint8_t opcode = randGen(); uint8_t opcode = randGen();
// disregard all invalid commands, except of one (0x0c) // disregard all invalid commands, except of one (0x0c)
if ((dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || (randGen() > 250))) if ((dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || (randGen() > 250)))
@ -97,74 +159,58 @@ int main(int argc, char *argv[])
i--; i--;
} }
string const s = R"(
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x42",
"gasLimit" : "400000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
}
}
)";
mValue v; mValue v;
read_string(s, v); read_string(c_testExample, v);
// insert new random code // insert new random code
v.get_obj().find("randomStatetest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode + (randGen() > 128 ? "55" : "") + (randGen() > 128 ? "60005155" : ""); v.get_obj().find("randomStatetest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode + (randGen() > 128 ? "55" : "") + (randGen() > 128 ? "60005155" : "");
// insert new data in tx // insert new data in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["data"] = "0x" + randomCode; v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["data"] = "0x" + randomCode;
// insert new value in tx // insert new value in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["value"] = toString(randGenUniformInt()); v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["value"] = toString(randGenUniformInt());
// insert new gasLimit in tx // insert new gasLimit in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["gasLimit"] = "0x" + toHex(toCompactBigEndian((int)randGenUniformInt())); v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["gasLimit"] = "0x" + toHex(toCompactBigEndian((int)randGenUniformInt()));
// fill test
doStateTests(v);
// stream to output for further handling by the bash script
cout << json_spirit::write_string(v, true);
}
void doRandomCodeAlgo()
{
g_logVerbosity = 0;
dev::test::RandomCodeOptions options;
options.setWeight(dev::eth::Instruction::STOP, 10); //default 50
options.setWeight(dev::eth::Instruction::SSTORE, 70);
options.setWeight(dev::eth::Instruction::CALL, 75);
options.addAddress(Address("0xffffffffffffffffffffffffffffffffffffffff"));
options.addAddress(Address("0x1000000000000000000000000000000000000000"));
options.addAddress(Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87")); //coinbase
options.addAddress(Address("0x945304eb96065b2a98b57a48a06ae28d285a71b5"));
options.addAddress(Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"));
options.smartCodeProbability = 35;
string randomCode = dev::test::RandomCode::generate(10, options);
string randomData = dev::test::RandomCode::generate(10, options);
mValue v;
read_string(c_testExample, v);
// insert new random code
v.get_obj().find("randomStatetest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode;
// insert new data in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["data"] = "0x" + randomData;
// insert new value in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["value"] = dev::test::RandomCode::randomUniIntHex();
// insert new gasLimit in tx
v.get_obj().find("randomStatetest")->second.get_obj().find("transaction")->second.get_obj()["gasLimit"] = dev::test::RandomCode::randomUniIntHex();
// fill test // fill test
doStateTests(v); doStateTests(v);
// stream to output for further handling by the bash script // stream to output for further handling by the bash script
cout << json_spirit::write_string(v, true); cout << json_spirit::write_string(v, true);
return 0;
} }
void doStateTests(json_spirit::mValue& _v) void doStateTests(json_spirit::mValue& _v)
@ -179,30 +225,34 @@ void doStateTests(json_spirit::mValue& _v)
assert(o.count("env") > 0); assert(o.count("env") > 0);
assert(o.count("pre") > 0); assert(o.count("pre") > 0);
assert(o.count("transaction") > 0); assert(o.count("transaction") > 0);
test::ImportTest importer(o, true);
eth::State theState = importer.m_statePre;
bytes output; bytes output;
try try
{ {
output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; test::ImportTest importer(o, true);
} eth::State theState = importer.m_statePre;
catch (Exception const& _e) try
{ {
cnote << "state execution did throw an exception: " << diagnostic_information(_e); output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output;
theState.commit(); }
} catch (Exception const& _e)
catch (std::exception const& _e) {
{ cnote << "state execution did throw an exception: " << diagnostic_information(_e);
cnote << "state execution did throw an exception: " << _e.what(); theState.commit();
} }
catch (std::exception const& _e)
{
cnote << "state execution did throw an exception: " << _e.what();
}
#if ETH_FATDB #if ETH_FATDB
importer.exportTest(output, theState); importer.exportTest(output, theState);
#else #else
cout << "You can not fill tests when FATDB is switched off"; cout << "You can not fill tests when FATDB is switched off";
#endif #endif
}
catch(...)
{
cnote << "Error filling test, probably...";
}
} }
} }

224
test/fuzzTesting/fuzzHelper.cpp

@ -0,0 +1,224 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file fuzzHelper.cpp
* @author Dimitry Khokhlov <winsvega@mail.ru>
* @date 2015
*/
#include "fuzzHelper.h"
#include <chrono>
#include <boost/random.hpp>
#include <boost/filesystem/path.hpp>
#include <libevmcore/Instruction.h>
namespace dev
{
namespace test
{
boost::random::mt19937 RandomCode::gen;
boostIntDistrib RandomCode::opCodeDist = boostIntDistrib (0, 255);
boostIntDistrib RandomCode::opLengDist = boostIntDistrib (1, 32);
boostIntDistrib RandomCode::uniIntDist = boostIntDistrib (0, 0x7fffffff);
boostIntGenerator RandomCode::randOpCodeGen = boostIntGenerator(gen, opCodeDist);
boostIntGenerator RandomCode::randOpLengGen = boostIntGenerator(gen, opLengDist);
boostIntGenerator RandomCode::randUniIntGen = boostIntGenerator(gen, uniIntDist);
std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType)
{
refreshSeed();
std::string hash;
_length = (_sizeType == SizeStrictness::Strict) ? std::max(1, _length) : randomUniInt() % _length;
for (auto i = 0; i < _length; i++)
{
uint8_t byte = randOpCodeGen();
hash += toCompactHex(byte);
}
return hash;
}
//generate smart random code
std::string RandomCode::generate(int _maxOpNumber, RandomCodeOptions _options)
{
refreshSeed();
std::string code;
//random opCode amount
boostIntDistrib sizeDist (0, _maxOpNumber);
boostIntGenerator rndSizeGen(gen, sizeDist);
int size = (int)rndSizeGen();
boostWeightGenerator randOpCodeWeight (gen, _options.opCodeProbability);
bool weightsDefined = _options.opCodeProbability.probabilities().size() == 255;
for (auto i = 0; i < size; i++)
{
uint8_t opcode = weightsDefined ? randOpCodeWeight() : randOpCodeGen();
dev::eth::InstructionInfo info = dev::eth::instructionInfo((dev::eth::Instruction) opcode);
if (info.name.find_first_of("INVALID_INSTRUCTION") > 0)
{
//Byte code is yet not implemented
if (_options.useUndefinedOpCodes == false)
{
i--;
continue;
}
}
else
code += fillArguments((dev::eth::Instruction) opcode, _options);
std::string byte = toCompactHex(opcode);
code += (byte == "") ? "00" : byte;
}
return code;
}
std::string RandomCode::randomUniIntHex()
{
refreshSeed();
return "0x" + toCompactHex((int)randUniIntGen());
}
int RandomCode::randomUniInt()
{
refreshSeed();
return (int)randUniIntGen();
}
void RandomCode::refreshSeed()
{
auto now = std::chrono::steady_clock::now().time_since_epoch();
auto timeSinceEpoch = std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
gen.seed(static_cast<unsigned int>(timeSinceEpoch));
}
std::string RandomCode::getPushCode(std::string const& _hex)
{
int length = _hex.length() / 2;
int pushCode = 96 + length - 1;
return toCompactHex(pushCode) + _hex;
}
std::string RandomCode::getPushCode(int _value)
{
std::string hexString = toCompactHex(_value);
return getPushCode(hexString);
}
std::string RandomCode::fillArguments(dev::eth::Instruction _opcode, RandomCodeOptions const& _options)
{
dev::eth::InstructionInfo info = dev::eth::instructionInfo(_opcode);
std::string code;
bool smart = false;
unsigned num = info.args;
int rand = randUniIntGen() % 100;
if (rand < _options.smartCodeProbability)
smart = true;
if (smart)
{
switch (_opcode)
{
case dev::eth::Instruction::CALL:
//(CALL gaslimit address value memstart1 memlen1 memstart2 memlen2)
code += getPushCode(randUniIntGen() % 32); //memlen2
code += getPushCode(randUniIntGen() % 32); //memstart2
code += getPushCode(randUniIntGen() % 32); //memlen1
code += getPushCode(randUniIntGen() % 32); //memlen1
code += getPushCode(randUniIntGen()); //value
code += getPushCode(toString(_options.getRandomAddress()));//address
code += getPushCode(randUniIntGen()); //gaslimit
break;
default:
smart = false;
}
}
if (smart == false)
for (unsigned i = 0; i < num; i++)
{
//generate random parameters
int length = randOpLengGen();
code += getPushCode(rndByteSequence(length));
}
return code;
}
//Ramdom Code Options
RandomCodeOptions::RandomCodeOptions() : useUndefinedOpCodes(false), smartCodeProbability(50)
{
//each op code with same weight-probability
for (auto i = 0; i < 255; i++)
mapWeights.insert(std::pair<int, int>(i, 50));
setWeights();
}
void RandomCodeOptions::setWeight(dev::eth::Instruction _opCode, int _weight)
{
mapWeights.at((int)_opCode) = _weight;
setWeights();
}
void RandomCodeOptions::addAddress(dev::Address const& _address)
{
addressList.push_back(_address);
}
dev::Address RandomCodeOptions::getRandomAddress() const
{
if (addressList.size() > 0)
{
int index = RandomCode::randomUniInt() % addressList.size();
return addressList[index];
}
return Address(RandomCode::rndByteSequence(20));
}
void RandomCodeOptions::setWeights()
{
std::vector<int> weights;
for (auto const& element: mapWeights)
weights.push_back(element.second);
opCodeProbability = boostDescreteDistrib(weights);
}
BOOST_AUTO_TEST_SUITE(RandomCodeTests)
BOOST_AUTO_TEST_CASE(rndCode)
{
std::string code;
std::cerr << "Testing Random Code: ";
try
{
code = dev::test::RandomCode::generate(10);
}
catch(...)
{
BOOST_ERROR("Exception thrown when generating random code!");
}
std::cerr << code;
}
BOOST_AUTO_TEST_SUITE_END()
}
}

97
test/fuzzTesting/fuzzHelper.h

@ -0,0 +1,97 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file fuzzHelper.h
* @author Dimitry Khokhlov <winsvega@mail.ru>
* @date 2015
*/
#include <string>
#include <boost/random.hpp>
#include <boost/filesystem/path.hpp>
#include <test/TestHelper.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libevmcore/Instruction.h>
#pragma once
namespace dev
{
namespace test
{
typedef boost::random::uniform_int_distribution<> boostIntDistrib;
typedef boost::random::discrete_distribution<> boostDescreteDistrib;
typedef boost::random::variate_generator<boost::mt19937&, boostIntDistrib > boostIntGenerator;
typedef boost::random::variate_generator<boost::mt19937&, boostDescreteDistrib > boostWeightGenerator;
struct RandomCodeOptions
{
public:
RandomCodeOptions();
void setWeight(dev::eth::Instruction _opCode, int _weight);
void addAddress(dev::Address const& _address);
dev::Address getRandomAddress() const;
bool useUndefinedOpCodes;
int smartCodeProbability;
boostDescreteDistrib opCodeProbability;
private:
void setWeights();
std::map<int, int> mapWeights;
std::vector<dev::Address> addressList;
};
enum class SizeStrictness
{
Strict,
Random
};
class RandomCode
{
public:
/// Generate random vm code
static std::string generate(int _maxOpNumber = 1, RandomCodeOptions _options = RandomCodeOptions());
/// Generate random byte string of a given length
static std::string rndByteSequence(int _length = 1, SizeStrictness _sizeType = SizeStrictness::Strict);
/// Generate random uniForm Int with reasonable value 0..0x7fffffff
static std::string randomUniIntHex();
static int randomUniInt();
private:
static std::string fillArguments(dev::eth::Instruction _opcode, RandomCodeOptions const& _options);
static std::string getPushCode(int _value);
static std::string getPushCode(std::string const& _hex);
static void refreshSeed();
static boost::random::mt19937 gen; ///< Random generator
static boostIntDistrib opCodeDist; ///< 0..255 opcodes
static boostIntDistrib opLengDist; ///< 1..32 byte string
static boostIntDistrib uniIntDist; ///< 0..0x7fffffff
static boostIntGenerator randUniIntGen; ///< Generate random UniformInt from uniIntDist
static boostIntGenerator randOpCodeGen; ///< Generate random value from opCodeDist
static boostIntGenerator randOpLengGen; ///< Generate random length from opLengDist
};
}
}

102
test/libethereum/BlockTestsFiller/bcUncleTestFiller.json

@ -85,6 +85,108 @@
] ]
}, },
"UncleIsBrother" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "131072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"expect" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "20"
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"nonce" : "2"
}
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"nonce" : "0",
"code" : "",
"storage": {}
}
},
"blocks" : [
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "1",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"transactions" : [
{
"data" : "",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "2",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10"
}
],
"uncleHeaders" : [
{
"overwriteAndRedoPoW" : "parentHashIsBlocksParent",
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b",
"difficulty" : "131072",
"extraData" : "0x",
"gasLimit" : "4141592",
"gasUsed" : "150000",
"hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04",
"mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770",
"nonce" : "18a524c1790fa83b",
"number" : "2",
"parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd",
"timestamp" : "142813170",
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}
]
}
]
},
"uncleHeaderWithGeneration0" : { "uncleHeaderWithGeneration0" : {
"genesisBlockHeader" : { "genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",

20
test/libethereum/blockchain.cpp

@ -19,7 +19,7 @@
* @date 2015 * @date 2015
* block test functions. * block test functions.
*/ */
#include "test/fuzzTesting/fuzzHelper.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <libdevcore/FileSystem.h> #include <libdevcore/FileSystem.h>
#include <libdevcore/TransientDirectory.h> #include <libdevcore/TransientDirectory.h>
@ -214,9 +214,12 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
uncleStream.appendRaw(uncleRlp.out()); uncleStream.appendRaw(uncleRlp.out());
} }
// update unclehash in case of invalid uncles if (vBiUncles.size())
current_BlockHeader.sha3Uncles = sha3(uncleStream.out()); {
updatePoW(current_BlockHeader); // update unclehash in case of invalid uncles
current_BlockHeader.sha3Uncles = sha3(uncleStream.out());
updatePoW(current_BlockHeader);
}
if (blObj.count("blockHeader")) if (blObj.count("blockHeader"))
overwriteBlockHeader(current_BlockHeader, blObj); overwriteBlockHeader(current_BlockHeader, blObj);
@ -242,7 +245,10 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
blObj["rlp"] = toHex(block2.out(), 2, HexPrefix::Add); blObj["rlp"] = toHex(block2.out(), 2, HexPrefix::Add);
if (sha3(RLP(state.blockData())[0].data()) != sha3(RLP(block2.out())[0].data())) if (sha3(RLP(state.blockData())[0].data()) != sha3(RLP(block2.out())[0].data()))
cnote << "block header mismatch\n"; {
cnote << "block header mismatch state.blockData() vs updated state.info()\n";
cerr << toHex(state.blockData()) << "vs" << toHex(block2.out());
}
if (sha3(RLP(state.blockData())[1].data()) != sha3(RLP(block2.out())[1].data())) if (sha3(RLP(state.blockData())[1].data()) != sha3(RLP(block2.out())[1].data()))
cnote << "txs mismatch\n"; cnote << "txs mismatch\n";
@ -547,6 +553,10 @@ mArray importUncles(mObject const& blObj, vector<BlockInfo>& vBiUncles, vector<B
uncleBlockFromFields.gasUsed = overwrite == "gasUsed" ? toInt(uncleHeaderObj["gasUsed"]) : uncleBlockFromFields.gasUsed; uncleBlockFromFields.gasUsed = overwrite == "gasUsed" ? toInt(uncleHeaderObj["gasUsed"]) : uncleBlockFromFields.gasUsed;
uncleBlockFromFields.parentHash = overwrite == "parentHash" ? h256(uncleHeaderObj["parentHash"].get_str()) : uncleBlockFromFields.parentHash; uncleBlockFromFields.parentHash = overwrite == "parentHash" ? h256(uncleHeaderObj["parentHash"].get_str()) : uncleBlockFromFields.parentHash;
uncleBlockFromFields.stateRoot = overwrite == "stateRoot" ? h256(uncleHeaderObj["stateRoot"].get_str()) : uncleBlockFromFields.stateRoot; uncleBlockFromFields.stateRoot = overwrite == "stateRoot" ? h256(uncleHeaderObj["stateRoot"].get_str()) : uncleBlockFromFields.stateRoot;
if (overwrite == "parentHashIsBlocksParent")
uncleBlockFromFields.populateFromParent(vBiBlocks[vBiBlocks.size() - 1]);
if (overwrite == "timestamp") if (overwrite == "timestamp")
{ {
uncleBlockFromFields.timestamp = toInt(uncleHeaderObj["timestamp"]); uncleBlockFromFields.timestamp = toInt(uncleHeaderObj["timestamp"]);

27
test/libsolidity/SolidityABIJSON.cpp

@ -568,6 +568,33 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
checkInterface(sourceCode, interface); checkInterface(sourceCode, interface);
} }
BOOST_AUTO_TEST_CASE(strings_and_arrays)
{
// bug #1801
char const* sourceCode = R"(
contract test {
function f(string a, bytes b, uint[] c) external {}
}
)";
char const* interface = R"(
[
{
"constant" : false,
"name": "f",
"inputs": [
{ "name": "a", "type": "string" },
{ "name": "b", "type": "bytes" },
{ "name": "c", "type": "uint256[]" }
],
"outputs": [],
"type" : "function"
}
]
)";
checkInterface(sourceCode, interface);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

192
test/libsolidity/SolidityCompiler.cpp

@ -1,192 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Unit tests for the solidity compiler.
*/
#include <string>
#include <iostream>
#include <boost/test/unit_test.hpp>
#include <libdevcore/Log.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/AST.h>
using namespace std;
using namespace dev::eth;
namespace dev
{
namespace solidity
{
namespace test
{
namespace
{
bytes compileContract(const string& _sourceCode)
{
Parser parser;
ASTPointer<SourceUnit> sourceUnit;
BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver({});
resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
Compiler compiler;
compiler.compileContract(*contract, map<ContractDefinition const*, bytes const*>{});
// debug
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode();
}
BOOST_FAIL("No contract found in source.");
return bytes();
}
/// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation.
/// This is necessary since the compiler will add boilerplate add the beginning that is not
/// tested here.
void checkCodePresentAt(bytes const& _compiledCode, bytes const& _expectation, unsigned _offset)
{
BOOST_REQUIRE(_compiledCode.size() >= _offset + _expectation.size());
auto checkStart = _compiledCode.begin() + _offset;
BOOST_CHECK_EQUAL_COLLECTIONS(checkStart, checkStart + _expectation.size(),
_expectation.begin(), _expectation.end());
}
} // end anonymous namespace
BOOST_AUTO_TEST_SUITE(SolidityCompiler)
BOOST_AUTO_TEST_CASE(smoke_test)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = 2; }\n"
"}\n";
bytes code = compileContract(sourceCode);
unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize local variable x
byte(Instruction::PUSH1), 0x2,
byte(Instruction::SWAP1),
byte(Instruction::POP),
byte(Instruction::JUMPDEST),
byte(Instruction::POP),
byte(Instruction::JUMP)});
checkCodePresentAt(code, expectation, boilerplateSize);
}
BOOST_AUTO_TEST_CASE(ifStatement)
{
char const* sourceCode = "contract test {\n"
" function f() { bool x; if (x) 77; else if (!x) 78; else 79; }"
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 60;
unsigned boilerplateSize = 73;
bytes expectation({
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1),
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x0f + shift), // "false" target
byte(Instruction::JUMPI),
// "if" body
byte(Instruction::PUSH1), 0x4d,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::JUMP),
// new check "else if" condition
byte(Instruction::JUMPDEST),
byte(Instruction::DUP1),
byte(Instruction::ISZERO),
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x1c + shift),
byte(Instruction::JUMPI),
// "else if" body
byte(Instruction::PUSH1), 0x4e,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x20 + shift),
byte(Instruction::JUMP),
// "else" body
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x4f,
byte(Instruction::POP),
});
checkCodePresentAt(code, expectation, boilerplateSize);
}
BOOST_AUTO_TEST_CASE(loops)
{
char const* sourceCode = "contract test {\n"
" function f() { while(true){1;break;2;continue;3;return;4;} }"
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 60;
unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x1,
byte(Instruction::ISZERO),
byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::JUMPI),
byte(Instruction::PUSH1), 0x1,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::JUMP), // break
byte(Instruction::PUSH1), 0x2,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x2 + shift),
byte(Instruction::JUMP), // continue
byte(Instruction::PUSH1), 0x3,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x22 + shift),
byte(Instruction::JUMP), // return
byte(Instruction::PUSH1), 0x4,
byte(Instruction::POP),
byte(Instruction::PUSH1), byte(0x2 + shift),
byte(Instruction::JUMP),
byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST),
byte(Instruction::JUMP)});
checkCodePresentAt(code, expectation, boilerplateSize);
}
BOOST_AUTO_TEST_SUITE_END()
}
}
} // end namespaces

66
test/libsolidity/SolidityEndToEndTest.cpp

@ -4080,7 +4080,6 @@ BOOST_AUTO_TEST_CASE(struct_delete_member)
} }
)"; )";
compileAndRun(sourceCode, 0, "test"); compileAndRun(sourceCode, 0, "test");
auto res = callContractFunction("deleteMember()");
BOOST_CHECK(callContractFunction("deleteMember()") == encodeArgs(0)); BOOST_CHECK(callContractFunction("deleteMember()") == encodeArgs(0));
} }
@ -4106,10 +4105,73 @@ BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping)
} }
)"; )";
compileAndRun(sourceCode, 0, "test"); compileAndRun(sourceCode, 0, "test");
auto res = callContractFunction("deleteIt()");
BOOST_CHECK(callContractFunction("deleteIt()") == encodeArgs(0)); BOOST_CHECK(callContractFunction("deleteIt()") == encodeArgs(0));
} }
BOOST_AUTO_TEST_CASE(evm_exceptions_out_of_band_access)
{
char const* sourceCode = R"(
contract A {
uint[3] arr;
bool public test = false;
function getElement(uint i) returns (uint)
{
return arr[i];
}
function testIt() returns (bool)
{
uint i = this.getElement(5);
test = true;
return true;
}
}
)";
compileAndRun(sourceCode, 0, "A");
BOOST_CHECK(callContractFunction("test()") == encodeArgs(false));
BOOST_CHECK(callContractFunction("testIt()") == encodeArgs());
BOOST_CHECK(callContractFunction("test()") == encodeArgs(false));
}
BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_call_fail)
{
char const* sourceCode = R"(
contract A {
function A()
{
this.call("123");
}
}
contract B {
uint public test = 1;
function testIt()
{
A a = new A();
++test;
}
}
)";
compileAndRun(sourceCode, 0, "B");
BOOST_CHECK(callContractFunction("testIt()") == encodeArgs());
BOOST_CHECK(callContractFunction("test()") == encodeArgs(2));
}
BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund)
{
char const* sourceCode = R"(
contract A {
uint public test = 1;
uint[3] arr;
function A()
{
test = arr[5];
++test;
}
}
)";
BOOST_CHECK(compileAndRunWthoutCheck(sourceCode, 0, "A").empty());
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

33
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -1783,6 +1783,39 @@ BOOST_AUTO_TEST_CASE(uninitialized_var)
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
} }
BOOST_AUTO_TEST_CASE(string)
{
char const* sourceCode = R"(
contract C {
string s;
function f(string x) external { s = x; }
}
)";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode));
}
BOOST_AUTO_TEST_CASE(string_index)
{
char const* sourceCode = R"(
contract C {
string s;
function f() { var a = s[2]; }
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(string_length)
{
char const* sourceCode = R"(
contract C {
string s;
function f() { var a = s.length; }
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

32
test/libsolidity/SolidityOptimizer.cpp

@ -1004,6 +1004,38 @@ BOOST_AUTO_TEST_CASE(block_deduplicator)
BOOST_CHECK_EQUAL(pushTags.size(), 2); BOOST_CHECK_EQUAL(pushTags.size(), 2);
} }
BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
{
AssemblyItems input{
u256(0),
eth::Instruction::SLOAD,
AssemblyItem(PushTag, 1),
AssemblyItem(PushTag, 2),
eth::Instruction::JUMPI,
eth::Instruction::JUMP,
AssemblyItem(Tag, 1),
u256(5),
u256(6),
eth::Instruction::SSTORE,
AssemblyItem(PushTag, 1),
eth::Instruction::JUMP,
AssemblyItem(Tag, 2),
u256(5),
u256(6),
eth::Instruction::SSTORE,
AssemblyItem(PushTag, 2),
eth::Instruction::JUMP,
};
BlockDeduplicator dedup(input);
dedup.deduplicate();
set<u256> pushTags;
for (AssemblyItem const& item: input)
if (item.type() == PushTag)
pushTags.insert(item.data());
BOOST_CHECK_EQUAL(pushTags.size(), 1);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

41
test/libsolidity/solidityExecutionFramework.h

@ -42,21 +42,25 @@ class ExecutionFramework
public: public:
ExecutionFramework() { g_logVerbosity = 0; } ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") bytes const& compileAndRunWthoutCheck(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
{ {
m_compiler.reset(false, m_addStandardSources); m_compiler.reset(false, m_addStandardSources);
m_compiler.addSource("", _sourceCode); m_compiler.addSource("", _sourceCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed"); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed");
bytes code = m_compiler.getBytecode(_contractName); bytes code = m_compiler.getBytecode(_contractName);
sendMessage(code, true, _value); sendMessage(code, true, _value);
return m_output;
}
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "")
{
compileAndRunWthoutCheck(_sourceCode, _value, _contractName);
BOOST_REQUIRE(!m_output.empty()); BOOST_REQUIRE(!m_output.empty());
return m_output; return m_output;
} }
template <class... Args> template <class... Args>
bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments)
Args const&... _arguments)
{ {
FixedHash<4> hash(dev::sha3(_sig)); FixedHash<4> hash(dev::sha3(_sig));
sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value);
@ -74,21 +78,30 @@ public:
{ {
bytes solidityResult = callContractFunction(_sig, _arguments...); bytes solidityResult = callContractFunction(_sig, _arguments...);
bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...); bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match." BOOST_CHECK_MESSAGE(
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult)); solidityResult == cppResult,
"Computed values do not match.\nSolidity: " +
toHex(solidityResult) +
"\nC++: " +
toHex(cppResult));
} }
template <class CppFunction, class... Args> template <class CppFunction, class... Args>
void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction, void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction, u256 const& _rangeStart, u256 const& _rangeEnd)
u256 const& _rangeStart, u256 const& _rangeEnd)
{ {
for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument) for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument)
{ {
bytes solidityResult = callContractFunction(_sig, argument); bytes solidityResult = callContractFunction(_sig, argument);
bytes cppResult = callCppAndEncodeResult(_cppFunction, argument); bytes cppResult = callCppAndEncodeResult(_cppFunction, argument);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match." BOOST_CHECK_MESSAGE(
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult) + solidityResult == cppResult,
"\nArgument: " + toHex(encode(argument))); "Computed values do not match.\nSolidity: " +
toHex(solidityResult) +
"\nC++: " +
toHex(cppResult) +
"\nArgument: " +
toHex(encode(argument))
);
} }
} }
@ -135,8 +148,10 @@ protected:
{ {
m_state.addBalance(m_sender, _value); // just in case m_state.addBalance(m_sender, _value); // just in case
eth::Executive executive(m_state, eth::LastHashes(), 0); eth::Executive executive(m_state, eth::LastHashes(), 0);
eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) eth::Transaction t =
: eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); _isCreation ?
eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) :
eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
bytes transactionRLP = t.rlp(); bytes transactionRLP = t.rlp();
try try
{ {

Loading…
Cancel
Save