Browse Source

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

cl-refactor
Gav Wood 10 years ago
parent
commit
a9c7f79b05
  1. 146
      CMakeLists.txt
  2. 2
      alethzero/MainWin.cpp
  3. 4
      eth/CMakeLists.txt
  4. 11
      extdep/getstuff.bat
  5. 207
      libevmcore/Assembly.cpp
  6. 20
      libevmcore/Assembly.h
  7. 1
      libevmcore/CMakeLists.txt
  8. 260
      libevmcore/ControlFlowGraph.cpp
  9. 108
      libevmcore/ControlFlowGraph.h
  10. 19
      libevmcore/SemanticInformation.cpp
  11. 1
      libevmcore/SemanticInformation.h
  12. 1
      liblll/CMakeLists.txt
  13. 16
      libp2p/Common.cpp
  14. 37
      libp2p/Common.h
  15. 149
      libp2p/Host.cpp
  16. 7
      libp2p/Host.h
  17. 63
      libp2p/NodeTable.cpp
  18. 63
      libp2p/NodeTable.h
  19. 2
      libp2p/Peer.cpp
  20. 5
      libp2p/Peer.h
  21. 12
      libp2p/Session.cpp
  22. 56
      libsolidity/AST.cpp
  23. 5
      libsolidity/AST.h
  24. 1
      libsolidity/Compiler.cpp
  25. 5
      libsolidity/Compiler.h
  26. 6
      libsolidity/CompilerContext.h
  27. 18
      libsolidity/CompilerStack.cpp
  28. 7
      libsolidity/CompilerStack.h
  29. 16
      libsolidity/LValue.cpp
  30. 4
      libwebthree/WebThree.cpp
  31. 2
      mix/ClientModel.cpp
  32. 2
      mix/CodeHighlighter.cpp
  33. 20
      mix/CodeModel.cpp
  34. 2
      mix/ContractCallDataEncoder.cpp
  35. 3
      mix/FileIo.cpp
  36. 3
      mix/MixClient.cpp
  37. 14
      mix/qml/WebPreview.qml
  38. 4
      mix/qml/html/cm/errorannotation.js
  39. 10
      mix/test/qml/TestMain.qml
  40. 5
      mix/test/qml/js/TestDebugger.js
  41. 10
      mix/test/qml/js/TestMiner.js
  42. 4
      mix/test/qml/js/TestProject.js
  43. 44
      solc/CommandLineInterface.cpp
  44. 23
      test/SolidityEndToEndTest.cpp
  45. 37
      test/SolidityNameAndTypeResolution.cpp
  46. 80
      test/SolidityOptimizer.cpp
  47. 32
      test/TestHelper.cpp
  48. 1
      test/TestHelper.h
  49. 18
      test/blockchain.cpp
  50. 45
      test/net.cpp
  51. 47
      test/peer.cpp
  52. 2
      test/stQuadraticComplexityTestFiller.json
  53. 78
      test/stTransactionTestFiller.json
  54. 3
      test/transaction.cpp
  55. 3
      test/tt10mbDataFieldFiller.json
  56. 2
      test/ttTransactionTestFiller.json
  57. 4
      test/vm.cpp

146
CMakeLists.txt

@ -8,30 +8,26 @@ project(ethereum)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
######################################################################################################
# user defined, defaults
# Normally, set(...CACHE...) creates cache variables, but does not modify them.
function(createDefaultCacheConfig)
set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)")
set(PARANOID OFF CACHE BOOL "Additional run-time checks")
set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on")
set(FATDB OFF CACHE BOOL "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents.")
set(USENPM OFF CACHE BOOL "Use npm to recompile ethereum.js if it was changed")
set(PROFILING OFF CACHE BOOL "Build in support for profiling")
set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).")
set(SOLIDITY ON CACHE BOOL "Build the Solidity language components")
set(SERPENT ON CACHE BOOL "Build the Serpent language components")
set(TOOLS ON CACHE BOOL "Build the tools components")
set(NCURSES ON CACHE BOOL "Build the NCurses components")
set(GUI ON CACHE BOOL "Build GUI components (AlethZero, Mix)")
set(TESTS ON CACHE BOOL "Build the tests.")
set(EVMJIT OFF CACHE BOOL "Build just-in-time compiler for EVM code (requires LLVM)")
set(ETHASHCL OFF CACHE BOOL "Build in support for GPU mining via OpenCL")
endfunction()
option(VMTRACE "VM tracing and run-time checks (useful for cross-implementation VM debugging)" OFF)
option(PARANOID "Additional run-time checks" OFF)
option(JSONRPC "Build with jsonprc. default on" ON)
option(FATDB "Build with ability to list entries in the Trie. Doubles DB size, slows everything down, but good for looking at state diffs and trie contents." OFF)
option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF)
option(PROFILING "Build in support for profiling" OFF)
set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).")
option(SOLIDITY "Build the Solidity language components" ON)
option(SERPENT "Build the Serpent language components" ON)
option(TOOLS "Build the tools components" ON)
option(NCURSES "Build the NCurses components" ON)
option(GUI "Build GUI components (AlethZero, Mix)" ON)
option(TESTS "Build the tests." ON)
option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF)
option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF)
# propagates CMake configuration options to the compiler
function(configureProject)
@ -143,97 +139,57 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
endif ()
endif ()
createDefaultCacheConfig()
# Force chromium.
set (ETH_HAVE_WEBENGINE 1)
# Normalise build options
# TODO: Abstract into something sensible and move into a function.
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(DECENT_PLATFORM OFF)
else ()
set(DECENT_PLATFORM ON)
endif ()
# Backwards compatibility
if (HEADLESS)
message("*** WARNING: -DHEADLESS=1 option is DEPRECATED! Use -DBUNDLE=minimal or -DGUI=0")
set(BUNDLE "minimal")
endif ()
if (PARANOID)
set(PARANOID ON)
else ()
set(PARANOID OFF)
endif ()
if (VMTRACE)
set(VMTRACE ON)
else ()
set(VMTRACE OFF)
endif ()
if (EVMJIT)
set(EVMJIT ON)
else ()
set(EVMJIT OFF)
endif()
if (FATDB)
set(FATDB ON)
else ()
set(FATDB OFF)
endif()
if (JSONRPC)
set(JSONRPC ON)
else ()
set(JSONRPC OFF)
endif ()
if (USENPM)
set(USENPM ON)
else ()
set(USENPM OFF)
endif ()
if (PROFILING)
set(PROFILING ON)
# TODO: Abstract into something sensible and move into a function.
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(DECENT_PLATFORM OFF)
else ()
set(PROFILING OFF)
set(DECENT_PLATFORM ON)
endif ()
macro(eth_format_option O)
if (${${O}})
set(${O} ON)
else()
set(${O} OFF)
endif()
endmacro()
macro(eth_format_option_on_decent_platform O)
if (${${O}})
set(${O} ${DECENT_PLATFORM})
else()
set(${O} OFF)
endif()
endmacro()
# Normalise build options
eth_format_option(PARANOID)
eth_format_option(VMTRACE)
eth_format_option(EVMJIT)
eth_format_option(FATDB)
eth_format_option(JSONRPC)
eth_format_option(USENPM)
eth_format_option(PROFILING)
eth_format_option(SOLIDITY)
eth_format_option(GUI)
eth_format_option(TESTS)
eth_format_option(TOOLS)
eth_format_option(ETHASHCL)
eth_format_option_on_decent_platform(SERPENT)
eth_format_option_on_decent_platform(NCURSES)
if (SOLIDITY)
set(SOLIDITY ON)
else ()
set(SOLIDITY OFF)
endif()
if (SERPENT)
set(SERPENT ${DECENT_PLATFORM})
else ()
set(SERPENT OFF)
endif()
if (GUI)
set(GUI ON)
set(JSONRPC ON)
else ()
set(GUI OFF)
endif ()
if (TESTS)
set(TESTS ON)
else ()
set(TESTS OFF)
endif ()
if (TOOLS)
set(TOOLS ON)
else ()
set(TOOLS OFF)
endif ()
if (ETHASHCL)
set(ETHASHCL ON)
else ()
set(ETHASHCL OFF)
endif()
if (NCURSES)
set(NCURSES ${DECENT_PLATFORM})
else ()
set(NCURSES OFF)
endif ()
if (BUNDLE STREQUAL "minimal")
set(SERPENT OFF)

2
alethzero/MainWin.cpp

@ -1038,7 +1038,7 @@ void Main::refreshNetwork()
for (p2p::Peer const& i: ns)
ui->nodes->insertItem(sessions.count(i.id) ? 0 : ui->nodes->count(), QString("[%1 %3] %2 - ( =%5s | /%4s%6 ) - *%7 $%8")
.arg(QString::fromStdString(i.id.abridged()))
.arg(QString::fromStdString(i.peerEndpoint().address().to_string()))
.arg(QString::fromStdString(i.endpoint.address.to_string()))
.arg(i.id == web3()->id() ? "self" : sessions.count(i.id) ? sessions[i.id] : "disconnected")
.arg(i.isOffline() ? " | " + QString::fromStdString(reasonOf(i.lastDisconnect())) + " | " + QString::number(i.failedAttempts()) + "x" : "")
.arg(i.rating())

4
eth/CMakeLists.txt

@ -25,7 +25,9 @@ if (JSONRPC)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
endif()
endif()
target_link_libraries(${EXECUTABLE} webthree)

11
extdep/getstuff.bat

@ -23,10 +23,15 @@ set eth_version=%2
cd download
if not exist %eth_name%-%eth_version%.tar.gz curl -k -o %eth_name%-%eth_version%.tar.gz %eth_server%/%eth_name%-%eth_version%.tar.gz
if not exist %eth_name%-%eth_version% tar -zxvf %eth_name%-%eth_version%.tar.gz
if not exist %eth_name%-%eth_version%.tar.gz (
bitsadmin /cancel %eth_name%-%eth_version%.tar.gz
bitsadmin /create %eth_name%-%eth_version%.tar.gz
bitsadmin /transfer %eth_name%-%eth_version%.tar.gz /download /priority normal %eth_server%/%eth_name%-%eth_version%.tar.gz %cd%\%eth_name%-%eth_version%.tar.gz
bitsadmin /cancel %eth_name%-%eth_version%.tar.gz
)
if not exist %eth_name%-%eth_version% cmake -E tar -zxvf %eth_name%-%eth_version%.tar.gz
cmake -E copy_directory %eth_name%-%eth_version% ..\install\windows
cd ..\download
cd ..
goto :EOF

207
libevmcore/Assembly.cpp

@ -23,7 +23,8 @@
#include <fstream>
#include <libdevcore/Log.h>
#include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/ControlFlowGraph.h>
#include <json/json.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -64,6 +65,13 @@ void Assembly::append(Assembly const& _a, int _deposit)
}
}
string Assembly::out() const
{
stringstream ret;
stream(ret);
return ret.str();
}
unsigned Assembly::bytesRequired() const
{
for (unsigned br = 1;; ++br)
@ -100,7 +108,7 @@ string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLoc
return move(cut);
}
ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
{
_out << _prefix << ".code:" << endl;
for (AssemblyItem const& i: m_items)
@ -156,6 +164,115 @@ ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const&
return _out;
}
Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType) const
{
Json::Value value;
value["name"] = _name;
value["begin"] = _begin;
value["end"] = _end;
if (!_value.empty())
value["value"] = _value;
if (!_jumpType.empty())
value["jumpType"] = _jumpType;
return value;
}
string toStringInHex(u256 _value)
{
std::stringstream hexStr;
hexStr << hex << _value;
return hexStr.str();
}
Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes) const
{
Json::Value root;
Json::Value collection(Json::arrayValue);
for (AssemblyItem const& i: m_items)
{
switch (i.type())
{
case Operation:
collection.append(
createJsonValue(instructionInfo(i.instruction()).name, i.getLocation().start, i.getLocation().end, i.getJumpTypeAsString()));
break;
case Push:
collection.append(
createJsonValue("PUSH", i.getLocation().start, i.getLocation().end, toStringInHex(i.data()), i.getJumpTypeAsString()));
break;
case PushString:
collection.append(
createJsonValue("PUSH tag", i.getLocation().start, i.getLocation().end, m_strings.at((h256)i.data())));
break;
case PushTag:
collection.append(
createJsonValue("PUSH [tag]", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
break;
case PushSub:
collection.append(
createJsonValue("PUSH [$]", i.getLocation().start, i.getLocation().end, dev::toString(h256(i.data()))));
break;
case PushSubSize:
collection.append(
createJsonValue("PUSH #[$]", i.getLocation().start, i.getLocation().end, dev::toString(h256(i.data()))));
break;
case PushProgramSize:
collection.append(
createJsonValue("PUSHSIZE", i.getLocation().start, i.getLocation().end));
break;
case Tag:
{
collection.append(
createJsonValue("tag", i.getLocation().start, i.getLocation().end, string(i.data())));
collection.append(
createJsonValue("JUMDEST", i.getLocation().start, i.getLocation().end));
}
break;
case PushData:
{
Json::Value pushData;
pushData["name"] = "PUSH hex";
collection.append(createJsonValue("PUSH hex", i.getLocation().start, i.getLocation().end, toStringInHex(i.data())));
}
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
}
root[".code"] = collection;
if (!m_data.empty() || !m_subs.empty())
{
Json::Value data;
for (auto const& i: m_data)
if (u256(i.first) >= m_subs.size())
data[toStringInHex((u256)i.first)] = toHex(i.second);
for (size_t i = 0; i < m_subs.size(); ++i)
{
std::stringstream hexStr;
hexStr << hex << i;
data[hexStr.str()] = m_subs[i].stream(_out, "", _sourceCodes, true);
}
root[".data"] = data;
_out << root;
}
return root;
}
Json::Value Assembly::stream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes, bool _inJsonFormat) const
{
if (_inJsonFormat)
return streamAsmJson(_out, _sourceCodes);
else
{
streamAsm(_out, _prefix, _sourceCodes);
return Json::Value();
}
}
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
{
m_deposit += _i.deposit();
@ -197,6 +314,18 @@ Assembly& Assembly::optimise(bool _enable)
copt << *this;
count = 0;
copt << "Performing control flow analysis...";
{
ControlFlowGraph cfg(m_items);
AssemblyItems optItems = cfg.optimisedItems();
if (optItems.size() < m_items.size())
{
copt << "Old size: " << m_items.size() << ", new size: " << optItems.size();
m_items = move(optItems);
count++;
}
}
copt << "Performing common subexpression elimination...";
for (auto iter = m_items.begin(); iter != m_items.end();)
{
@ -225,80 +354,6 @@ Assembly& Assembly::optimise(bool _enable)
iter = m_items.erase(orig, iter);
}
}
for (unsigned i = 0; i < m_items.size(); ++i)
{
for (auto const& r: rules)
{
auto vr = AssemblyItemsConstRef(&m_items).cropped(i, r.first.size());
if (matches(vr, &r.first))
{
auto rw = r.second(vr);
if (rw.size() < vr.size())
{
copt << "Rule " << vr << " matches " << AssemblyItemsConstRef(&r.first) << " becomes...";
copt << AssemblyItemsConstRef(&rw) << "\n";
if (rw.size() > vr.size())
{
// create hole in the vector
unsigned sizeIncrease = rw.size() - vr.size();
m_items.resize(m_items.size() + sizeIncrease, AssemblyItem(UndefinedItem));
move_backward(m_items.begin() + i, m_items.end() - sizeIncrease, m_items.end());
}
else
m_items.erase(m_items.begin() + i + rw.size(), m_items.begin() + i + vr.size());
copy(rw.begin(), rw.end(), m_items.begin() + i);
count++;
copt << "Now:" << m_items;
}
}
}
if (m_items[i].type() == Operation && m_items[i].instruction() == Instruction::JUMP)
{
bool o = false;
while (m_items.size() > i + 1 && m_items[i + 1].type() != Tag)
{
m_items.erase(m_items.begin() + i + 1);
o = true;
}
if (o)
{
copt << "Jump with no tag. Now:\n" << m_items;
++count;
}
}
}
map<u256, unsigned> tags;
for (unsigned i = 0; i < m_items.size(); ++i)
if (m_items[i].type() == Tag)
tags.insert(make_pair(m_items[i].data(), i));
for (auto const& i: m_items)
if (i.type() == PushTag)
tags.erase(i.data());
if (!tags.empty())
{
auto t = *tags.begin();
unsigned i = t.second;
if (i && m_items[i - 1].type() == Operation && m_items[i - 1].instruction() == Instruction::JUMP)
while (i < m_items.size() && (m_items[i].type() != Tag || tags.count(m_items[i].data())))
{
if (m_items[i].type() == Tag && tags.count(m_items[i].data()))
tags.erase(m_items[i].data());
m_items.erase(m_items.begin() + i);
}
else
{
m_items.erase(m_items.begin() + i);
tags.erase(t.first);
}
copt << "Unused tag. Now:\n" << m_items;
++count;
}
}
copt << total << " optimisations done.";

20
libevmcore/Assembly.h

@ -29,7 +29,12 @@
#include <libevmcore/Instruction.h>
#include <libevmcore/AssemblyItem.h>
#include "Exceptions.h"
#include <json/json.h>
namespace Json
{
class Value;
}
namespace dev
{
namespace eth
@ -76,7 +81,7 @@ public:
void popTo(int _deposit) { while (m_deposit > _deposit) append(Instruction::POP); }
void injectStart(AssemblyItem const& _i);
std::string out() const { std::stringstream ret; stream(ret); return ret.str(); }
std::string out() const;
int deposit() const { return m_deposit; }
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
void setDeposit(int _deposit) { m_deposit = _deposit; if (asserts(m_deposit >= 0)) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@ -86,13 +91,24 @@ public:
bytes assemble() const;
Assembly& optimise(bool _enable);
std::ostream& stream(std::ostream& _out, std::string const& _prefix = "", const StringMap &_sourceCodes = StringMap()) const;
Json::Value stream(
std::ostream& _out,
std::string const& _prefix = "",
const StringMap &_sourceCodes = StringMap(),
bool _inJsonFormat = false
) const;
protected:
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()); }
unsigned bytesRequired() const;
private:
Json::Value streamAsmJson(std::ostream& _out, const StringMap &_sourceCodes) const;
std::ostream& streamAsm(std::ostream& _out, std::string const& _prefix, StringMap const& _sourceCodes) const;
Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const;
protected:
unsigned m_usedTags = 0;
AssemblyItems m_items;
mutable std::map<h256, bytes> m_data;

1
libevmcore/CMakeLists.txt

@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})

260
libevmcore/ControlFlowGraph.cpp

@ -0,0 +1,260 @@
/*
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 ControlFlowGraph.cpp
* @author Christian <c@ethdev.com>
* @date 2015
* Control flow analysis for the optimizer.
*/
#include <libevmcore/ControlFlowGraph.h>
#include <map>
#include <libevmcore/Exceptions.h>
#include <libevmcore/AssemblyItem.h>
#include <libevmcore/SemanticInformation.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
BlockId::BlockId(u256 const& _id): m_id(_id)
{
assertThrow( _id < initial().m_id, OptimizerException, "Tag number too large.");
}
AssemblyItems ControlFlowGraph::optimisedItems()
{
if (m_items.empty())
return m_items;
findLargestTag();
splitBlocks();
resolveNextLinks();
removeUnusedBlocks();
setPrevLinks();
return rebuildCode();
}
void ControlFlowGraph::findLargestTag()
{
m_lastUsedId = 0;
for (auto const& item: m_items)
if (item.type() == Tag || item.type() == PushTag)
{
// Assert that it can be converted.
BlockId(item.data());
m_lastUsedId = max(unsigned(item.data()), m_lastUsedId);
}
}
void ControlFlowGraph::splitBlocks()
{
m_blocks.clear();
BlockId id = BlockId::initial();
m_blocks[id].begin = 0;
for (size_t index = 0; index < m_items.size(); ++index)
{
AssemblyItem const& item = m_items.at(index);
if (item.type() == Tag)
{
if (id)
m_blocks[id].end = index;
id = BlockId::invalid();
}
if (!id)
{
id = item.type() == Tag ? BlockId(item.data()) : generateNewId();
m_blocks[id].begin = index;
}
if (item.type() == PushTag)
m_blocks[id].pushedTags.push_back(BlockId(item.data()));
if (SemanticInformation::altersControlFlow(item))
{
m_blocks[id].end = index + 1;
if (item == Instruction::JUMP)
m_blocks[id].endType = BasicBlock::EndType::JUMP;
else if (item == Instruction::JUMPI)
m_blocks[id].endType = BasicBlock::EndType::JUMPI;
else
m_blocks[id].endType = BasicBlock::EndType::STOP;
id = BlockId::invalid();
}
}
if (id)
{
m_blocks[id].end = m_items.size();
if (m_blocks[id].endType == BasicBlock::EndType::HANDOVER)
m_blocks[id].endType = BasicBlock::EndType::STOP;
}
}
void ControlFlowGraph::resolveNextLinks()
{
map<unsigned, BlockId> blockByBeginPos;
for (auto const& idAndBlock: m_blocks)
if (idAndBlock.second.begin != idAndBlock.second.end)
blockByBeginPos[idAndBlock.second.begin] = idAndBlock.first;
for (auto& idAndBlock: m_blocks)
{
BasicBlock& block = idAndBlock.second;
switch (block.endType)
{
case BasicBlock::EndType::JUMPI:
case BasicBlock::EndType::HANDOVER:
assertThrow(
blockByBeginPos.count(block.end),
OptimizerException,
"Successor block not found."
);
block.next = blockByBeginPos.at(block.end);
break;
default:
break;
}
}
}
void ControlFlowGraph::removeUnusedBlocks()
{
vector<BlockId> blocksToProcess{BlockId::initial()};
set<BlockId> neededBlocks{BlockId::initial()};
while (!blocksToProcess.empty())
{
BasicBlock const& block = m_blocks.at(blocksToProcess.back());
blocksToProcess.pop_back();
for (BlockId tag: block.pushedTags)
if (!neededBlocks.count(tag))
{
neededBlocks.insert(tag);
blocksToProcess.push_back(tag);
}
if (block.next && !neededBlocks.count(block.next))
{
neededBlocks.insert(block.next);
blocksToProcess.push_back(block.next);
}
}
for (auto it = m_blocks.begin(); it != m_blocks.end();)
if (neededBlocks.count(it->first))
++it;
else
m_blocks.erase(it++);
}
void ControlFlowGraph::setPrevLinks()
{
for (auto& idAndBlock: m_blocks)
{
BasicBlock& block = idAndBlock.second;
switch (block.endType)
{
case BasicBlock::EndType::JUMPI:
case BasicBlock::EndType::HANDOVER:
assertThrow(
!m_blocks.at(block.next).prev,
OptimizerException,
"Successor already has predecessor."
);
m_blocks[block.next].prev = idAndBlock.first;
break;
default:
break;
}
}
// If block ends with jump to not yet linked block, link them removing the jump
for (auto& idAndBlock: m_blocks)
{
BlockId blockId = idAndBlock.first;
BasicBlock& block = idAndBlock.second;
if (block.endType != BasicBlock::EndType::JUMP || block.end - block.begin < 2)
continue;
AssemblyItem const& push = m_items.at(block.end - 2);
if (push.type() != PushTag)
continue;
BlockId nextId(push.data());
if (m_blocks.at(nextId).prev)
continue;
bool hasLoop = false;
for (BlockId id = nextId; id && !hasLoop; id = m_blocks.at(id).next)
hasLoop = (id == blockId);
if (hasLoop)
continue;
m_blocks[nextId].prev = blockId;
block.next = nextId;
block.end -= 2;
assertThrow(
!block.pushedTags.empty() && block.pushedTags.back() == nextId,
OptimizerException,
"Last pushed tag not at end of pushed list."
);
block.pushedTags.pop_back();
block.endType = BasicBlock::EndType::HANDOVER;
}
}
AssemblyItems ControlFlowGraph::rebuildCode()
{
map<BlockId, unsigned> pushes;
for (auto& idAndBlock: m_blocks)
for (BlockId ref: idAndBlock.second.pushedTags)
pushes[ref]++;
set<BlockId> blocksToAdd;
for (auto it: m_blocks)
blocksToAdd.insert(it.first);
set<BlockId> blocksAdded;
AssemblyItems code;
for (
BlockId blockId = BlockId::initial();
blockId;
blockId = blocksToAdd.empty() ? BlockId::invalid() : *blocksToAdd.begin()
)
{
bool previousHandedOver = (blockId == BlockId::initial());
while (m_blocks.at(blockId).prev)
blockId = m_blocks.at(blockId).prev;
for (; blockId; blockId = m_blocks.at(blockId).next)
{
BasicBlock const& block = m_blocks.at(blockId);
blocksToAdd.erase(blockId);
blocksAdded.insert(blockId);
auto begin = m_items.begin() + block.begin;
auto end = m_items.begin() + block.end;
if (begin == end)
continue;
// If block starts with unused tag, skip it.
if (previousHandedOver && !pushes[blockId] && begin->type() == Tag)
++begin;
previousHandedOver = (block.endType == BasicBlock::EndType::HANDOVER);
copy(begin, end, back_inserter(code));
}
}
return code;
}
BlockId ControlFlowGraph::generateNewId()
{
BlockId id = BlockId(++m_lastUsedId);
assertThrow(id < BlockId::initial(), OptimizerException, "Out of block IDs.");
return id;
}

108
libevmcore/ControlFlowGraph.h

@ -0,0 +1,108 @@
/*
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 ControlFlowGraph.h
* @author Christian <c@ethdev.com>
* @date 2015
* Control flow analysis for the optimizer.
*/
#pragma once
#include <vector>
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
namespace dev
{
namespace eth
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
/**
* Identifier for a block, coincides with the tag number of an AssemblyItem but adds a special
* ID for the inital block.
*/
class BlockId
{
public:
BlockId() { *this = invalid(); }
explicit BlockId(unsigned _id): m_id(_id) {}
explicit BlockId(u256 const& _id);
static BlockId initial() { return BlockId(-2); }
static BlockId invalid() { return BlockId(-1); }
bool operator==(BlockId const& _other) const { return m_id == _other.m_id; }
bool operator!=(BlockId const& _other) const { return m_id != _other.m_id; }
bool operator<(BlockId const& _other) const { return m_id < _other.m_id; }
explicit operator bool() const { return *this != invalid(); }
private:
unsigned m_id;
};
/**
* Control flow block inside which instruction counter is always incremented by one
* (except for possibly the last instruction).
*/
struct BasicBlock
{
/// Start index into assembly item list.
unsigned begin = 0;
/// End index (excluded) inte assembly item list.
unsigned end = 0;
/// Tags pushed inside this block, with multiplicity.
std::vector<BlockId> pushedTags;
/// ID of the block that always follows this one (either JUMP or flow into new block),
/// or BlockId::invalid() otherwise
BlockId next = BlockId::invalid();
/// ID of the block that has to precede this one.
BlockId prev = BlockId::invalid();
enum class EndType { JUMP, JUMPI, STOP, HANDOVER };
EndType endType = EndType::HANDOVER;
};
class ControlFlowGraph
{
public:
/// Initializes the control flow graph.
/// @a _items has to persist across the usage of this class.
ControlFlowGraph(AssemblyItems const& _items): m_items(_items) {}
/// @returns the collection of optimised items, should be called only once.
AssemblyItems optimisedItems();
private:
void findLargestTag();
void splitBlocks();
void resolveNextLinks();
void removeUnusedBlocks();
void setPrevLinks();
AssemblyItems rebuildCode();
BlockId generateNewId();
unsigned m_lastUsedId = 0;
AssemblyItems const& m_items;
std::map<BlockId, BasicBlock> m_blocks;
};
}
}

19
libevmcore/SemanticInformation.cpp

@ -103,3 +103,22 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
{
return _item == AssemblyItem(Instruction::JUMP) || _item == AssemblyItem(Instruction::JUMPI);
}
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
switch (_item.instruction())
{
// note that CALL, CALLCODE and CREATE do not really alter the control flow, because we
// continue on the next instruction (unless an exception happens which can always happen)
case Instruction::JUMP:
case Instruction::JUMPI:
case Instruction::RETURN:
case Instruction::SUICIDE:
case Instruction::STOP:
return true;
default:
return false;
}
}

1
libevmcore/SemanticInformation.h

@ -44,6 +44,7 @@ struct SemanticInformation
static bool isDupInstruction(AssemblyItem const& _item);
static bool isSwapInstruction(AssemblyItem const& _item);
static bool isJumpInstruction(AssemblyItem const& _item);
static bool altersControlFlow(AssemblyItem const& _item);
};
}

1
liblll/CMakeLists.txt

@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})

16
libp2p/Common.cpp

@ -27,6 +27,11 @@ using namespace dev::p2p;
const unsigned dev::p2p::c_protocolVersion = 3;
const unsigned dev::p2p::c_defaultIPPort = 30303;
const dev::p2p::NodeIPEndpoint dev::p2p::UnspecifiedNodeIPEndpoint = NodeIPEndpoint(bi::address(), 0, 0);
const dev::p2p::Node dev::p2p::UnspecifiedNode = dev::p2p::Node(NodeId(), UnspecifiedNodeIPEndpoint);
bool dev::p2p::NodeIPEndpoint::test_allowLocal = false;
bool p2p::isPublicAddress(std::string const& _addressToCheck)
{
return _addressToCheck.empty() ? false : isPublicAddress(bi::address::from_string(_addressToCheck));
@ -111,8 +116,13 @@ std::string p2p::reasonOf(DisconnectReason _r)
}
}
void Node::cullEndpoint()
namespace dev {
std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep)
{
if (!isPublicAddress(endpoint.tcp.address()) && isPublicAddress(endpoint.udp.address()))
endpoint.tcp.address(endpoint.udp.address());
_out << _ep.address << _ep.udpPort << _ep.tcpPort;
return _out;
}
}

37
libp2p/Common.h

@ -52,6 +52,11 @@ namespace p2p
extern const unsigned c_protocolVersion;
extern const unsigned c_defaultIPPort;
struct NodeIPEndpoint;
struct Node;
extern const NodeIPEndpoint UnspecifiedNodeIPEndpoint;
extern const Node UnspecifiedNode;
using NodeId = h512;
bool isPrivateAddress(bi::address const& _addressToCheck);
@ -156,29 +161,30 @@ using PeerSessionInfos = std::vector<PeerSessionInfo>;
*/
struct NodeIPEndpoint
{
NodeIPEndpoint(): udp(bi::udp::endpoint()), tcp(bi::tcp::endpoint()) {}
NodeIPEndpoint(bi::udp::endpoint _udp): udp(_udp) {}
NodeIPEndpoint(bi::tcp::endpoint _tcp): tcp(_tcp) {}
NodeIPEndpoint(bi::udp::endpoint _udp, bi::tcp::endpoint _tcp): udp(_udp), tcp(_tcp) {}
/// Setting true causes isAllowed to return true for all addresses. (Used by test fixtures)
static bool test_allowLocal;
NodeIPEndpoint(bi::address _addr, uint16_t _udp, uint16_t _tcp): address(_addr), udpPort(_udp), tcpPort(_tcp) {}
bi::udp::endpoint udp;
bi::tcp::endpoint tcp;
bi::address address;
uint16_t udpPort;
uint16_t tcpPort;
operator bi::udp::endpoint() const { return std::move(bi::udp::endpoint(address, udpPort)); }
operator bi::tcp::endpoint() const { return std::move(bi::tcp::endpoint(address, tcpPort)); }
operator bool() const { return !udp.address().is_unspecified() || !tcp.address().is_unspecified(); }
operator bool() const { return !address.is_unspecified() && udpPort > 0 && tcpPort > 0; }
bool isAllowed() const { return NodeIPEndpoint::test_allowLocal ? !address.is_unspecified() : isPublicAddress(address); }
};
struct Node
{
Node(): endpoint(NodeIPEndpoint()) {};
Node(Public _pubk, NodeIPEndpoint _ip, bool _required = false): id(_pubk), endpoint(_ip), required(_required) {}
Node(Public _pubk, bi::udp::endpoint _udp, bool _required = false): Node(_pubk, NodeIPEndpoint(_udp), _required) {}
virtual NodeId const& address() const { return id; }
virtual Public const& publicKey() const { return id; }
/// Adopt UDP address for TCP if TCP isn't public and UDP is. (to be removed when protocol is updated for nat)
void cullEndpoint();
NodeId id;
/// Endpoints by which we expect to reach node.
@ -192,4 +198,7 @@ struct Node
};
}
/// Simple stream output for a NodeIPEndpoint.
std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep);
}

149
libp2p/Host.cpp

@ -174,17 +174,25 @@ void Host::doneWorking()
void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint)
{
// session maybe ingress or egress so m_peers and node table entries may not exist
shared_ptr<Peer> p;
if (!m_peers.count(_id))
ETH_RECURSIVE_GUARDED(x_sessions)
{
p.reset(new Peer());
p->id = _id;
if (m_peers.count(_id))
p = m_peers[_id];
else
{
// peer doesn't exist, try to get port info from node table
if (Node n = m_nodeTable->node(_id))
p.reset(new Peer(n));
else
p.reset(new Peer(Node(_id, UnspecifiedNodeIPEndpoint)));
m_peers[_id] = p;
}
}
else
p = m_peers[_id];
if (p->isOffline())
p->m_lastConnected = std::chrono::system_clock::now();
p->endpoint.tcp.address(_endpoint.address());
p->endpoint.address = _endpoint.address();
auto protocolVersion = _rlp[0].toInt<unsigned>();
auto clientVersion = _rlp[1].toString();
@ -231,46 +239,32 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
}
clog(NetNote) << "p2p.host.peer.register" << _id.abridged();
StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount());
StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->endpoint, ps->m_peer->m_lastConnected, clientVersion, peerCount());
}
void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
{
if (_e == NodeEntryAdded)
{
clog(NetNote) << "p2p.host.nodeTable.events.nodeEntryAdded " << _n;
auto n = m_nodeTable->node(_n);
if (n)
// only add iff node is in node table
if (Node n = m_nodeTable->node(_n))
{
shared_ptr<Peer> p;
ETH_RECURSIVE_GUARDED(x_sessions)
{
RecursiveGuard l(x_sessions);
if (m_peers.count(_n))
{
p = m_peers[_n];
p->endpoint = n.endpoint;
}
else
{
// TODO p2p: construct peer from node
p.reset(new Peer());
p->id = _n;
p->endpoint = NodeIPEndpoint(n.endpoint.udp, n.endpoint.tcp);
p->required = n.required;
p.reset(new Peer(n));
m_peers[_n] = p;
clog(NetNote) << "p2p.host.peers.events.peersAdded " << _n << "udp:" << p->endpoint.udp.address() << "tcp:" << p->endpoint.tcp.address();
clog(NetNote) << "p2p.host.peers.events.peerAdded " << _n << p->endpoint;
}
p->endpoint.tcp = n.endpoint.tcp;
}
// TODO: Implement similar to discover. Attempt connecting to nodes
// until ideal peer count is reached; if all nodes are tried,
// repeat. Notably, this is an integrated process such that
// when onNodeTableEvent occurs we should also update +/-
// the list of nodes to be tried. Thus:
// 1) externalize connection attempts
// 2) attempt copies potentialPeer list
// 3) replace this logic w/maintenance of potentialPeers
if (peerCount() < m_idealPeerCount)
connect(p);
}
@ -278,7 +272,6 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
else if (_e == NodeEntryDropped)
{
clog(NetNote) << "p2p.host.nodeTable.events.NodeEntryDropped " << _n;
RecursiveGuard l(x_sessions);
m_peers.erase(_n);
}
@ -381,7 +374,7 @@ string Host::pocHost()
return "poc-" + strs[1] + ".ethdev.com";
}
void Host::addNode(NodeId const& _node, bi::address const& _addr, unsigned short _udpNodePort, unsigned short _tcpPeerPort)
void Host::addNode(NodeId const& _node, NodeIPEndpoint const& _endpoint)
{
// return if network is stopped while waiting on Host::run() or nodeTable to start
while (!haveNetwork())
@ -390,43 +383,33 @@ void Host::addNode(NodeId const& _node, bi::address const& _addr, unsigned short
else
return;
if (_tcpPeerPort < 30300 || _tcpPeerPort > 30305)
cwarn << "Non-standard port being recorded: " << _tcpPeerPort;
if (_endpoint.tcpPort < 30300 || _endpoint.tcpPort > 30305)
clog(NetConnect) << "Non-standard port being recorded: " << _endpoint.tcpPort;
if (_tcpPeerPort >= /*49152*/32768)
{
cwarn << "Private port being recorded - setting to 0";
_tcpPeerPort = 0;
}
if (m_nodeTable)
m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(_addr, _udpNodePort), bi::tcp::endpoint(_addr, _tcpPeerPort))));
m_nodeTable->addNode(Node(_node, _endpoint));
}
void Host::requirePeer(NodeId const& _n, bi::address const& _udpAddr, unsigned short _udpPort, bi::address const& _tcpAddr, unsigned short _tcpPort)
void Host::requirePeer(NodeId const& _n, NodeIPEndpoint const& _endpoint)
{
auto naddr = _udpAddr;
auto paddr = _tcpAddr.is_unspecified() ? naddr : _tcpAddr;
auto udp = bi::udp::endpoint(naddr, _udpPort);
auto tcp = bi::tcp::endpoint(paddr, _tcpPort ? _tcpPort : _udpPort);
Node node(_n, NodeIPEndpoint(udp, tcp));
Node node(_n, _endpoint, true);
if (_n)
{
// add or replace peer
// create or update m_peers entry
shared_ptr<Peer> p;
ETH_RECURSIVE_GUARDED(x_sessions)
{
RecursiveGuard l(x_sessions);
if (m_peers.count(_n))
{
p = m_peers[_n];
p->endpoint = node.endpoint;
p->required = true;
}
else
{
p.reset(new Peer());
p->id = _n;
p->required = true;
p.reset(new Peer(node));
m_peers[_n] = p;
}
p->endpoint.udp = node.endpoint.udp;
p->endpoint.tcp = node.endpoint.tcp;
}
connect(p);
}
@ -441,7 +424,7 @@ void Host::requirePeer(NodeId const& _n, bi::address const& _udpAddr, unsigned s
{
if (!_ec && m_nodeTable)
if (auto n = m_nodeTable->node(_n))
requirePeer(n.id, n.endpoint.udp.address(), n.endpoint.udp.port(), n.endpoint.tcp.address(), n.endpoint.tcp.port());
requirePeer(n.id, n.endpoint);
});
}
}
@ -482,22 +465,23 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
m_pendingPeerConns.insert(nptr);
}
clog(NetConnect) << "Attempting connection to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "from" << id().abridged();
bi::tcp::endpoint ep(_p->endpoint);
clog(NetConnect) << "Attempting connection to node" << _p->id.abridged() << "@" << ep << "from" << id().abridged();
auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService));
socket->ref().async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec)
socket->ref().async_connect(ep, [=](boost::system::error_code const& ec)
{
_p->m_lastAttempted = std::chrono::system_clock::now();
_p->m_failedAttempts++;
if (ec)
{
clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")";
clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << ep << "(" << ec.message() << ")";
// Manually set error (session not present)
_p->m_lastDisconnect = TCPError;
}
else
{
clog(NetConnect) << "Connecting to" << _p->id.abridged() << "@" << _p->peerEndpoint();
clog(NetConnect) << "Connecting to" << _p->id.abridged() << "@" << ep;
auto handshake = make_shared<RLPXHandshake>(this, socket, _p->id);
{
Guard l(x_connecting);
@ -579,7 +563,7 @@ void Host::run(boost::system::error_code const&)
// is always live and to ensure reputation and fallback timers are properly
// updated. // disconnectLatePeers();
auto openSlots = m_idealPeerCount - peerCount();
int openSlots = m_idealPeerCount - peerCount();
if (openSlots > 0)
{
list<shared_ptr<Peer>> toConnect;
@ -637,7 +621,7 @@ void Host::startedWorking()
else
clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "TCP Listen port is invalid or unavailable.";
shared_ptr<NodeTable> nodeTable(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort()));
shared_ptr<NodeTable> nodeTable(new NodeTable(m_ioService, m_alias, NodeIPEndpoint(bi::address::from_string(listenAddress()), listenPort(), listenPort())));
nodeTable->setEventHandler(new HostNodeTableHandler(*this));
m_nodeTable = nodeTable;
restoreNetwork(&m_restoreNetwork);
@ -696,14 +680,13 @@ bytes Host::saveNetwork() const
{
// Only save peers which have connected within 2 days, with properly-advertised port and public IP address
// todo: e2e ipv6 support
bi::tcp::endpoint endpoint(p.peerEndpoint());
if (!endpoint.address().is_v4())
if (!p.endpoint.address.is_v4())
continue;
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && endpoint.port() > 0 && endpoint.port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(endpoint.address()))
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.endpoint.tcpPort > 0 && p.id != id() && (p.required || p.endpoint.isAllowed()))
{
network.appendList(10);
network << endpoint.port() << p.id << p.required
network << p.endpoint.address.to_v4().to_bytes() << p.endpoint.tcpPort << p.id << p.required
<< chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count()
<< chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count()
<< p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating;
@ -715,14 +698,14 @@ bytes Host::saveNetwork() const
{
auto state = m_nodeTable->snapshot();
state.sort();
for (auto const& s: state)
for (auto const& entry: state)
{
network.appendList(3);
if (s.endpoint.tcp.address().is_v4())
network << s.endpoint.tcp.address().to_v4().to_bytes();
if (entry.endpoint.address.is_v4())
network << entry.endpoint.address.to_v4().to_bytes();
else
network << s.endpoint.tcp.address().to_v6().to_bytes();
network << s.endpoint.tcp.port() << s.id;
network << entry.endpoint.address.to_v6().to_bytes();
network << entry.endpoint.tcpPort << entry.id;
count++;
}
}
@ -755,36 +738,28 @@ void Host::restoreNetwork(bytesConstRef _b)
for (auto i: r[2])
{
// todo: e2e ipv6 support
// bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
if (i[0].itemCount() != 4)
continue;
bi::tcp::endpoint tcp;
bi::udp::endpoint udp;
tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address()))
continue;
auto id = (NodeId)i[2];
if (i.itemCount() == 3)
m_nodeTable->addNode(id, udp, tcp);
// todo: ipv6, bi::address_v6(i[0].toArray<byte, 16>()
Node n((NodeId)i[2], NodeIPEndpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<uint16_t>(), i[1].toInt<uint16_t>()));
if (i.itemCount() == 3 && n.endpoint.isAllowed())
m_nodeTable->addNode(n);
else if (i.itemCount() == 10)
{
shared_ptr<Peer> p = make_shared<Peer>();
p->id = id;
p->required = i[3].toInt<bool>();
n.required = i[3].toInt<bool>();
if (!n.endpoint.isAllowed() && !n.required)
continue;
shared_ptr<Peer> p = make_shared<Peer>(n);
p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>()));
p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
p->m_failedAttempts = i[6].toInt<unsigned>();
p->m_lastDisconnect = (DisconnectReason)i[7].toInt<unsigned>();
p->m_score = (int)i[8].toInt<unsigned>();
p->m_rating = (int)i[9].toInt<unsigned>();
p->endpoint.tcp = tcp;
p->endpoint.udp = udp;
m_peers[p->id] = p;
if (p->required)
requirePeer(p->id, p->endpoint.udp.address(), p->endpoint.udp.port());
requirePeer(p->id, n.endpoint);
else
m_nodeTable->addNode(*p.get());
}

7
libp2p/Host.h

@ -103,10 +103,13 @@ public:
template <class T> std::shared_ptr<T> cap() const { try { return std::static_pointer_cast<T>(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } }
/// Add node as a peer candidate. Node is added if discovery ping is successful and table has capacity.
void addNode(NodeId const& _node, bi::address const& _addr, unsigned short _udpPort, unsigned short _tcpPort);
void addNode(NodeId const& _node, NodeIPEndpoint const& _endpoint);
/// Create Peer and attempt keeping peer connected.
void requirePeer(NodeId const& _node, bi::address const& _udpAddr, unsigned short _udpPort, bi::address const& _tcpAddr = bi::address(), unsigned short _tcpPort = 0);
void requirePeer(NodeId const& _node, NodeIPEndpoint const& _endpoint);
/// Create Peer and attempt keeping peer connected.
void requirePeer(NodeId const& _node, bi::address const& _addr, unsigned short _udpPort, unsigned short _tcpPort) { requirePeer(_node, NodeIPEndpoint(_addr, _udpPort, _tcpPort)); }
/// Note peer as no longer being required.
void relinquishPeer(NodeId const& _node);

63
libp2p/NodeTable.cpp

@ -25,13 +25,12 @@ using namespace dev;
using namespace dev::p2p;
NodeEntry::NodeEntry(Node _src, Public _pubk, NodeIPEndpoint _gw): Node(_pubk, _gw), distance(NodeTable::distance(_src.id,_pubk)) {}
NodeEntry::NodeEntry(Node _src, Public _pubk, bi::udp::endpoint _udp): Node(_pubk, NodeIPEndpoint(_udp)), distance(NodeTable::distance(_src.id,_pubk)) {}
NodeTable::NodeTable(ba::io_service& _io, KeyPair _alias, bi::address const& _udpAddress, uint16_t _udp):
m_node(Node(_alias.pub(), bi::udp::endpoint(_udpAddress, _udp))),
NodeTable::NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint):
m_node(Node(_alias.pub(), _endpoint)),
m_secret(_alias.sec()),
m_io(_io),
m_socket(new NodeSocket(m_io, *this, m_node.endpoint.udp)),
m_socket(new NodeSocket(m_io, *this, (bi::udp::endpoint)m_node.endpoint)),
m_socketPointer(m_socket.get()),
m_bucketRefreshTimer(m_io),
m_evictionCheckTimer(m_io)
@ -62,9 +61,9 @@ void NodeTable::processEvents()
m_nodeEventHandler->processEvents();
}
shared_ptr<NodeEntry> NodeTable::addNode(Public const& _pubk, bi::udp::endpoint const& _udp, bi::tcp::endpoint const& _tcp)
shared_ptr<NodeEntry> NodeTable::addNode(Public const& _pubk, NodeIPEndpoint const& _ep)
{
auto node = Node(_pubk, NodeIPEndpoint(_udp, _tcp));
auto node = Node(_pubk, _ep);
return addNode(node);
}
@ -72,7 +71,7 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node)
{
// re-enable tcp checks when NAT hosts are handled by discover
// we handle when tcp endpoint is 0 below
if (_node.endpoint.udp.address().to_string() == "0.0.0.0")
if (_node.endpoint.address.to_string() == "0.0.0.0")
{
clog(NodeTableWarn) << "addNode Failed. Invalid UDP address 0.0.0.0 for" << _node.id.abridged();
return move(shared_ptr<NodeEntry>());
@ -81,12 +80,12 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node)
// ping address to recover nodeid if nodeid is empty
if (!_node.id)
{
clog(NodeTableConnect) << "Sending public key discovery Ping to" << _node.endpoint.udp << "(Advertising:" << m_node.endpoint.udp << ")";
clog(NodeTableConnect) << "Sending public key discovery Ping to" << (bi::udp::endpoint)_node.endpoint << "(Advertising:" << (bi::udp::endpoint)m_node.endpoint << ")";
{
Guard l(x_pubkDiscoverPings);
m_pubkDiscoverPings[_node.endpoint.udp.address()] = std::chrono::steady_clock::now();
m_pubkDiscoverPings[_node.endpoint.address] = std::chrono::steady_clock::now();
}
PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port());
PingNode p(_node.endpoint, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort);
p.sign(m_secret);
m_socketPointer->send(p);
return move(shared_ptr<NodeEntry>());
@ -98,11 +97,10 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node)
return m_nodes[_node.id];
}
shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, NodeIPEndpoint(_node.endpoint.udp, _node.endpoint.tcp)));
shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, _node.endpoint));
m_nodes[_node.id] = ret;
ret->cullEndpoint();
clog(NodeTableConnect) << "addNode pending for" << _node.endpoint.udp << _node.endpoint.tcp;
PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port());
clog(NodeTableConnect) << "addNode pending for" << _node.endpoint;
PingNode p(_node.endpoint, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort);
p.sign(m_secret);
m_socketPointer->send(p);
return ret;
@ -143,10 +141,10 @@ Node NodeTable::node(NodeId const& _id)
if (m_nodes.count(_id))
{
auto entry = m_nodes[_id];
Node n(_id, NodeIPEndpoint(entry->endpoint.udp, entry->endpoint.tcp), entry->required);
Node n(_id, entry->endpoint, entry->required);
return move(n);
}
return move(Node());
return UnspecifiedNode;
}
shared_ptr<NodeEntry> NodeTable::nodeEntry(NodeId _id)
@ -176,7 +174,7 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr<set<shared_pt
{
auto r = nearest[i];
tried.push_back(r);
FindNode p(r->endpoint.udp, _node);
FindNode p(r->endpoint, _node);
p.sign(m_secret);
m_findNodeTimeout.push_back(make_pair(r->id, chrono::steady_clock::now()));
m_socketPointer->send(p);
@ -274,13 +272,14 @@ vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeId _target)
vector<shared_ptr<NodeEntry>> ret;
for (auto& nodes: found)
for (auto n: nodes.second)
ret.push_back(n);
if (n->endpoint.isAllowed())
ret.push_back(n);
return move(ret);
}
void NodeTable::ping(bi::udp::endpoint _to) const
{
PingNode p(_to, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port());
PingNode p(_to, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort);
p.sign(m_secret);
m_socketPointer->send(p);
}
@ -288,7 +287,7 @@ void NodeTable::ping(bi::udp::endpoint _to) const
void NodeTable::ping(NodeEntry* _n) const
{
if (_n)
ping(_n->endpoint.udp);
ping(_n->endpoint);
}
void NodeTable::evict(shared_ptr<NodeEntry> _leastSeen, shared_ptr<NodeEntry> _new)
@ -307,16 +306,15 @@ void NodeTable::evict(shared_ptr<NodeEntry> _leastSeen, shared_ptr<NodeEntry> _n
void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _endpoint)
{
if (_pubk == m_node.address())
if (_pubk == m_node.address() || !NodeIPEndpoint(_endpoint.address(), _endpoint.port(), _endpoint.port()).isAllowed())
return;
shared_ptr<NodeEntry> node = nodeEntry(_pubk);
if (!!node && !node->pending)
{
clog(NodeTableConnect) << "Noting active node:" << _pubk.abridged() << _endpoint.address().to_string() << ":" << _endpoint.port();
node->endpoint.udp.address(_endpoint.address());
node->endpoint.udp.port(_endpoint.port());
node->cullEndpoint();
node->endpoint.address = _endpoint.address();
node->endpoint.udpPort = _endpoint.port();
shared_ptr<NodeEntry> contested;
{
@ -446,7 +444,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
m_pubkDiscoverPings.erase(_from.address());
}
if (!haveNode(nodeid))
addNode(nodeid, _from, bi::tcp::endpoint(_from.address(), _from.port()));
addNode(nodeid, NodeIPEndpoint(_from.address(), _from.port(), _from.port()));
}
else
return; // unsolicited pong; don't note node as active
@ -477,7 +475,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
Neighbours in = Neighbours::fromBytesConstRef(_from, rlpBytes);
for (auto n: in.nodes)
addNode(n.node, bi::udp::endpoint(bi::address::from_string(n.ipAddress), n.port), bi::tcp::endpoint(bi::address::from_string(n.ipAddress), n.port));
addNode(n.node, NodeIPEndpoint(bi::address::from_string(n.ipAddress), n.udpPort, n.udpPort));
break;
}
@ -508,7 +506,8 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
return;
}
addNode(nodeid, _from, bi::tcp::endpoint(bi::address::from_string(in.ipAddress), in.port));
// TODO: Feedback if _from.address() != in.ipAddress
addNode(nodeid, NodeIPEndpoint(_from.address(), _from.port(), in.tcpPort));
Pong p(_from);
p.echo = sha3(rlpBytes);
@ -586,7 +585,7 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec)
void PingNode::streamRLP(RLPStream& _s) const
{
_s.appendList(4);
_s << dev::p2p::c_protocolVersion << ipAddress << port << expiration;
_s << dev::p2p::c_protocolVersion << ipAddress << tcpPort << ts;
}
void PingNode::interpretRLP(bytesConstRef _bytes)
@ -596,15 +595,15 @@ void PingNode::interpretRLP(bytesConstRef _bytes)
{
version = 2;
ipAddress = r[0].toString();
port = r[1].toInt<unsigned>(RLP::Strict);
expiration = r[2].toInt<unsigned>(RLP::Strict);
tcpPort = r[1].toInt<unsigned>(RLP::Strict);
ts = r[2].toInt<unsigned>(RLP::Strict);
}
else if (r.itemCountStrict() == 4)
{
version = r[0].toInt<unsigned>(RLP::Strict);
ipAddress = r[1].toString();
port = r[2].toInt<unsigned>(RLP::Strict);
expiration = r[3].toInt<unsigned>(RLP::Strict);
tcpPort = r[2].toInt<unsigned>(RLP::Strict);
ts = r[3].toInt<unsigned>(RLP::Strict);
}
else
BOOST_THROW_EXCEPTION(InvalidRLP());

63
libp2p/NodeTable.h

@ -41,10 +41,7 @@ namespace p2p
struct NodeEntry: public Node
{
NodeEntry(Node _src, Public _pubk, NodeIPEndpoint _gw);
NodeEntry(Node _src, Public _pubk, bi::udp::endpoint _udp);
unsigned const distance; ///< Node's distance (xor of _src as integer).
bool pending = true; ///< Node will be ignored until Pong is received
};
@ -137,7 +134,7 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this<NodeTable>
public:
/// Constructor requiring host for I/O, credentials, and IP Address and port to listen on.
NodeTable(ba::io_service& _io, KeyPair _alias, bi::address const& _udpAddress, uint16_t _udpPort = 30303);
NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint);
~NodeTable();
/// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.
@ -150,7 +147,7 @@ public:
void processEvents();
/// Add node. Node will be pinged and empty shared_ptr is returned if NodeId is uknown.
std::shared_ptr<NodeEntry> addNode(Public const& _pubk, bi::udp::endpoint const& _udp, bi::tcp::endpoint const& _tcp);
std::shared_ptr<NodeEntry> addNode(Public const& _pubk, NodeIPEndpoint const& _ep);
/// Add node. Node will be pinged and empty shared_ptr is returned if node has never been seen.
std::shared_ptr<NodeEntry> addNode(Node const& _node);
@ -213,7 +210,7 @@ private:
void ping(NodeEntry* _n) const;
/// Returns center node entry which describes this node and used with dist() to calculate xor metric for node table nodes.
NodeEntry center() const { return NodeEntry(m_node, m_node.publicKey(), m_node.endpoint.udp); }
NodeEntry center() const { return NodeEntry(m_node, m_node.publicKey(), m_node.endpoint); }
/// Used by asynchronous operations to return NodeEntry which is active and managed by node table.
std::shared_ptr<NodeEntry> nodeEntry(NodeId _id);
@ -285,10 +282,10 @@ private:
inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable)
{
_out << _nodeTable.center().address() << "\t" << "0\t" << _nodeTable.center().endpoint.udp.address() << ":" << _nodeTable.center().endpoint.udp.port() << std::endl;
_out << _nodeTable.center().address() << "\t" << "0\t" << _nodeTable.center().endpoint.address << ":" << _nodeTable.center().endpoint.udpPort << std::endl;
auto s = _nodeTable.snapshot();
for (auto n: s)
_out << n.address() << "\t" << n.distance << "\t" << n.endpoint.udp.address() << ":" << n.endpoint.udp.port() << std::endl;
_out << n.address() << "\t" << n.distance << "\t" << n.endpoint.address << ":" << n.endpoint.udpPort << std::endl;
return _out;
}
@ -296,7 +293,7 @@ struct InvalidRLP: public Exception {};
/**
* Ping packet: Sent to check if node is alive.
* PingNode is cached and regenerated after expiration - t, where t is timeout.
* PingNode is cached and regenerated after ts + t, where t is timeout.
*
* Ping is used to implement evict. When a new node is seen for
* a given bucket which is full, the least-responsive node is pinged.
@ -310,7 +307,6 @@ struct InvalidRLP: public Exception {};
* signature: Signature of message.
* ipAddress: Our IP address.
* port: Our port.
* expiration: Triggers regeneration of packet. May also provide control over synchronization.
*
* @todo uint128_t for ip address (<->integer ipv4/6, asio-address, asio-endpoint)
*
@ -318,14 +314,15 @@ struct InvalidRLP: public Exception {};
struct PingNode: RLPXDatagram<PingNode>
{
PingNode(bi::udp::endpoint _ep): RLPXDatagram<PingNode>(_ep) {}
PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram<PingNode>(_ep), ipAddress(_src), port(_srcPort), expiration(futureFromEpoch(_expiration)) {}
PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _ts = std::chrono::seconds(60)): RLPXDatagram<PingNode>(_ep), ipAddress(_src), tcpPort(_srcPort), ts(futureFromEpoch(_ts)) {}
static const uint8_t type = 1;
unsigned version = 0;
std::string ipAddress;
unsigned port;
unsigned expiration;
// uint16_t udpPort;
uint16_t tcpPort;
unsigned ts;
void streamRLP(RLPStream& _s) const override;
void interpretRLP(bytesConstRef _bytes) override;
@ -340,20 +337,20 @@ struct PingNode: RLPXDatagram<PingNode>
*/
struct Pong: RLPXDatagram<Pong>
{
Pong(bi::udp::endpoint _ep): RLPXDatagram<Pong>(_ep), expiration(futureFromEpoch(std::chrono::seconds(60))) {}
Pong(bi::udp::endpoint _ep): RLPXDatagram<Pong>(_ep), ts(futureFromEpoch(std::chrono::seconds(60))) {}
static const uint8_t type = 2;
h256 echo; ///< MCD of PingNode
unsigned expiration;
unsigned ts;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << echo << expiration; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); echo = (h256)r[0]; expiration = r[1].toInt<unsigned>(); }
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << echo << ts; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); echo = (h256)r[0]; ts = r[1].toInt<unsigned>(); }
};
/**
* FindNode Packet: Request k-nodes, closest to the target.
* FindNode is cached and regenerated after expiration - t, where t is timeout.
* FindNode is cached and regenerated after ts + t, where t is timeout.
* FindNode implicitly results in finding neighbours of a given node.
*
* RLP Encoded Items: 2
@ -361,21 +358,20 @@ struct Pong: RLPXDatagram<Pong>
* Maximum Encoded Size: 30 bytes
*
* target: NodeId of node. The responding node will send back nodes closest to the target.
* expiration: Triggers regeneration of packet. May also provide control over synchronization.
*
*/
struct FindNode: RLPXDatagram<FindNode>
{
FindNode(bi::udp::endpoint _ep): RLPXDatagram<FindNode>(_ep) {}
FindNode(bi::udp::endpoint _ep, NodeId _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram<FindNode>(_ep), target(_target), expiration(futureFromEpoch(_expiration)) {}
FindNode(bi::udp::endpoint _ep, NodeId _target, std::chrono::seconds _ts = std::chrono::seconds(60)): RLPXDatagram<FindNode>(_ep), target(_target), ts(futureFromEpoch(_ts)) {}
static const uint8_t type = 3;
h512 target;
unsigned expiration;
unsigned ts;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash<h512>(); expiration = r[1].toInt<unsigned>(); }
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << ts; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash<h512>(); ts = r[1].toInt<unsigned>(); }
};
/**
@ -391,21 +387,22 @@ struct Neighbours: RLPXDatagram<Neighbours>
Node() = default;
Node(RLP const& _r) { interpretRLP(_r); }
std::string ipAddress;
unsigned port;
uint16_t udpPort;
// uint16_t tcpPort;
NodeId node;
void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << node; }
void interpretRLP(RLP const& _r) { ipAddress = _r[0].toString(); port = _r[1].toInt<unsigned>(); node = h512(_r[2].toBytes()); }
void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << udpPort << node; }
void interpretRLP(RLP const& _r) { ipAddress = _r[0].toString(); udpPort = _r[1].toInt<uint16_t>(); node = h512(_r[2].toBytes()); }
};
Neighbours(bi::udp::endpoint _ep): RLPXDatagram<Neighbours>(_ep), expiration(futureFromEpoch(std::chrono::seconds(30))) {}
Neighbours(bi::udp::endpoint _to, std::vector<std::shared_ptr<NodeEntry>> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram<Neighbours>(_to), expiration(futureFromEpoch(std::chrono::seconds(30)))
Neighbours(bi::udp::endpoint _ep): RLPXDatagram<Neighbours>(_ep), ts(futureFromEpoch(std::chrono::seconds(30))) {}
Neighbours(bi::udp::endpoint _to, std::vector<std::shared_ptr<NodeEntry>> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram<Neighbours>(_to), ts(futureFromEpoch(std::chrono::seconds(30)))
{
auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size();
for (auto i = _offset; i < limit; i++)
{
Node node;
node.ipAddress = _nearest[i]->endpoint.udp.address().to_string();
node.port = _nearest[i]->endpoint.udp.port();
node.ipAddress = _nearest[i]->endpoint.address.to_string();
node.udpPort = _nearest[i]->endpoint.udpPort;
node.node = _nearest[i]->publicKey();
nodes.push_back(node);
}
@ -413,10 +410,10 @@ struct Neighbours: RLPXDatagram<Neighbours>
static const uint8_t type = 4;
std::vector<Node> nodes;
unsigned expiration = 1;
unsigned ts = 1;
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << expiration; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); expiration = r[1].toInt<unsigned>(); }
void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << ts; }
void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); ts = r[1].toInt<unsigned>(); }
};
struct NodeTableWarn: public LogChannel { static const char* name() { return "!P!"; } static const int verbosity = 0; };

2
libp2p/Peer.cpp

@ -33,7 +33,7 @@ namespace p2p
bool Peer::shouldReconnect() const
{
return chrono::system_clock::now() > m_lastAttempted + chrono::seconds(fallbackSeconds());
return id && endpoint && chrono::system_clock::now() > m_lastAttempted + chrono::seconds(fallbackSeconds());
}
unsigned Peer::fallbackSeconds() const

5
libp2p/Peer.h

@ -57,10 +57,11 @@ class Peer: public Node
friend class RLPXHandshake;
public:
/// Construct Peer from Node.
Peer(Node const& _node): Node(_node.id, _node.endpoint, _node.required) {}
bool isOffline() const { return !m_session.lock(); }
bi::tcp::endpoint const& peerEndpoint() const { return endpoint.tcp; }
virtual bool operator<(Peer const& _p) const;
/// WIP: Returns current peer rating.

12
libp2p/Session.cpp

@ -139,11 +139,11 @@ void Session::serviceNodesRequest()
auto rs = randomSelection(peers, 10);
for (auto const& i: rs)
{
clogS(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.peerEndpoint();
if (i.peerEndpoint().address().is_v4())
s.appendList(3) << bytesConstRef(i.peerEndpoint().address().to_v4().to_bytes().data(), 4) << i.peerEndpoint().port() << i.id;
clogS(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.endpoint;
if (i.endpoint.address.is_v4())
s.appendList(3) << bytesConstRef(i.endpoint.address.to_v4().to_bytes().data(), 4) << i.endpoint.tcpPort << i.id;
else// if (i.second.address().is_v6()) - assumed
s.appendList(3) << bytesConstRef(i.peerEndpoint().address().to_v6().to_bytes().data(), 16) << i.peerEndpoint().port() << i.id;
s.appendList(3) << bytesConstRef(i.endpoint.address.to_v6().to_bytes().data(), 16) << i.endpoint.tcpPort << i.id;
}
sealAndSend(s);
m_theyRequestedNodes = false;
@ -238,7 +238,7 @@ bool Session::interpret(PacketType _t, RLP const& _r)
// OK passed all our checks. Assume it's good.
addRating(1000);
m_server->addNode(id, ep.address(), ep.port(), ep.port());
m_server->addNode(id, NodeIPEndpoint(ep.address(), ep.port(), ep.port()));
clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")";
CONTINUE:;
LAMEPEER:;
@ -371,7 +371,7 @@ void Session::disconnect(DisconnectReason _reason)
clogS(NetConnect) << "Disconnecting (our reason:" << reasonOf(_reason) << ")";
StructuredLogger::p2pDisconnected(
m_info.id.abridged(),
m_peer->peerEndpoint(),
m_peer->endpoint, // TODO: may not be 100% accurate
m_server->peerCount()
);
if (m_socket.is_open())

56
libsolidity/AST.cpp

@ -54,6 +54,7 @@ void ContractDefinition::checkTypeRequirements()
checkIllegalOverrides();
checkAbstractFunctions();
checkAbstractConstructors();
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
@ -152,6 +153,45 @@ void ContractDefinition::checkAbstractFunctions()
}
}
void ContractDefinition::checkAbstractConstructors()
{
set<ContractDefinition const*> argumentsNeeded;
// check that we get arguments for all base constructors that need it.
// If not mark the contract as abstract (not fully implemented)
vector<ContractDefinition const*> const& bases = getLinearizedBaseContracts();
for (ContractDefinition const* contract: bases)
if (FunctionDefinition const* constructor = contract->getConstructor())
if (contract != this && !constructor->getParameters().empty())
argumentsNeeded.insert(contract);
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->getConstructor())
for (auto const& modifier: constructor->getModifiers())
{
auto baseContract = dynamic_cast<ContractDefinition const*>(
modifier->getName()->getReferencedDeclaration()
);
if (baseContract)
argumentsNeeded.erase(baseContract);
}
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{
auto baseContract = dynamic_cast<ContractDefinition const*>(
base->getName()->getReferencedDeclaration()
);
solAssert(baseContract, "");
if (!base->getArguments().empty())
argumentsNeeded.erase(baseContract);
}
}
if (!argumentsNeeded.empty())
setFullyImplemented(false);
}
void ContractDefinition::checkIllegalOverrides() const
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
@ -281,7 +321,7 @@ void InheritanceSpecifier::checkTypeRequirements()
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(m_baseName->getReferencedDeclaration());
solAssert(base, "Base contract not available.");
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
@ -348,8 +388,8 @@ void FunctionDefinition::checkTypeRequirements()
}
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
modifier->checkTypeRequirements(isConstructor() ?
dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() :
vector<ASTPointer<InheritanceSpecifier>>());
dynamic_cast<ContractDefinition const&>(*getScope()).getLinearizedBaseContracts() :
vector<ContractDefinition const*>());
if (m_body)
m_body->checkTypeRequirements();
}
@ -426,7 +466,7 @@ void ModifierDefinition::checkTypeRequirements()
m_body->checkTypeRequirements();
}
void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpecifier>> const& _bases)
void ModifierInvocation::checkTypeRequirements(vector<ContractDefinition const*> const& _bases)
{
m_modifierName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
@ -439,10 +479,10 @@ void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpec
parameters = &modifier->getParameters();
else
// check parameters for Base constructors
for (auto const& base: _bases)
if (declaration == base->getName()->getReferencedDeclaration())
for (auto const* base: _bases)
if (declaration == base)
{
if (auto referencedConstructor = dynamic_cast<ContractDefinition const&>(*declaration).getConstructor())
if (auto referencedConstructor = base->getConstructor())
parameters = &referencedConstructor->getParameters();
else
parameters = &emptyParameterList;
@ -686,7 +726,7 @@ void NewExpression::checkTypeRequirements()
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
if (!m_contract->isFullyImplemented())
BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract."));
BOOST_THROW_EXCEPTION(createTypeError("Trying to create an instance of an abstract contract."));
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},

5
libsolidity/AST.h

@ -284,6 +284,7 @@ public:
private:
void checkIllegalOverrides() const;
void checkAbstractFunctions();
void checkAbstractConstructors();
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
@ -376,7 +377,7 @@ class EnumValue: public Declaration
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
TypePointer getType(ContractDefinition const* = nullptr) const;
virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
};
/**
@ -566,7 +567,7 @@ public:
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
/// @param _bases is the list of base contracts for base constructor calls. For modifiers an empty vector should be passed.
void checkTypeRequirements(std::vector<ASTPointer<InheritanceSpecifier>> const& _bases);
void checkTypeRequirements(std::vector<ContractDefinition const*> const& _bases);
private:
ASTPointer<Identifier> m_modifierName;

1
libsolidity/Compiler.cpp

@ -136,6 +136,7 @@ void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
FunctionType constructorType(_constructor);
if (!constructorType.getParameterTypes().empty())
{
solAssert(m_baseArguments.count(&_constructor), "");
std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor];
solAssert(arguments, "");
for (unsigned i = 0; i < arguments->size(); ++i)

5
libsolidity/Compiler.h

@ -42,9 +42,10 @@ public:
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
/// @arg _sourceCodes is the map of input files to source code strings
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const
/// @arg _inJsonFromat shows whether the out should be in Json format
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
{
m_context.streamAssembly(_stream, _sourceCodes);
m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat);
}
/// @returns Assembly items of the normal compiler context
eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); }

6
libsolidity/CompilerContext.h

@ -122,7 +122,11 @@ public:
eth::Assembly const& getAssembly() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); }
/// @arg _inJsonFormat shows whether the out should be in Json format
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
{
m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
}
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }

18
libsolidity/CompilerStack.cpp

@ -157,14 +157,16 @@ bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
return getBytecode();
}
eth::AssemblyItems const& CompilerStack::getAssemblyItems(string const& _contractName) const
eth::AssemblyItems const* CompilerStack::getAssemblyItems(string const& _contractName) const
{
return getContract(_contractName).compiler->getAssemblyItems();
Contract const& contract = getContract(_contractName);
return contract.compiler ? &getContract(_contractName).compiler->getAssemblyItems() : nullptr;
}
eth::AssemblyItems const& CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const
eth::AssemblyItems const* CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const
{
return getContract(_contractName).compiler->getRuntimeAssemblyItems();
Contract const& contract = getContract(_contractName);
return contract.compiler ? &getContract(_contractName).compiler->getRuntimeAssemblyItems() : nullptr;
}
bytes const& CompilerStack::getBytecode(string const& _contractName) const
@ -182,9 +184,13 @@ dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
return dev::sha3(getRuntimeBytecode(_contractName));
}
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
{
getContract(_contractName).compiler->streamAssembly(_outStream, _sourceCodes);
Contract const& contract = getContract(_contractName);
if (contract.compiler)
contract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat);
else
_outStream << "Contract not fully implemented" << endl;
}
string const& CompilerStack::getInterface(string const& _contractName) const

7
libsolidity/CompilerStack.h

@ -94,16 +94,17 @@ public:
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
/// @returns normal contract assembly items
eth::AssemblyItems const& getAssemblyItems(std::string const& _contractName = "") const;
eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const;
/// @returns runtime contract assembly items
eth::AssemblyItems const& getRuntimeAssemblyItems(std::string const& _contractName = "") const;
eth::AssemblyItems const* getRuntimeAssemblyItems(std::string const& _contractName = "") const;
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
dev::h256 getContractCodeHash(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream.
/// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFromat shows whether the out should be in Json format
/// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const;
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const;
/// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile.

16
libsolidity/LValue.cpp

@ -107,6 +107,11 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
<< u256(0x100) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV;
if (m_dataType.getCategory() == Type::Category::FixedBytes)
m_context << (u256(0x1) << (256 - 8 * m_dataType.getStorageBytes())) << eth::Instruction::MUL;
else if (
m_dataType.getCategory() == Type::Category::Integer &&
dynamic_cast<IntegerType const&>(m_dataType).isSigned()
)
m_context << u256(m_dataType.getStorageBytes() - 1) << eth::Instruction::SIGNEXTEND;
else
m_context << ((u256(0x1) << (8 * m_dataType.getStorageBytes())) - 1) << eth::Instruction::AND;
}
@ -148,6 +153,17 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
m_context
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(m_dataType).getNumBytes()))
<< eth::Instruction::SWAP1 << eth::Instruction::DIV;
else if (
m_dataType.getCategory() == Type::Category::Integer &&
dynamic_cast<IntegerType const&>(m_dataType).isSigned()
)
// remove the higher order bits
m_context
<< (u256(1) << (8 * (32 - m_dataType.getStorageBytes())))
<< eth::Instruction::SWAP1
<< eth::Instruction::DUP2
<< eth::Instruction::MUL
<< eth::Instruction::DIV;
m_context << eth::Instruction::MUL << eth::Instruction::OR;
// stack: value storage_ref updated_value
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;

4
libwebthree/WebThree.cpp

@ -104,12 +104,12 @@ bytes WebThreeDirect::saveNetwork()
void WebThreeDirect::addNode(NodeId const& _node, bi::tcp::endpoint const& _host)
{
m_net.addNode(_node, _host.address(), _host.port(), _host.port());
m_net.addNode(_node, NodeIPEndpoint(_host.address(), _host.port(), _host.port()));
}
void WebThreeDirect::requirePeer(NodeId const& _node, bi::tcp::endpoint const& _host)
{
m_net.requirePeer(_node, _host.address(), _host.port());
m_net.requirePeer(_node, NodeIPEndpoint(_host.address(), _host.port(), _host.port()));
}

2
mix/ClientModel.cpp

@ -498,6 +498,8 @@ QVariant ClientModel::formatStorageValue(SolidityType const& _type, map<u256, u2
bytes slotBytes = toBigEndian(slotValue);
auto start = slotBytes.end() - _type.size - offset;
bytes val(32 - _type.size); //prepend with zeroes
if (_type.type == SolidityType::SignedInteger && (*start & 0x80)) //extend sign
std::fill(val.begin(), val.end(), 0xff);
val.insert(val.end(), start, start + _type.size);
values.append(decoder.decode(_type, val));
offset += _type.size;

2
mix/CodeHighlighter.cpp

@ -109,7 +109,7 @@ void CodeHighlighter::processError(dev::Exception const& _exception)
void CodeHighlighter::processComments(std::string const& _source)
{
unsigned i = 0;
unsigned size = _source.size();
size_t size = _source.size();
if (size == 0)
return;
while (i < size - 1)

20
mix/CodeModel.cpp

@ -135,8 +135,8 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
CollectDeclarationsVisitor visitor(&m_functions, &m_locals);
m_storage = collectStorage(contractDefinition);
contractDefinition.accept(visitor);
m_assemblyItems = _compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = _compiler.getAssemblyItems(name);
m_assemblyItems = *_compiler.getRuntimeAssemblyItems(name);
m_constructorAssemblyItems = *_compiler.getAssemblyItems(name);
}
QString CompiledContract::codeHex() const
@ -299,12 +299,18 @@ void CodeModel::runCompilationJob(int _jobId)
{
std::ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs);
SourceLocation const* location = boost::get_error_info<solidity::errinfo_sourceLocation>(_exception);
QString message = QString::fromStdString(error.str());
CompiledContract* contract = nullptr;
if (location && location->sourceName.get() && (contract = contractByDocumentId(QString::fromStdString(*location->sourceName))))
message = message.replace(QString::fromStdString(*location->sourceName), contract->contract()->name()); //substitute the location to match our contract names
compilationError(message, QString::fromStdString(*location->sourceName));
QString sourceName;
if (SourceLocation const* location = boost::get_error_info<solidity::errinfo_sourceLocation>(_exception))
{
if (location->sourceName)
sourceName = QString::fromStdString(*location->sourceName);
if (!sourceName.isEmpty())
if (CompiledContract* contract = contractByDocumentId(sourceName))
//substitute the location to match our contract names
message = message.replace(sourceName, contract->contract()->name());
}
compilationError(message, sourceName);
}
m_compiling = false;
emit stateChanged();

2
mix/ContractCallDataEncoder.cpp

@ -127,7 +127,7 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, Solidit
}
}
unsigned dataSize = _type.dynamicSize ? result.size() : alignSize;
size_t 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());

3
mix/FileIo.cpp

@ -102,6 +102,7 @@ void FileIo::writeFile(QString const& _url, QString const& _data)
}
else
error(tr("Error writing file %1").arg(_url));
file.close();
m_watcher->addPath(path);
}
@ -184,7 +185,7 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder)
QUrl url(_deploymentFolder + "package.dapp");
QFile compressed(url.path());
QByteArray qFileBytes((char*)dapp.data(), dapp.size());
QByteArray qFileBytes((char*)dapp.data(), static_cast<int>(dapp.size()));
if (compressed.open(QIODevice::WriteOnly))
{
compressed.write(qFileBytes);

3
mix/MixClient.cpp

@ -40,7 +40,7 @@ namespace mix
{
Secret const c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
u256 const c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow
u256 const c_mixGenesisDifficulty = 131072; //TODO: make it lower for Mix somehow
bytes MixBlockChain::createGenesisBlock(h256 _stateRoot)
{
@ -117,6 +117,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256();
State execState = _state;
execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation
Executive execution(execState, lastHashes, 0);
execution.initialize(&rlp);
execution.execute();

14
mix/qml/WebPreview.qml

@ -377,7 +377,7 @@ Item {
font.italic: true
font.pointSize: appStyle.absoluteSize(-3)
anchors.verticalCenter: parent.verticalCenter
property bool active: false
property var history: []
property int index: -1
@ -394,12 +394,20 @@ Item {
expressionInput.text = history[index];
}
onTextChanged: {
active = text !== "";
if (!active)
index = -1;
}
Keys.onDownPressed: {
displayCache(1);
if (active)
displayCache(-1);
}
Keys.onUpPressed: {
displayCache(-1);
displayCache(1);
active = true;
}
Keys.onEnterPressed:

4
mix/qml/html/cm/errorannotation.js

@ -23,13 +23,13 @@ ErrorAnnotation.prototype.init = function()
ErrorAnnotation.prototype.open = function()
{
if (this.errorMark.find())
if (this.line)
{
var node = document.createElement("div");
node.id = "annotation"
node.innerHTML = this.content;
node.className = "CodeMirror-errorannotation-context";
this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: false });
this.lineWidget = this.editor.addLineWidget(this.line, node, { coverGutter: false });
this.opened = true;
}
}

10
mix/test/qml/TestMain.qml

@ -58,11 +58,20 @@ TestCase
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
}
function waitForMining()
{
while (mainApplication.clientModel.mining)
ts.waitForSignal(mainApplication.clientModel, "miningComplete()", 5000);
wait(1); //allow events to propagate 2 times for transaction log to be updated
wait(1);
}
function waitForExecution()
{
while (mainApplication.clientModel.running)
ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000);
wait(1); //allow events to propagate 2 times for transaction log to be updated
wait(1);
}
function editHtml(c)
@ -89,6 +98,7 @@ TestCase
function test_dbg_vm() { TestDebugger.test_vmDebugging(); }
function test_miner_getDefaultiner() { TestMiner.test_getDefaultMiner(); }
function test_miner_selectMiner() { TestMiner.test_selectMiner(); }
function test_miner_mine() { TestMiner.test_mine(); }
function test_project_contractRename() { TestProject.test_contractRename(); }
}

5
mix/test/qml/js/TestDebugger.js

@ -91,17 +91,20 @@ function test_arrayParametersAndStorage()
" {\r" +
" m = x;\r" +
" s = 5;\r" +
" signed = 6534;\r" +
" }\r" +
" \r" +
" function setMV(uint72[5] x) external\r" +
" {\r" +
" mv = x;\r" +
" s = 42;\r" +
" signed = -534;\r" +
" }\r" +
" \r" +
" uint256[] m;\r" +
" uint72[5] mv;\r" +
" uint256 s;\r" +
" int48 signed;\r" +
" }\r");
mainApplication.projectModel.stateListModel.editState(0);
@ -127,11 +130,13 @@ function test_arrayParametersAndStorage()
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");
tryCompare(mainApplication.mainContent.rightPane.solStorage.item.value, "signed", "6534");
//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.solStorage.item.value, "signed", "-534");
tryCompare(mainApplication.mainContent.rightPane.solCallStack.listModel, 0, "setMV");
}

10
mix/test/qml/js/TestMiner.js

@ -18,3 +18,13 @@ function test_selectMiner()
compare(state.miner.secret, account.secret);
}
function test_mine()
{
newProject();
mainApplication.mainContent.startQuickDebugging();
waitForExecution();
mainApplication.clientModel.mine();
waitForMining();
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "contract", " - Block - ");
}

4
mix/test/qml/js/TestProject.js

@ -1,11 +1,12 @@
function test_contractRename()
{
newProject();
waitForExecution();
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Contract");
editContract("contract Renamed {}");
mainApplication.mainContent.startQuickDebugging();
waitForExecution();
wait(1000);
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "Renamed");
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Renamed");
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
@ -14,5 +15,4 @@ function test_contractRename()
tryCompare(transactionDialog, "functionId", "Renamed");
transactionDialog.close();
mainApplication.projectModel.stateDialog.close();
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "Renamed");
}

44
solc/CommandLineInterface.cpp

@ -55,6 +55,7 @@ namespace solidity
static string const g_argAbiStr = "json-abi";
static string const g_argSolAbiStr = "sol-abi";
static string const g_argAsmStr = "asm";
static string const g_argAsmJsonStr = "asm-json";
static string const g_argAstStr = "ast";
static string const g_argAstJson = "ast-json";
static string const g_argBinaryStr = "binary";
@ -80,10 +81,15 @@ static bool needStdout(po::variables_map const& _args)
{
return
argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argSolAbiStr) ||
argToStdout(_args, g_argNatspecUserStr) || argToStdout(_args, g_argAstJson) ||
argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) ||
argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr);
argToStdout(_args, g_argAbiStr) ||
argToStdout(_args, g_argSolAbiStr) ||
argToStdout(_args, g_argNatspecUserStr) ||
argToStdout(_args, g_argAstJson) ||
argToStdout(_args, g_argNatspecDevStr) ||
argToStdout(_args, g_argAsmStr) ||
argToStdout(_args, g_argAsmJsonStr) ||
argToStdout(_args, g_argOpcodesStr) ||
argToStdout(_args, g_argBinaryStr);
}
static inline bool outputToFile(OutputType type)
@ -215,23 +221,25 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
("add-std", po::value<bool>()->default_value(false), "Add standard contracts")
("input-file", po::value<vector<string>>(), "input file")
(g_argAstStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the AST of the contract.")
"Request to output the AST of the contract.")
(g_argAstJson.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the AST of the contract in JSON format.")
"Request to output the AST of the contract in JSON format.")
(g_argAsmStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the EVM assembly of the contract.")
"Request to output the EVM assembly of the contract.")
(g_argAsmJsonStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the EVM assembly of the contract in JSON format.")
(g_argOpcodesStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the Opcodes of the contract.")
"Request to output the Opcodes of the contract.")
(g_argBinaryStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the contract in binary (hexadecimal).")
"Request to output the contract in binary (hexadecimal).")
(g_argAbiStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the contract's JSON ABI interface.")
"Request to output the contract's JSON ABI interface.")
(g_argSolAbiStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the contract's Solidity ABI interface.")
"Request to output the contract's Solidity ABI interface.")
(g_argNatspecUserStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the contract's Natspec user documentation.")
"Request to output the contract's Natspec user documentation.")
(g_argNatspecDevStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the contract's Natspec developer documentation.");
"Request to output the contract's Natspec developer documentation.");
// All positional options should be interpreted as input files
po::positional_options_description p;
@ -411,19 +419,19 @@ void CommandLineInterface::actOnInput()
cout << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
if (m_args.count(g_argAsmStr))
if (m_args.count(g_argAsmStr) || m_args.count(g_argAsmJsonStr))
{
auto choice = m_args[g_argAsmStr].as<OutputType>();
auto choice = m_args.count(g_argAsmStr) ? m_args[g_argAsmStr].as<OutputType>() : m_args[g_argAsmJsonStr].as<OutputType>();
if (outputToStdout(choice))
{
cout << "EVM assembly:" << endl;
m_compiler->streamAssembly(cout, contract, m_sourceCodes);
m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
}
if (outputToFile(choice))
{
ofstream outFile(contract + ".evm");
m_compiler->streamAssembly(outFile, contract, m_sourceCodes);
ofstream outFile(contract + (m_args.count(g_argAsmJsonStr) ? "_evm.json" : ".evm"));
m_compiler->streamAssembly(outFile, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
outFile.close();
}
}

23
test/SolidityEndToEndTest.cpp

@ -3676,6 +3676,29 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_with_bytes0)
BOOST_CHECK(callContractFunction("test()") == encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(packed_storage_signed)
{
char const* sourceCode = R"(
contract C {
int8 a;
uint8 b;
int8 c;
uint8 d;
function test() returns (uint x1, uint x2, uint x3, uint x4) {
a = -2;
b = -uint8(a) * 2;
c = a * int8(120) * int8(121);
x1 = uint(a);
x2 = b;
x3 = uint(c);
x4 = d;
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK( callContractFunction("test()") == encodeArgs(u256(-2), u256(4), u256(-112), u256(0)));
}
BOOST_AUTO_TEST_SUITE_END()
}

37
test/SolidityNameAndTypeResolution.cpp

@ -407,6 +407,39 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract BaseBase { function BaseBase(uint j); }
contract base is BaseBase { function foo(); }
contract derived is base {
function derived(uint i) BaseBase(i){}
function foo() {}
}
)";
ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(text), "Parsing and name resolving failed");
}
BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract BaseBase { function BaseBase(uint j); }
contract base is BaseBase { function foo(); }
contract derived is base {
function derived(uint i) {}
function foo() {}
}
)";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name resolving failed");
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->getNodes();
BOOST_CHECK_EQUAL(nodes.size(), 3);
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
BOOST_CHECK(derived);
BOOST_CHECK(!derived->isFullyImplemented());
}
BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
{
ASTPointer<SourceUnit> sourceUnit;
@ -665,7 +698,7 @@ BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments)
contract A { function A(uint a) { } }
contract B is A { }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
}
BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
@ -674,7 +707,7 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
contract A { function A(uint a) { } }
contract B is A { }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
}
BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion)

80
test/SolidityOptimizer.cpp

@ -28,6 +28,7 @@
#include <boost/lexical_cast.hpp>
#include <test/solidityExecutionFramework.h>
#include <libevmcore/CommonSubexpressionEliminator.h>
#include <libevmcore/ControlFlowGraph.h>
#include <libevmcore/Assembly.h>
using namespace std;
@ -96,6 +97,18 @@ public:
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation)
{
AssemblyItems output = _input;
// Running it four times should be enough for these tests.
for (unsigned i = 0; i < 4; ++i)
{
eth::ControlFlowGraph cfg(output);
output = cfg.optimisedItems();
}
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
protected:
Address m_optimizedContract;
Address m_nonOptimizedContract;
@ -731,6 +744,73 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between
BOOST_CHECK_EQUAL(1, count(output.begin(), output.end(), AssemblyItem(Instruction::SHA3)));
}
BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused)
{
// remove parts of the code that are unused
AssemblyItems input{
AssemblyItem(PushTag, 1),
Instruction::JUMP,
u256(7),
AssemblyItem(Tag, 1),
};
checkCFG(input, {});
}
BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused_loop)
{
AssemblyItems input{
AssemblyItem(PushTag, 3),
Instruction::JUMP,
AssemblyItem(Tag, 1),
u256(7),
AssemblyItem(PushTag, 2),
Instruction::JUMP,
AssemblyItem(Tag, 2),
u256(8),
AssemblyItem(PushTag, 1),
Instruction::JUMP,
AssemblyItem(Tag, 3),
u256(11)
};
checkCFG(input, {u256(11)});
}
BOOST_AUTO_TEST_CASE(control_flow_graph_reconnect_single_jump_source)
{
// move code that has only one unconditional jump source
AssemblyItems input{
u256(1),
AssemblyItem(PushTag, 1),
Instruction::JUMP,
AssemblyItem(Tag, 2),
u256(2),
AssemblyItem(PushTag, 3),
Instruction::JUMP,
AssemblyItem(Tag, 1),
u256(3),
AssemblyItem(PushTag, 2),
Instruction::JUMP,
AssemblyItem(Tag, 3),
u256(4),
};
checkCFG(input, {u256(1), u256(3), u256(2), u256(4)});
}
BOOST_AUTO_TEST_CASE(control_flow_graph_do_not_remove_returned_to)
{
// do not remove parts that are "returned to"
AssemblyItems input{
AssemblyItem(PushTag, 1),
AssemblyItem(PushTag, 2),
Instruction::JUMP,
AssemblyItem(Tag, 2),
Instruction::JUMP,
AssemblyItem(Tag, 1),
u256(2)
};
checkCFG(input, {u256(2)});
}
BOOST_AUTO_TEST_SUITE_END()
}

32
test/TestHelper.cpp

@ -119,6 +119,32 @@ ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller):
}
}
json_spirit::mObject& ImportTest::makeAllFieldsHex(json_spirit::mObject& _o)
{
static const set<string> hashes {"bloom" , "coinbase", "hash", "mixHash", "parentHash", "receiptTrie",
"stateRoot", "transactionsTrie", "uncleHash", "currentCoinbase",
"previousHash", "to", "address", "caller", "origin", "secretKey"};
for (auto& i: _o)
{
std::string key = i.first;
if (hashes.count(key))
continue;
std::string str;
json_spirit::mValue value = i.second;
if (value.type() == json_spirit::int_type)
str = toString(value.get_int());
else if (value.type() == json_spirit::str_type)
str = value.get_str();
else continue;
_o[key] = (str.substr(0, 2) == "0x") ? str : "0x" + toHex(toCompactBigEndian(toInt(str)));
}
return _o;
}
void ImportTest::importEnv(json_spirit::mObject& _o)
{
assert(_o.count("previousHash") > 0);
@ -325,6 +351,8 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost)
// export pre state
m_TestObject["pre"] = fillJsonWithState(m_statePre);
m_TestObject["env"] = makeAllFieldsHex(m_TestObject["env"].get_obj());
m_TestObject["transaction"] = makeAllFieldsHex(m_TestObject["transaction"].get_obj());
}
json_spirit::mObject fillJsonWithState(State _state)
@ -335,8 +363,8 @@ json_spirit::mObject fillJsonWithState(State _state)
for (auto const& a: _state.addresses())
{
json_spirit::mObject o;
o["balance"] = toString(_state.balance(a.first));
o["nonce"] = toString(_state.transactionsFrom(a.first));
o["balance"] = "0x" + toHex(toCompactBigEndian(_state.balance(a.first)));
o["nonce"] = "0x" + toHex(toCompactBigEndian(_state.transactionsFrom(a.first)));
{
json_spirit::mObject store;
for (auto const& s: _state.storage(a.first))

1
test/TestHelper.h

@ -121,6 +121,7 @@ public:
static void importState(json_spirit::mObject& _o, eth::State& _state);
static void importState(json_spirit::mObject& _o, eth::State& _state, stateOptionsMap& _stateOptionsMap);
void importTransaction(json_spirit::mObject& _o);
static json_spirit::mObject& makeAllFieldsHex(json_spirit::mObject& _o);
void exportTest(bytes const& _output, eth::State const& _statePost);
static void checkExpectedState(eth::State const& _stateExpect, eth::State const& _statePost, stateOptionsMap const _expectedStateOptions = stateOptionsMap(), WhenError _throw = WhenError::Throw);

18
test/blockchain.cpp

@ -90,7 +90,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
for (auto const& bl: o["blocks"].get_array())
{
mObject blObj = bl.get_obj();
stateTemp = state;
// get txs
TransactionQueue txs;
ZeroGasPricer gp;
@ -222,6 +222,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
txObject["v"] = to_string(txi.second.signature().v + 27);
txObject["to"] = txi.second.isCreation() ? "" : toString(txi.second.receiveAddress());
txObject["value"] = toString(txi.second.value());
txObject = ImportTest::makeAllFieldsHex(txObject);
txArray.push_back(txObject);
}
@ -301,6 +302,11 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
o["blocks"] = blArray;
o["postState"] = fillJsonWithState(state);
//make all values hex
State prestate(OverlayDB(), BaseState::Empty, biGenesisBlock.coinbaseAddress);
importer.importState(o["pre"].get_obj(), prestate);
o["pre"] = fillJsonWithState(prestate);
}//_fillin
else
@ -619,11 +625,11 @@ void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi)
_o["transactionsTrie"] = toString(_bi.transactionsRoot);
_o["receiptTrie"] = toString(_bi.receiptsRoot);
_o["bloom"] = toString(_bi.logBloom);
_o["difficulty"] = toString(_bi.difficulty);
_o["number"] = toString(_bi.number);
_o["gasLimit"] = toString(_bi.gasLimit);
_o["gasUsed"] = toString(_bi.gasUsed);
_o["timestamp"] = toString(_bi.timestamp);
_o["difficulty"] = "0x" + toHex(toCompactBigEndian(_bi.difficulty));
_o["number"] = "0x" + toHex(toCompactBigEndian(_bi.number));
_o["gasLimit"] = "0x" + toHex(toCompactBigEndian(_bi.gasLimit));
_o["gasUsed"] = "0x" + toHex(toCompactBigEndian(_bi.gasUsed));
_o["timestamp"] = "0x" + toHex(toCompactBigEndian(_bi.timestamp));
_o["extraData"] ="0x" + toHex(_bi.extraData);
_o["mixHash"] = toString(_bi.mixHash);
_o["nonce"] = toString(_bi.nonce);

45
test/net.cpp

@ -32,7 +32,13 @@ using namespace dev::p2p;
namespace ba = boost::asio;
namespace bi = ba::ip;
BOOST_AUTO_TEST_SUITE(net)
struct NetFixture
{
NetFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = true; }
~NetFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = false; }
};
BOOST_FIXTURE_TEST_SUITE(net, NetFixture)
/**
* Only used for testing. Not useful beyond tests.
@ -53,7 +59,7 @@ protected:
struct TestNodeTable: public NodeTable
{
/// Constructor
TestNodeTable(ba::io_service& _io, KeyPair _alias, bi::address const& _addr, uint16_t _port = 30300): NodeTable(_io, _alias, _addr, _port) {}
TestNodeTable(ba::io_service& _io, KeyPair _alias, bi::address const& _addr, uint16_t _port = 30300): NodeTable(_io, _alias, NodeIPEndpoint(_addr, _port, _port)) {}
static std::vector<std::pair<KeyPair,unsigned>> createTestNodes(unsigned _count)
{
@ -93,7 +99,7 @@ struct TestNodeTable: public NodeTable
// manually add node for test
{
Guard ln(x_nodes);
shared_ptr<NodeEntry> node(new NodeEntry(m_node, n.first.pub(), NodeIPEndpoint(bi::udp::endpoint(ourIp, n.second), bi::tcp::endpoint(ourIp, n.second))));
shared_ptr<NodeEntry> node(new NodeEntry(m_node, n.first.pub(), NodeIPEndpoint(ourIp, n.second, n.second)));
node->pending = false;
m_nodes[node->id] = node;
}
@ -240,7 +246,7 @@ BOOST_AUTO_TEST_CASE(neighboursPacketLength)
{
Neighbours::Node node;
node.ipAddress = boost::asio::ip::address::from_string("200.200.200.200").to_string();
node.port = testNodes[i].second;
node.udpPort = testNodes[i].second;
node.node = testNodes[i].first.pub();
out.nodes.push_back(node);
}
@ -261,7 +267,7 @@ BOOST_AUTO_TEST_CASE(test_neighbours_packet)
{
Neighbours::Node node;
node.ipAddress = boost::asio::ip::address::from_string("127.0.0.1").to_string();
node.port = n.second;
node.udpPort = n.second;
node.node = n.first.pub();
out.nodes.push_back(node);
}
@ -273,7 +279,7 @@ BOOST_AUTO_TEST_CASE(test_neighbours_packet)
int count = 0;
for (auto n: in.nodes)
{
BOOST_REQUIRE_EQUAL(testNodes[count].second, n.port);
BOOST_REQUIRE_EQUAL(testNodes[count].second, n.udpPort);
BOOST_REQUIRE_EQUAL(testNodes[count].first.pub(), n.node);
BOOST_REQUIRE_EQUAL(sha3(testNodes[count].first.pub()), sha3(n.node));
count++;
@ -337,3 +343,30 @@ BOOST_AUTO_TEST_CASE(test_udp_once)
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(netTypes)
BOOST_AUTO_TEST_CASE(unspecifiedNode)
{
Node n = UnspecifiedNode;
BOOST_REQUIRE(!n);
Node node(Public(sha3("0")), NodeIPEndpoint(bi::address(), 0, 0));
BOOST_REQUIRE(node);
BOOST_REQUIRE(n != node);
Node nodeEq(Public(sha3("0")), NodeIPEndpoint(bi::address(), 0, 0));
BOOST_REQUIRE_EQUAL(node, nodeEq);
}
BOOST_AUTO_TEST_CASE(nodeTableReturnsUnspecifiedNode)
{
ba::io_service io;
NodeTable t(io, KeyPair::create(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30303, 30303));
if (Node n = t.node(NodeId()))
BOOST_REQUIRE(false);
else
BOOST_REQUIRE(n == UnspecifiedNode);
}
BOOST_AUTO_TEST_SUITE_END()

47
test/peer.cpp

@ -28,7 +28,13 @@ using namespace std;
using namespace dev;
using namespace dev::p2p;
BOOST_AUTO_TEST_SUITE(p2p)
struct P2PFixture
{
P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = true; }
~P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = false; }
};
BOOST_FIXTURE_TEST_SUITE(p2p, P2PFixture)
BOOST_AUTO_TEST_CASE(host)
{
@ -45,7 +51,7 @@ BOOST_AUTO_TEST_CASE(host)
auto node2 = host2.id();
host2.start();
host1.addNode(node2, bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort);
host1.addNode(node2, NodeIPEndpoint(bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort));
this_thread::sleep_for(chrono::seconds(3));
@ -82,11 +88,11 @@ BOOST_AUTO_TEST_CASE(save_nodes)
Host& host = *hosts.front();
for (auto const& h: hosts)
host.addNode(h->id(), bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort());
host.addNode(h->id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort()));
Host& host2 = *hosts.back();
for (auto const& h: hosts)
host2.addNode(h->id(), bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort());
host2.addNode(h->id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort()));
this_thread::sleep_for(chrono::milliseconds(2000));
bytes firstHostNetwork(host.saveNetwork());
@ -101,7 +107,36 @@ BOOST_AUTO_TEST_CASE(save_nodes)
BOOST_REQUIRE(r.itemCount() == 3);
BOOST_REQUIRE(r[0].toInt<unsigned>() == dev::p2p::c_protocolVersion);
BOOST_REQUIRE_EQUAL(r[1].toBytes().size(), 32); // secret
BOOST_REQUIRE_EQUAL(r[2].itemCount(), 5);
BOOST_REQUIRE(r[2].itemCount() >= 5);
for (auto i: r[2])
{
BOOST_REQUIRE(i.itemCount() == 3 || i.itemCount() == 10);
BOOST_REQUIRE(i[0].itemCount() == 4 || i[0].itemCount() == 16);
}
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(peerTypes)
BOOST_AUTO_TEST_CASE(emptySharedPeer)
{
shared_ptr<Peer> p;
BOOST_REQUIRE(!p);
std::map<NodeId, std::shared_ptr<Peer>> peers;
p = peers[NodeId()];
BOOST_REQUIRE(!p);
p.reset(new Peer(UnspecifiedNode));
BOOST_REQUIRE(!p->id);
BOOST_REQUIRE(!*p);
p.reset(new Peer(Node(NodeId(EmptySHA3), UnspecifiedNodeIPEndpoint)));
BOOST_REQUIRE(!(!*p));
BOOST_REQUIRE(*p);
BOOST_REQUIRE(p);
}
BOOST_AUTO_TEST_SUITE_END()
@ -131,7 +166,7 @@ int peerTest(int argc, char** argv)
Host ph("Test", NetworkPreferences(listenPort));
if (!remoteHost.empty() && !remoteAlias)
ph.addNode(remoteAlias, bi::address::from_string(remoteHost), remotePort, remotePort);
ph.addNode(remoteAlias, NodeIPEndpoint(bi::address::from_string(remoteHost), remotePort, remotePort));
this_thread::sleep_for(chrono::milliseconds(200));

2
test/stQuadraticComplexityTestFiller.json

@ -43,7 +43,7 @@
},
"transaction" :
{
"//" : "run(int256)",
"data" : "run(int256)",
"data" : "0x61a47706000000000000000000000000000000000000000000000000000000000000c350",
"gasLimit" : "904+68*x+e",
"gasLimit" : "350000000",

78
test/stTransactionTestFiller.json

@ -5,7 +5,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -42,7 +42,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -85,7 +85,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -128,7 +128,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -167,7 +167,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -207,7 +207,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -280,7 +280,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1100",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -316,7 +316,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "22000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -370,7 +370,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "47766",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -424,7 +424,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "220000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -479,7 +479,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "21100",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -515,7 +515,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "21100",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -552,7 +552,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "10000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -609,7 +609,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "100000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -678,7 +678,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -761,7 +761,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -834,7 +834,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "10000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -925,7 +925,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "10000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1003,7 +1003,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1056,7 +1056,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1110,7 +1110,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1170,7 +1170,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "10000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1226,7 +1226,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "100000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1284,7 +1284,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1333,7 +1333,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"post" : {
@ -1381,7 +1381,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1417,7 +1417,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "100000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1453,7 +1453,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "10000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1490,7 +1490,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "10000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1533,7 +1533,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "100000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1570,7 +1570,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "10000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1613,7 +1613,7 @@
"currentGasLimit" : "(2**256)-1",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1655,7 +1655,7 @@
"currentGasLimit" : "(2**256)-1",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1695,7 +1695,7 @@
"currentGasLimit" : "(2**256)-1",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1733,7 +1733,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "100000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1769,7 +1769,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1806,7 +1806,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1846,7 +1846,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {
@ -1894,7 +1894,7 @@
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"expect" : {

3
test/transaction.cpp

@ -51,7 +51,8 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
if (!txFromFields.signature().isValid())
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") );
o["sender"] = toString(txFromFields.sender());
o["sender"] = toString(txFromFields.sender());
o["transaction"] = ImportTest::makeAllFieldsHex(tObj);
}
catch(Exception const& _e)
{

3
test/tt10mbDataFieldFiller.json

File diff suppressed because one or more lines are too long

2
test/ttTransactionTestFiller.json

@ -176,7 +176,7 @@
},
"SenderTest" : {
"//" : "sender 0f65fe9276bc9a24ae7083ae28e2660ef72df99e",
"senderExpect" : "sender 0f65fe9276bc9a24ae7083ae28e2660ef72df99e",
"expect" : "valid",
"transaction" :
{

4
test/vm.cpp

@ -85,7 +85,7 @@ void FakeExtVM::reset(u256 _myBalance, u256 _myNonce, map<u256, u256> const& _st
void FakeExtVM::push(mObject& o, string const& _n, u256 _v)
{
o[_n] = toString(_v);
o[_n] = "0x" + toHex(toCompactBigEndian(_v));
}
void FakeExtVM::push(mArray& a, u256 _v)
@ -448,7 +448,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
BOOST_AUTO_TEST_SUITE(VMTests)
BOOST_AUTO_TEST_CASE(vm_tests)
BOOST_AUTO_TEST_CASE(vmtests)
{
dev::test::executeTests("vmtests", "/VMTests", dev::test::doVMTests);
}

Loading…
Cancel
Save