From 71bf6e7eded7e17cc64b82f6a9fd8d89458a4f85 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 16 Dec 2014 22:01:39 +0100 Subject: [PATCH 01/54] udp != tcp. history-commit. --- libp2p/Host.cpp | 30 +++++----- libp2p/Host.h | 6 +- libp2p/Network.cpp | 8 ++- libp2p/Network.h | 102 +++++++++++++++++++++++++++++++++- libp2p/Session.cpp | 2 +- libp2p/Session.h | 4 +- test/kademlia.cpp | 33 +++++++++++ test/{network.cpp => net.cpp} | 0 8 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 test/kademlia.cpp rename test/{network.cpp => net.cpp} (100%) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 93a6ce672..4a99ac90f 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -43,7 +43,7 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool m_netPrefs(_n), m_ifAddresses(Network::getInterfaceAddresses()), m_ioService(2), - m_acceptorV4(m_ioService), + m_tcp4Acceptor(m_ioService), m_key(KeyPair::create()) { for (auto address: m_ifAddresses) @@ -95,9 +95,9 @@ void Host::doneWorking() m_ioService.reset(); // shutdown acceptor - m_acceptorV4.cancel(); - if (m_acceptorV4.is_open()) - m_acceptorV4.close(); + m_tcp4Acceptor.cancel(); + if (m_tcp4Acceptor.is_open()) + m_tcp4Acceptor.close(); // There maybe an incoming connection which started but hasn't finished. // Wait for acceptor to end itself instead of assuming it's complete. @@ -280,7 +280,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) { if (!m_peerAddresses.count(reqpublicaddr)) m_peerAddresses.insert(reqpublicaddr); - m_public = reqpublic; + m_tcpPublic = reqpublic; return; } @@ -288,7 +288,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) for (auto addr: m_peerAddresses) if (addr.is_v4() && !isPrivateAddress(addr)) { - m_public = bi::tcp::endpoint(*m_peerAddresses.begin(), m_listenPort); + m_tcpPublic = bi::tcp::endpoint(*m_peerAddresses.begin(), m_listenPort); return; } @@ -301,7 +301,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) { if (!m_peerAddresses.count(upnpep.address())) m_peerAddresses.insert(upnpep.address()); - m_public = upnpep; + m_tcpPublic = upnpep; return; } } @@ -312,12 +312,12 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) for (auto addr: m_peerAddresses) if (addr.is_v4() && isPrivateAddress(addr)) { - m_public = bi::tcp::endpoint(addr, m_listenPort); + m_tcpPublic = bi::tcp::endpoint(addr, m_listenPort); return; } // otherwise address is unspecified - m_public = bi::tcp::endpoint(bi::address(), m_listenPort); + m_tcpPublic = bi::tcp::endpoint(bi::address(), m_listenPort); } void Host::runAcceptor() @@ -326,10 +326,10 @@ void Host::runAcceptor() if (m_run && !m_accepting) { - clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; + clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_tcpPublic << ")"; m_accepting = true; m_socket.reset(new bi::tcp::socket(m_ioService)); - m_acceptorV4.async_accept(*m_socket, [=](boost::system::error_code ec) + m_tcp4Acceptor.async_accept(*m_socket, [=](boost::system::error_code ec) { bool success = false; if (!ec) @@ -656,7 +656,7 @@ void Host::startedWorking() } // try to open acceptor (todo: ipv6) - m_listenPort = Network::listen4(m_acceptorV4, m_netPrefs.listenPort); + m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort); // start capability threads for (auto const& h: m_capabilities) @@ -674,8 +674,8 @@ void Host::startedWorking() // if m_public address is valid then add us to node list // todo: abstract empty() and emplace logic - if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id())) - noteNode(id(), m_public, Origin::Perfect, false); + if (!m_tcpPublic.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id())) + noteNode(id(), m_tcpPublic, Origin::Perfect, false); clog(NetNote) << "Id:" << id().abridged(); @@ -739,7 +739,7 @@ void Host::restoreNodes(bytesConstRef _b) { auto oldId = id(); m_key = KeyPair(r[1].toHash()); - noteNode(id(), m_public, Origin::Perfect, false, oldId); + noteNode(id(), m_tcpPublic, Origin::Perfect, false, oldId); for (auto i: r[2]) { diff --git a/libp2p/Host.h b/libp2p/Host.h index a146d6a66..8ed25f2ae 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -152,7 +152,7 @@ public: void pingAll(); /// Get the port we're listening on currently. - unsigned short listenPort() const { return m_public.port(); } + unsigned short listenPort() const { return m_tcpPublic.port(); } /// Serialise the set of known peers. bytes saveNodes() const; @@ -219,7 +219,7 @@ private: int m_listenPort = -1; ///< What port are we listening on. -1 means binding failed or acceptor hasn't been initialized. ba::io_service m_ioService; ///< IOService for network stuff. - bi::tcp::acceptor m_acceptorV4; ///< Listening acceptor. + bi::tcp::acceptor m_tcp4Acceptor; ///< Listening acceptor. std::unique_ptr m_socket; ///< Listening socket. std::unique_ptr m_timer; ///< Timer which, when network is running, calls scheduler() every c_timerInterval ms. @@ -229,7 +229,7 @@ private: std::set m_pendingNodeConns; /// Used only by connect(Node&) to limit concurrently connecting to same node. See connect(shared_ptrconst&). Mutex x_pendingNodeConns; - bi::tcp::endpoint m_public; ///< Our public listening endpoint. + bi::tcp::endpoint m_tcpPublic; ///< Our public listening endpoint. KeyPair m_key; ///< Our unique ID. bool m_hadNewNodes = false; diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 8ca8dd135..94413e93f 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -38,6 +38,12 @@ using namespace std; using namespace dev; using namespace dev::p2p; +template +Socket::Socket(SocketEventFace* _seface): m_eventDelegate(_seface), m_socket(m_eventDelegate->ioService()) {} + +template +Socket::Socket(SocketEventFace* _seface, endpointType _endpoint): m_eventDelegate(_seface), m_socket(m_eventDelegate->ioService(), _endpoint) {} + std::vector Network::getInterfaceAddresses() { std::vector addresses; @@ -111,7 +117,7 @@ std::vector Network::getInterfaceAddresses() return std::move(addresses); } -int Network::listen4(bi::tcp::acceptor& _acceptor, unsigned short _listenPort) +int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort) { int retport = -1; for (unsigned i = 0; i < 2; ++i) diff --git a/libp2p/Network.h b/libp2p/Network.h index 944d390c8..3f96fd457 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -22,13 +22,17 @@ #pragma once +#include #include +#include +#include +#include +#include "Common.h" namespace ba = boost::asio; namespace bi = ba::ip; namespace dev { - namespace p2p { @@ -42,6 +46,98 @@ struct NetworkPreferences bool localNetworking = false; }; +struct Packet +{ + bytes payload() const { return s.out(); } + + bool required = false; + RLPStream s; +}; + +class SocketFace +{ + virtual void send(Packet const& _msg) = 0; +}; +class SocketEventFace; + +/** + * @brief Generic Socket Interface + * Owners of sockets must outlive the socket. + * Boost ASIO uses lowercase template for udp/tcp, which is adopted here. + */ +template +class Socket: SocketFace, public std::enable_shared_from_this> +{ +public: + using socketType = typename T::socket; + using endpointType = typename T::endpoint; + Socket(SocketEventFace* _seface); + Socket(SocketEventFace* _seface, endpointType _endpoint); + +protected: + void send(Packet const& _msg) + { + if (!m_started) + return; + + Guard l(x_sendQ); + sendQ.push_back(_msg.payload()); + if (sendQ.size() == 1 && !m_stopped) + doWrite(); + } + + void doWrite() + { + const bytes& bytes = sendQ[0]; + auto self(Socket::shared_from_this()); +// boost::asio::async_write(m_socket, boost::asio::buffer(bytes), [this, self](boost::system::error_code _ec, std::size_t /*length*/) +// { +// if (_ec) +// return stopWithError(_ec); +// else +// { +// Guard l(x_sendQ); +// sendQ.pop_front(); +// if (sendQ.empty()) +// return; +// } +// doWrite(); +// }); + } + + void stopWithError(boost::system::error_code _ec); + + std::atomic m_stopped; ///< Set when connection is stopping or stopped. Handshake cannot occur unless m_stopped is true. + std::atomic m_started; ///< Atomically ensure connection is started once. Start cannot occur unless m_started is false. Managed by start() and shutdown(bool). + + SocketEventFace* m_eventDelegate = nullptr; + + Mutex x_sendQ; + std::deque sendQ; + bytes recvBuffer; + size_t recvdBytes = 0; + socketType m_socket; + + Mutex x_socketError; ///< Mutex for error which can occur from host or IO thread. + boost::system::error_code socketError; ///< Set when shut down due to error. +}; + +class SocketEventFace +{ +public: + virtual ba::io_service& ioService() = 0; + virtual void onStopped(SocketFace*) = 0; + virtual void onReceive(SocketFace*, Packet&) = 0; +}; + +struct UDPSocket: public Socket +{ + UDPSocket(ba::io_service& _io, unsigned _port): Socket(nullptr, bi::udp::endpoint(bi::udp::v4(), _port)) {} + ~UDPSocket() { boost::system::error_code ec; m_socket.shutdown(bi::udp::socket::shutdown_both, ec); m_socket.close(); } + +// bi::udp::socket m_socket; +}; + /** * @brief Network Class * Static network operations and interface(s). @@ -53,8 +149,8 @@ public: static std::vector getInterfaceAddresses(); /// Try to bind and listen on _listenPort, else attempt net-allocated port. - static int listen4(bi::tcp::acceptor& _acceptor, unsigned short _listenPort); - + static int tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort); + /// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port. static bi::tcp::endpoint traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); }; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index cb0a60a92..3a0dcf1cf 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -522,7 +522,7 @@ void Session::start() << m_server->protocolVersion() << m_server->m_clientVersion << m_server->caps() - << m_server->m_public.port() + << m_server->m_tcpPublic.port() << m_server->id(); sealAndSend(s); ping(); diff --git a/libp2p/Session.h b/libp2p/Session.h index cd2dbf5a7..cabef2cbf 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -107,8 +107,8 @@ private: mutable bi::tcp::socket m_socket; ///< Socket for the peer's connection. Mutable to ask for native_handle(). Mutex x_writeQueue; ///< Mutex for the write queue. std::deque m_writeQueue; ///< The write queue. - std::array m_data; ///< Data buffer for the write queue. - bytes m_incoming; ///< The incoming read queue of bytes. + std::array m_data; ///< Buffer for ingress packet data. + bytes m_incoming; ///< Read buffer for ingress bytes. PeerInfo m_info; ///< Dynamic information about this peer. diff --git a/test/kademlia.cpp b/test/kademlia.cpp new file mode 100644 index 000000000..21c28cb87 --- /dev/null +++ b/test/kademlia.cpp @@ -0,0 +1,33 @@ +/* + 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 . +*/ +/** @file kademlia.cpp + * @author Alex Leverington + * @date 2014 + * Basic networking tests + */ + +#include +#include +using namespace std; +using namespace dev; +using namespace dev::p2p; + +BOOST_AUTO_TEST_CASE(host_listen_udp4) +{ + +} + diff --git a/test/network.cpp b/test/net.cpp similarity index 100% rename from test/network.cpp rename to test/net.cpp From cef5c1a843a1f691ed9225fb92bf3319c3557b84 Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 18 Dec 2014 08:35:12 +0100 Subject: [PATCH 02/54] initial interface for udp. test sending/receiving udp. --- libp2p/Network.cpp | 6 -- libp2p/Network.h | 93 +---------------------- libp2p/UDP.cpp | 20 +++++ libp2p/UDP.h | 181 +++++++++++++++++++++++++++++++++++++++++++++ test/kademlia.cpp | 12 --- test/net.cpp | 86 ++++++++++++++------- 6 files changed, 262 insertions(+), 136 deletions(-) create mode 100644 libp2p/UDP.cpp create mode 100644 libp2p/UDP.h diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 94413e93f..d0276d67e 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -38,12 +38,6 @@ using namespace std; using namespace dev; using namespace dev::p2p; -template -Socket::Socket(SocketEventFace* _seface): m_eventDelegate(_seface), m_socket(m_eventDelegate->ioService()) {} - -template -Socket::Socket(SocketEventFace* _seface, endpointType _endpoint): m_eventDelegate(_seface), m_socket(m_eventDelegate->ioService(), _endpoint) {} - std::vector Network::getInterfaceAddresses() { std::vector addresses; diff --git a/libp2p/Network.h b/libp2p/Network.h index 3f96fd457..aeeabf329 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include "Common.h" @@ -46,98 +47,6 @@ struct NetworkPreferences bool localNetworking = false; }; -struct Packet -{ - bytes payload() const { return s.out(); } - - bool required = false; - RLPStream s; -}; - -class SocketFace -{ - virtual void send(Packet const& _msg) = 0; -}; -class SocketEventFace; - -/** - * @brief Generic Socket Interface - * Owners of sockets must outlive the socket. - * Boost ASIO uses lowercase template for udp/tcp, which is adopted here. - */ -template -class Socket: SocketFace, public std::enable_shared_from_this> -{ -public: - using socketType = typename T::socket; - using endpointType = typename T::endpoint; - Socket(SocketEventFace* _seface); - Socket(SocketEventFace* _seface, endpointType _endpoint); - -protected: - void send(Packet const& _msg) - { - if (!m_started) - return; - - Guard l(x_sendQ); - sendQ.push_back(_msg.payload()); - if (sendQ.size() == 1 && !m_stopped) - doWrite(); - } - - void doWrite() - { - const bytes& bytes = sendQ[0]; - auto self(Socket::shared_from_this()); -// boost::asio::async_write(m_socket, boost::asio::buffer(bytes), [this, self](boost::system::error_code _ec, std::size_t /*length*/) -// { -// if (_ec) -// return stopWithError(_ec); -// else -// { -// Guard l(x_sendQ); -// sendQ.pop_front(); -// if (sendQ.empty()) -// return; -// } -// doWrite(); -// }); - } - - void stopWithError(boost::system::error_code _ec); - - std::atomic m_stopped; ///< Set when connection is stopping or stopped. Handshake cannot occur unless m_stopped is true. - std::atomic m_started; ///< Atomically ensure connection is started once. Start cannot occur unless m_started is false. Managed by start() and shutdown(bool). - - SocketEventFace* m_eventDelegate = nullptr; - - Mutex x_sendQ; - std::deque sendQ; - bytes recvBuffer; - size_t recvdBytes = 0; - socketType m_socket; - - Mutex x_socketError; ///< Mutex for error which can occur from host or IO thread. - boost::system::error_code socketError; ///< Set when shut down due to error. -}; - -class SocketEventFace -{ -public: - virtual ba::io_service& ioService() = 0; - virtual void onStopped(SocketFace*) = 0; - virtual void onReceive(SocketFace*, Packet&) = 0; -}; - -struct UDPSocket: public Socket -{ - UDPSocket(ba::io_service& _io, unsigned _port): Socket(nullptr, bi::udp::endpoint(bi::udp::v4(), _port)) {} - ~UDPSocket() { boost::system::error_code ec; m_socket.shutdown(bi::udp::socket::shutdown_both, ec); m_socket.close(); } - -// bi::udp::socket m_socket; -}; - /** * @brief Network Class * Static network operations and interface(s). diff --git a/libp2p/UDP.cpp b/libp2p/UDP.cpp new file mode 100644 index 000000000..9fc65b7c3 --- /dev/null +++ b/libp2p/UDP.cpp @@ -0,0 +1,20 @@ +/* + 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 . + */ +/** @file UDP.cpp + * @author Alex Leverington + * @date 2014 + */ diff --git a/libp2p/UDP.h b/libp2p/UDP.h new file mode 100644 index 000000000..ff94df2ac --- /dev/null +++ b/libp2p/UDP.h @@ -0,0 +1,181 @@ +/* + 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 . + */ +/** @file UDP.h + * @author Alex Leverington + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "Common.h" +namespace ba = boost::asio; +namespace bi = ba::ip; + +namespace dev +{ +namespace p2p +{ + +struct UDPDatagram +{ + bi::udp::endpoint to; + bytes data; +}; + +struct UDPSocketFace +{ + virtual void send(UDPDatagram const& _msg) = 0; + virtual void disconnect() = 0; +}; + +struct UDPSocketEvents +{ + virtual void onDisconnected(UDPSocketFace*) {}; + virtual void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packetData) = 0; +}; + +/** + * @brief UDP Interface + * Handler must implement UDPSocketEvents. S is maximum data size (bytes) of UDP datagram. + */ +template +class UDPSocket: UDPSocketFace, public std::enable_shared_from_this> +{ +public: + static constexpr unsigned datagramSize = S; + static_assert(datagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes"); + + /// Construct open socket to endpoint. + UDPSocket(ba::io_service& _io, Handler& _host, unsigned _port): m_host(_host), m_socket(_io, bi::udp::endpoint(bi::udp::v4(), _port)) {}; + virtual ~UDPSocket() { disconnect(); } + + void connect() + { + bool no = false; + if (!m_started.compare_exchange_strong(no, true)) + return; + m_closed = false; + doRead(); + } + + void send(UDPDatagram const& _datagram) + { + if (!m_started) + return; + + Guard l(x_sendQ); + sendQ.push_back(_datagram); + if (sendQ.size() == 1 && !m_closed) + doWrite(); + } + + void disconnect() { disconnectWithError(boost::asio::error::connection_reset); } + +protected: + void doRead() + { + auto self(UDPSocket::shared_from_this()); + m_socket.async_receive_from(boost::asio::buffer(recvData), recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) + { + if (_ec) + return disconnectWithError(_ec); + + assert(_len); + m_host.onReceived(this, recvEndpoint, bytesConstRef(recvData.data(), _len)); + if (!m_closed) + doRead(); + }); + } + + void doWrite() + { + const UDPDatagram& datagram = sendQ[0]; + auto self(UDPSocket::shared_from_this()); + m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.to, [this, self](boost::system::error_code _ec, std::size_t) + { + if (_ec) + return disconnectWithError(_ec); + else + { + Guard l(x_sendQ); + sendQ.pop_front(); + if (sendQ.empty()) + return; + } + doWrite(); + }); + } + + void disconnectWithError(boost::system::error_code _ec) + { + // If !started and already stopped, shutdown has already occured. (EOF or Operation canceled) + if (!m_started && m_closed) + return; + + assert(_ec); + { + // disconnect-operation following prior non-zero errors are ignored + Guard l(x_socketError); + if (socketError != boost::system::error_code()) + return; + socketError = _ec; + } + // TODO: (if non-zero error) schedule high-priority writes + + // prevent concurrent disconnect + bool yes = true; + if (!m_started.compare_exchange_strong(yes, false)) + return; + + // set m_closed to true to prevent undeliverable egress messages + bool wasClosed = m_closed; + m_closed = true; + + // close sockets + boost::system::error_code ec; + m_socket.shutdown(bi::udp::socket::shutdown_both, ec); + m_socket.close(); + + // socket never started if it never left stopped-state (pre-handshake) + if (wasClosed) + return; + + m_host.onDisconnected(this); + } + + std::atomic m_closed; ///< Set when connection is stopping or stopped. Handshake cannot occur unless m_closed is true. + std::atomic m_started; ///< Atomically ensure connection is started once. Start cannot occur unless m_started is false. Managed by start and disconnectWithError. + + Handler& m_host; ///< Interface which owns this socket. + + Mutex x_sendQ; + std::deque sendQ; + std::array recvData; ///< Buffer for ingress datagrams. + bi::udp::endpoint recvEndpoint; ///< Endpoint data was received from. + bi::udp::socket m_socket; + + Mutex x_socketError; ///< Mutex for error which can occur from host or IO thread. + boost::system::error_code socketError; ///< Set when shut down due to error. +}; + +} +} \ No newline at end of file diff --git a/test/kademlia.cpp b/test/kademlia.cpp index 21c28cb87..a9d7701cf 100644 --- a/test/kademlia.cpp +++ b/test/kademlia.cpp @@ -17,17 +17,5 @@ /** @file kademlia.cpp * @author Alex Leverington * @date 2014 - * Basic networking tests */ -#include -#include -using namespace std; -using namespace dev; -using namespace dev::p2p; - -BOOST_AUTO_TEST_CASE(host_listen_udp4) -{ - -} - diff --git a/test/net.cpp b/test/net.cpp index acdd649d9..e52654411 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -14,42 +14,76 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file network.cpp - * @author Marko Simovic +/** @file net.cpp + * @author Alex Leverington * @date 2014 - * Basic networking tests */ #include -#include -#include -#include -#include -#include "TestHelper.h" +#include +#include using namespace std; using namespace dev; -using namespace dev::eth; +using namespace dev::p2p; +namespace ba = boost::asio; +namespace bi = ba::ip; -// Disabled since tests shouldn't block (not the worst offender, but timeout should be reduced anyway). -/* -BOOST_AUTO_TEST_CASE(listen_port_busy) +class TestA: UDPSocketEvents, public Worker { - short port = 20000; +public: + TestA(): Worker("test",0), m_io(), m_socket(new UDPSocket(m_io, *this, 30300)) {} + ~TestA() { m_io.stop(); stopWorking(); } + + void start() { startWorking(); } + void doWork() { m_io.run(); } + + void onDisconnected(UDPSocketFace*) {}; + void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { if(_packet.toString() == "AAAA") success = true; }; - //make use of the port ahead of our client - ba::io_service ioService; - bi::tcp::endpoint endPoint(bi::tcp::v4(), port); - bi::tcp::acceptor acceptor(ioService, endPoint); - acceptor.listen(10); + ba::io_service m_io; + shared_ptr> m_socket; + + bool success = false; +}; - //prepare client and try to listen on same, used, port - Client c1("TestClient1", KeyPair::create().address(), - (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path()).string()); +//struct TestBProtocol: UDPSocketEvents +//{ +// void onDisconnected(UDPSocketFace*) {}; +// void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { cout << "received TestBProtocol" << endl; }; +//}; +// +//class TestB: TestBProtocol +//{ +//public: +// TestB(): m_io(), m_socket(m_io, *this, 30300) {} +////private: +// ba::io_service m_io; +// UDPSocket m_socket; +//}; +// +//class TestC +//{ +//public: +// TestC(): m_io(), m_socket(m_io, m_rpc, 30300) {} +////private: +// ba::io_service m_io; +// TestBProtocol m_rpc; +// UDPSocket m_socket; +//}; - c1.startNetwork(port); +BOOST_AUTO_TEST_SUITE(p2p) - BOOST_REQUIRE(c1.haveNetwork()); - BOOST_REQUIRE(c1.peerServer()->listenPort() != 0); - BOOST_REQUIRE(c1.peerServer()->listenPort() != port); +BOOST_AUTO_TEST_CASE(test) +{ + UDPDatagram d; + d.to = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300); + d.data = bytes({65,65,65,65}); + + TestA a; a.start(); a.m_socket->connect(); + a.m_socket->send(d); + sleep(1); + BOOST_REQUIRE_EQUAL(true, a.success); } -*/ + +BOOST_AUTO_TEST_SUITE_END() + From ecccc68e306b8a52feb36595f825d43afdf25c7d Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 18 Dec 2014 17:21:06 +0100 Subject: [PATCH 03/54] spacing --- test/net.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/net.cpp b/test/net.cpp index e52654411..0b3208302 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -38,7 +38,7 @@ public: void doWork() { m_io.run(); } void onDisconnected(UDPSocketFace*) {}; - void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { if(_packet.toString() == "AAAA") success = true; }; + void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { if (_packet.toString() == "AAAA") success = true; } ba::io_service m_io; shared_ptr> m_socket; @@ -73,7 +73,7 @@ public: BOOST_AUTO_TEST_SUITE(p2p) -BOOST_AUTO_TEST_CASE(test) +BOOST_AUTO_TEST_CASE(test_txrx_one) { UDPDatagram d; d.to = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300); From d1aa994f8e77b87b0bab9907e7431fbf41fbf98b Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 18 Dec 2014 20:25:36 +0100 Subject: [PATCH 04/54] stash --- libp2p/UDP.h | 2 ++ test/net.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/libp2p/UDP.h b/libp2p/UDP.h index ff94df2ac..74499da11 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -68,11 +68,13 @@ public: UDPSocket(ba::io_service& _io, Handler& _host, unsigned _port): m_host(_host), m_socket(_io, bi::udp::endpoint(bi::udp::v4(), _port)) {}; virtual ~UDPSocket() { disconnect(); } + /// Socket will begin listening for and delivering packets void connect() { bool no = false; if (!m_started.compare_exchange_strong(no, true)) return; + m_closed = false; doRead(); } diff --git a/test/net.cpp b/test/net.cpp index 0b3208302..1e8d20c54 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -28,6 +28,21 @@ using namespace dev::p2p; namespace ba = boost::asio; namespace bi = ba::ip; +class Kademlia: UDPSocketEvents +{ +public: + Kademlia(): Worker("test",0), m_io(), m_socket(new UDPSocket(m_io, *this, 30300)) {} + ~Kademlia() { m_io.stop(); stopWorking(); } + + void onDisconnected(UDPSocketFace*) {}; + void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { if (_packet.toString() == "AAAA") success = true; } + + ba::io_service m_io; + shared_ptr> m_socket; + + bool success = false; +}; + class TestA: UDPSocketEvents, public Worker { public: From 6e363446324a7eb5704b3b3ed595cc7ca59df673 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 18 Dec 2014 18:53:43 +0100 Subject: [PATCH 05/54] Possibility for binary operators to yield types different from their operands'. --- libsolidity/AST.cpp | 35 ++++++++++++---------------- libsolidity/Types.cpp | 52 ++++++++++++++++++++++++++++++++++-------- libsolidity/Types.h | 53 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 99 insertions(+), 41 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 1fa6d8f6f..c7d617b4f 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -180,12 +180,18 @@ void Assignment::checkTypeRequirements() //@todo later, assignments to structs might be possible, but not to mappings if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue()) BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue.")); - m_rightHandSide->expectType(*m_leftHandSide->getType()); m_type = m_leftHandSide->getType(); - if (m_assigmentOperator != Token::ASSIGN) + if (m_assigmentOperator == Token::ASSIGN) + m_rightHandSide->expectType(*m_type); + else + { // compound assignment - if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) + m_rightHandSide->checkTypeRequirements(); + TypePointer resultType = Type::binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), + m_type, m_rightHandSide->getType()); + if (!resultType || *resultType != *m_type) BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); + } } void ExpressionStatement::checkTypeRequirements() @@ -225,24 +231,13 @@ void BinaryOperation::checkTypeRequirements() { m_left->checkTypeRequirements(); m_right->checkTypeRequirements(); - if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) - m_commonType = m_left->getType(); - else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) - m_commonType = m_right->getType(); - else - BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation: " + - m_left->getType()->toString() + " vs. " + + m_commonType = Type::binaryOperatorResult(m_operator, m_left->getType(), m_right->getType()); + if (!m_commonType) + BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + + " not compatible with types " + + m_left->getType()->toString() + " and " + m_right->getType()->toString())); - if (Token::isCompareOp(m_operator)) - m_type = make_shared(); - else - { - m_type = m_commonType; - if (!m_commonType->acceptsBinaryOperator(m_operator)) - BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + - " not compatible with type " + - m_commonType->toString())); - } + m_type = Token::isCompareOp(m_operator) ? make_shared() : m_commonType; } void FunctionCall::checkTypeRequirements() diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 71319c3ac..664b56ff6 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -100,6 +100,16 @@ shared_ptr Type::forLiteral(Literal const& _literal) } } +TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) +{ + if (_b->isImplicitlyConvertibleTo(*_a)) + return _a; + else if (_a->isImplicitlyConvertibleTo(*_b)) + return _b; + else + return TypePointer(); +} + const MemberList Type::EmptyMemberList = MemberList(); shared_ptr IntegerType::smallestTypeForLiteral(string const& _literal) @@ -146,16 +156,6 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT; } -bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const -{ - if (isAddress()) - return Token::isCompareOp(_operator); - else if (isHash()) - return Token::isCompareOp(_operator) || Token::isBitOp(_operator); - else - return true; -} - bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const { if (_operator == Token::DELETE) @@ -192,6 +192,28 @@ u256 IntegerType::literalValue(Literal const& _literal) const return u256(value); } +TypePointer IntegerType::binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const +{ + if (getCategory() != _other->getCategory()) + return TypePointer(); + auto commonType = dynamic_pointer_cast(Type::commonType(_this, _other)); + + if (!commonType) + return TypePointer(); + + if (commonType->isAddress()) + { + if (!Token::isCompareOp(_operator)) + return TypePointer(); + } + else if (commonType->isHash()) + { + if (!(Token::isCompareOp(_operator) || Token::isBitOp(_operator))) + return TypePointer(); + } + return commonType; +} + const MemberList IntegerType::AddressMemberList = MemberList({{"balance", make_shared(256)}, @@ -266,6 +288,16 @@ u256 BoolType::literalValue(Literal const& _literal) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); } +TypePointer BoolType::binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const +{ + if (getCategory() != _other->getCategory()) + return TypePointer(); + if (Token::isCompareOp(_operator) || _operator == Token::AND || _operator == Token::OR) + return _this; + else + return TypePointer(); +} + bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (isImplicitlyConvertibleTo(_convertTo)) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 48539a1d7..0afa0ccd3 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -81,15 +81,23 @@ public: ///@{ ///@name Factory functions /// Factory functions that convert an AST @ref TypeName to a Type. - static std::shared_ptr fromElementaryTypeName(Token::Value _typeToken); - static std::shared_ptr fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); - static std::shared_ptr fromMapping(Mapping const& _typeName); - static std::shared_ptr fromFunction(FunctionDefinition const& _function); + static TypePointer fromElementaryTypeName(Token::Value _typeToken); + static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); + static TypePointer fromMapping(Mapping const& _typeName); + static TypePointer fromFunction(FunctionDefinition const& _function); /// @} /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does /// not fit any type. - static std::shared_ptr forLiteral(Literal const& _literal); + static TypePointer forLiteral(Literal const& _literal); + /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise + static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); + /// @returns the resulting type of applying the given operator or an empty pointer if this is not possible. + /// The default implementation allows comparison operators if a common type exists + static TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _a, TypePointer const& _b) + { + return _a->binaryOperatorResultImpl(_operator, _a, _b); + } virtual Category getCategory() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } @@ -97,7 +105,6 @@ public: { return isImplicitlyConvertibleTo(_convertTo); } - virtual bool acceptsBinaryOperator(Token::Value) const { return false; } virtual bool acceptsUnaryOperator(Token::Value) const { return false; } virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); } @@ -131,6 +138,11 @@ public: } protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _a, TypePointer const& _b) const + { + return Token::isCompareOp(_operator) ? commonType(_a, _b) : TypePointer(); + } + /// Convenience object used when returning an empty member list. static const MemberList EmptyMemberList; }; @@ -155,7 +167,6 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool acceptsBinaryOperator(Token::Value _operator) const override; virtual bool acceptsUnaryOperator(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; @@ -173,6 +184,9 @@ public: bool isAddress() const { return m_modifier == Modifier::ADDRESS; } int isSigned() const { return m_modifier == Modifier::SIGNED; } +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override; + private: int m_bits; Modifier m_modifier; @@ -217,10 +231,6 @@ public: BoolType() {} virtual Category getCategory() const { return Category::BOOL; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool acceptsBinaryOperator(Token::Value _operator) const override - { - return _operator == Token::AND || _operator == Token::OR; - } virtual bool acceptsUnaryOperator(Token::Value _operator) const override { return _operator == Token::NOT || _operator == Token::DELETE; @@ -231,6 +241,9 @@ public: virtual std::string toString() const override { return "bool"; } virtual u256 literalValue(Literal const& _literal) const override; + +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override; }; /** @@ -369,6 +382,12 @@ public: virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } + +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override + { + return TypePointer(); + } }; /** @@ -389,6 +408,12 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override + { + return TypePointer(); + } + private: TypePointer m_actualType; }; @@ -413,6 +438,12 @@ public: virtual std::string toString() const override; +protected: + virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override + { + return TypePointer(); + } + private: Kind m_kind; From 27d79a2f17f3140258d1b9b89914dc9b166aef1d Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 19 Dec 2014 22:14:11 +0100 Subject: [PATCH 06/54] initialize atomics so udp messages are delivered on linux #656 --- libp2p/UDP.h | 10 +++++----- test/net.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 74499da11..fd48aa59d 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -65,16 +65,16 @@ public: static_assert(datagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes"); /// Construct open socket to endpoint. - UDPSocket(ba::io_service& _io, Handler& _host, unsigned _port): m_host(_host), m_socket(_io, bi::udp::endpoint(bi::udp::v4(), _port)) {}; + UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, unsigned _port): m_host(_host), m_socket(_io, bi::udp::endpoint(bi::udp::v4(), _port)) { m_started.store(false); m_closed.store(true); }; virtual ~UDPSocket() { disconnect(); } /// Socket will begin listening for and delivering packets void connect() { - bool no = false; - if (!m_started.compare_exchange_strong(no, true)) + bool expect = false; + if (!m_started.compare_exchange_strong(expect, true)) return; - + m_closed = false; doRead(); } @@ -86,7 +86,7 @@ public: Guard l(x_sendQ); sendQ.push_back(_datagram); - if (sendQ.size() == 1 && !m_closed) + if (sendQ.size() == 1 && !m_closed.load()) doWrite(); } diff --git a/test/net.cpp b/test/net.cpp index 1e8d20c54..34b20ccc7 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(test_txrx_one) d.to = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300); d.data = bytes({65,65,65,65}); - TestA a; a.start(); a.m_socket->connect(); + TestA a; a.m_socket->connect(); a.start(); a.m_socket->send(d); sleep(1); BOOST_REQUIRE_EQUAL(true, a.success); From 46a286f6e7c8f29bab408b0766693b2bf8e86fbb Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 19 Dec 2014 22:40:44 +0100 Subject: [PATCH 07/54] fix the fix --- libp2p/UDP.h | 4 +++- test/net.cpp | 16 +--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/libp2p/UDP.h b/libp2p/UDP.h index fd48aa59d..31ec170f6 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -37,6 +37,8 @@ namespace p2p struct UDPDatagram { + UDPDatagram() = default; + UDPDatagram(bi::udp::endpoint _ep, bytes _data): to(_ep), data(std::move(_data)) {} bi::udp::endpoint to; bytes data; }; @@ -167,7 +169,7 @@ protected: std::atomic m_closed; ///< Set when connection is stopping or stopped. Handshake cannot occur unless m_closed is true. std::atomic m_started; ///< Atomically ensure connection is started once. Start cannot occur unless m_started is false. Managed by start and disconnectWithError. - Handler& m_host; ///< Interface which owns this socket. + UDPSocketEvents& m_host; ///< Interface which owns this socket. Mutex x_sendQ; std::deque sendQ; diff --git a/test/net.cpp b/test/net.cpp index 34b20ccc7..6e9efff16 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -20,6 +20,7 @@ */ #include +#include #include #include using namespace std; @@ -28,21 +29,6 @@ using namespace dev::p2p; namespace ba = boost::asio; namespace bi = ba::ip; -class Kademlia: UDPSocketEvents -{ -public: - Kademlia(): Worker("test",0), m_io(), m_socket(new UDPSocket(m_io, *this, 30300)) {} - ~Kademlia() { m_io.stop(); stopWorking(); } - - void onDisconnected(UDPSocketFace*) {}; - void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { if (_packet.toString() == "AAAA") success = true; } - - ba::io_service m_io; - shared_ptr> m_socket; - - bool success = false; -}; - class TestA: UDPSocketEvents, public Worker { public: From dd4c7152b4a6f45f5709d96085fb8a71ebe520d0 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 21 Dec 2014 09:11:57 +0100 Subject: [PATCH 08/54] socket is created in disconnected state. socket can't be created in open state because shared_ptr methods aren't available to doRead until after class has been constructed and the socket is dependent on ioservice running. --- libp2p/UDP.h | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 31ec170f6..ec491288b 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -57,17 +57,17 @@ struct UDPSocketEvents /** * @brief UDP Interface - * Handler must implement UDPSocketEvents. S is maximum data size (bytes) of UDP datagram. + * Handler must implement UDPSocketEvents. */ -template -class UDPSocket: UDPSocketFace, public std::enable_shared_from_this> +template +class UDPSocket: UDPSocketFace, public std::enable_shared_from_this> { public: - static constexpr unsigned datagramSize = S; - static_assert(datagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes"); + static constexpr unsigned maxDatagramSize = MaxDatagramSize; + static_assert(maxDatagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes"); /// Construct open socket to endpoint. - UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, unsigned _port): m_host(_host), m_socket(_io, bi::udp::endpoint(bi::udp::v4(), _port)) { m_started.store(false); m_closed.store(true); }; + UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, unsigned _port): m_host(_host), m_endpoint(bi::udp::v4(), _port), m_socket(_io) { m_started.store(false); m_closed.store(true); }; virtual ~UDPSocket() { disconnect(); } /// Socket will begin listening for and delivering packets @@ -76,6 +76,9 @@ public: bool expect = false; if (!m_started.compare_exchange_strong(expect, true)) return; + + m_socket.open(bi::udp::v4()); + m_socket.bind(m_endpoint); m_closed = false; doRead(); @@ -83,7 +86,7 @@ public: void send(UDPDatagram const& _datagram) { - if (!m_started) + if (m_closed) return; Guard l(x_sendQ); @@ -91,13 +94,15 @@ public: if (sendQ.size() == 1 && !m_closed.load()) doWrite(); } + + bool isOpen() { return !m_closed; } void disconnect() { disconnectWithError(boost::asio::error::connection_reset); } protected: void doRead() { - auto self(UDPSocket::shared_from_this()); + auto self(UDPSocket::shared_from_this()); m_socket.async_receive_from(boost::asio::buffer(recvData), recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) { if (_ec) @@ -113,7 +118,7 @@ protected: void doWrite() { const UDPDatagram& datagram = sendQ[0]; - auto self(UDPSocket::shared_from_this()); + auto self(UDPSocket::shared_from_this()); m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.to, [this, self](boost::system::error_code _ec, std::size_t) { if (_ec) @@ -132,7 +137,7 @@ protected: void disconnectWithError(boost::system::error_code _ec) { // If !started and already stopped, shutdown has already occured. (EOF or Operation canceled) - if (!m_started && m_closed) + if (!m_started && m_closed && !m_socket.is_open()) return; assert(_ec); @@ -146,8 +151,8 @@ protected: // TODO: (if non-zero error) schedule high-priority writes // prevent concurrent disconnect - bool yes = true; - if (!m_started.compare_exchange_strong(yes, false)) + bool expected = true; + if (!m_started.compare_exchange_strong(expected, false)) return; // set m_closed to true to prevent undeliverable egress messages @@ -170,10 +175,11 @@ protected: std::atomic m_started; ///< Atomically ensure connection is started once. Start cannot occur unless m_started is false. Managed by start and disconnectWithError. UDPSocketEvents& m_host; ///< Interface which owns this socket. + bi::udp::endpoint m_endpoint; ///< Endpoint which we listen to. Mutex x_sendQ; std::deque sendQ; - std::array recvData; ///< Buffer for ingress datagrams. + std::array recvData; ///< Buffer for ingress datagrams. bi::udp::endpoint recvEndpoint; ///< Endpoint data was received from. bi::udp::socket m_socket; From 7ab02cf4874ad7cd90c75be908e6ac6ce93ee43f Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 22 Dec 2014 01:51:19 +0100 Subject: [PATCH 09/54] move some things for udp. added a class for kademlia. --- libp2p/UDP.h | 37 +++- test/net.cpp | 567 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 562 insertions(+), 42 deletions(-) diff --git a/libp2p/UDP.h b/libp2p/UDP.h index ec491288b..a28646369 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include "Common.h" namespace ba = boost::asio; namespace bi = ba::ip; @@ -40,12 +43,30 @@ struct UDPDatagram UDPDatagram() = default; UDPDatagram(bi::udp::endpoint _ep, bytes _data): to(_ep), data(std::move(_data)) {} bi::udp::endpoint to; + bytes data; }; + +struct RLPDatagram: UDPDatagram +{ + void seal(Secret const& _k) + { + RLPStream packet; + streamRLP(packet); + bytes b(packet.out()); + Signature sig = dev::sign(_k, dev::sha3(b)); + data.resize(data.size() + Signature::size); + sig.ref().copyTo(&data); + memcpy(data.data()+sizeof(Signature),b.data(),b.size()); + } + +protected: + virtual void streamRLP(RLPStream& _s) const {}; +}; struct UDPSocketFace { - virtual void send(UDPDatagram const& _msg) = 0; + virtual bool send(UDPDatagram const& _msg) = 0; virtual void disconnect() = 0; }; @@ -80,19 +101,25 @@ public: m_socket.open(bi::udp::v4()); m_socket.bind(m_endpoint); + // clear write queue so reconnect doesn't send stale messages + Guard l(x_sendQ); + sendQ.clear(); + m_closed = false; doRead(); } - void send(UDPDatagram const& _datagram) + bool send(UDPDatagram const& _datagram) { if (m_closed) - return; + return false; Guard l(x_sendQ); sendQ.push_back(_datagram); - if (sendQ.size() == 1 && !m_closed.load()) + if (sendQ.size() == 1) doWrite(); + + return true; } bool isOpen() { return !m_closed; } @@ -137,7 +164,7 @@ protected: void disconnectWithError(boost::system::error_code _ec) { // If !started and already stopped, shutdown has already occured. (EOF or Operation canceled) - if (!m_started && m_closed && !m_socket.is_open()) + if (!m_started && m_closed && !m_socket.is_open() /* todo: veirfy this logic*/) return; assert(_ec); diff --git a/test/net.cpp b/test/net.cpp index 6e9efff16..0a22a7e63 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -20,8 +20,8 @@ */ #include -#include #include +#include #include using namespace std; using namespace dev; @@ -29,58 +29,551 @@ using namespace dev::p2p; namespace ba = boost::asio; namespace bi = ba::ip; -class TestA: UDPSocketEvents, public Worker +/** + * Ping packet: Check if node is alive. + * PingNode is cached and regenerated after expiration - t, where t is timeout. + * + * signature: Signature of message. + * ipAddress: Our IP address. + * port: Our port. + * expiration: Triggers regeneration of packet. May also provide control over synchronization. + * + * 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. + * If the pinged node doesn't respond then it is removed and the new + * node is inserted. + */ +struct PingNode: RLPDatagram +{ + bytes ipAddress; + uint16_t port; + uint64_t expiration; + + Signature signature; + +// void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } +}; + +struct Pong: RLPDatagram { + // todo: weak-signed pong + Address from; + uint64_t replyTo; /// expiration from PingNode + + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << from << replyTo; } +}; + +/** + * FindNeighbors Packet: Request k-nodes, closest to the target. + * FindNeighbors is cached and regenerated after expiration - t, where t is timeout. + * + * signature: Signature of message. + * target: Address of NodeId. The responding node will send back nodes closest to the target. + * expiration: Triggers regeneration of packet. May also provide control over synchronization. + * + */ +struct FindNeighbors: RLPDatagram +{ + h160 target; + uint64_t expiration; + + Signature signature; + + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } +}; + +/** + * Node Packet: Multiple node packets are sent in response to FindNeighbors. + */ +struct Neighbors: RLPDatagram +{ + struct Node + { + bytes ipAddress; + uint16_t port; + NodeId node; +// void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << node; } + }; + + std::set nodes; + h256 nonce; + + Signature signature; + +// void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << nonce; } +}; + +/** + * NodeTable using S/Kademlia system for node discovery and preference. + * untouched buckets are refreshed if they have not been touched within an hour + * + * Thread-safety is ensured by modifying NodeEntry details via + * shared_ptr replacement instead of mutating values. + * + * @todo don't try to evict node if node isRequired. (support for makeRequired) + * @todo optimize (use tree for state (or set w/custom compare for cache)) + * @todo constructor support for m_node, m_secret + * @todo use s_bitsPerStep for find and refresh/ping + * @todo exclude bucket from refresh if we have node as peer + * @todo restore nodes + */ +class NodeTable: UDPSocketEvents, public std::enable_shared_from_this +{ + using nodeSocket = UDPSocket; + using timePoint = std::chrono::steady_clock::time_point; + + static unsigned const s_bucketSize = 16; // Denoted by k in [Kademlia]. Number of nodes stored in each bucket. +// const unsigned s_bitsPerStep = 5; // @todo Denoted by b in [Kademlia]. Bits by which address space will be divided for find responses. + static unsigned const s_alpha = 3; // Denoted by \alpha in [Kademlia]. Number of concurrent FindNeighbors requests. + const unsigned s_findTimout = 300; // How long to wait between find queries. +// const unsigned s_siblings = 5; // @todo Denoted by s in [S/Kademlia]. User-defined by sub-protocols. + const unsigned s_bucketRefresh = 3600; // Refresh interval prevents bucket from becoming stale. [Kademlia] + const unsigned s_bits = sizeof(Address); // Denoted by n. + const unsigned s_buckets = 8 * s_bits - 1; + const unsigned s_evictionCheckInterval = 75; // Interval by which eviction timeouts are checked. + const unsigned s_pingTimeout = 500; + static size_t const s_tableSize = Address::size * 8 - 1; // Address::size + +public: + static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } + +protected: + struct NodeDefaultEndpoint + { + NodeDefaultEndpoint(bi::udp::endpoint _udp): udp(_udp) {} + bi::udp::endpoint udp; + }; + + struct NodeEntry + { + NodeEntry(Address _id, Public _pubk, bi::udp::endpoint _udp): id(_id), pubk(_pubk), endpoint(NodeDefaultEndpoint(_udp)), distance(0) {} + NodeEntry(NodeEntry _src, Address _id, Public _pubk, bi::udp::endpoint _udp): id(_id), pubk(_pubk), endpoint(NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_id)) {} + NodeEntry(NodeEntry _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): id(_id), pubk(_pubk), endpoint(_gw), distance(dist(_src.id,_id)) {} + Address id; + Public pubk; + NodeDefaultEndpoint endpoint; ///< How we've previously connected to this node. (must match node's reported endpoint) + const unsigned distance; + timePoint activePing; + }; + + struct NodeBucket + { + unsigned distance; + timePoint modified; + std::list> nodes; + }; + + using EvictionTimeout = std::pair,Address>; + public: - TestA(): Worker("test",0), m_io(), m_socket(new UDPSocket(m_io, *this, 30300)) {} - ~TestA() { m_io.stop(); stopWorking(); } + NodeTable(ba::io_service& _io): + m_node(NodeEntry(Address(), Public(), bi::udp::endpoint())), + m_socket(new nodeSocket(_io, *this, 30300)), + m_socketPtr(m_socket.get()), + m_io(_io), + m_bucketRefreshTimer(m_io), + m_evictionCheckTimer(m_io) + { + for (unsigned i = 0; i < s_buckets; i++) + m_state[i].distance = i, m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); + doRefreshBuckets(boost::system::error_code()); + } + + ~NodeTable() { + m_evictionCheckTimer.cancel(); + m_bucketRefreshTimer.cancel(); + m_socketPtr->disconnect(); + } + + void join() { doFindNode(m_node.id); } + + std::list
nodes() const + { + std::list
nodes; + Guard l(x_nodes); + for (auto& i: m_nodes) + nodes.push_back(i.second->id); + return std::move(nodes); + } + + NodeEntry operator[](Address _id) + { + Guard l(x_nodes); + return *m_nodes[_id]; + } + +protected: + void requestNeighbors(NodeEntry const& _node, Address _target) const + { + FindNeighbors p; + p.target = _target; + + p.to = _node.endpoint.udp; + p.seal(m_secret); + m_socketPtr->send(p); + } + + /// Dispatches udp requests in order to populate node table to be as close as possible to _node. + void doFindNode(Address _node, unsigned _round = 0, std::shared_ptr>> _tried = std::shared_ptr>>()) + { + if (!m_socketPtr->isOpen() || _round == 7) + return; + + auto nearest = findNearest(_node); + std::list> tried; + for (unsigned i = 0; i < nearest.size() && tried.size() < s_alpha; i++) + if (!_tried->count(nearest[i])) + { + tried.push_back(nearest[i]); + requestNeighbors(*nearest[i], _node); + } + else + continue; + + while (auto n = tried.front()) + { + _tried->insert(n); + tried.pop_front(); + } + + auto self(shared_from_this()); + m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_findTimout)); + m_evictionCheckTimer.async_wait([this, self, _node, _round, _tried](boost::system::error_code const& _ec) + { + if (_ec) + return; + doFindNode(_node, _round + 1, _tried); + }); + } + + std::vector> findNearest(Address _target) + { + // send s_alpha FindNeighbors packets to nodes we know, closest to target + unsigned head = dist(m_node.id, _target); + unsigned tail = (head - 1) % (s_tableSize - 1); + + // todo: optimize with tree + std::map>> found; + unsigned count = 0; + + // if d is 0, then we roll look forward, if last, we reverse, else, spread from d + if (head != 0 && tail != s_tableSize) + while (head != tail && count < s_bucketSize) + { + Guard l(x_state); + for (auto& n: m_state[head].nodes) + if (auto p = n.lock()) + { + if (count < s_bucketSize) + found[dist(_target, p->id)].push_back(p); + else + break; + } + + if (count < s_bucketSize && head) + for (auto& n: m_state[tail].nodes) + if (auto p = n.lock()) + { + if (count < s_bucketSize) + found[dist(_target, p->id)].push_back(p); + else + break; + } + head++; + tail = (tail - 1) % (s_tableSize - 1); + } + else if (head == 0) + while (head < s_bucketSize && count < s_bucketSize) + { + Guard l(x_state); + for (auto& n: m_state[head].nodes) + if (auto p = n.lock()) + { + if (count < s_bucketSize) + found[dist(_target, p->id)].push_back(p); + else + break; + } + head--; + } + else if (tail == s_tableSize - 1) + while (tail > 0 && count < s_bucketSize) + { + Guard l(x_state); + for (auto& n: m_state[tail].nodes) + if (auto p = n.lock()) + { + if (count < s_bucketSize) + found[dist(_target, p->id)].push_back(p); + else + break; + } + tail--; + } + + std::vector> ret; + for (auto& nodes: found) + for (auto& n: nodes.second) + ret.push_back(n); + return std::move(ret); + } + + void ping(bi::address _address, unsigned _port) const + { + PingNode p; + string ip = m_node.endpoint.udp.address().to_string(); + p.ipAddress = asBytes(ip); + p.port = m_node.endpoint.udp.port(); +// p.expiration; + p.seal(m_secret); + m_socketPtr->send(p); + } + + void ping(NodeEntry* _n) const + { + if (_n && _n->endpoint.udp.address().is_v4()) + ping(_n->endpoint.udp.address(), _n->endpoint.udp.port()); + } + + void evict(std::shared_ptr _leastSeen, std::shared_ptr _new) + { + if (!m_socketPtr->isOpen()) + return; + + Guard l(x_evictions); + m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id)); + if (m_evictions.size() == 1) + doCheckEvictions(boost::system::error_code()); + + m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id)); + ping(_leastSeen.get()); + } + void noteNode(Public _pubk, bi::udp::endpoint _endpoint) + { + Address id = right160(sha3(_pubk)); + std::shared_ptr node; + { + Guard l(x_nodes); + auto n = m_nodes.find(id); + if (n == m_nodes.end()) + { + m_nodes[id] = std::shared_ptr(new NodeEntry(m_node, id, _pubk, _endpoint)); + node = m_nodes[id]; + } + else + node = n->second; + } + + noteNode(node); + } + + void noteNode(std::shared_ptr _n) + { + std::shared_ptr contested; + { + NodeBucket s = bucket(_n.get()); + Guard l(x_state); + s.nodes.remove_if([&_n](std::weak_ptr n) + { + auto p = n.lock(); + if (!p || p == _n) + return true; + return false; + }); + + if (s.nodes.size() >= s_bucketSize) + { + contested = s.nodes.front().lock(); + if (!contested) + { + s.nodes.pop_front(); + s.nodes.push_back(_n); + } + } + else + s.nodes.push_back(_n); + } + + if (contested) + evict(contested, _n); + } + + void dropNode(std::shared_ptr _n) + { + NodeBucket s = bucket(_n.get()); + { + Guard l(x_state); + s.nodes.remove_if([&_n](std::weak_ptr n) { return n.lock() == _n; }); + } + Guard l(x_nodes); + m_nodes.erase(_n->id); + } + + NodeBucket const& bucket(NodeEntry* _n) const + { + return m_state[_n->distance]; + } + + void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) + { + RLP rlp(_packet); + + + // whenever a pong is received, first check if it's in m_evictions, if so, remove it + Guard l(x_evictions); + } + + void onDisconnected(UDPSocketFace*) + { + + } + + void doCheckEvictions(boost::system::error_code const& _ec) + { + if (_ec || !m_socketPtr->isOpen()) + return; + + m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_evictionCheckInterval)); + auto self(shared_from_this()); + m_evictionCheckTimer.async_wait([this, self](boost::system::error_code const& _ec) + { + if (_ec) + return; + + bool evictionsRemain = false; + std::list> drop; + { + Guard l(x_evictions); + for (auto& e: m_evictions) + if (chrono::steady_clock::now() - e.first.second > chrono::milliseconds(s_pingTimeout)) + { + Guard l(x_nodes); + drop.push_back(m_nodes[e.second]); + } + evictionsRemain = m_evictions.size() - drop.size() > 0; + } + + for (auto& n: drop) + dropNode(n); + + if (evictionsRemain) + doCheckEvictions(boost::system::error_code()); + }); + } + + void doRefreshBuckets(boost::system::error_code const& _ec) + { + cout << "refreshing buckets" << endl; + if (_ec) + return; + + // first check if there are any pending evictions + + + bool connected = m_socketPtr->isOpen(); + bool refreshed = false; + if (connected) + { + Guard l(x_state); + for (auto& d: m_state) + if (chrono::steady_clock::now() - d.modified > chrono::seconds(s_bucketRefresh)) + while (!d.nodes.empty()) + { + auto n = d.nodes.front(); + if (auto p = n.lock()) + { + refreshed = true; + ping(p.get()); + break; + } + d.nodes.pop_front(); + } + } + + unsigned nextRefresh = connected ? (refreshed ? 200 : s_bucketRefresh*1000) : 10000; + auto runcb = [this](boost::system::error_code const& error) -> void { doRefreshBuckets(error); }; + m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(nextRefresh)); + m_bucketRefreshTimer.async_wait(runcb); + } + +private: + NodeEntry m_node; ///< This node. + Secret m_secret; ///< This nodes secret key. + + mutable Mutex x_nodes; ///< Mutable for thread-safe copy in nodes() const. + std::map> m_nodes; ///< Address -> Node table (most common lookup path) + + Mutex x_state; + std::array m_state; ///< State table; logbinned nodes. + + Mutex x_evictions; + std::deque m_evictions; ///< Eviction timeouts. + + shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. + nodeSocket* m_socketPtr; ///< Set to m_socket.get(). + ba::io_service& m_io; ///< Used by bucket refresh timer. + boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. + boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. +}; + +/** + * Only used for testing. Not useful beyond tests. + */ +class TestHost: public Worker +{ +public: + TestHost(): Worker("test",0), m_io() {}; + ~TestHost() { m_io.stop(); stopWorking(); } + void start() { startWorking(); } + void doWork() { m_io.run(); } + +protected: + ba::io_service m_io; +}; + +/** + * Only used for testing. Not useful beyond tests. + */ +class TestNodeHost: public TestHost +{ +public: + TestNodeHost(): m_nodes(m_io) {}; + ~TestNodeHost() { m_io.stop(); stopWorking(); } + void start() { startWorking(); } + void doWork() { m_io.run(); } + + NodeTable m_nodes; +}; + +class TestUDPSocket: UDPSocketEvents, public TestHost +{ +public: + TestUDPSocket(): m_socket(new UDPSocket(m_io, *this, 30300)) {} + ~TestUDPSocket() { m_io.stop(); stopWorking(); } void start() { startWorking(); } void doWork() { m_io.run(); } void onDisconnected(UDPSocketFace*) {}; - void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { if (_packet.toString() == "AAAA") success = true; } + void onReceived(UDPSocketFace*, bi::udp::endpoint const&, bytesConstRef _packet) { if (_packet.toString() == "AAAA") success = true; } - ba::io_service m_io; - shared_ptr> m_socket; + shared_ptr> m_socket; bool success = false; }; -//struct TestBProtocol: UDPSocketEvents -//{ -// void onDisconnected(UDPSocketFace*) {}; -// void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { cout << "received TestBProtocol" << endl; }; -//}; -// -//class TestB: TestBProtocol -//{ -//public: -// TestB(): m_io(), m_socket(m_io, *this, 30300) {} -////private: -// ba::io_service m_io; -// UDPSocket m_socket; -//}; -// -//class TestC -//{ -//public: -// TestC(): m_io(), m_socket(m_io, m_rpc, 30300) {} -////private: -// ba::io_service m_io; -// TestBProtocol m_rpc; -// UDPSocket m_socket; -//}; - BOOST_AUTO_TEST_SUITE(p2p) -BOOST_AUTO_TEST_CASE(test_txrx_one) +BOOST_AUTO_TEST_CASE(kademlia) { - UDPDatagram d; - d.to = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300); - d.data = bytes({65,65,65,65}); + TestNodeHost nodeHost; - TestA a; a.m_socket->connect(); a.start(); +} + +BOOST_AUTO_TEST_CASE(test_txrx_one) +{ + UDPDatagram d(bi::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300), bytes({65,65,65,65})); + TestUDPSocket a; a.m_socket->connect(); a.start(); a.m_socket->send(d); sleep(1); BOOST_REQUIRE_EQUAL(true, a.success); From 5da56314000ef5ca77a5ba98145350623835a9a5 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 22 Dec 2014 03:56:07 +0100 Subject: [PATCH 10/54] repot. --- libp2p/UDP.cpp | 15 ++ libp2p/UDP.h | 236 ++++++++++++------------ test/net.cpp | 489 +------------------------------------------------ 3 files changed, 138 insertions(+), 602 deletions(-) diff --git a/libp2p/UDP.cpp b/libp2p/UDP.cpp index 9fc65b7c3..a398eb1fa 100644 --- a/libp2p/UDP.cpp +++ b/libp2p/UDP.cpp @@ -18,3 +18,18 @@ * @author Alex Leverington * @date 2014 */ + +#include "UDP.h" +using namespace dev; +using namespace dev::p2p; + +void RLPDatagram::seal(Secret const& _k) +{ + RLPStream packet; + streamRLP(packet); + bytes b(packet.out()); + Signature sig = dev::sign(_k, dev::sha3(b)); + data.resize(data.size() + Signature::size); + sig.ref().copyTo(&data); + memcpy(data.data()+sizeof(Signature),b.data(),b.size()); +} diff --git a/libp2p/UDP.h b/libp2p/UDP.h index a28646369..0f4499c84 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -43,23 +43,12 @@ struct UDPDatagram UDPDatagram() = default; UDPDatagram(bi::udp::endpoint _ep, bytes _data): to(_ep), data(std::move(_data)) {} bi::udp::endpoint to; - bytes data; }; - + struct RLPDatagram: UDPDatagram { - void seal(Secret const& _k) - { - RLPStream packet; - streamRLP(packet); - bytes b(packet.out()); - Signature sig = dev::sign(_k, dev::sha3(b)); - data.resize(data.size() + Signature::size); - sig.ref().copyTo(&data); - memcpy(data.data()+sizeof(Signature),b.data(),b.size()); - } - + virtual void seal(Secret const& _k); protected: virtual void streamRLP(RLPStream& _s) const {}; }; @@ -92,127 +81,146 @@ public: virtual ~UDPSocket() { disconnect(); } /// Socket will begin listening for and delivering packets - void connect() - { - bool expect = false; - if (!m_started.compare_exchange_strong(expect, true)) - return; - - m_socket.open(bi::udp::v4()); - m_socket.bind(m_endpoint); - - // clear write queue so reconnect doesn't send stale messages - Guard l(x_sendQ); - sendQ.clear(); - - m_closed = false; - doRead(); - } + void connect(); - bool send(UDPDatagram const& _datagram) - { - if (m_closed) - return false; - - Guard l(x_sendQ); - sendQ.push_back(_datagram); - if (sendQ.size() == 1) - doWrite(); - - return true; - } + /// Send datagram. + bool send(UDPDatagram const& _datagram); + /// Returns if socket is open. bool isOpen() { return !m_closed; } + /// Disconnect socket. void disconnect() { disconnectWithError(boost::asio::error::connection_reset); } protected: - void doRead() - { - auto self(UDPSocket::shared_from_this()); - m_socket.async_receive_from(boost::asio::buffer(recvData), recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) - { - if (_ec) - return disconnectWithError(_ec); - - assert(_len); - m_host.onReceived(this, recvEndpoint, bytesConstRef(recvData.data(), _len)); - if (!m_closed) - doRead(); - }); - } - void doWrite() + void doRead(); + + void doWrite(); + + void disconnectWithError(boost::system::error_code _ec); + + std::atomic m_started; ///< Atomically ensure connection is started once. Start cannot occur unless m_started is false. Managed by start and disconnectWithError. + std::atomic m_closed; ///< Connection availability. + + UDPSocketEvents& m_host; ///< Interface which owns this socket. + bi::udp::endpoint m_endpoint; ///< Endpoint which we listen to. + + Mutex x_sendQ; + std::deque sendQ; ///< Queue for egress data. + std::array recvData; ///< Buffer for ingress data. + bi::udp::endpoint recvEndpoint; ///< Endpoint data was received from. + bi::udp::socket m_socket; ///< Boost asio udp socket. + + Mutex x_socketError; ///< Mutex for error which can be set from host or IO thread. + boost::system::error_code socketError; ///< Set when shut down due to error. +}; + +template +void UDPSocket::connect() +{ + bool expect = false; + if (!m_started.compare_exchange_strong(expect, true)) + return; + + m_socket.open(bi::udp::v4()); + m_socket.bind(m_endpoint); + + // clear write queue so reconnect doesn't send stale messages + Guard l(x_sendQ); + sendQ.clear(); + + m_closed = false; + doRead(); +} + +template +bool UDPSocket::send(UDPDatagram const& _datagram) +{ + if (m_closed) + return false; + + Guard l(x_sendQ); + sendQ.push_back(_datagram); + if (sendQ.size() == 1) + doWrite(); + + return true; +} + +template +void UDPSocket::doRead() +{ + auto self(UDPSocket::shared_from_this()); + m_socket.async_receive_from(boost::asio::buffer(recvData), recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) { - const UDPDatagram& datagram = sendQ[0]; - auto self(UDPSocket::shared_from_this()); - m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.to, [this, self](boost::system::error_code _ec, std::size_t) - { - if (_ec) - return disconnectWithError(_ec); - else - { - Guard l(x_sendQ); - sendQ.pop_front(); - if (sendQ.empty()) - return; - } - doWrite(); - }); - } + if (_ec) + return disconnectWithError(_ec); + + assert(_len); + m_host.onReceived(this, recvEndpoint, bytesConstRef(recvData.data(), _len)); + if (!m_closed) + doRead(); + }); +} - void disconnectWithError(boost::system::error_code _ec) +template +void UDPSocket::doWrite() +{ + const UDPDatagram& datagram = sendQ[0]; + auto self(UDPSocket::shared_from_this()); + m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.to, [this, self](boost::system::error_code _ec, std::size_t) { - // If !started and already stopped, shutdown has already occured. (EOF or Operation canceled) - if (!m_started && m_closed && !m_socket.is_open() /* todo: veirfy this logic*/) - return; - - assert(_ec); + if (_ec) + return disconnectWithError(_ec); + else { - // disconnect-operation following prior non-zero errors are ignored - Guard l(x_socketError); - if (socketError != boost::system::error_code()) + Guard l(x_sendQ); + sendQ.pop_front(); + if (sendQ.empty()) return; - socketError = _ec; } - // TODO: (if non-zero error) schedule high-priority writes + doWrite(); + }); +} - // prevent concurrent disconnect - bool expected = true; - if (!m_started.compare_exchange_strong(expected, false)) - return; - - // set m_closed to true to prevent undeliverable egress messages - bool wasClosed = m_closed; - m_closed = true; - - // close sockets - boost::system::error_code ec; - m_socket.shutdown(bi::udp::socket::shutdown_both, ec); - m_socket.close(); - - // socket never started if it never left stopped-state (pre-handshake) - if (wasClosed) - return; +template +void UDPSocket::disconnectWithError(boost::system::error_code _ec) +{ + // If !started and already stopped, shutdown has already occured. (EOF or Operation canceled) + if (!m_started && m_closed && !m_socket.is_open() /* todo: veirfy this logic*/) + return; - m_host.onDisconnected(this); + assert(_ec); + { + // disconnect-operation following prior non-zero errors are ignored + Guard l(x_socketError); + if (socketError != boost::system::error_code()) + return; + socketError = _ec; } + // TODO: (if non-zero error) schedule high-priority writes - std::atomic m_closed; ///< Set when connection is stopping or stopped. Handshake cannot occur unless m_closed is true. - std::atomic m_started; ///< Atomically ensure connection is started once. Start cannot occur unless m_started is false. Managed by start and disconnectWithError. - - UDPSocketEvents& m_host; ///< Interface which owns this socket. - bi::udp::endpoint m_endpoint; ///< Endpoint which we listen to. + // prevent concurrent disconnect + bool expected = true; + if (!m_started.compare_exchange_strong(expected, false)) + return; - Mutex x_sendQ; - std::deque sendQ; - std::array recvData; ///< Buffer for ingress datagrams. - bi::udp::endpoint recvEndpoint; ///< Endpoint data was received from. - bi::udp::socket m_socket; + // set m_closed to true to prevent undeliverable egress messages + bool wasClosed = m_closed; + m_closed = true; - Mutex x_socketError; ///< Mutex for error which can occur from host or IO thread. - boost::system::error_code socketError; ///< Set when shut down due to error. -}; + // close sockets + boost::system::error_code ec; + m_socket.shutdown(bi::udp::socket::shutdown_both, ec); + m_socket.close(); + + // socket never started if it never left stopped-state (pre-handshake) + if (wasClosed) + return; + + m_host.onDisconnected(this); +} } } \ No newline at end of file diff --git a/test/net.cpp b/test/net.cpp index 0a22a7e63..3d2e08eb6 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -23,500 +23,13 @@ #include #include #include +#include using namespace std; using namespace dev; using namespace dev::p2p; namespace ba = boost::asio; namespace bi = ba::ip; -/** - * Ping packet: Check if node is alive. - * PingNode is cached and regenerated after expiration - t, where t is timeout. - * - * signature: Signature of message. - * ipAddress: Our IP address. - * port: Our port. - * expiration: Triggers regeneration of packet. May also provide control over synchronization. - * - * 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. - * If the pinged node doesn't respond then it is removed and the new - * node is inserted. - */ -struct PingNode: RLPDatagram -{ - bytes ipAddress; - uint16_t port; - uint64_t expiration; - - Signature signature; - -// void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } -}; - -struct Pong: RLPDatagram -{ - // todo: weak-signed pong - Address from; - uint64_t replyTo; /// expiration from PingNode - - void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << from << replyTo; } -}; - -/** - * FindNeighbors Packet: Request k-nodes, closest to the target. - * FindNeighbors is cached and regenerated after expiration - t, where t is timeout. - * - * signature: Signature of message. - * target: Address of NodeId. The responding node will send back nodes closest to the target. - * expiration: Triggers regeneration of packet. May also provide control over synchronization. - * - */ -struct FindNeighbors: RLPDatagram -{ - h160 target; - uint64_t expiration; - - Signature signature; - - void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } -}; - -/** - * Node Packet: Multiple node packets are sent in response to FindNeighbors. - */ -struct Neighbors: RLPDatagram -{ - struct Node - { - bytes ipAddress; - uint16_t port; - NodeId node; -// void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << node; } - }; - - std::set nodes; - h256 nonce; - - Signature signature; - -// void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << nonce; } -}; - -/** - * NodeTable using S/Kademlia system for node discovery and preference. - * untouched buckets are refreshed if they have not been touched within an hour - * - * Thread-safety is ensured by modifying NodeEntry details via - * shared_ptr replacement instead of mutating values. - * - * @todo don't try to evict node if node isRequired. (support for makeRequired) - * @todo optimize (use tree for state (or set w/custom compare for cache)) - * @todo constructor support for m_node, m_secret - * @todo use s_bitsPerStep for find and refresh/ping - * @todo exclude bucket from refresh if we have node as peer - * @todo restore nodes - */ -class NodeTable: UDPSocketEvents, public std::enable_shared_from_this -{ - using nodeSocket = UDPSocket; - using timePoint = std::chrono::steady_clock::time_point; - - static unsigned const s_bucketSize = 16; // Denoted by k in [Kademlia]. Number of nodes stored in each bucket. -// const unsigned s_bitsPerStep = 5; // @todo Denoted by b in [Kademlia]. Bits by which address space will be divided for find responses. - static unsigned const s_alpha = 3; // Denoted by \alpha in [Kademlia]. Number of concurrent FindNeighbors requests. - const unsigned s_findTimout = 300; // How long to wait between find queries. -// const unsigned s_siblings = 5; // @todo Denoted by s in [S/Kademlia]. User-defined by sub-protocols. - const unsigned s_bucketRefresh = 3600; // Refresh interval prevents bucket from becoming stale. [Kademlia] - const unsigned s_bits = sizeof(Address); // Denoted by n. - const unsigned s_buckets = 8 * s_bits - 1; - const unsigned s_evictionCheckInterval = 75; // Interval by which eviction timeouts are checked. - const unsigned s_pingTimeout = 500; - static size_t const s_tableSize = Address::size * 8 - 1; // Address::size - -public: - static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } - -protected: - struct NodeDefaultEndpoint - { - NodeDefaultEndpoint(bi::udp::endpoint _udp): udp(_udp) {} - bi::udp::endpoint udp; - }; - - struct NodeEntry - { - NodeEntry(Address _id, Public _pubk, bi::udp::endpoint _udp): id(_id), pubk(_pubk), endpoint(NodeDefaultEndpoint(_udp)), distance(0) {} - NodeEntry(NodeEntry _src, Address _id, Public _pubk, bi::udp::endpoint _udp): id(_id), pubk(_pubk), endpoint(NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_id)) {} - NodeEntry(NodeEntry _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): id(_id), pubk(_pubk), endpoint(_gw), distance(dist(_src.id,_id)) {} - Address id; - Public pubk; - NodeDefaultEndpoint endpoint; ///< How we've previously connected to this node. (must match node's reported endpoint) - const unsigned distance; - timePoint activePing; - }; - - struct NodeBucket - { - unsigned distance; - timePoint modified; - std::list> nodes; - }; - - using EvictionTimeout = std::pair,Address>; - -public: - NodeTable(ba::io_service& _io): - m_node(NodeEntry(Address(), Public(), bi::udp::endpoint())), - m_socket(new nodeSocket(_io, *this, 30300)), - m_socketPtr(m_socket.get()), - m_io(_io), - m_bucketRefreshTimer(m_io), - m_evictionCheckTimer(m_io) - { - for (unsigned i = 0; i < s_buckets; i++) - m_state[i].distance = i, m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); - doRefreshBuckets(boost::system::error_code()); - } - - ~NodeTable() { - m_evictionCheckTimer.cancel(); - m_bucketRefreshTimer.cancel(); - m_socketPtr->disconnect(); - } - - void join() { doFindNode(m_node.id); } - - std::list
nodes() const - { - std::list
nodes; - Guard l(x_nodes); - for (auto& i: m_nodes) - nodes.push_back(i.second->id); - return std::move(nodes); - } - - NodeEntry operator[](Address _id) - { - Guard l(x_nodes); - return *m_nodes[_id]; - } - -protected: - void requestNeighbors(NodeEntry const& _node, Address _target) const - { - FindNeighbors p; - p.target = _target; - - p.to = _node.endpoint.udp; - p.seal(m_secret); - m_socketPtr->send(p); - } - - /// Dispatches udp requests in order to populate node table to be as close as possible to _node. - void doFindNode(Address _node, unsigned _round = 0, std::shared_ptr>> _tried = std::shared_ptr>>()) - { - if (!m_socketPtr->isOpen() || _round == 7) - return; - - auto nearest = findNearest(_node); - std::list> tried; - for (unsigned i = 0; i < nearest.size() && tried.size() < s_alpha; i++) - if (!_tried->count(nearest[i])) - { - tried.push_back(nearest[i]); - requestNeighbors(*nearest[i], _node); - } - else - continue; - - while (auto n = tried.front()) - { - _tried->insert(n); - tried.pop_front(); - } - - auto self(shared_from_this()); - m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_findTimout)); - m_evictionCheckTimer.async_wait([this, self, _node, _round, _tried](boost::system::error_code const& _ec) - { - if (_ec) - return; - doFindNode(_node, _round + 1, _tried); - }); - } - - std::vector> findNearest(Address _target) - { - // send s_alpha FindNeighbors packets to nodes we know, closest to target - unsigned head = dist(m_node.id, _target); - unsigned tail = (head - 1) % (s_tableSize - 1); - - // todo: optimize with tree - std::map>> found; - unsigned count = 0; - - // if d is 0, then we roll look forward, if last, we reverse, else, spread from d - if (head != 0 && tail != s_tableSize) - while (head != tail && count < s_bucketSize) - { - Guard l(x_state); - for (auto& n: m_state[head].nodes) - if (auto p = n.lock()) - { - if (count < s_bucketSize) - found[dist(_target, p->id)].push_back(p); - else - break; - } - - if (count < s_bucketSize && head) - for (auto& n: m_state[tail].nodes) - if (auto p = n.lock()) - { - if (count < s_bucketSize) - found[dist(_target, p->id)].push_back(p); - else - break; - } - head++; - tail = (tail - 1) % (s_tableSize - 1); - } - else if (head == 0) - while (head < s_bucketSize && count < s_bucketSize) - { - Guard l(x_state); - for (auto& n: m_state[head].nodes) - if (auto p = n.lock()) - { - if (count < s_bucketSize) - found[dist(_target, p->id)].push_back(p); - else - break; - } - head--; - } - else if (tail == s_tableSize - 1) - while (tail > 0 && count < s_bucketSize) - { - Guard l(x_state); - for (auto& n: m_state[tail].nodes) - if (auto p = n.lock()) - { - if (count < s_bucketSize) - found[dist(_target, p->id)].push_back(p); - else - break; - } - tail--; - } - - std::vector> ret; - for (auto& nodes: found) - for (auto& n: nodes.second) - ret.push_back(n); - return std::move(ret); - } - - void ping(bi::address _address, unsigned _port) const - { - PingNode p; - string ip = m_node.endpoint.udp.address().to_string(); - p.ipAddress = asBytes(ip); - p.port = m_node.endpoint.udp.port(); -// p.expiration; - p.seal(m_secret); - m_socketPtr->send(p); - } - - void ping(NodeEntry* _n) const - { - if (_n && _n->endpoint.udp.address().is_v4()) - ping(_n->endpoint.udp.address(), _n->endpoint.udp.port()); - } - - void evict(std::shared_ptr _leastSeen, std::shared_ptr _new) - { - if (!m_socketPtr->isOpen()) - return; - - Guard l(x_evictions); - m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id)); - if (m_evictions.size() == 1) - doCheckEvictions(boost::system::error_code()); - - m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id)); - ping(_leastSeen.get()); - } - - void noteNode(Public _pubk, bi::udp::endpoint _endpoint) - { - Address id = right160(sha3(_pubk)); - std::shared_ptr node; - { - Guard l(x_nodes); - auto n = m_nodes.find(id); - if (n == m_nodes.end()) - { - m_nodes[id] = std::shared_ptr(new NodeEntry(m_node, id, _pubk, _endpoint)); - node = m_nodes[id]; - } - else - node = n->second; - } - - noteNode(node); - } - - void noteNode(std::shared_ptr _n) - { - std::shared_ptr contested; - { - NodeBucket s = bucket(_n.get()); - Guard l(x_state); - s.nodes.remove_if([&_n](std::weak_ptr n) - { - auto p = n.lock(); - if (!p || p == _n) - return true; - return false; - }); - - if (s.nodes.size() >= s_bucketSize) - { - contested = s.nodes.front().lock(); - if (!contested) - { - s.nodes.pop_front(); - s.nodes.push_back(_n); - } - } - else - s.nodes.push_back(_n); - } - - if (contested) - evict(contested, _n); - } - - void dropNode(std::shared_ptr _n) - { - NodeBucket s = bucket(_n.get()); - { - Guard l(x_state); - s.nodes.remove_if([&_n](std::weak_ptr n) { return n.lock() == _n; }); - } - Guard l(x_nodes); - m_nodes.erase(_n->id); - } - - NodeBucket const& bucket(NodeEntry* _n) const - { - return m_state[_n->distance]; - } - - void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) - { - RLP rlp(_packet); - - - // whenever a pong is received, first check if it's in m_evictions, if so, remove it - Guard l(x_evictions); - } - - void onDisconnected(UDPSocketFace*) - { - - } - - void doCheckEvictions(boost::system::error_code const& _ec) - { - if (_ec || !m_socketPtr->isOpen()) - return; - - m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_evictionCheckInterval)); - auto self(shared_from_this()); - m_evictionCheckTimer.async_wait([this, self](boost::system::error_code const& _ec) - { - if (_ec) - return; - - bool evictionsRemain = false; - std::list> drop; - { - Guard l(x_evictions); - for (auto& e: m_evictions) - if (chrono::steady_clock::now() - e.first.second > chrono::milliseconds(s_pingTimeout)) - { - Guard l(x_nodes); - drop.push_back(m_nodes[e.second]); - } - evictionsRemain = m_evictions.size() - drop.size() > 0; - } - - for (auto& n: drop) - dropNode(n); - - if (evictionsRemain) - doCheckEvictions(boost::system::error_code()); - }); - } - - void doRefreshBuckets(boost::system::error_code const& _ec) - { - cout << "refreshing buckets" << endl; - if (_ec) - return; - - // first check if there are any pending evictions - - - bool connected = m_socketPtr->isOpen(); - bool refreshed = false; - if (connected) - { - Guard l(x_state); - for (auto& d: m_state) - if (chrono::steady_clock::now() - d.modified > chrono::seconds(s_bucketRefresh)) - while (!d.nodes.empty()) - { - auto n = d.nodes.front(); - if (auto p = n.lock()) - { - refreshed = true; - ping(p.get()); - break; - } - d.nodes.pop_front(); - } - } - - unsigned nextRefresh = connected ? (refreshed ? 200 : s_bucketRefresh*1000) : 10000; - auto runcb = [this](boost::system::error_code const& error) -> void { doRefreshBuckets(error); }; - m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(nextRefresh)); - m_bucketRefreshTimer.async_wait(runcb); - } - -private: - NodeEntry m_node; ///< This node. - Secret m_secret; ///< This nodes secret key. - - mutable Mutex x_nodes; ///< Mutable for thread-safe copy in nodes() const. - std::map> m_nodes; ///< Address -> Node table (most common lookup path) - - Mutex x_state; - std::array m_state; ///< State table; logbinned nodes. - - Mutex x_evictions; - std::deque m_evictions; ///< Eviction timeouts. - - shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. - nodeSocket* m_socketPtr; ///< Set to m_socket.get(). - ba::io_service& m_io; ///< Used by bucket refresh timer. - boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. - boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. -}; - /** * Only used for testing. Not useful beyond tests. */ From 6131bd62d665b293809759e5acd2141faed2f3a3 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 22 Dec 2014 03:56:22 +0100 Subject: [PATCH 11/54] NodeTable into own files. --- libp2p/NodeTable.cpp | 351 +++++++++++++++++++++++++++++++++++++++++++ libp2p/NodeTable.h | 226 ++++++++++++++++++++++++++++ 2 files changed, 577 insertions(+) create mode 100644 libp2p/NodeTable.cpp create mode 100644 libp2p/NodeTable.h diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp new file mode 100644 index 000000000..0011e0e45 --- /dev/null +++ b/libp2p/NodeTable.cpp @@ -0,0 +1,351 @@ +/* + 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 . + */ +/** @file NodeTable.cpp + * @author Alex Leverington + * @date 2014 + */ + +#include "NodeTable.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; + +NodeTable::NodeTable(ba::io_service& _io): + m_node(NodeEntry(Address(), Public(), bi::udp::endpoint())), + m_socket(new nodeSocket(_io, *this, 30300)), + m_socketPtr(m_socket.get()), + m_io(_io), + m_bucketRefreshTimer(m_io), + m_evictionCheckTimer(m_io) + { + for (unsigned i = 0; i < s_bins; i++) + m_state[i].distance = i, m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); + doRefreshBuckets(boost::system::error_code()); + } + +NodeTable::~NodeTable() +{ + m_evictionCheckTimer.cancel(); + m_bucketRefreshTimer.cancel(); + m_socketPtr->disconnect(); +} + + void NodeTable::join() +{ + doFindNode(m_node.id); +} + +std::list
NodeTable::nodes() const +{ + std::list
nodes; + Guard l(x_nodes); + for (auto& i: m_nodes) + nodes.push_back(i.second->id); + return std::move(nodes); +} + +NodeTable::NodeEntry NodeTable::operator[](Address _id) +{ + Guard l(x_nodes); + return *m_nodes[_id]; +} + +void NodeTable::requestNeighbors(NodeEntry const& _node, Address _target) const +{ + FindNeighbors p; + p.target = _target; + + p.to = _node.endpoint.udp; + p.seal(m_secret); + m_socketPtr->send(p); +} + +void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptr>> _tried) +{ + if (!m_socketPtr->isOpen() || _round == 7) + return; + + auto nearest = findNearest(_node); + std::list> tried; + for (unsigned i = 0; i < nearest.size() && tried.size() < s_alpha; i++) + if (!_tried->count(nearest[i])) + { + tried.push_back(nearest[i]); + requestNeighbors(*nearest[i], _node); + } + else + continue; + + while (auto n = tried.front()) + { + _tried->insert(n); + tried.pop_front(); + } + + auto self(shared_from_this()); + m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_findTimout)); + m_evictionCheckTimer.async_wait([this, self, _node, _round, _tried](boost::system::error_code const& _ec) + { + if (_ec) + return; + doFindNode(_node, _round + 1, _tried); + }); +} + +std::vector> NodeTable::findNearest(Address _target) +{ + // send s_alpha FindNeighbors packets to nodes we know, closest to target + unsigned head = dist(m_node.id, _target); + unsigned tail = (head - 1) % (s_bits - 1); + + // todo: optimize with tree + std::map>> found; + unsigned count = 0; + + // if d is 0, then we roll look forward, if last, we reverse, else, spread from d + if (head != 0 && tail != s_bits) + while (head != tail && count < s_bucketSize) + { + Guard l(x_state); + for (auto& n: m_state[head].nodes) + if (auto p = n.lock()) + { + if (count < s_bucketSize) + found[dist(_target, p->id)].push_back(p); + else + break; + } + + if (count < s_bucketSize && head) + for (auto& n: m_state[tail].nodes) + if (auto p = n.lock()) + { + if (count < s_bucketSize) + found[dist(_target, p->id)].push_back(p); + else + break; + } + head++; + tail = (tail - 1) % (s_bits - 1); + } + else if (head == 0) + while (head < s_bucketSize && count < s_bucketSize) + { + Guard l(x_state); + for (auto& n: m_state[head].nodes) + if (auto p = n.lock()) + { + if (count < s_bucketSize) + found[dist(_target, p->id)].push_back(p); + else + break; + } + head--; + } + else if (tail == s_bits - 1) + while (tail > 0 && count < s_bucketSize) + { + Guard l(x_state); + for (auto& n: m_state[tail].nodes) + if (auto p = n.lock()) + { + if (count < s_bucketSize) + found[dist(_target, p->id)].push_back(p); + else + break; + } + tail--; + } + + std::vector> ret; + for (auto& nodes: found) + for (auto& n: nodes.second) + ret.push_back(n); + return std::move(ret); +} + +void NodeTable::ping(bi::address _address, unsigned _port) const +{ + PingNode p; + string ip = m_node.endpoint.udp.address().to_string(); + p.ipAddress = asBytes(ip); + p.port = m_node.endpoint.udp.port(); +// p.expiration; + p.seal(m_secret); + m_socketPtr->send(p); +} + +void NodeTable::ping(NodeEntry* _n) const +{ + if (_n && _n->endpoint.udp.address().is_v4()) + ping(_n->endpoint.udp.address(), _n->endpoint.udp.port()); +} + +void NodeTable::evict(std::shared_ptr _leastSeen, std::shared_ptr _new) +{ + if (!m_socketPtr->isOpen()) + return; + + Guard l(x_evictions); + m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id)); + if (m_evictions.size() == 1) + doCheckEvictions(boost::system::error_code()); + + m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id)); + ping(_leastSeen.get()); +} + +void NodeTable::noteNode(Public _pubk, bi::udp::endpoint _endpoint) +{ + Address id = right160(sha3(_pubk)); + std::shared_ptr node; + { + Guard l(x_nodes); + auto n = m_nodes.find(id); + if (n == m_nodes.end()) + { + m_nodes[id] = std::shared_ptr(new NodeEntry(m_node, id, _pubk, _endpoint)); + node = m_nodes[id]; + } + else + node = n->second; + } + + noteNode(node); +} + +void NodeTable::noteNode(std::shared_ptr _n) +{ + std::shared_ptr contested; + { + NodeBucket s = bucket(_n.get()); + Guard l(x_state); + s.nodes.remove_if([&_n](std::weak_ptr n) + { + auto p = n.lock(); + if (!p || p == _n) + return true; + return false; + }); + + if (s.nodes.size() >= s_bucketSize) + { + contested = s.nodes.front().lock(); + if (!contested) + { + s.nodes.pop_front(); + s.nodes.push_back(_n); + } + } + else + s.nodes.push_back(_n); + } + + if (contested) + evict(contested, _n); +} + +void NodeTable::dropNode(std::shared_ptr _n) +{ + NodeBucket s = bucket(_n.get()); + { + Guard l(x_state); + s.nodes.remove_if([&_n](std::weak_ptr n) { return n.lock() == _n; }); + } + Guard l(x_nodes); + m_nodes.erase(_n->id); +} + +NodeTable::NodeBucket const& NodeTable::bucket(NodeEntry* _n) const +{ + return m_state[_n->distance]; +} + +void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) +{ + RLP rlp(_packet); + + + // whenever a pong is received, first check if it's in m_evictions, if so, remove it + Guard l(x_evictions); +} + +void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) +{ + if (_ec || !m_socketPtr->isOpen()) + return; + + m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_evictionCheckInterval)); + auto self(shared_from_this()); + m_evictionCheckTimer.async_wait([this, self](boost::system::error_code const& _ec) + { + if (_ec) + return; + + bool evictionsRemain = false; + std::list> drop; + { + Guard l(x_evictions); + for (auto& e: m_evictions) + if (chrono::steady_clock::now() - e.first.second > chrono::milliseconds(s_pingTimeout)) + { + Guard l(x_nodes); + drop.push_back(m_nodes[e.second]); + } + evictionsRemain = m_evictions.size() - drop.size() > 0; + } + + for (auto& n: drop) + dropNode(n); + + if (evictionsRemain) + doCheckEvictions(boost::system::error_code()); + }); +} + +void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) +{ + cout << "refreshing buckets" << endl; + if (_ec) + return; + + bool connected = m_socketPtr->isOpen(); + bool refreshed = false; + if (connected) + { + Guard l(x_state); + for (auto& d: m_state) + if (chrono::steady_clock::now() - d.modified > chrono::seconds(s_bucketRefresh)) + while (!d.nodes.empty()) + { + auto n = d.nodes.front(); + if (auto p = n.lock()) + { + refreshed = true; + ping(p.get()); + break; + } + d.nodes.pop_front(); + } + } + + unsigned nextRefresh = connected ? (refreshed ? 200 : s_bucketRefresh*1000) : 10000; + auto runcb = [this](boost::system::error_code const& error) -> void { doRefreshBuckets(error); }; + m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(nextRefresh)); + m_bucketRefreshTimer.async_wait(runcb); +} + diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h new file mode 100644 index 000000000..1f15540bf --- /dev/null +++ b/libp2p/NodeTable.h @@ -0,0 +1,226 @@ +/* + 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 . + */ +/** @file NodeTable.h + * @author Alex Leverington + * @date 2014 + */ + +#pragma once + +#include +#include + +namespace dev +{ +namespace p2p +{ + +/** + * Ping packet: Check if node is alive. + * PingNode is cached and regenerated after expiration - t, where t is timeout. + * + * signature: Signature of message. + * ipAddress: Our IP address. + * port: Our port. + * expiration: Triggers regeneration of packet. May also provide control over synchronization. + * + * 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. + * If the pinged node doesn't respond then it is removed and the new + * node is inserted. + */ +struct PingNode: RLPDatagram +{ + bytes ipAddress; + uint16_t port; + uint64_t expiration; + + Signature signature; + + void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } +}; + +struct Pong: RLPDatagram +{ + // todo: weak-signed pong + Address from; + uint64_t replyTo; /// expiration from PingNode + + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << from << replyTo; } +}; + +/** + * FindNeighbors Packet: Request k-nodes, closest to the target. + * FindNeighbors is cached and regenerated after expiration - t, where t is timeout. + * + * signature: Signature of message. + * target: Address of NodeId. The responding node will send back nodes closest to the target. + * expiration: Triggers regeneration of packet. May also provide control over synchronization. + * + */ +struct FindNeighbors: RLPDatagram +{ + h160 target; + uint64_t expiration; + + Signature signature; + + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } +}; + +/** + * Node Packet: Multiple node packets are sent in response to FindNeighbors. + */ +struct Neighbors: RLPDatagram +{ + struct Node + { + bytes ipAddress; + uint16_t port; + NodeId node; + void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << node; } + }; + + std::set nodes; + h256 nonce; + + Signature signature; + + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << nonce; } +}; + +/** + * NodeTable using S/Kademlia system for node discovery and preference. + * untouched buckets are refreshed if they have not been touched within an hour + * + * Thread-safety is ensured by modifying NodeEntry details via + * shared_ptr replacement instead of mutating values. + * + * @todo don't try to evict node if node isRequired. (support for makeRequired) + * @todo optimize (use tree for state (or set w/custom compare for cache)) + * @todo constructor support for m_node, m_secret + * @todo use s_bitsPerStep for find and refresh/ping + * @todo exclude bucket from refresh if we have node as peer + * @todo restore nodes + */ +class NodeTable: UDPSocketEvents, public std::enable_shared_from_this +{ + using nodeSocket = UDPSocket; + using timePoint = std::chrono::steady_clock::time_point; + + static unsigned const s_bucketSize = 16; // Denoted by k in [Kademlia]. Number of nodes stored in each bucket. +// const unsigned s_bitsPerStep = 5; // @todo Denoted by b in [Kademlia]. Bits by which address space will be divided for find responses. + static unsigned const s_alpha = 3; // Denoted by \alpha in [Kademlia]. Number of concurrent FindNeighbors requests. + const unsigned s_findTimout = 300; // How long to wait between find queries. +// const unsigned s_siblings = 5; // @todo Denoted by s in [S/Kademlia]. User-defined by sub-protocols. + const unsigned s_bucketRefresh = 3600; // Refresh interval prevents bucket from becoming stale. [Kademlia] + static unsigned const s_bits = 8 * Address::size; // Denoted by n. + static unsigned const s_bins = s_bits - 1; // + const unsigned s_evictionCheckInterval = 75; // Interval by which eviction timeouts are checked. + const unsigned s_pingTimeout = 500; + +public: + static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } + + struct NodeDefaultEndpoint + { + NodeDefaultEndpoint(bi::udp::endpoint _udp): udp(_udp) {} + bi::udp::endpoint udp; + }; + + struct NodeEntry + { + NodeEntry(Address _id, Public _pubk, bi::udp::endpoint _udp): id(_id), pubk(_pubk), endpoint(NodeDefaultEndpoint(_udp)), distance(0) {} + NodeEntry(NodeEntry _src, Address _id, Public _pubk, bi::udp::endpoint _udp): id(_id), pubk(_pubk), endpoint(NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_id)) {} + NodeEntry(NodeEntry _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): id(_id), pubk(_pubk), endpoint(_gw), distance(dist(_src.id,_id)) {} + Address id; + Public pubk; + NodeDefaultEndpoint endpoint; ///< How we've previously connected to this node. (must match node's reported endpoint) + const unsigned distance; + timePoint activePing; + }; + + struct NodeBucket + { + unsigned distance; + timePoint modified; + std::list> nodes; + }; + + using EvictionTimeout = std::pair,Address>; + + NodeTable(ba::io_service& _io); + ~NodeTable(); + + void join(); + + std::list
nodes() const; + + NodeEntry operator[](Address _id); + +protected: + void requestNeighbors(NodeEntry const& _node, Address _target) const; + + /// Sends requests to other nodes requesting nodes "near" to us in order to populate node table such that connected nodes form centrality. + void doFindNode(Address _node, unsigned _round = 0, std::shared_ptr>> _tried = std::shared_ptr>>()); + + std::vector> findNearest(Address _target); + + void ping(bi::address _address, unsigned _port) const; + + void ping(NodeEntry* _n) const; + + void evict(std::shared_ptr _leastSeen, std::shared_ptr _new); + + void noteNode(Public _pubk, bi::udp::endpoint _endpoint); + + void noteNode(std::shared_ptr _n); + + void dropNode(std::shared_ptr _n); + + NodeBucket const& bucket(NodeEntry* _n) const; + + void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet); + + void onDisconnected(UDPSocketFace*) {}; + + void doCheckEvictions(boost::system::error_code const& _ec); + + void doRefreshBuckets(boost::system::error_code const& _ec); + +private: + NodeEntry m_node; ///< This node. + Secret m_secret; ///< This nodes secret key. + + mutable Mutex x_nodes; ///< Mutable for thread-safe copy in nodes() const. + std::map> m_nodes; ///< Address -> Node table (most common lookup path) + + Mutex x_state; + std::array m_state; ///< State table of binned nodes. + + Mutex x_evictions; + std::deque m_evictions; ///< Eviction timeouts. + + std::shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. + nodeSocket* m_socketPtr; ///< Set to m_socket.get(). + ba::io_service& m_io; ///< Used by bucket refresh timer. + boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. + boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. +}; + +} +} \ No newline at end of file From da5acab432b1639a9a58e594e7fadd0af7140391 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 23 Dec 2014 08:56:56 +0100 Subject: [PATCH 12/54] refactor constants to be determined during compile. added RLPXDatagram. constructors and updates to datagram constructors. docs and logging. WIP tests and host/harness for NodeTable. --- libp2p/NodeTable.cpp | 91 ++++++++++++------------ libp2p/NodeTable.h | 162 +++++++++++++++++++++++++++++-------------- libp2p/UDP.cpp | 8 ++- libp2p/UDP.h | 46 +++++++++--- test/net.cpp | 71 ++++++++++++++----- 5 files changed, 251 insertions(+), 127 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 0011e0e45..0bc024b03 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -24,18 +24,19 @@ using namespace std; using namespace dev; using namespace dev::p2p; -NodeTable::NodeTable(ba::io_service& _io): - m_node(NodeEntry(Address(), Public(), bi::udp::endpoint())), - m_socket(new nodeSocket(_io, *this, 30300)), - m_socketPtr(m_socket.get()), - m_io(_io), - m_bucketRefreshTimer(m_io), - m_evictionCheckTimer(m_io) - { - for (unsigned i = 0; i < s_bins; i++) - m_state[i].distance = i, m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); - doRefreshBuckets(boost::system::error_code()); - } +NodeTable::NodeTable(ba::io_service& _io, uint16_t _port): + m_node(Node(Address(), Public(), bi::udp::endpoint())), + m_socket(new nodeSocket(_io, *this, _port)), + m_socketPtr(m_socket.get()), + m_io(_io), + m_bucketRefreshTimer(m_io), + m_evictionCheckTimer(m_io) +{ + for (unsigned i = 0; i < s_bins; i++) + m_state[i].distance = i, m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); + doRefreshBuckets(boost::system::error_code()); + m_socketPtr->connect(); +} NodeTable::~NodeTable() { @@ -44,7 +45,7 @@ NodeTable::~NodeTable() m_socketPtr->disconnect(); } - void NodeTable::join() +void NodeTable::join() { doFindNode(m_node.id); } @@ -66,17 +67,14 @@ NodeTable::NodeEntry NodeTable::operator[](Address _id) void NodeTable::requestNeighbors(NodeEntry const& _node, Address _target) const { - FindNeighbors p; - p.target = _target; - - p.to = _node.endpoint.udp; - p.seal(m_secret); + FindNode p(_node.endpoint.udp, _target); + p.sign(m_secret); m_socketPtr->send(p); } void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptr>> _tried) { - if (!m_socketPtr->isOpen() || _round == 7) + if (!m_socketPtr->isOpen() || _round == s_maxSteps) return; auto nearest = findNearest(_node); @@ -84,20 +82,27 @@ void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptrcount(nearest[i])) { - tried.push_back(nearest[i]); - requestNeighbors(*nearest[i], _node); + auto r = nearest[i]; + tried.push_back(r); + FindNode p(r->endpoint.udp, _node); + p.sign(m_secret); + m_socketPtr->send(p); } - else - continue; - while (auto n = tried.front()) + if (tried.empty()) { - _tried->insert(n); + clog(NodeTableWarn) << "Terminating doFindNode after " << _round << " rounds."; + return; + } + + while (!tried.empty()) + { + _tried->insert(tried.front()); tried.pop_front(); } auto self(shared_from_this()); - m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_findTimout)); + m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(c_reqTimeout.count())); m_evictionCheckTimer.async_wait([this, self, _node, _round, _tried](boost::system::error_code const& _ec) { if (_ec) @@ -108,15 +113,16 @@ void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptr> NodeTable::findNearest(Address _target) { - // send s_alpha FindNeighbors packets to nodes we know, closest to target + // send s_alpha FindNode packets to nodes we know, closest to target unsigned head = dist(m_node.id, _target); - unsigned tail = (head - 1) % (s_bits - 1); + unsigned tail = (head - 1) % s_bins; // todo: optimize with tree std::map>> found; unsigned count = 0; // if d is 0, then we roll look forward, if last, we reverse, else, spread from d +#pragma warning TODO: This should probably be s_bins instead of s_bits. if (head != 0 && tail != s_bits) while (head != tail && count < s_bucketSize) { @@ -140,7 +146,7 @@ std::vector> NodeTable::findNearest(Addres break; } head++; - tail = (tail - 1) % (s_bits - 1); + tail = (tail - 1) % s_bins; } else if (head == 0) while (head < s_bucketSize && count < s_bucketSize) @@ -156,7 +162,7 @@ std::vector> NodeTable::findNearest(Addres } head--; } - else if (tail == s_bits - 1) + else if (tail == s_bins) while (tail > 0 && count < s_bucketSize) { Guard l(x_state); @@ -178,21 +184,17 @@ std::vector> NodeTable::findNearest(Addres return std::move(ret); } -void NodeTable::ping(bi::address _address, unsigned _port) const +void NodeTable::ping(bi::udp::endpoint _to) const { - PingNode p; - string ip = m_node.endpoint.udp.address().to_string(); - p.ipAddress = asBytes(ip); - p.port = m_node.endpoint.udp.port(); -// p.expiration; - p.seal(m_secret); + PingNode p(_to, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); + p.sign(m_secret); m_socketPtr->send(p); } void NodeTable::ping(NodeEntry* _n) const { - if (_n && _n->endpoint.udp.address().is_v4()) - ping(_n->endpoint.udp.address(), _n->endpoint.udp.port()); + if (_n) + ping(_n->endpoint.udp); } void NodeTable::evict(std::shared_ptr _leastSeen, std::shared_ptr _new) @@ -279,6 +281,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes { RLP rlp(_packet); + clog(NodeTableNote) << "Received message from " << _from.address().to_string() << ":" << _from.port(); // whenever a pong is received, first check if it's in m_evictions, if so, remove it Guard l(x_evictions); @@ -289,8 +292,8 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) if (_ec || !m_socketPtr->isOpen()) return; - m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_evictionCheckInterval)); auto self(shared_from_this()); + m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_evictionCheckInterval)); m_evictionCheckTimer.async_wait([this, self](boost::system::error_code const& _ec) { if (_ec) @@ -301,7 +304,7 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) { Guard l(x_evictions); for (auto& e: m_evictions) - if (chrono::steady_clock::now() - e.first.second > chrono::milliseconds(s_pingTimeout)) + if (chrono::steady_clock::now() - e.first.second > c_reqTimeout) { Guard l(x_nodes); drop.push_back(m_nodes[e.second]); @@ -319,7 +322,7 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) { - cout << "refreshing buckets" << endl; + clog(NodeTableNote) << "refreshing buckets"; if (_ec) return; @@ -329,7 +332,7 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) { Guard l(x_state); for (auto& d: m_state) - if (chrono::steady_clock::now() - d.modified > chrono::seconds(s_bucketRefresh)) + if (chrono::steady_clock::now() - d.modified > c_bucketRefresh) while (!d.nodes.empty()) { auto n = d.nodes.front(); @@ -343,7 +346,7 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) } } - unsigned nextRefresh = connected ? (refreshed ? 200 : s_bucketRefresh*1000) : 10000; + unsigned nextRefresh = connected ? (refreshed ? 200 : c_bucketRefresh.count()*1000) : 10000; auto runcb = [this](boost::system::error_code const& error) -> void { doRefreshBuckets(error); }; m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(nextRefresh)); m_bucketRefreshTimer.async_wait(runcb); diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 1f15540bf..b29cffe7d 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include @@ -42,51 +43,58 @@ namespace p2p * a given bucket which is full, the least-responsive node is pinged. * If the pinged node doesn't respond then it is removed and the new * node is inserted. + * + * @todo uint128_t for ip address (<->integer ipv4/6, asio-address, asio-endpoint) + * */ -struct PingNode: RLPDatagram +struct PingNode: RLPXDatagram { - bytes ipAddress; + using RLPXDatagram::RLPXDatagram; + PingNode(bi::udp::endpoint _to, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_to), ipAddress(_src), port(_srcPort), expiration(fromNow(_expiration)) {} + + std::string ipAddress; uint16_t port; uint64_t expiration; - Signature signature; - void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } }; -struct Pong: RLPDatagram +struct Pong: RLPXDatagram { - // todo: weak-signed pong - Address from; - uint64_t replyTo; /// expiration from PingNode + using RLPXDatagram::RLPXDatagram; + + h256 replyTo; /// TBD - void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << from << replyTo; } + void streamRLP(RLPStream& _s) const { _s.appendList(1); _s << replyTo; } }; /** - * FindNeighbors Packet: Request k-nodes, closest to the target. - * FindNeighbors is cached and regenerated after expiration - t, where t is timeout. + * FindNode Packet: Request k-nodes, closest to the target. + * FindNode is cached and regenerated after expiration - t, where t is timeout. + * FindNode implicitly results in finding neighbors of a given node. * - * signature: Signature of message. * target: Address of NodeId. The responding node will send back nodes closest to the target. * expiration: Triggers regeneration of packet. May also provide control over synchronization. * */ -struct FindNeighbors: RLPDatagram +struct FindNode: RLPXDatagram { + using RLPXDatagram::RLPXDatagram; + FindNode(bi::udp::endpoint _to, Address _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_to), target(_target), expiration(fromNow(_expiration)) {} + h160 target; uint64_t expiration; - - Signature signature; - + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } }; /** - * Node Packet: Multiple node packets are sent in response to FindNeighbors. + * Node Packet: Multiple node packets are sent in response to FindNode. */ -struct Neighbors: RLPDatagram +struct Neighbors: RLPXDatagram { + using RLPXDatagram::RLPXDatagram; + struct Node { bytes ipAddress; @@ -110,31 +118,32 @@ struct Neighbors: RLPDatagram * Thread-safety is ensured by modifying NodeEntry details via * shared_ptr replacement instead of mutating values. * - * @todo don't try to evict node if node isRequired. (support for makeRequired) - * @todo optimize (use tree for state (or set w/custom compare for cache)) + * [Interface] * @todo constructor support for m_node, m_secret - * @todo use s_bitsPerStep for find and refresh/ping - * @todo exclude bucket from refresh if we have node as peer + * @todo don't try to evict node if node isRequired. (support for makeRequired) + * @todo exclude bucket from refresh if we have node as peer (support for makeRequired) * @todo restore nodes + * @todo std::shared_ptr m_cachedPingPacket; + * @todo std::shared_ptr m_cachedFindSelfPacket; + * + * [Networking] + * @todo use eth/stun/ice/whatever for public-discovery + * + * [Protocol] + * @todo optimize knowledge at opposite edges; eg, s_bitsPerStep lookups. (Can be done via pointers to NodeBucket) + * @todo ^ s_bitsPerStep = 5; // Denoted by b in [Kademlia]. Bits by which address space is divided. + * @todo optimize (use tree for state and/or custom compare for cache) + * @todo reputation (aka universal siblings lists) + * @todo dht (aka siblings) + * + * [Maintenance] + * @todo pretty logs */ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this { - using nodeSocket = UDPSocket; + using nodeSocket = UDPSocket; using timePoint = std::chrono::steady_clock::time_point; - - static unsigned const s_bucketSize = 16; // Denoted by k in [Kademlia]. Number of nodes stored in each bucket. -// const unsigned s_bitsPerStep = 5; // @todo Denoted by b in [Kademlia]. Bits by which address space will be divided for find responses. - static unsigned const s_alpha = 3; // Denoted by \alpha in [Kademlia]. Number of concurrent FindNeighbors requests. - const unsigned s_findTimout = 300; // How long to wait between find queries. -// const unsigned s_siblings = 5; // @todo Denoted by s in [S/Kademlia]. User-defined by sub-protocols. - const unsigned s_bucketRefresh = 3600; // Refresh interval prevents bucket from becoming stale. [Kademlia] - static unsigned const s_bits = 8 * Address::size; // Denoted by n. - static unsigned const s_bins = s_bits - 1; // - const unsigned s_evictionCheckInterval = 75; // Interval by which eviction timeouts are checked. - const unsigned s_pingTimeout = 500; - -public: - static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } + using EvictionTimeout = std::pair,Address>; struct NodeDefaultEndpoint { @@ -142,16 +151,29 @@ public: bi::udp::endpoint udp; }; - struct NodeEntry + struct Node { - NodeEntry(Address _id, Public _pubk, bi::udp::endpoint _udp): id(_id), pubk(_pubk), endpoint(NodeDefaultEndpoint(_udp)), distance(0) {} - NodeEntry(NodeEntry _src, Address _id, Public _pubk, bi::udp::endpoint _udp): id(_id), pubk(_pubk), endpoint(NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_id)) {} - NodeEntry(NodeEntry _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): id(_id), pubk(_pubk), endpoint(_gw), distance(dist(_src.id,_id)) {} + Node(Address _id, Public _pubk, NodeDefaultEndpoint _udp): id(_id), pubk(_pubk), endpoint(_udp) {} + Node(Address _id, Public _pubk, bi::udp::endpoint _udp): Node(_id, _pubk, NodeDefaultEndpoint(_udp)) {} + + virtual Address const& address() const { return id; } + virtual Public const& publicKey() const { return pubk; } + Address id; Public pubk; - NodeDefaultEndpoint endpoint; ///< How we've previously connected to this node. (must match node's reported endpoint) - const unsigned distance; - timePoint activePing; + NodeDefaultEndpoint endpoint; + }; + + /** + * NodeEntry + * @todo Type of id will become template parameter. + */ + struct NodeEntry: public Node + { + NodeEntry(Node _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): Node(_id, _pubk, _gw), distance(dist(_src.id,_id)) {} + NodeEntry(Node _src, Address _id, Public _pubk, bi::udp::endpoint _udp): Node(_id, _pubk, NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_id)) {} + + const unsigned distance; ///< Node's distance from _src (see constructor). }; struct NodeBucket @@ -161,9 +183,30 @@ public: std::list> nodes; }; - using EvictionTimeout = std::pair,Address>; +public: + + /// Constants for Kademlia, mostly derived from address space. + + static constexpr unsigned s_addressByteSize = sizeof(NodeEntry::id); ///< Size of address type in bytes. + static constexpr unsigned s_bits = 8 * s_addressByteSize; ///< Denoted by n in [Kademlia]. + static constexpr unsigned s_bins = s_bits - 1; ///< Size of m_state (excludes root, which is us). + static constexpr unsigned s_maxSteps = boost::static_log2::value; ///< Max iterations of discovery. (doFindNode) + + /// Chosen constants + + static constexpr unsigned s_bucketSize = 16; ///< Denoted by k in [Kademlia]. Number of nodes stored in each bucket. + static constexpr unsigned s_alpha = 3; ///< Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests. + static constexpr uint16_t s_defaultPort = 30300; ///< Default port to listen on. + + /// Intervals + + static constexpr unsigned s_evictionCheckInterval = 75; ///< Interval at which eviction timeouts are checked. + std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). + std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] + + static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } - NodeTable(ba::io_service& _io); + NodeTable(ba::io_service& _io, uint16_t _port = s_defaultPort); ~NodeTable(); void join(); @@ -173,14 +216,13 @@ public: NodeEntry operator[](Address _id); protected: - void requestNeighbors(NodeEntry const& _node, Address _target) const; - - /// Sends requests to other nodes requesting nodes "near" to us in order to populate node table such that connected nodes form centrality. + /// Repeatedly sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to . void doFindNode(Address _node, unsigned _round = 0, std::shared_ptr>> _tried = std::shared_ptr>>()); + /// Returns nodes nearest to target. std::vector> findNearest(Address _target); - void ping(bi::address _address, unsigned _port) const; + void ping(bi::udp::endpoint _to) const; void ping(NodeEntry* _n) const; @@ -194,16 +236,27 @@ protected: NodeBucket const& bucket(NodeEntry* _n) const; + /// Network Events + void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet); void onDisconnected(UDPSocketFace*) {}; + /// Tasks + void doCheckEvictions(boost::system::error_code const& _ec); void doRefreshBuckets(boost::system::error_code const& _ec); - + +#ifndef BOOST_AUTO_TEST_SUITE private: - NodeEntry m_node; ///< This node. +#else +protected: +#endif + /// Sends s_alpha concurrent FindNeighbor requests to nodes closest to target until + void requestNeighbors(NodeEntry const& _node, Address _target) const; + + Node m_node; ///< This node. Secret m_secret; ///< This nodes secret key. mutable Mutex x_nodes; ///< Mutable for thread-safe copy in nodes() const. @@ -215,12 +268,15 @@ private: Mutex x_evictions; std::deque m_evictions; ///< Eviction timeouts. - std::shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. + std::shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. nodeSocket* m_socketPtr; ///< Set to m_socket.get(). ba::io_service& m_io; ///< Used by bucket refresh timer. boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. }; + +struct NodeTableWarn: public LogChannel { static const char* name() { return "!P!"; } static const int verbosity = 0; }; +struct NodeTableNote: public LogChannel { static const char* name() { return "*P*"; } static const int verbosity = 1; }; } } \ No newline at end of file diff --git a/libp2p/UDP.cpp b/libp2p/UDP.cpp index a398eb1fa..9af74c10f 100644 --- a/libp2p/UDP.cpp +++ b/libp2p/UDP.cpp @@ -23,13 +23,15 @@ using namespace dev; using namespace dev::p2p; -void RLPDatagram::seal(Secret const& _k) +h256 RLPXDatagram::sign(Secret const& _k) { RLPStream packet; streamRLP(packet); bytes b(packet.out()); - Signature sig = dev::sign(_k, dev::sha3(b)); + h256 h(dev::sha3(b)); + Signature sig = dev::sign(_k, h); data.resize(data.size() + Signature::size); sig.ref().copyTo(&data); - memcpy(data.data()+sizeof(Signature),b.data(),b.size()); + memcpy(data.data() + sizeof(Signature), b.data(), b.size()); + return std::move(h); } diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 0f4499c84..878c21906 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -38,27 +38,49 @@ namespace dev namespace p2p { -struct UDPDatagram +/** + * UDP Datagram + * @todo make data private + */ +class UDPDatagram { - UDPDatagram() = default; - UDPDatagram(bi::udp::endpoint _ep, bytes _data): to(_ep), data(std::move(_data)) {} - bi::udp::endpoint to; +public: + UDPDatagram(bi::udp::endpoint _ep): locus(_ep) {} + UDPDatagram(bi::udp::endpoint _ep, bytes _data): data(_data), locus(_ep) {} + bi::udp::endpoint const& endpoint() const { return locus; } + bytes data; +protected: + bi::udp::endpoint locus; }; -struct RLPDatagram: UDPDatagram +/** + * @brief RLPX Datagram which can be signed. + */ +struct RLPXDatagram: public UDPDatagram { - virtual void seal(Secret const& _k); -protected: - virtual void streamRLP(RLPStream& _s) const {}; + static uint64_t fromNow(std::chrono::milliseconds _ms) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _ms).time_since_epoch()).count(); } + static uint64_t fromNow(std::chrono::seconds _sec) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); } + + RLPXDatagram(bi::udp::endpoint _ep): UDPDatagram(_ep) {} + + virtual h256 sign(Secret const& _from); + virtual void streamRLP(RLPStream&) const = 0; + Signature signature; }; +/** + * @brief Interface which UDPSocket will implement. + */ struct UDPSocketFace { virtual bool send(UDPDatagram const& _msg) = 0; virtual void disconnect() = 0; }; +/** + * @brief Interface which a UDPSocket's owner must implement. + */ struct UDPSocketEvents { virtual void onDisconnected(UDPSocketFace*) {}; @@ -68,6 +90,9 @@ struct UDPSocketEvents /** * @brief UDP Interface * Handler must implement UDPSocketEvents. + * + * @todo multiple endpoints (we cannot advertise 0.0.0.0) + * @todo decouple deque from UDPDatagram and add ref() to datagram for fire&forget */ template class UDPSocket: UDPSocketFace, public std::enable_shared_from_this> @@ -76,7 +101,6 @@ public: static constexpr unsigned maxDatagramSize = MaxDatagramSize; static_assert(maxDatagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes"); - /// Construct open socket to endpoint. UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, unsigned _port): m_host(_host), m_endpoint(bi::udp::v4(), _port), m_socket(_io) { m_started.store(false); m_closed.store(true); }; virtual ~UDPSocket() { disconnect(); } @@ -142,6 +166,7 @@ bool UDPSocket::send(UDPDatagram const& _datagram) Guard l(x_sendQ); sendQ.push_back(_datagram); + clog(NoteChannel) << "qued datagram"; if (sendQ.size() == 1) doWrite(); @@ -169,7 +194,7 @@ void UDPSocket::doWrite() { const UDPDatagram& datagram = sendQ[0]; auto self(UDPSocket::shared_from_this()); - m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.to, [this, self](boost::system::error_code _ec, std::size_t) + m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.endpoint(), [this, self](boost::system::error_code _ec, std::size_t) { if (_ec) return disconnectWithError(_ec); @@ -180,6 +205,7 @@ void UDPSocket::doWrite() if (sendQ.empty()) return; } + clog(NoteChannel) << "sent datagram"; doWrite(); }); } diff --git a/test/net.cpp b/test/net.cpp index 3d2e08eb6..886e17a47 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -30,6 +30,8 @@ using namespace dev::p2p; namespace ba = boost::asio; namespace bi = ba::ip; +BOOST_AUTO_TEST_SUITE(p2p) + /** * Only used for testing. Not useful beyond tests. */ @@ -37,7 +39,7 @@ class TestHost: public Worker { public: TestHost(): Worker("test",0), m_io() {}; - ~TestHost() { m_io.stop(); stopWorking(); } + virtual ~TestHost() { m_io.stop(); stopWorking(); } void start() { startWorking(); } void doWork() { m_io.run(); } @@ -45,28 +47,62 @@ protected: ba::io_service m_io; }; +struct TestNodeTable: public NodeTable +{ + void generateTestNodes(int _count = 10) + { + asserts(_count < 1000); + static uint16_t s_basePort = 30500; + + m_testNodes.clear(); + for (auto i = 0; i < _count; i++) + m_testNodes.push_back(make_pair(KeyPair::create(),s_basePort++)); + } + std::vector> m_testNodes; // keypair and port + + /// Constructor + using NodeTable::NodeTable; + + void setup() + { + /// Phase 1 test: populate with pings + /// Phase 2 test: pre-populate *expected* ping-responses, send pings + + bi::address ourIp = bi::address::from_string("127.0.0.1"); + uint16_t ourPort = 30300; + bi::udp::endpoint ourEndpoint(ourIp, ourPort); + + generateTestNodes(); + for (auto& n: m_testNodes) + ping(bi::udp::endpoint(ourIp, n.second)); + + // wait 1ms between each send + // send PingNode for each s_bootstrapNodes + // wait until nodecount is s_testNodes.count() + + } + + void reset() + { + Guard l(x_state); + for (auto& n: m_state) n.nodes.clear(); + } +}; + /** * Only used for testing. Not useful beyond tests. */ -class TestNodeHost: public TestHost +struct TestNodeTableHost: public TestHost { -public: - TestNodeHost(): m_nodes(m_io) {}; - ~TestNodeHost() { m_io.stop(); stopWorking(); } - void start() { startWorking(); } - void doWork() { m_io.run(); } - - NodeTable m_nodes; + TestNodeTableHost(): nodeTable(new TestNodeTable(m_io)) {}; + shared_ptr nodeTable; }; class TestUDPSocket: UDPSocketEvents, public TestHost { public: TestUDPSocket(): m_socket(new UDPSocket(m_io, *this, 30300)) {} - ~TestUDPSocket() { m_io.stop(); stopWorking(); } - void start() { startWorking(); } - void doWork() { m_io.run(); } - + void onDisconnected(UDPSocketFace*) {}; void onReceived(UDPSocketFace*, bi::udp::endpoint const&, bytesConstRef _packet) { if (_packet.toString() == "AAAA") success = true; } @@ -75,12 +111,13 @@ public: bool success = false; }; -BOOST_AUTO_TEST_SUITE(p2p) - BOOST_AUTO_TEST_CASE(kademlia) { - TestNodeHost nodeHost; - +// TestNodeTableHost node; +// node.start(); +// node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for +// node.nodeTable->setup(); +// sleep(1); } BOOST_AUTO_TEST_CASE(test_txrx_one) From 2a1ea35f7fb770e24ae7d0a3132ec4a0e0c8ea9a Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 23 Dec 2014 09:25:36 +0100 Subject: [PATCH 13/54] send/receive messages (not yet interepreted) --- libp2p/NodeTable.cpp | 13 ++++++----- libp2p/NodeTable.h | 14 ++++++------ libp2p/UDP.h | 1 - test/net.cpp | 52 +++++++++++++++++++++++++++----------------- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 0bc024b03..b3c31b56b 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -24,9 +24,10 @@ using namespace std; using namespace dev; using namespace dev::p2p; -NodeTable::NodeTable(ba::io_service& _io, uint16_t _port): - m_node(Node(Address(), Public(), bi::udp::endpoint())), - m_socket(new nodeSocket(_io, *this, _port)), +NodeTable::NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _listenPort): + m_node(Node(_alias.address(), _alias.pub(), bi::udp::endpoint())), + m_secret(_alias.sec()), + m_socket(new NodeSocket(_io, *this, _listenPort)), m_socketPtr(m_socket.get()), m_io(_io), m_bucketRefreshTimer(m_io), @@ -281,7 +282,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes { RLP rlp(_packet); - clog(NodeTableNote) << "Received message from " << _from.address().to_string() << ":" << _from.port(); + clog(NodeTableNote) << "Received X from " << _from.address().to_string() << ":" << _from.port(); // whenever a pong is received, first check if it's in m_evictions, if so, remove it Guard l(x_evictions); @@ -322,10 +323,10 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) { - clog(NodeTableNote) << "refreshing buckets"; if (_ec) return; - + + clog(NodeTableNote) << "refreshing buckets"; bool connected = m_socketPtr->isOpen(); bool refreshed = false; if (connected) diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index b29cffe7d..a27811b0b 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -141,9 +141,9 @@ struct Neighbors: RLPXDatagram */ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this { - using nodeSocket = UDPSocket; - using timePoint = std::chrono::steady_clock::time_point; - using EvictionTimeout = std::pair,Address>; + using NodeSocket = UDPSocket; + using TimePoint = std::chrono::steady_clock::time_point; + using EvictionTimeout = std::pair,Address>; struct NodeDefaultEndpoint { @@ -179,7 +179,7 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this struct NodeBucket { unsigned distance; - timePoint modified; + TimePoint modified; std::list> nodes; }; @@ -206,7 +206,7 @@ public: static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } - NodeTable(ba::io_service& _io, uint16_t _port = s_defaultPort); + NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _port = s_defaultPort); ~NodeTable(); void join(); @@ -268,8 +268,8 @@ protected: Mutex x_evictions; std::deque m_evictions; ///< Eviction timeouts. - std::shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. - nodeSocket* m_socketPtr; ///< Set to m_socket.get(). + std::shared_ptr m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. + NodeSocket* m_socketPtr; ///< Set to m_socket.get(). ba::io_service& m_io; ///< Used by bucket refresh timer. boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 878c21906..0db491272 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -166,7 +166,6 @@ bool UDPSocket::send(UDPDatagram const& _datagram) Guard l(x_sendQ); sendQ.push_back(_datagram); - clog(NoteChannel) << "qued datagram"; if (sendQ.size() == 1) doWrite(); diff --git a/test/net.cpp b/test/net.cpp index 886e17a47..d3aaae53d 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -49,21 +49,10 @@ protected: struct TestNodeTable: public NodeTable { - void generateTestNodes(int _count = 10) - { - asserts(_count < 1000); - static uint16_t s_basePort = 30500; - - m_testNodes.clear(); - for (auto i = 0; i < _count; i++) - m_testNodes.push_back(make_pair(KeyPair::create(),s_basePort++)); - } - std::vector> m_testNodes; // keypair and port - /// Constructor using NodeTable::NodeTable; - void setup() + void setup(std::vector> const& _testNodes) { /// Phase 1 test: populate with pings /// Phase 2 test: pre-populate *expected* ping-responses, send pings @@ -72,8 +61,7 @@ struct TestNodeTable: public NodeTable uint16_t ourPort = 30300; bi::udp::endpoint ourEndpoint(ourIp, ourPort); - generateTestNodes(); - for (auto& n: m_testNodes) + for (auto& n: _testNodes) ping(bi::udp::endpoint(ourIp, n.second)); // wait 1ms between each send @@ -94,8 +82,32 @@ struct TestNodeTable: public NodeTable */ struct TestNodeTableHost: public TestHost { - TestNodeTableHost(): nodeTable(new TestNodeTable(m_io)) {}; + TestNodeTableHost(): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)) {}; + + void generateTestNodes(int _count = 10) + { + asserts(_count < 1000); + static uint16_t s_basePort = 30500; + + m_testNodes.clear(); + for (auto i = 0; i < _count; i++) + { + KeyPair k = KeyPair::create(); + m_testNodes.push_back(make_pair(k,s_basePort+i)); + testNodes.push_back(make_shared(m_io,k,s_basePort+i)); + } + } + std::vector> m_testNodes; // keypair and port + + void setup() + { + generateTestNodes(); + nodeTable->setup(m_testNodes); + } + + KeyPair m_alias; shared_ptr nodeTable; + std::vector> testNodes; }; class TestUDPSocket: UDPSocketEvents, public TestHost @@ -113,11 +125,11 @@ public: BOOST_AUTO_TEST_CASE(kademlia) { -// TestNodeTableHost node; -// node.start(); -// node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for -// node.nodeTable->setup(); -// sleep(1); + TestNodeTableHost node; + node.start(); + node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for + node.setup(); + sleep(1); } BOOST_AUTO_TEST_CASE(test_txrx_one) From 82d40b6ff8fb643023afd9ac0d9059dd64e49232 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 23 Dec 2014 11:11:07 +0100 Subject: [PATCH 14/54] more implementating of packets --- libp2p/NodeTable.cpp | 47 +++++++++++++++++++++++++++++--- libp2p/NodeTable.h | 65 +++++++++++++++++++++++++++++++++++--------- libp2p/UDP.cpp | 2 +- libp2p/UDP.h | 6 ++-- 4 files changed, 100 insertions(+), 20 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index b3c31b56b..c6cf24b71 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -280,12 +280,51 @@ NodeTable::NodeBucket const& NodeTable::bucket(NodeEntry* _n) const void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { - RLP rlp(_packet); + if (_packet.size() < 69) + { + clog(NodeTableMessageSummary) << "Invalid Message received from " << _from.address().to_string() << ":" << _from.port(); + return; + } - clog(NodeTableNote) << "Received X from " << _from.address().to_string() << ":" << _from.port(); + /// 3 items is PingNode, 2 items w/no lists is FindNode, 2 items w/first item as list is Neighbors, 1 item is Pong + RLP rlp(bytesConstRef(_packet.cropped(65, _packet.size() - 65))); + unsigned itemCount = rlp.itemCount(); - // whenever a pong is received, first check if it's in m_evictions, if so, remove it - Guard l(x_evictions); +// bytesConstRef sig(_packet.cropped(0, 65)); // verify signature (deferred) + + switch (itemCount) { + case 1: + { + clog(NodeTableMessageSummary) << "Received Pong from " << _from.address().to_string() << ":" << _from.port(); + // whenever a pong is received, first check if it's in m_evictions, if so, remove it + Guard l(x_evictions); + break; + } + + case 2: + if (rlp[0].isList()) + { + clog(NodeTableMessageSummary) << "Received Neighbors from " << _from.address().to_string() << ":" << _from.port(); + } + else + { + clog(NodeTableMessageSummary) << "Received FindNode from " << _from.address().to_string() << ":" << _from.port(); + } + break; + + case 3: + { + clog(NodeTableMessageSummary) << "Received PingNode from " << _from.address().to_string() << ":" << _from.port(); + // todo: if we know the node, send a pong. otherwise ignore him. + // let's send a pong! + + break; + } + + default: + clog(NodeTableMessageSummary) << "Invalid Message received from " << _from.address().to_string() << ":" << _from.port(); + return; + } } void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index a27811b0b..36ca0fd7d 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -34,6 +34,10 @@ namespace p2p * Ping packet: Check if node is alive. * PingNode is cached and regenerated after expiration - t, where t is timeout. * + * RLP Encoded Items: 3 + * Minimum Encoded Size: 18 bytes + * Maximum Encoded Size: bytes // todo after u128 addresses + * * signature: Signature of message. * ipAddress: Our IP address. * port: Our port. @@ -47,25 +51,39 @@ namespace p2p * @todo uint128_t for ip address (<->integer ipv4/6, asio-address, asio-endpoint) * */ -struct PingNode: RLPXDatagram +struct PingNode: public RLPXDatagram { + friend class NodeTable; using RLPXDatagram::RLPXDatagram; PingNode(bi::udp::endpoint _to, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_to), ipAddress(_src), port(_srcPort), expiration(fromNow(_expiration)) {} - std::string ipAddress; - uint16_t port; - uint64_t expiration; - + std::string ipAddress; // rlp encoded bytes min: 16 + uint16_t port; // rlp encoded bytes min: 1 max: 3 + uint64_t expiration; // rlp encoded bytes min: 1 max: 9 + +protected: void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = (std::string)r[0]; port = (uint16_t)r[1]; expiration = (uint64_t)r[2]; } }; +/** + * Pong packet: response to ping + * + * RLP Encoded Items: 1 + * Minimum Encoded Size: 33 bytes + * Maximum Encoded Size: 33 bytes + * + * @todo value of replyTo + */ struct Pong: RLPXDatagram { + friend class NodeTable; using RLPXDatagram::RLPXDatagram; - h256 replyTo; /// TBD + h256 replyTo; void streamRLP(RLPStream& _s) const { _s.appendList(1); _s << replyTo; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); replyTo = (h256)r[0]; } }; /** @@ -73,12 +91,17 @@ struct Pong: RLPXDatagram * FindNode is cached and regenerated after expiration - t, where t is timeout. * FindNode implicitly results in finding neighbors of a given node. * - * target: Address of NodeId. The responding node will send back nodes closest to the target. + * RLP Encoded Items: 2 + * Minimum Encoded Size: 21 bytes + * Maximum Encoded Size: 30 bytes + * + * target: Address 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 { + friend class NodeTable; using RLPXDatagram::RLPXDatagram; FindNode(bi::udp::endpoint _to, Address _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_to), target(_target), expiration(fromNow(_expiration)) {} @@ -86,29 +109,37 @@ struct FindNode: RLPXDatagram uint64_t expiration; void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = (h160)r[0]; expiration = (uint64_t)r[1]; } }; /** * Node Packet: Multiple node packets are sent in response to FindNode. + * + * RLP Encoded Items: 2 (first item is list) + * Minimum Encoded Size: 10 bytes + * */ struct Neighbors: RLPXDatagram { + friend class NodeTable; using RLPXDatagram::RLPXDatagram; struct Node { - bytes ipAddress; + Node() = default; + Node(bytesConstRef _bytes) { interpretRLP(_bytes); } + std::string ipAddress; uint16_t port; NodeId node; void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << node; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = r[0].toString(); port = (uint16_t)r[1]; node = (h512)r[2]; } }; - std::set nodes; + std::list nodes; h256 nonce; - - Signature signature; - + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << nonce; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n.toBytesConstRef())); nonce = (h256)r[1]; } }; /** @@ -274,9 +305,17 @@ protected: boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. }; - + struct NodeTableWarn: public LogChannel { static const char* name() { return "!P!"; } static const int verbosity = 0; }; struct NodeTableNote: public LogChannel { static const char* name() { return "*P*"; } static const int verbosity = 1; }; +struct NodeTableMessageSummary: public LogChannel { static const char* name() { return "-P-"; } static const int verbosity = 2; }; +struct NodeTableConnect: public LogChannel { static const char* name() { return "+P+"; } static const int verbosity = 10; }; +struct NodeTableMessageDetail: public LogChannel { static const char* name() { return "=P="; } static const int verbosity = 5; }; +struct NodeTableTriviaSummary: public LogChannel { static const char* name() { return "-P-"; } static const int verbosity = 10; }; +struct NodeTableTriviaDetail: public LogChannel { static const char* name() { return "=P="; } static const int verbosity = 11; }; +struct NodeTableAllDetail: public LogChannel { static const char* name() { return "=P="; } static const int verbosity = 13; }; +struct NodeTableEgress: public LogChannel { static const char* name() { return ">>P"; } static const int verbosity = 14; }; +struct NodeTableIngress: public LogChannel { static const char* name() { return "<((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); } RLPXDatagram(bi::udp::endpoint _ep): UDPDatagram(_ep) {} - virtual h256 sign(Secret const& _from); - virtual void streamRLP(RLPStream&) const = 0; + +protected: + virtual void streamRLP(RLPStream&) const =0; + virtual void interpretRLP(bytesConstRef _bytes) =0; Signature signature; }; From c06054f63ed5135cbf13a4ff95ccf055fba8b3e1 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 23 Dec 2014 13:44:52 +0100 Subject: [PATCH 15/54] basic implementation of packets --- libp2p/NodeTable.cpp | 79 +++++++++----- libp2p/NodeTable.h | 240 ++++++++++++++++++++++--------------------- libp2p/UDP.cpp | 13 --- libp2p/UDP.h | 37 +++++-- test/net.cpp | 6 -- 5 files changed, 204 insertions(+), 171 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index c6cf24b71..6c1a4cda0 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -287,43 +287,66 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes } /// 3 items is PingNode, 2 items w/no lists is FindNode, 2 items w/first item as list is Neighbors, 1 item is Pong - RLP rlp(bytesConstRef(_packet.cropped(65, _packet.size() - 65))); + bytesConstRef rlpBytes(_packet.cropped(65, _packet.size() - 65)); + RLP rlp(rlpBytes); unsigned itemCount = rlp.itemCount(); // bytesConstRef sig(_packet.cropped(0, 65)); // verify signature (deferred) - switch (itemCount) { - case 1: - { - clog(NodeTableMessageSummary) << "Received Pong from " << _from.address().to_string() << ":" << _from.port(); - // whenever a pong is received, first check if it's in m_evictions, if so, remove it - Guard l(x_evictions); - break; - } - - case 2: - if (rlp[0].isList()) + try { + switch (itemCount) { + case 1: { - clog(NodeTableMessageSummary) << "Received Neighbors from " << _from.address().to_string() << ":" << _from.port(); + clog(NodeTableMessageSummary) << "Received Pong from " << _from.address().to_string() << ":" << _from.port(); + Pong in = Pong::fromBytesConstRef(_from, rlpBytes); + + // whenever a pong is received, first check if it's in m_evictions, if so, remove it + // otherwise check if we're expecting a pong. if we weren't, blacklist IP for 300 seconds + + break; } - else + + case 2: + if (rlp[0].isList()) + { + clog(NodeTableMessageSummary) << "Received Neighbors from " << _from.address().to_string() << ":" << _from.port(); + Neighbors in = Neighbors::fromBytesConstRef(_from, rlpBytes); + for (auto n: in.nodes) + noteNode(n.node, bi::udp::endpoint(bi::address::from_string(n.ipAddress), n.port)); + } + else + { + clog(NodeTableMessageSummary) << "Received FindNode from " << _from.address().to_string() << ":" << _from.port(); + FindNode in = FindNode::fromBytesConstRef(_from, rlpBytes); + + std::vector> nearest = findNearest(in.target); + Neighbors out(_from, nearest); + out.sign(m_secret); + m_socketPtr->send(out); + } + break; + + case 3: { - clog(NodeTableMessageSummary) << "Received FindNode from " << _from.address().to_string() << ":" << _from.port(); + clog(NodeTableMessageSummary) << "Received PingNode from " << _from.address().to_string() << ":" << _from.port(); + // todo: if we know the node, reply, otherwise ignore. + PingNode in = PingNode::fromBytesConstRef(_from, rlpBytes); + + Pong p(_from); + p.replyTo = sha3(rlpBytes); + p.sign(m_secret); + m_socketPtr->send(p); + break; } - break; - - case 3: - { - clog(NodeTableMessageSummary) << "Received PingNode from " << _from.address().to_string() << ":" << _from.port(); - // todo: if we know the node, send a pong. otherwise ignore him. - // let's send a pong! - - break; + + default: + clog(NodeTableMessageSummary) << "Invalid Message received from " << _from.address().to_string() << ":" << _from.port(); + return; } - - default: - clog(NodeTableMessageSummary) << "Invalid Message received from " << _from.address().to_string() << ":" << _from.port(); - return; + } + catch (...) + { + } } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 36ca0fd7d..a5dd41f07 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -30,118 +30,6 @@ namespace dev namespace p2p { -/** - * Ping packet: Check if node is alive. - * PingNode is cached and regenerated after expiration - t, where t is timeout. - * - * RLP Encoded Items: 3 - * Minimum Encoded Size: 18 bytes - * Maximum Encoded Size: bytes // todo after u128 addresses - * - * signature: Signature of message. - * ipAddress: Our IP address. - * port: Our port. - * expiration: Triggers regeneration of packet. May also provide control over synchronization. - * - * 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. - * If the pinged node doesn't respond then it is removed and the new - * node is inserted. - * - * @todo uint128_t for ip address (<->integer ipv4/6, asio-address, asio-endpoint) - * - */ -struct PingNode: public RLPXDatagram -{ - friend class NodeTable; - using RLPXDatagram::RLPXDatagram; - PingNode(bi::udp::endpoint _to, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_to), ipAddress(_src), port(_srcPort), expiration(fromNow(_expiration)) {} - - std::string ipAddress; // rlp encoded bytes min: 16 - uint16_t port; // rlp encoded bytes min: 1 max: 3 - uint64_t expiration; // rlp encoded bytes min: 1 max: 9 - -protected: - void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = (std::string)r[0]; port = (uint16_t)r[1]; expiration = (uint64_t)r[2]; } -}; - -/** - * Pong packet: response to ping - * - * RLP Encoded Items: 1 - * Minimum Encoded Size: 33 bytes - * Maximum Encoded Size: 33 bytes - * - * @todo value of replyTo - */ -struct Pong: RLPXDatagram -{ - friend class NodeTable; - using RLPXDatagram::RLPXDatagram; - - h256 replyTo; - - void streamRLP(RLPStream& _s) const { _s.appendList(1); _s << replyTo; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); replyTo = (h256)r[0]; } -}; - -/** - * FindNode Packet: Request k-nodes, closest to the target. - * FindNode is cached and regenerated after expiration - t, where t is timeout. - * FindNode implicitly results in finding neighbors of a given node. - * - * RLP Encoded Items: 2 - * Minimum Encoded Size: 21 bytes - * Maximum Encoded Size: 30 bytes - * - * target: Address 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 -{ - friend class NodeTable; - using RLPXDatagram::RLPXDatagram; - FindNode(bi::udp::endpoint _to, Address _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_to), target(_target), expiration(fromNow(_expiration)) {} - - h160 target; - uint64_t expiration; - - void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = (h160)r[0]; expiration = (uint64_t)r[1]; } -}; - -/** - * Node Packet: Multiple node packets are sent in response to FindNode. - * - * RLP Encoded Items: 2 (first item is list) - * Minimum Encoded Size: 10 bytes - * - */ -struct Neighbors: RLPXDatagram -{ - friend class NodeTable; - using RLPXDatagram::RLPXDatagram; - - struct Node - { - Node() = default; - Node(bytesConstRef _bytes) { interpretRLP(_bytes); } - std::string ipAddress; - uint16_t port; - NodeId node; - void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << node; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = r[0].toString(); port = (uint16_t)r[1]; node = (h512)r[2]; } - }; - - std::list nodes; - h256 nonce; - - void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << nonce; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n.toBytesConstRef())); nonce = (h256)r[1]; } -}; - /** * NodeTable using S/Kademlia system for node discovery and preference. * untouched buckets are refreshed if they have not been touched within an hour @@ -158,20 +46,20 @@ struct Neighbors: RLPXDatagram * @todo std::shared_ptr m_cachedFindSelfPacket; * * [Networking] - * @todo use eth/stun/ice/whatever for public-discovery + * @todo use eth/upnp/natpmp/stun/ice/etc for public-discovery + * @todo firewall * * [Protocol] + * @todo ping newly added nodes for eviction * @todo optimize knowledge at opposite edges; eg, s_bitsPerStep lookups. (Can be done via pointers to NodeBucket) * @todo ^ s_bitsPerStep = 5; // Denoted by b in [Kademlia]. Bits by which address space is divided. * @todo optimize (use tree for state and/or custom compare for cache) * @todo reputation (aka universal siblings lists) * @todo dht (aka siblings) - * - * [Maintenance] - * @todo pretty logs */ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this { + friend struct Neighbors; using NodeSocket = UDPSocket; using TimePoint = std::chrono::steady_clock::time_point; using EvictionTimeout = std::pair,Address>; @@ -305,6 +193,126 @@ protected: boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. }; + +/** + * Ping packet: Check if node is alive. + * PingNode is cached and regenerated after expiration - t, where t is timeout. + * + * RLP Encoded Items: 3 + * Minimum Encoded Size: 18 bytes + * Maximum Encoded Size: bytes // todo after u128 addresses + * + * signature: Signature of message. + * ipAddress: Our IP address. + * port: Our port. + * expiration: Triggers regeneration of packet. May also provide control over synchronization. + * + * 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. + * If the pinged node doesn't respond then it is removed and the new + * node is inserted. + * + * @todo uint128_t for ip address (<->integer ipv4/6, asio-address, asio-endpoint) + * + */ +struct PingNode: RLPXDatagram +{ + using RLPXDatagram::RLPXDatagram; + PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_ep), ipAddress(_src), port(_srcPort), expiration(futureFromEpoch(_expiration)) {} + + std::string ipAddress; // rlp encoded bytes min: 16 + uint16_t port; // rlp encoded bytes min: 1 max: 3 + uint64_t expiration; // rlp encoded bytes min: 1 max: 9 + + void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = (std::string)r[0]; port = (uint16_t)r[1]; expiration = (uint64_t)r[2]; } +}; + +/** + * Pong packet: response to ping + * + * RLP Encoded Items: 1 + * Minimum Encoded Size: 33 bytes + * Maximum Encoded Size: 33 bytes + * + * @todo value of replyTo + * @todo create from PingNode (reqs RLPXDatagram verify flag) + */ +struct Pong: RLPXDatagram +{ + using RLPXDatagram::RLPXDatagram; + + h256 replyTo; // hash of rlp of PingNode + + void streamRLP(RLPStream& _s) const { _s.appendList(1); _s << replyTo; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); replyTo = (h256)r[0]; } +}; + +/** + * FindNode Packet: Request k-nodes, closest to the target. + * FindNode is cached and regenerated after expiration - t, where t is timeout. + * FindNode implicitly results in finding neighbors of a given node. + * + * RLP Encoded Items: 2 + * Minimum Encoded Size: 21 bytes + * Maximum Encoded Size: 30 bytes + * + * target: Address 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 +{ + using RLPXDatagram::RLPXDatagram; + FindNode(bi::udp::endpoint _ep, Address _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_ep), target(_target), expiration(futureFromEpoch(_expiration)) {} + + h160 target; + uint64_t expiration; + + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = (h160)r[0]; expiration = (uint64_t)r[1]; } +}; + +/** + * Node Packet: Multiple node packets are sent in response to FindNode. + * + * RLP Encoded Items: 2 (first item is list) + * Minimum Encoded Size: 10 bytes + * + * @todo nonce + */ +struct Neighbors: RLPXDatagram +{ + struct Node + { + Node() = default; + Node(bytesConstRef _bytes) { interpretRLP(_bytes); } + std::string ipAddress; + uint16_t port; + NodeId node; + void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << node; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = r[0].toString(); port = (uint16_t)r[1]; node = (h512)r[2]; } + }; + + using RLPXDatagram::RLPXDatagram; + Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest): RLPXDatagram(_to), nonce(h256()) + { + for (auto& n: _nearest) + { + Node node; + node.ipAddress = n->endpoint.udp.address().to_string(); + node.port = n->endpoint.udp.port(); + node.node = n->publicKey(); + nodes.push_back(node); + } + } + + std::list nodes; + h256 nonce; + + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << nonce; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n.toBytesConstRef())); nonce = (h256)r[1]; } +}; struct NodeTableWarn: public LogChannel { static const char* name() { return "!P!"; } static const int verbosity = 0; }; struct NodeTableNote: public LogChannel { static const char* name() { return "*P*"; } static const int verbosity = 1; }; diff --git a/libp2p/UDP.cpp b/libp2p/UDP.cpp index ea5845da7..f98c31e09 100644 --- a/libp2p/UDP.cpp +++ b/libp2p/UDP.cpp @@ -22,16 +22,3 @@ #include "UDP.h" using namespace dev; using namespace dev::p2p; - -h256 RLPXDatagram::sign(Secret const& _k) -{ - RLPStream packet; - streamRLP(packet); - bytes b(packet.out()); - h256 h(dev::sha3(b)); - Signature sig = dev::sign(_k, h); - data.resize(b.size() + Signature::size); - sig.ref().copyTo(&data); - memcpy(data.data() + sizeof(Signature), b.data(), b.size()); - return std::move(h); -} diff --git a/libp2p/UDP.h b/libp2p/UDP.h index ab694fba5..5de8053a0 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -40,13 +40,13 @@ namespace p2p /** * UDP Datagram - * @todo make data private + * @todo make data protected/functional */ class UDPDatagram { public: - UDPDatagram(bi::udp::endpoint _ep): locus(_ep) {} - UDPDatagram(bi::udp::endpoint _ep, bytes _data): data(_data), locus(_ep) {} + UDPDatagram(bi::udp::endpoint const& _ep): locus(_ep) {} + UDPDatagram(bi::udp::endpoint const& _ep, bytes _data): data(_data), locus(_ep) {} bi::udp::endpoint const& endpoint() const { return locus; } bytes data; @@ -56,20 +56,41 @@ protected: /** * @brief RLPX Datagram which can be signed. + * @todo compact templates + * @todo make data private/functional (see UDPDatagram) + * @todo valid=true/false (based on signature) */ +template struct RLPXDatagram: public UDPDatagram { - static uint64_t fromNow(std::chrono::milliseconds _ms) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _ms).time_since_epoch()).count(); } - static uint64_t fromNow(std::chrono::seconds _sec) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); } - - RLPXDatagram(bi::udp::endpoint _ep): UDPDatagram(_ep) {} + static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } + static uint64_t futureFromEpoch(std::chrono::milliseconds _ms) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _ms).time_since_epoch()).count(); } + static uint64_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); } + + RLPXDatagram(bi::udp::endpoint const& _ep): UDPDatagram(_ep) {} virtual h256 sign(Secret const& _from); -protected: virtual void streamRLP(RLPStream&) const =0; virtual void interpretRLP(bytesConstRef _bytes) =0; + +protected: Signature signature; }; + +template +h256 RLPXDatagram::sign(Secret const& _k) +{ + RLPStream packet; + streamRLP(packet); + bytes b(packet.out()); + h256 h(dev::sha3(b)); + Signature sig = dev::sign(_k, h); + data.resize(b.size() + Signature::size); + sig.ref().copyTo(&data); + memcpy(data.data() + sizeof(Signature), b.data(), b.size()); + return std::move(h); +} + /** * @brief Interface which UDPSocket will implement. diff --git a/test/net.cpp b/test/net.cpp index d3aaae53d..8277c1f70 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -54,7 +54,6 @@ struct TestNodeTable: public NodeTable void setup(std::vector> const& _testNodes) { - /// Phase 1 test: populate with pings /// Phase 2 test: pre-populate *expected* ping-responses, send pings bi::address ourIp = bi::address::from_string("127.0.0.1"); @@ -63,11 +62,6 @@ struct TestNodeTable: public NodeTable for (auto& n: _testNodes) ping(bi::udp::endpoint(ourIp, n.second)); - - // wait 1ms between each send - // send PingNode for each s_bootstrapNodes - // wait until nodecount is s_testNodes.count() - } void reset() From 4216162fc17cf2f2d3092beee28937f940fc1761 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 24 Dec 2014 10:22:27 +0100 Subject: [PATCH 16/54] message signing and verification. shutdown io/thread before dealloc in nodetable/testnodetables. --- libp2p/NodeTable.cpp | 65 +++++++++++++++++++++++++++----------------- libp2p/NodeTable.h | 33 +++++++++++++++------- libp2p/UDP.cpp | 21 ++++++++++++++ libp2p/UDP.h | 38 +++++++++++--------------- test/net.cpp | 34 +++++++++++++++++------ 5 files changed, 125 insertions(+), 66 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 6c1a4cda0..3799a4eef 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -45,7 +45,7 @@ NodeTable::~NodeTable() m_bucketRefreshTimer.cancel(); m_socketPtr->disconnect(); } - + void NodeTable::join() { doFindNode(m_node.id); @@ -60,6 +60,16 @@ std::list
NodeTable::nodes() const return std::move(nodes); } +list NodeTable::state() const +{ + list ret; + Guard l(x_state); + for (auto s: m_state) + for (auto n: s.nodes) + ret.push_back(*n.lock()); + return move(ret); +} + NodeTable::NodeEntry NodeTable::operator[](Address _id) { Guard l(x_nodes); @@ -115,17 +125,16 @@ void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptr> NodeTable::findNearest(Address _target) { // send s_alpha FindNode packets to nodes we know, closest to target + static unsigned lastBin = s_bins - 1; unsigned head = dist(m_node.id, _target); - unsigned tail = (head - 1) % s_bins; + unsigned tail = head == 0 ? lastBin : (head - 1) % s_bins; - // todo: optimize with tree std::map>> found; unsigned count = 0; // if d is 0, then we roll look forward, if last, we reverse, else, spread from d -#pragma warning TODO: This should probably be s_bins instead of s_bits. - if (head != 0 && tail != s_bits) - while (head != tail && count < s_bucketSize) + if (head > 1 && tail != lastBin) + while (head != tail && head < s_bins && count < s_bucketSize) { Guard l(x_state); for (auto& n: m_state[head].nodes) @@ -137,7 +146,7 @@ std::vector> NodeTable::findNearest(Addres break; } - if (count < s_bucketSize && head) + if (count < s_bucketSize && tail) for (auto& n: m_state[tail].nodes) if (auto p = n.lock()) { @@ -146,11 +155,13 @@ std::vector> NodeTable::findNearest(Addres else break; } + head++; - tail = (tail - 1) % s_bins; + if (tail) + tail--; } - else if (head == 0) - while (head < s_bucketSize && count < s_bucketSize) + else if (head < 2) + while (head < s_bins && count < s_bucketSize) { Guard l(x_state); for (auto& n: m_state[head].nodes) @@ -161,9 +172,9 @@ std::vector> NodeTable::findNearest(Addres else break; } - head--; + head++; } - else if (tail == s_bins) + else while (tail > 0 && count < s_bucketSize) { Guard l(x_state); @@ -221,8 +232,8 @@ void NodeTable::noteNode(Public _pubk, bi::udp::endpoint _endpoint) auto n = m_nodes.find(id); if (n == m_nodes.end()) { - m_nodes[id] = std::shared_ptr(new NodeEntry(m_node, id, _pubk, _endpoint)); - node = m_nodes[id]; + node.reset(new NodeEntry(m_node, id, _pubk, _endpoint)); + m_nodes[id] = node; } else node = n->second; @@ -235,12 +246,11 @@ void NodeTable::noteNode(std::shared_ptr _n) { std::shared_ptr contested; { - NodeBucket s = bucket(_n.get()); + NodeBucket& s = bucket(_n.get()); Guard l(x_state); s.nodes.remove_if([&_n](std::weak_ptr n) { - auto p = n.lock(); - if (!p || p == _n) + if (n.lock() == _n) return true; return false; }); @@ -264,7 +274,7 @@ void NodeTable::noteNode(std::shared_ptr _n) void NodeTable::dropNode(std::shared_ptr _n) { - NodeBucket s = bucket(_n.get()); + NodeBucket &s = bucket(_n.get()); { Guard l(x_state); s.nodes.remove_if([&_n](std::weak_ptr n) { return n.lock() == _n; }); @@ -273,9 +283,9 @@ void NodeTable::dropNode(std::shared_ptr _n) m_nodes.erase(_n->id); } -NodeTable::NodeBucket const& NodeTable::bucket(NodeEntry* _n) const +NodeTable::NodeBucket& NodeTable::bucket(NodeEntry const* _n) { - return m_state[_n->distance]; + return m_state[_n->distance - 1]; } void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) @@ -286,12 +296,19 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes return; } - /// 3 items is PingNode, 2 items w/no lists is FindNode, 2 items w/first item as list is Neighbors, 1 item is Pong + // 3 items is PingNode, 2 items w/no lists is FindNode, 2 items w/first item as list is Neighbors, 1 item is Pong bytesConstRef rlpBytes(_packet.cropped(65, _packet.size() - 65)); RLP rlp(rlpBytes); unsigned itemCount = rlp.itemCount(); -// bytesConstRef sig(_packet.cropped(0, 65)); // verify signature (deferred) + bytesConstRef sigBytes(_packet.cropped(0, 65)); + Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(rlpBytes))); + if (!nodeid) + { + clog(NodeTableMessageSummary) << "Invalid Message Signature from " << _from.address().to_string() << ":" << _from.port(); + return; + } + noteNode(nodeid, _from); try { switch (itemCount) { @@ -300,8 +317,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes clog(NodeTableMessageSummary) << "Received Pong from " << _from.address().to_string() << ":" << _from.port(); Pong in = Pong::fromBytesConstRef(_from, rlpBytes); - // whenever a pong is received, first check if it's in m_evictions, if so, remove it - // otherwise check if we're expecting a pong. if we weren't, blacklist IP for 300 seconds + // whenever a pong is received, first check if it's in m_evictions break; } @@ -329,7 +345,6 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes case 3: { clog(NodeTableMessageSummary) << "Received PingNode from " << _from.address().to_string() << ":" << _from.port(); - // todo: if we know the node, reply, otherwise ignore. PingNode in = PingNode::fromBytesConstRef(_from, rlpBytes); Pong p(_from); diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index a5dd41f07..89456b4fd 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -38,19 +38,20 @@ namespace p2p * shared_ptr replacement instead of mutating values. * * [Interface] - * @todo constructor support for m_node, m_secret - * @todo don't try to evict node if node isRequired. (support for makeRequired) - * @todo exclude bucket from refresh if we have node as peer (support for makeRequired) - * @todo restore nodes + * @todo restore nodes: affects refreshbuckets + * @todo TCP endpoints + * @todo makeRequired: don't try to evict node if node isRequired. + * @todo makeRequired: exclude bucket from refresh if we have node as peer. * @todo std::shared_ptr m_cachedPingPacket; * @todo std::shared_ptr m_cachedFindSelfPacket; * * [Networking] - * @todo use eth/upnp/natpmp/stun/ice/etc for public-discovery + * @todo TCP endpoints + * @todo eth/upnp/natpmp/stun/ice/etc for public-discovery * @todo firewall * * [Protocol] - * @todo ping newly added nodes for eviction + * @todo post-eviction pong * @todo optimize knowledge at opposite edges; eg, s_bitsPerStep lookups. (Can be done via pointers to NodeBucket) * @todo ^ s_bitsPerStep = 5; // Denoted by b in [Kademlia]. Bits by which address space is divided. * @todo optimize (use tree for state and/or custom compare for cache) @@ -130,10 +131,13 @@ public: void join(); + NodeEntry root() const { return NodeEntry(m_node, m_node.address(), m_node.publicKey(), m_node.endpoint.udp); } std::list
nodes() const; + std::list state() const; NodeEntry operator[](Address _id); + protected: /// Repeatedly sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to . void doFindNode(Address _node, unsigned _round = 0, std::shared_ptr>> _tried = std::shared_ptr>>()); @@ -153,9 +157,9 @@ protected: void dropNode(std::shared_ptr _n); - NodeBucket const& bucket(NodeEntry* _n) const; - - /// Network Events + NodeBucket& bucket(NodeEntry const* _n); + + /// General Network Events void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet); @@ -181,7 +185,7 @@ protected: mutable Mutex x_nodes; ///< Mutable for thread-safe copy in nodes() const. std::map> m_nodes; ///< Address -> Node table (most common lookup path) - Mutex x_state; + mutable Mutex x_state; std::array m_state; ///< State table of binned nodes. Mutex x_evictions; @@ -194,6 +198,15 @@ protected: boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. }; +std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) +{ + _out << _nodeTable.root().address() << "\t" << "0\t" << _nodeTable.root().endpoint.udp.address() << ":" << _nodeTable.root().endpoint.udp.port() << std::endl; + auto s = _nodeTable.state(); + for (auto n: s) + _out << n.address() << "\t" << n.distance << "\t" << n.endpoint.udp.address() << ":" << n.endpoint.udp.port() << std::endl; + return _out; +} + /** * Ping packet: Check if node is alive. * PingNode is cached and regenerated after expiration - t, where t is timeout. diff --git a/libp2p/UDP.cpp b/libp2p/UDP.cpp index f98c31e09..42f5e4cab 100644 --- a/libp2p/UDP.cpp +++ b/libp2p/UDP.cpp @@ -22,3 +22,24 @@ #include "UDP.h" using namespace dev; using namespace dev::p2p; + +//template +h256 RLPXDatagramFace::sign(Secret const& _k) +{ + RLPStream packet; + streamRLP(packet); + bytes b(packet.out()); + h256 h(dev::sha3(b)); + Signature sig = dev::sign(_k, h); + data.resize(b.size() + Signature::size); + sig.ref().copyTo(&data); + memcpy(data.data() + sizeof(Signature), b.data(), b.size()); + return std::move(h); +}; + +//template +Public RLPXDatagramFace::authenticate(bytesConstRef _sig, bytesConstRef _rlp) +{ + Signature const& sig = *(Signature const*)_sig.data(); + return std::move(dev::recover(sig, sha3(_rlp))); +}; \ No newline at end of file diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 5de8053a0..498d37f0c 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -58,39 +58,28 @@ protected: * @brief RLPX Datagram which can be signed. * @todo compact templates * @todo make data private/functional (see UDPDatagram) - * @todo valid=true/false (based on signature) */ -template -struct RLPXDatagram: public UDPDatagram +//template +struct RLPXDatagramFace: public UDPDatagram { - static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } +// static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } static uint64_t futureFromEpoch(std::chrono::milliseconds _ms) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _ms).time_since_epoch()).count(); } static uint64_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); } + static Public authenticate(bytesConstRef _sig, bytesConstRef _rlp); - RLPXDatagram(bi::udp::endpoint const& _ep): UDPDatagram(_ep) {} + RLPXDatagramFace(bi::udp::endpoint const& _ep): UDPDatagram(_ep) {} virtual h256 sign(Secret const& _from); virtual void streamRLP(RLPStream&) const =0; virtual void interpretRLP(bytesConstRef _bytes) =0; - -protected: - Signature signature; }; template -h256 RLPXDatagram::sign(Secret const& _k) +struct RLPXDatagram: public RLPXDatagramFace { - RLPStream packet; - streamRLP(packet); - bytes b(packet.out()); - h256 h(dev::sha3(b)); - Signature sig = dev::sign(_k, h); - data.resize(b.size() + Signature::size); - sig.ref().copyTo(&data); - memcpy(data.data() + sizeof(Signature), b.data(), b.size()); - return std::move(h); -} - + static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } + using RLPXDatagramFace::RLPXDatagramFace; +}; /** * @brief Interface which UDPSocket will implement. @@ -198,6 +187,9 @@ bool UDPSocket::send(UDPDatagram const& _datagram) template void UDPSocket::doRead() { + if (m_closed) + return; + auto self(UDPSocket::shared_from_this()); m_socket.async_receive_from(boost::asio::buffer(recvData), recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) { @@ -206,14 +198,16 @@ void UDPSocket::doRead() assert(_len); m_host.onReceived(this, recvEndpoint, bytesConstRef(recvData.data(), _len)); - if (!m_closed) - doRead(); + doRead(); }); } template void UDPSocket::doWrite() { + if (m_closed) + return; + const UDPDatagram& datagram = sendQ[0]; auto self(UDPSocket::shared_from_this()); m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.endpoint(), [this, self](boost::system::error_code _ec, std::size_t) diff --git a/test/net.cpp b/test/net.cpp index 8277c1f70..274f729c6 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -42,6 +42,7 @@ public: virtual ~TestHost() { m_io.stop(); stopWorking(); } void start() { startWorking(); } void doWork() { m_io.run(); } + void doneWorking() { m_io.reset(); m_io.poll(); m_io.reset(); } protected: ba::io_service m_io; @@ -52,18 +53,20 @@ struct TestNodeTable: public NodeTable /// Constructor using NodeTable::NodeTable; - void setup(std::vector> const& _testNodes) + void pingAll(std::vector> const& _testNodes) { - /// Phase 2 test: pre-populate *expected* ping-responses, send pings - bi::address ourIp = bi::address::from_string("127.0.0.1"); - uint16_t ourPort = 30300; - bi::udp::endpoint ourEndpoint(ourIp, ourPort); - for (auto& n: _testNodes) ping(bi::udp::endpoint(ourIp, n.second)); } + void populate(std::vector> const& _testNodes) + { + bi::address ourIp = bi::address::from_string("127.0.0.1"); + for (auto& n: _testNodes) + noteNode(n.first.pub(), bi::udp::endpoint(ourIp, n.second)); + } + void reset() { Guard l(x_state); @@ -77,8 +80,9 @@ struct TestNodeTable: public NodeTable struct TestNodeTableHost: public TestHost { TestNodeTableHost(): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)) {}; + ~TestNodeTableHost() { m_io.stop(); stopWorking(); } - void generateTestNodes(int _count = 10) + void generateTestNodes(int _count = 30) { asserts(_count < 1000); static uint16_t s_basePort = 30500; @@ -96,7 +100,16 @@ struct TestNodeTableHost: public TestHost void setup() { generateTestNodes(); - nodeTable->setup(m_testNodes); + } + + void pingAll() + { + nodeTable->pingAll(m_testNodes); + } + + void populate() + { + nodeTable->populate(m_testNodes); } KeyPair m_alias; @@ -123,7 +136,10 @@ BOOST_AUTO_TEST_CASE(kademlia) node.start(); node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for node.setup(); - sleep(1); + node.pingAll(); + this_thread::sleep_for(chrono::milliseconds(500)); + + cout << "NodeTable:\n" << *node.nodeTable.get() << endl; } BOOST_AUTO_TEST_CASE(test_txrx_one) From ba455270c09cdf4202c8d23bf5d3eedce9a05f45 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 24 Dec 2014 14:48:47 +0100 Subject: [PATCH 17/54] memory --- libp2p/NodeTable.cpp | 44 +++++++++++++++++++++++++++++--------------- libp2p/NodeTable.h | 36 +++++++++++++++++++++--------------- libp2p/UDP.h | 1 - test/net.cpp | 43 ++++++++++++++++++++++++++++++++++++------- 4 files changed, 86 insertions(+), 38 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 3799a4eef..b5b261321 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -35,8 +35,8 @@ NodeTable::NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _listenPort): { for (unsigned i = 0; i < s_bins; i++) m_state[i].distance = i, m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); - doRefreshBuckets(boost::system::error_code()); m_socketPtr->connect(); + doRefreshBuckets(boost::system::error_code()); } NodeTable::~NodeTable() @@ -87,7 +87,15 @@ void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptrisOpen() || _round == s_maxSteps) return; - + + if (_round == s_maxSteps) + { + clog(NodeTableWarn) << "Terminating doFindNode after " << _round << " rounds."; + return; + } + else + _tried.reset(new std::set>()); + auto nearest = findNearest(_node); std::list> tried; for (unsigned i = 0; i < nearest.size() && tried.size() < s_alpha; i++) @@ -137,7 +145,7 @@ std::vector> NodeTable::findNearest(Addres while (head != tail && head < s_bins && count < s_bucketSize) { Guard l(x_state); - for (auto& n: m_state[head].nodes) + for (auto n: m_state[head].nodes) if (auto p = n.lock()) { if (count < s_bucketSize) @@ -147,7 +155,7 @@ std::vector> NodeTable::findNearest(Addres } if (count < s_bucketSize && tail) - for (auto& n: m_state[tail].nodes) + for (auto n: m_state[tail].nodes) if (auto p = n.lock()) { if (count < s_bucketSize) @@ -164,7 +172,7 @@ std::vector> NodeTable::findNearest(Addres while (head < s_bins && count < s_bucketSize) { Guard l(x_state); - for (auto& n: m_state[head].nodes) + for (auto n: m_state[head].nodes) if (auto p = n.lock()) { if (count < s_bucketSize) @@ -178,7 +186,7 @@ std::vector> NodeTable::findNearest(Addres while (tail > 0 && count < s_bucketSize) { Guard l(x_state); - for (auto& n: m_state[tail].nodes) + for (auto n: m_state[tail].nodes) if (auto p = n.lock()) { if (count < s_bucketSize) @@ -191,7 +199,7 @@ std::vector> NodeTable::findNearest(Addres std::vector> ret; for (auto& nodes: found) - for (auto& n: nodes.second) + for (auto n: nodes.second) ret.push_back(n); return std::move(ret); } @@ -223,9 +231,14 @@ void NodeTable::evict(std::shared_ptr _leastSeen, std::shared_ptr node; { Guard l(x_nodes); @@ -325,6 +338,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes case 2: if (rlp[0].isList()) { + // todo: chunk neighbors packet clog(NodeTableMessageSummary) << "Received Neighbors from " << _from.address().to_string() << ":" << _from.port(); Neighbors in = Neighbors::fromBytesConstRef(_from, rlpBytes); for (auto n: in.nodes) @@ -371,7 +385,7 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) return; auto self(shared_from_this()); - m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(s_evictionCheckInterval)); + m_evictionCheckTimer.expires_from_now(c_evictionCheckInterval); m_evictionCheckTimer.async_wait([this, self](boost::system::error_code const& _ec) { if (_ec) @@ -380,17 +394,17 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) bool evictionsRemain = false; std::list> drop; { - Guard l(x_evictions); + Guard le(x_evictions); + Guard ln(x_nodes); for (auto& e: m_evictions) if (chrono::steady_clock::now() - e.first.second > c_reqTimeout) - { - Guard l(x_nodes); - drop.push_back(m_nodes[e.second]); - } + if (auto n = m_nodes[e.second]) + drop.push_back(n); evictionsRemain = m_evictions.size() - drop.size() > 0; } - for (auto& n: drop) + drop.unique(); + for (auto n: drop) dropNode(n); if (evictionsRemain) diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 89456b4fd..fe925fea8 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -120,9 +120,9 @@ public: /// Intervals - static constexpr unsigned s_evictionCheckInterval = 75; ///< Interval at which eviction timeouts are checked. - std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). - std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] + boost::posix_time::milliseconds const c_evictionCheckInterval = boost::posix_time::milliseconds(75); ///< Interval at which eviction timeouts are checked. + std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). + std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } @@ -151,7 +151,7 @@ protected: void evict(std::shared_ptr _leastSeen, std::shared_ptr _new); - void noteNode(Public _pubk, bi::udp::endpoint _endpoint); + void noteNode(Public const& _pubk, bi::udp::endpoint const& _endpoint); void noteNode(std::shared_ptr _n); @@ -234,11 +234,11 @@ struct PingNode: RLPXDatagram PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_ep), ipAddress(_src), port(_srcPort), expiration(futureFromEpoch(_expiration)) {} std::string ipAddress; // rlp encoded bytes min: 16 - uint16_t port; // rlp encoded bytes min: 1 max: 3 - uint64_t expiration; // rlp encoded bytes min: 1 max: 9 + unsigned port; // rlp encoded bytes min: 1 max: 3 + unsigned expiration; // rlp encoded bytes min: 1 max: 9 void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = (std::string)r[0]; port = (uint16_t)r[1]; expiration = (uint64_t)r[2]; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = r[0].toString(); port = r[1].toInt(); expiration = r[2].toInt(); } }; /** @@ -280,10 +280,10 @@ struct FindNode: RLPXDatagram FindNode(bi::udp::endpoint _ep, Address _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_ep), target(_target), expiration(futureFromEpoch(_expiration)) {} h160 target; - uint64_t expiration; + unsigned expiration; void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = (h160)r[0]; expiration = (uint64_t)r[1]; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash(); expiration = r[1].toInt(); } }; /** @@ -299,12 +299,16 @@ struct Neighbors: RLPXDatagram struct Node { Node() = default; - Node(bytesConstRef _bytes) { interpretRLP(_bytes); } + Node(RLP const& _r) { interpretRLP(_r); } std::string ipAddress; - uint16_t port; + unsigned port; NodeId node; - void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << node; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = r[0].toString(); port = (uint16_t)r[1]; node = (h512)r[2]; } + 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(); node = h512(_r[2].toBytes()); + } }; using RLPXDatagram::RLPXDatagram; @@ -323,8 +327,10 @@ struct Neighbors: RLPXDatagram std::list nodes; h256 nonce; - void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << nonce; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n.toBytesConstRef())); nonce = (h256)r[1]; } + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << 1; } + void interpretRLP(bytesConstRef _bytes) { + RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); nonce = (h256)r[1]; + } }; struct NodeTableWarn: public LogChannel { static const char* name() { return "!P!"; } static const int verbosity = 0; }; diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 498d37f0c..82b89b9c8 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -221,7 +221,6 @@ void UDPSocket::doWrite() if (sendQ.empty()) return; } - clog(NoteChannel) << "sent datagram"; doWrite(); }); } diff --git a/test/net.cpp b/test/net.cpp index 274f729c6..d5113b8b4 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -57,14 +57,23 @@ struct TestNodeTable: public NodeTable { bi::address ourIp = bi::address::from_string("127.0.0.1"); for (auto& n: _testNodes) + { ping(bi::udp::endpoint(ourIp, n.second)); + this_thread::sleep_for(chrono::milliseconds(5)); + } } - void populate(std::vector> const& _testNodes) + void populate(std::vector> const& _testNodes, size_t _count = 0) { + if (!_count) + _count = _testNodes.size(); + bi::address ourIp = bi::address::from_string("127.0.0.1"); for (auto& n: _testNodes) - noteNode(n.first.pub(), bi::udp::endpoint(ourIp, n.second)); + if (_count--) + noteNode(n.first.pub(), bi::udp::endpoint(ourIp, n.second)); + else + break; } void reset() @@ -82,7 +91,7 @@ struct TestNodeTableHost: public TestHost TestNodeTableHost(): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)) {}; ~TestNodeTableHost() { m_io.stop(); stopWorking(); } - void generateTestNodes(int _count = 30) + void generateTestNodes(int _count = 16) { asserts(_count < 1000); static uint16_t s_basePort = 30500; @@ -105,11 +114,13 @@ struct TestNodeTableHost: public TestHost void pingAll() { nodeTable->pingAll(m_testNodes); +// for (auto& n: testNodes) +// n->pingAll(m_testNodes); } - void populate() + void populate(size_t _count = 0) { - nodeTable->populate(m_testNodes); + nodeTable->populate(m_testNodes, _count); } KeyPair m_alias; @@ -130,6 +141,13 @@ public: bool success = false; }; +BOOST_AUTO_TEST_CASE(test_findnode_neighbors) +{ + // Executing findNode should result in a list which is serialized + // into Neighbors packet. Neighbors packet should then be deserialized + // into the same list of nearest nodes. +} + BOOST_AUTO_TEST_CASE(kademlia) { TestNodeTableHost node; @@ -137,12 +155,23 @@ BOOST_AUTO_TEST_CASE(kademlia) node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for node.setup(); node.pingAll(); + clog << "NodeTable:\n" << *node.nodeTable.get() << endl; + this_thread::sleep_for(chrono::milliseconds(10000)); + + node.nodeTable->reset(); + clog << "NodeTable:\n" << *node.nodeTable.get() << endl; + + node.populate(2); + clog << "NodeTable:\n" << *node.nodeTable.get() << endl; this_thread::sleep_for(chrono::milliseconds(500)); - cout << "NodeTable:\n" << *node.nodeTable.get() << endl; +// node.nodeTable->join(); +// this_thread::sleep_for(chrono::milliseconds(2000)); +// +// clog << "NodeTable:\n" << *node.nodeTable.get() << endl; } -BOOST_AUTO_TEST_CASE(test_txrx_one) +BOOST_AUTO_TEST_CASE(test_udp_once) { UDPDatagram d(bi::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300), bytes({65,65,65,65})); TestUDPSocket a; a.m_socket->connect(); a.start(); From bf05c50c7855b25b73733b1abeb89892d7eb4509 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 26 Dec 2014 00:56:50 +0100 Subject: [PATCH 18/54] test encoding/decoding neighbors. add MAC. --- libp2p/NodeTable.cpp | 21 ++++++--- libp2p/NodeTable.h | 24 +++++----- libp2p/UDP.cpp | 33 +++++++++----- test/net.cpp | 101 ++++++++++++++++++++++++++----------------- 4 files changed, 110 insertions(+), 69 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index b5b261321..8024f0151 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -235,7 +235,7 @@ void NodeTable::noteNode(Public const& _pubk, bi::udp::endpoint const& _endpoint { Address id = right160(sha3(_pubk)); - // Don't add ourself (would result in -1 bucket lookup) + // Don't add ourself if (id == m_node.address()) return; @@ -303,18 +303,27 @@ NodeTable::NodeBucket& NodeTable::bucket(NodeEntry const* _n) void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { - if (_packet.size() < 69) + // h256 + Signature + RLP + if (_packet.size() < 100) { - clog(NodeTableMessageSummary) << "Invalid Message received from " << _from.address().to_string() << ":" << _from.port(); + clog(NodeTableMessageSummary) << "Invalid Message size received from " << _from.address().to_string() << ":" << _from.port(); + return; + } + + bytesConstRef signedBytes(_packet.cropped(h256::size, _packet.size() - h256::size)); + h256 hashSigned(sha3(signedBytes)); + if (!_packet.cropped(0, h256::size).contentsEqual(hashSigned.asBytes())) + { + clog(NodeTableMessageSummary) << "Invalid Message hash received from " << _from.address().to_string() << ":" << _from.port(); return; } // 3 items is PingNode, 2 items w/no lists is FindNode, 2 items w/first item as list is Neighbors, 1 item is Pong - bytesConstRef rlpBytes(_packet.cropped(65, _packet.size() - 65)); + bytesConstRef rlpBytes(signedBytes.cropped(Signature::size, signedBytes.size() - Signature::size)); RLP rlp(rlpBytes); unsigned itemCount = rlp.itemCount(); - bytesConstRef sigBytes(_packet.cropped(0, 65)); + bytesConstRef sigBytes(_packet.cropped(h256::size, Signature::size)); Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(rlpBytes))); if (!nodeid) { @@ -339,8 +348,8 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes if (rlp[0].isList()) { // todo: chunk neighbors packet - clog(NodeTableMessageSummary) << "Received Neighbors from " << _from.address().to_string() << ":" << _from.port(); Neighbors in = Neighbors::fromBytesConstRef(_from, rlpBytes); + clog(NodeTableMessageSummary) << "Received " << in.nodes.size() << " Neighbors from " << _from.address().to_string() << ":" << _from.port(); for (auto n: in.nodes) noteNode(n.node, bi::udp::endpoint(bi::address::from_string(n.ipAddress), n.port)); } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index fe925fea8..399b48eaa 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -104,6 +104,8 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this }; public: + NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _port = 30300); + ~NodeTable(); /// Constants for Kademlia, mostly derived from address space. @@ -116,7 +118,6 @@ public: static constexpr unsigned s_bucketSize = 16; ///< Denoted by k in [Kademlia]. Number of nodes stored in each bucket. static constexpr unsigned s_alpha = 3; ///< Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests. - static constexpr uint16_t s_defaultPort = 30300; ///< Default port to listen on. /// Intervals @@ -125,9 +126,6 @@ public: std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } - - NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _port = s_defaultPort); - ~NodeTable(); void join(); @@ -233,9 +231,9 @@ struct PingNode: RLPXDatagram using RLPXDatagram::RLPXDatagram; PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_ep), ipAddress(_src), port(_srcPort), expiration(futureFromEpoch(_expiration)) {} - std::string ipAddress; // rlp encoded bytes min: 16 - unsigned port; // rlp encoded bytes min: 1 max: 3 - unsigned expiration; // rlp encoded bytes min: 1 max: 9 + std::string ipAddress; + unsigned port; + unsigned expiration; void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = r[0].toString(); port = r[1].toInt(); expiration = r[2].toInt(); } @@ -248,6 +246,7 @@ struct PingNode: RLPXDatagram * Minimum Encoded Size: 33 bytes * Maximum Encoded Size: 33 bytes * + * @todo expiration * @todo value of replyTo * @todo create from PingNode (reqs RLPXDatagram verify flag) */ @@ -256,6 +255,7 @@ struct Pong: RLPXDatagram using RLPXDatagram::RLPXDatagram; h256 replyTo; // hash of rlp of PingNode + unsigned expiration; void streamRLP(RLPStream& _s) const { _s.appendList(1); _s << replyTo; } void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); replyTo = (h256)r[0]; } @@ -292,7 +292,7 @@ struct FindNode: RLPXDatagram * RLP Encoded Items: 2 (first item is list) * Minimum Encoded Size: 10 bytes * - * @todo nonce + * @todo nonce: Should be replaced with expiration. */ struct Neighbors: RLPXDatagram { @@ -312,7 +312,7 @@ struct Neighbors: RLPXDatagram }; using RLPXDatagram::RLPXDatagram; - Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest): RLPXDatagram(_to), nonce(h256()) + Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest): RLPXDatagram(_to) { for (auto& n: _nearest) { @@ -325,11 +325,11 @@ struct Neighbors: RLPXDatagram } std::list nodes; - h256 nonce; + unsigned expiration = 1; - void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << 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)); nonce = (h256)r[1]; + RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); expiration = r[1].toInt(); } }; diff --git a/libp2p/UDP.cpp b/libp2p/UDP.cpp index 42f5e4cab..8b60196e9 100644 --- a/libp2p/UDP.cpp +++ b/libp2p/UDP.cpp @@ -23,23 +23,32 @@ using namespace dev; using namespace dev::p2p; -//template h256 RLPXDatagramFace::sign(Secret const& _k) { - RLPStream packet; - streamRLP(packet); - bytes b(packet.out()); - h256 h(dev::sha3(b)); - Signature sig = dev::sign(_k, h); - data.resize(b.size() + Signature::size); - sig.ref().copyTo(&data); - memcpy(data.data() + sizeof(Signature), b.data(), b.size()); - return std::move(h); + RLPStream rlpstream; + streamRLP(rlpstream); + bytes rlpBytes(rlpstream.out()); + + bytesConstRef rlp(&rlpBytes); + h256 hash(dev::sha3(rlp)); + Signature sig = dev::sign(_k, hash); + + data.resize(h256::size + Signature::size + rlp.size()); + bytesConstRef packetHash(&data[0], h256::size); + bytesConstRef signedPayload(&data[h256::size], Signature::size + rlp.size()); + bytesConstRef payloadSig(&data[h256::size], Signature::size); + bytesConstRef payload(&data[h256::size+Signature::size], rlp.size()); + + sig.ref().copyTo(payloadSig); + rlp.copyTo(payload); + dev::sha3(signedPayload).ref().copyTo(packetHash); + + return std::move(hash); }; -//template Public RLPXDatagramFace::authenticate(bytesConstRef _sig, bytesConstRef _rlp) { Signature const& sig = *(Signature const*)_sig.data(); return std::move(dev::recover(sig, sha3(_rlp))); -}; \ No newline at end of file +}; + diff --git a/test/net.cpp b/test/net.cpp index d5113b8b4..4c45475f3 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -53,7 +53,23 @@ struct TestNodeTable: public NodeTable /// Constructor using NodeTable::NodeTable; - void pingAll(std::vector> const& _testNodes) + static std::vector> createTestNodes(int _count = 16) + { + std::vector> ret; + asserts(_count < 1000); + static uint16_t s_basePort = 30500; + + ret.clear(); + for (auto i = 0; i < _count; i++) + { + KeyPair k = KeyPair::create(); + ret.push_back(make_pair(k,s_basePort+i)); + } + + return std::move(ret); + } + + void pingTestNodes(std::vector> const& _testNodes) { bi::address ourIp = bi::address::from_string("127.0.0.1"); for (auto& n: _testNodes) @@ -63,7 +79,7 @@ struct TestNodeTable: public NodeTable } } - void populate(std::vector> const& _testNodes, size_t _count = 0) + void populateTestNodes(std::vector> const& _testNodes, size_t _count = 0) { if (!_count) _count = _testNodes.size(); @@ -88,44 +104,19 @@ struct TestNodeTable: public NodeTable */ struct TestNodeTableHost: public TestHost { - TestNodeTableHost(): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)) {}; + TestNodeTableHost(): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)), testNodes(TestNodeTable::createTestNodes()) {}; ~TestNodeTableHost() { m_io.stop(); stopWorking(); } - - void generateTestNodes(int _count = 16) - { - asserts(_count < 1000); - static uint16_t s_basePort = 30500; - - m_testNodes.clear(); - for (auto i = 0; i < _count; i++) - { - KeyPair k = KeyPair::create(); - m_testNodes.push_back(make_pair(k,s_basePort+i)); - testNodes.push_back(make_shared(m_io,k,s_basePort+i)); - } - } - std::vector> m_testNodes; // keypair and port - void setup() - { - generateTestNodes(); - } + void setup() { for (auto n: testNodes) nodeTables.push_back(make_shared(m_io,n.first,n.second)); } - void pingAll() - { - nodeTable->pingAll(m_testNodes); -// for (auto& n: testNodes) -// n->pingAll(m_testNodes); - } + void pingAll() { for (auto& t: nodeTables) t->pingTestNodes(testNodes); } - void populate(size_t _count = 0) - { - nodeTable->populate(m_testNodes, _count); - } + void populate(size_t _count = 0) { nodeTable->populateTestNodes(testNodes, _count); } KeyPair m_alias; shared_ptr nodeTable; - std::vector> testNodes; + std::vector> testNodes; // keypair and port + std::vector> nodeTables; }; class TestUDPSocket: UDPSocketEvents, public TestHost @@ -141,6 +132,36 @@ public: bool success = false; }; +BOOST_AUTO_TEST_CASE(test_neighbors_packet) +{ + KeyPair k = KeyPair::create(); + std::vector> testNodes(TestNodeTable::createTestNodes()); + bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000); + + Neighbors out(to); + for (auto n: testNodes) + { + Neighbors::Node node; + node.ipAddress = boost::asio::ip::address::from_string("127.0.0.1").to_string(); + node.port = n.second; + node.node = n.first.pub(); + out.nodes.push_back(node); + } + out.sign(k.sec()); + + bytesConstRef packet(out.data.data(), out.data.size()); + bytesConstRef rlpBytes(packet.cropped(97, packet.size() - 97)); + Neighbors in = Neighbors::fromBytesConstRef(to, rlpBytes); + int count = 0; + for (auto n: in.nodes) + { + BOOST_REQUIRE_EQUAL(testNodes[count].second, n.port); + BOOST_REQUIRE_EQUAL(testNodes[count].first.pub(), n.node); + BOOST_REQUIRE_EQUAL(sha3(testNodes[count].first.pub()), sha3(n.node)); + count++; + } +} + BOOST_AUTO_TEST_CASE(test_findnode_neighbors) { // Executing findNode should result in a list which is serialized @@ -154,21 +175,23 @@ BOOST_AUTO_TEST_CASE(kademlia) node.start(); node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for node.setup(); + node.populate(); + clog << "NodeTable:\n" << *node.nodeTable.get() << endl; + node.pingAll(); + this_thread::sleep_for(chrono::milliseconds(4000)); clog << "NodeTable:\n" << *node.nodeTable.get() << endl; - this_thread::sleep_for(chrono::milliseconds(10000)); node.nodeTable->reset(); clog << "NodeTable:\n" << *node.nodeTable.get() << endl; node.populate(2); - clog << "NodeTable:\n" << *node.nodeTable.get() << endl; this_thread::sleep_for(chrono::milliseconds(500)); + clog << "NodeTable:\n" << *node.nodeTable.get() << endl; -// node.nodeTable->join(); -// this_thread::sleep_for(chrono::milliseconds(2000)); -// -// clog << "NodeTable:\n" << *node.nodeTable.get() << endl; + node.nodeTable->join(); + this_thread::sleep_for(chrono::milliseconds(2000)); + clog << "NodeTable:\n" << *node.nodeTable.get() << endl; } BOOST_AUTO_TEST_CASE(test_udp_once) From d5f0679fb378f37e517c82dcf40977253eb94edf Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 26 Dec 2014 02:35:31 +0100 Subject: [PATCH 19/54] send multiple neighbors packets when size is over datagram size limit. --- libp2p/NodeTable.cpp | 22 ++++++++++++---------- libp2p/NodeTable.h | 33 ++++++++++++++++++++++----------- libp2p/UDP.h | 2 +- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 8024f0151..64aabc2bb 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -306,7 +306,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes // h256 + Signature + RLP if (_packet.size() < 100) { - clog(NodeTableMessageSummary) << "Invalid Message size received from " << _from.address().to_string() << ":" << _from.port(); + clog(NodeTableMessageSummary) << "Invalid Message size from " << _from.address().to_string() << ":" << _from.port(); return; } @@ -314,11 +314,10 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes h256 hashSigned(sha3(signedBytes)); if (!_packet.cropped(0, h256::size).contentsEqual(hashSigned.asBytes())) { - clog(NodeTableMessageSummary) << "Invalid Message hash received from " << _from.address().to_string() << ":" << _from.port(); + clog(NodeTableMessageSummary) << "Invalid Message hash from " << _from.address().to_string() << ":" << _from.port(); return; } - - // 3 items is PingNode, 2 items w/no lists is FindNode, 2 items w/first item as list is Neighbors, 1 item is Pong + bytesConstRef rlpBytes(signedBytes.cropped(Signature::size, signedBytes.size() - Signature::size)); RLP rlp(rlpBytes); unsigned itemCount = rlp.itemCount(); @@ -327,7 +326,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(rlpBytes))); if (!nodeid) { - clog(NodeTableMessageSummary) << "Invalid Message Signature from " << _from.address().to_string() << ":" << _from.port(); + clog(NodeTableMessageSummary) << "Invalid Message signature from " << _from.address().to_string() << ":" << _from.port(); return; } noteNode(nodeid, _from); @@ -347,7 +346,6 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes case 2: if (rlp[0].isList()) { - // todo: chunk neighbors packet Neighbors in = Neighbors::fromBytesConstRef(_from, rlpBytes); clog(NodeTableMessageSummary) << "Received " << in.nodes.size() << " Neighbors from " << _from.address().to_string() << ":" << _from.port(); for (auto n: in.nodes) @@ -359,9 +357,13 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes FindNode in = FindNode::fromBytesConstRef(_from, rlpBytes); std::vector> nearest = findNearest(in.target); - Neighbors out(_from, nearest); - out.sign(m_secret); - m_socketPtr->send(out); + static unsigned const nlimit = (m_socketPtr->maxDatagramSize - 11) / 86; + for (unsigned offset = 0; offset < nearest.size(); offset += nlimit) + { + Neighbors out(_from, nearest, offset, nlimit); + out.sign(m_secret); + m_socketPtr->send(out); + } } break; @@ -384,7 +386,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes } catch (...) { - + // likely culprit is invalid rlp encoding } } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 399b48eaa..d8e886c31 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -109,15 +110,15 @@ public: /// Constants for Kademlia, mostly derived from address space. - static constexpr unsigned s_addressByteSize = sizeof(NodeEntry::id); ///< Size of address type in bytes. - static constexpr unsigned s_bits = 8 * s_addressByteSize; ///< Denoted by n in [Kademlia]. - static constexpr unsigned s_bins = s_bits - 1; ///< Size of m_state (excludes root, which is us). - static constexpr unsigned s_maxSteps = boost::static_log2::value; ///< Max iterations of discovery. (doFindNode) + static unsigned const s_addressByteSize = sizeof(NodeEntry::id); ///< Size of address type in bytes. + static unsigned const s_bits = 8 * s_addressByteSize; ///< Denoted by n in [Kademlia]. + static unsigned const s_bins = s_bits - 1; ///< Size of m_state (excludes root, which is us). + static unsigned const s_maxSteps = boost::static_log2::value; ///< Max iterations of discovery. (doFindNode) /// Chosen constants - static constexpr unsigned s_bucketSize = 16; ///< Denoted by k in [Kademlia]. Number of nodes stored in each bucket. - static constexpr unsigned s_alpha = 3; ///< Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests. + static unsigned const s_bucketSize = 16; ///< Denoted by k in [Kademlia]. Number of nodes stored in each bucket. + static unsigned const s_alpha = 3; ///< Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests. /// Intervals @@ -312,16 +313,26 @@ struct Neighbors: RLPXDatagram }; using RLPXDatagram::RLPXDatagram; - Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest): RLPXDatagram(_to) + Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to) { - for (auto& n: _nearest) + auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size(); + for (auto i = _offset; i < limit; i++) { Node node; - node.ipAddress = n->endpoint.udp.address().to_string(); - node.port = n->endpoint.udp.port(); - node.node = n->publicKey(); + node.ipAddress = _nearest[i]->endpoint.udp.address().to_string(); + node.port = _nearest[i]->endpoint.udp.port(); + node.node = _nearest[i]->publicKey(); nodes.push_back(node); } + +// for (auto& n: _nearest) +// { +// Node node; +// node.ipAddress = n->endpoint.udp.address().to_string(); // 16 +// node.port = n->endpoint.udp.port(); // 3 +// node.node = n->publicKey();// 67 +// nodes.push_back(node); +// } } std::list nodes; diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 82b89b9c8..6efe1908d 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -110,7 +110,7 @@ template class UDPSocket: UDPSocketFace, public std::enable_shared_from_this> { public: - static constexpr unsigned maxDatagramSize = MaxDatagramSize; + enum { maxDatagramSize = MaxDatagramSize }; static_assert(maxDatagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes"); UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, unsigned _port): m_host(_host), m_endpoint(bi::udp::v4(), _port), m_socket(_io) { m_started.store(false); m_closed.store(true); }; From cffc27417a37fc2d737f69b19ea2895ca5a2a2c9 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 26 Dec 2014 05:38:27 +0100 Subject: [PATCH 20/54] inline << operator, mute logging --- libp2p/NodeTable.cpp | 16 +++++++++++----- libp2p/NodeTable.h | 4 ++-- test/net.cpp | 14 +++++++------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 64aabc2bb..4cae6af0b 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -247,12 +247,18 @@ void NodeTable::noteNode(Public const& _pubk, bi::udp::endpoint const& _endpoint { node.reset(new NodeEntry(m_node, id, _pubk, _endpoint)); m_nodes[id] = node; +// clog(NodeTableMessageSummary) << "Adding node to cache: " << id; } else + { node = n->second; +// clog(NodeTableMessageSummary) << "Found node in cache: " << id; + } } - noteNode(node); + // todo: why is this necessary? + if (!!node) + noteNode(node); } void NodeTable::noteNode(std::shared_ptr _n) @@ -335,7 +341,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes switch (itemCount) { case 1: { - clog(NodeTableMessageSummary) << "Received Pong from " << _from.address().to_string() << ":" << _from.port(); +// clog(NodeTableMessageSummary) << "Received Pong from " << _from.address().to_string() << ":" << _from.port(); Pong in = Pong::fromBytesConstRef(_from, rlpBytes); // whenever a pong is received, first check if it's in m_evictions @@ -347,13 +353,13 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes if (rlp[0].isList()) { Neighbors in = Neighbors::fromBytesConstRef(_from, rlpBytes); - clog(NodeTableMessageSummary) << "Received " << in.nodes.size() << " Neighbors from " << _from.address().to_string() << ":" << _from.port(); +// clog(NodeTableMessageSummary) << "Received " << in.nodes.size() << " Neighbors from " << _from.address().to_string() << ":" << _from.port(); for (auto n: in.nodes) noteNode(n.node, bi::udp::endpoint(bi::address::from_string(n.ipAddress), n.port)); } else { - clog(NodeTableMessageSummary) << "Received FindNode from " << _from.address().to_string() << ":" << _from.port(); +// clog(NodeTableMessageSummary) << "Received FindNode from " << _from.address().to_string() << ":" << _from.port(); FindNode in = FindNode::fromBytesConstRef(_from, rlpBytes); std::vector> nearest = findNearest(in.target); @@ -369,7 +375,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes case 3: { - clog(NodeTableMessageSummary) << "Received PingNode from " << _from.address().to_string() << ":" << _from.port(); +// clog(NodeTableMessageSummary) << "Received PingNode from " << _from.address().to_string() << ":" << _from.port(); PingNode in = PingNode::fromBytesConstRef(_from, rlpBytes); Pong p(_from); diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index d8e886c31..32a700d73 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -197,7 +197,7 @@ protected: boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. }; -std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) +inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) { _out << _nodeTable.root().address() << "\t" << "0\t" << _nodeTable.root().endpoint.udp.address() << ":" << _nodeTable.root().endpoint.udp.port() << std::endl; auto s = _nodeTable.state(); @@ -205,7 +205,7 @@ std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) _out << n.address() << "\t" << n.distance << "\t" << n.endpoint.udp.address() << ":" << n.endpoint.udp.port() << std::endl; return _out; } - + /** * Ping packet: Check if node is alive. * PingNode is cached and regenerated after expiration - t, where t is timeout. diff --git a/test/net.cpp b/test/net.cpp index 4c45475f3..d9b5d1579 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -53,14 +53,14 @@ struct TestNodeTable: public NodeTable /// Constructor using NodeTable::NodeTable; - static std::vector> createTestNodes(int _count = 16) + static std::vector> createTestNodes(unsigned _count) { std::vector> ret; asserts(_count < 1000); static uint16_t s_basePort = 30500; ret.clear(); - for (auto i = 0; i < _count; i++) + for (unsigned i = 0; i < _count; i++) { KeyPair k = KeyPair::create(); ret.push_back(make_pair(k,s_basePort+i)); @@ -75,7 +75,7 @@ struct TestNodeTable: public NodeTable for (auto& n: _testNodes) { ping(bi::udp::endpoint(ourIp, n.second)); - this_thread::sleep_for(chrono::milliseconds(5)); + this_thread::sleep_for(chrono::milliseconds(2)); } } @@ -104,7 +104,7 @@ struct TestNodeTable: public NodeTable */ struct TestNodeTableHost: public TestHost { - TestNodeTableHost(): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)), testNodes(TestNodeTable::createTestNodes()) {}; + TestNodeTableHost(unsigned _count = 8): m_alias(KeyPair::create()), nodeTable(new TestNodeTable(m_io, m_alias)), testNodes(TestNodeTable::createTestNodes(_count)) {}; ~TestNodeTableHost() { m_io.stop(); stopWorking(); } void setup() { for (auto n: testNodes) nodeTables.push_back(make_shared(m_io,n.first,n.second)); } @@ -135,7 +135,7 @@ public: BOOST_AUTO_TEST_CASE(test_neighbors_packet) { KeyPair k = KeyPair::create(); - std::vector> testNodes(TestNodeTable::createTestNodes()); + std::vector> testNodes(TestNodeTable::createTestNodes(16)); bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000); Neighbors out(to); @@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE(test_findnode_neighbors) BOOST_AUTO_TEST_CASE(kademlia) { - TestNodeTableHost node; + TestNodeTableHost node(12); node.start(); node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for node.setup(); @@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(kademlia) clog << "NodeTable:\n" << *node.nodeTable.get() << endl; node.pingAll(); - this_thread::sleep_for(chrono::milliseconds(4000)); + this_thread::sleep_for(chrono::milliseconds(1000)); clog << "NodeTable:\n" << *node.nodeTable.get() << endl; node.nodeTable->reset(); From cf4c63a066b3c2a44e1d9c8594223d6361f7f5a4 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 26 Dec 2014 06:56:05 +0100 Subject: [PATCH 21/54] try to fix windows build --- libp2p/NodeTable.h | 18 +++++++++++------- libp2p/UDP.h | 1 + 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 32a700d73..8457555d3 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -38,11 +38,16 @@ namespace p2p * Thread-safety is ensured by modifying NodeEntry details via * shared_ptr replacement instead of mutating values. * - * [Interface] + * [Integration] + * @todo deadline-timer which maintains tcp/peer connections * @todo restore nodes: affects refreshbuckets * @todo TCP endpoints * @todo makeRequired: don't try to evict node if node isRequired. * @todo makeRequired: exclude bucket from refresh if we have node as peer. + * + * [Optimization] + * @todo Pong to include ip:port where ping was received + * @todo expiration and sha3(id) 'to' for messages which are replies (prevents replay) * @todo std::shared_ptr m_cachedPingPacket; * @todo std::shared_ptr m_cachedFindSelfPacket; * @@ -57,7 +62,6 @@ namespace p2p * @todo ^ s_bitsPerStep = 5; // Denoted by b in [Kademlia]. Bits by which address space is divided. * @todo optimize (use tree for state and/or custom compare for cache) * @todo reputation (aka universal siblings lists) - * @todo dht (aka siblings) */ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this { @@ -110,7 +114,7 @@ public: /// Constants for Kademlia, mostly derived from address space. - static unsigned const s_addressByteSize = sizeof(NodeEntry::id); ///< Size of address type in bytes. + static unsigned const s_addressByteSize = sizeof(Node::id); ///< Size of address type in bytes. static unsigned const s_bits = 8 * s_addressByteSize; ///< Denoted by n in [Kademlia]. static unsigned const s_bins = s_bits - 1; ///< Size of m_state (excludes root, which is us). static unsigned const s_maxSteps = boost::static_log2::value; ///< Max iterations of discovery. (doFindNode) @@ -229,7 +233,7 @@ inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) */ struct PingNode: RLPXDatagram { - using RLPXDatagram::RLPXDatagram; + using RLPXDatagram::RLPXDatagram; PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_ep), ipAddress(_src), port(_srcPort), expiration(futureFromEpoch(_expiration)) {} std::string ipAddress; @@ -253,7 +257,7 @@ struct PingNode: RLPXDatagram */ struct Pong: RLPXDatagram { - using RLPXDatagram::RLPXDatagram; + using RLPXDatagram::RLPXDatagram; h256 replyTo; // hash of rlp of PingNode unsigned expiration; @@ -277,7 +281,7 @@ struct Pong: RLPXDatagram */ struct FindNode: RLPXDatagram { - using RLPXDatagram::RLPXDatagram; + using RLPXDatagram::RLPXDatagram; FindNode(bi::udp::endpoint _ep, Address _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_ep), target(_target), expiration(futureFromEpoch(_expiration)) {} h160 target; @@ -312,7 +316,7 @@ struct Neighbors: RLPXDatagram } }; - using RLPXDatagram::RLPXDatagram; + using RLPXDatagram::RLPXDatagram; Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to) { auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size(); diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 6efe1908d..cc8e58b9f 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include From a54e2cee9af9753a8c8a5d54496e1e68266d1c0b Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 28 Dec 2014 13:10:59 +0100 Subject: [PATCH 22/54] code review updates --- libp2p/NodeTable.cpp | 23 ++++++++++++----------- libp2p/NodeTable.h | 10 +++++----- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 4cae6af0b..b586f377a 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -90,10 +90,11 @@ void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptr>()); auto nearest = findNearest(_node); @@ -110,7 +111,7 @@ void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptr> NodeTable::findNearest(Addres { // send s_alpha FindNode packets to nodes we know, closest to target static unsigned lastBin = s_bins - 1; - unsigned head = dist(m_node.id, _target); + unsigned head = hammingDist(m_node.id, _target); unsigned tail = head == 0 ? lastBin : (head - 1) % s_bins; std::map>> found; @@ -149,7 +150,7 @@ std::vector> NodeTable::findNearest(Addres if (auto p = n.lock()) { if (count < s_bucketSize) - found[dist(_target, p->id)].push_back(p); + found[hammingDist(_target, p->id)].push_back(p); else break; } @@ -159,7 +160,7 @@ std::vector> NodeTable::findNearest(Addres if (auto p = n.lock()) { if (count < s_bucketSize) - found[dist(_target, p->id)].push_back(p); + found[hammingDist(_target, p->id)].push_back(p); else break; } @@ -176,7 +177,7 @@ std::vector> NodeTable::findNearest(Addres if (auto p = n.lock()) { if (count < s_bucketSize) - found[dist(_target, p->id)].push_back(p); + found[hammingDist(_target, p->id)].push_back(p); else break; } @@ -190,7 +191,7 @@ std::vector> NodeTable::findNearest(Addres if (auto p = n.lock()) { if (count < s_bucketSize) - found[dist(_target, p->id)].push_back(p); + found[hammingDist(_target, p->id)].push_back(p); else break; } @@ -309,8 +310,8 @@ NodeTable::NodeBucket& NodeTable::bucket(NodeEntry const* _n) void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { - // h256 + Signature + RLP - if (_packet.size() < 100) + // h256 + Signature + RLP (smallest possible packet is empty neighbors packet which is 3 bytes) + if (_packet.size() < h256::size + Signature::size + 3) { clog(NodeTableMessageSummary) << "Invalid Message size from " << _from.address().to_string() << ":" << _from.port(); return; @@ -392,7 +393,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes } catch (...) { - // likely culprit is invalid rlp encoding + clog(NodeTableWarn) << "Exception processing message from " << _from.address().to_string() << ":" << _from.port(); } } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 8457555d3..132a0f979 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -95,8 +95,8 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this */ struct NodeEntry: public Node { - NodeEntry(Node _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): Node(_id, _pubk, _gw), distance(dist(_src.id,_id)) {} - NodeEntry(Node _src, Address _id, Public _pubk, bi::udp::endpoint _udp): Node(_id, _pubk, NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_id)) {} + NodeEntry(Node _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): Node(_id, _pubk, _gw), distance(hammingDist(_src.id,_id)) {} + NodeEntry(Node _src, Address _id, Public _pubk, bi::udp::endpoint _udp): Node(_id, _pubk, NodeDefaultEndpoint(_udp)), distance(hammingDist(_src.id,_id)) {} const unsigned distance; ///< Node's distance from _src (see constructor). }; @@ -130,7 +130,7 @@ public: std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] - static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } + static unsigned hammingDist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } void join(); @@ -142,7 +142,7 @@ public: protected: - /// Repeatedly sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to . + /// Repeatedly sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to s_maxSteps rounds. void doFindNode(Address _node, unsigned _round = 0, std::shared_ptr>> _tried = std::shared_ptr>>()); /// Returns nodes nearest to target. @@ -179,7 +179,7 @@ private: #else protected: #endif - /// Sends s_alpha concurrent FindNeighbor requests to nodes closest to target until + /// Sends FindNeighbor packet. See doFindNode. void requestNeighbors(NodeEntry const& _node, Address _target) const; Node m_node; ///< This node. From f37ff5ff4fc111851bb2f7d73a212f577f592644 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 28 Dec 2014 13:35:58 +0100 Subject: [PATCH 23/54] Clarified binary operator checking for integer types. --- libsolidity/Types.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 664b56ff6..c87ce2e60 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -201,17 +201,17 @@ TypePointer IntegerType::binaryOperatorResultImpl(Token::Value _operator, TypePo if (!commonType) return TypePointer(); + // All integer types can be compared + if (Token::isCompareOp(_operator)) + return commonType; + + // Nothing else can be done with addresses, but hashes can receive bit operators if (commonType->isAddress()) - { - if (!Token::isCompareOp(_operator)) - return TypePointer(); - } - else if (commonType->isHash()) - { - if (!(Token::isCompareOp(_operator) || Token::isBitOp(_operator))) - return TypePointer(); - } - return commonType; + return TypePointer(); + else if (commonType->isHash() && !Token::isBitOp(_operator)) + return TypePointer(); + else + return commonType; } const MemberList IntegerType::AddressMemberList = From c9982a4cb61c5fe98525400308c9f137bc272534 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 28 Dec 2014 14:24:21 +0100 Subject: [PATCH 24/54] manually populate nodes for test rather than leveraging ping --- test/net.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/net.cpp b/test/net.cpp index d9b5d1579..8a2e2af78 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -111,6 +111,8 @@ struct TestNodeTableHost: public TestHost void pingAll() { for (auto& t: nodeTables) t->pingTestNodes(testNodes); } + void populateAll(size_t _count = 0) { for (auto& t: nodeTables) t->populateTestNodes(testNodes, _count); } + void populate(size_t _count = 0) { nodeTable->populateTestNodes(testNodes, _count); } KeyPair m_alias; @@ -171,22 +173,21 @@ BOOST_AUTO_TEST_CASE(test_findnode_neighbors) BOOST_AUTO_TEST_CASE(kademlia) { - TestNodeTableHost node(12); + // Not yet a 'real' test. + TestNodeTableHost node(8); node.start(); node.nodeTable->join(); // ideally, joining with empty node table logs warning we can check for node.setup(); node.populate(); clog << "NodeTable:\n" << *node.nodeTable.get() << endl; - node.pingAll(); - this_thread::sleep_for(chrono::milliseconds(1000)); + node.populateAll(); clog << "NodeTable:\n" << *node.nodeTable.get() << endl; node.nodeTable->reset(); clog << "NodeTable:\n" << *node.nodeTable.get() << endl; - node.populate(2); - this_thread::sleep_for(chrono::milliseconds(500)); + node.populate(1); clog << "NodeTable:\n" << *node.nodeTable.get() << endl; node.nodeTable->join(); From 70b4389b096730b63901d33ced7e91f6077197f8 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 28 Dec 2014 18:35:08 +0100 Subject: [PATCH 25/54] distance isn't hamming --- libp2p/NodeTable.cpp | 10 +++++----- libp2p/NodeTable.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index b586f377a..7a98950ea 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -135,7 +135,7 @@ std::vector> NodeTable::findNearest(Addres { // send s_alpha FindNode packets to nodes we know, closest to target static unsigned lastBin = s_bins - 1; - unsigned head = hammingDist(m_node.id, _target); + unsigned head = dist(m_node.id, _target); unsigned tail = head == 0 ? lastBin : (head - 1) % s_bins; std::map>> found; @@ -150,7 +150,7 @@ std::vector> NodeTable::findNearest(Addres if (auto p = n.lock()) { if (count < s_bucketSize) - found[hammingDist(_target, p->id)].push_back(p); + found[dist(_target, p->id)].push_back(p); else break; } @@ -160,7 +160,7 @@ std::vector> NodeTable::findNearest(Addres if (auto p = n.lock()) { if (count < s_bucketSize) - found[hammingDist(_target, p->id)].push_back(p); + found[dist(_target, p->id)].push_back(p); else break; } @@ -177,7 +177,7 @@ std::vector> NodeTable::findNearest(Addres if (auto p = n.lock()) { if (count < s_bucketSize) - found[hammingDist(_target, p->id)].push_back(p); + found[dist(_target, p->id)].push_back(p); else break; } @@ -191,7 +191,7 @@ std::vector> NodeTable::findNearest(Addres if (auto p = n.lock()) { if (count < s_bucketSize) - found[hammingDist(_target, p->id)].push_back(p); + found[dist(_target, p->id)].push_back(p); else break; } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 132a0f979..314775955 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -95,8 +95,8 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this */ struct NodeEntry: public Node { - NodeEntry(Node _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): Node(_id, _pubk, _gw), distance(hammingDist(_src.id,_id)) {} - NodeEntry(Node _src, Address _id, Public _pubk, bi::udp::endpoint _udp): Node(_id, _pubk, NodeDefaultEndpoint(_udp)), distance(hammingDist(_src.id,_id)) {} + NodeEntry(Node _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): Node(_id, _pubk, _gw), distance(dist(_src.id,_id)) {} + NodeEntry(Node _src, Address _id, Public _pubk, bi::udp::endpoint _udp): Node(_id, _pubk, NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_id)) {} const unsigned distance; ///< Node's distance from _src (see constructor). }; @@ -130,7 +130,7 @@ public: std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] - static unsigned hammingDist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } + static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } void join(); From 44f80248ad5d65358963b0c5fc2975e0f6e986c5 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 4 Jan 2015 21:01:56 +0100 Subject: [PATCH 26/54] coding standards, h512 node id, mute warnings for clang builds. attempt inherited constructor fix for windows. --- libdevcore/Common.h | 6 ++++ libp2p/NodeTable.cpp | 34 ++++++++++++----------- libp2p/NodeTable.h | 65 +++++++++++++++++++------------------------- libp2p/UDP.cpp | 2 +- libp2p/UDP.h | 6 ++-- test/boostTest.cpp | 4 +++ test/net.cpp | 6 ++++ 7 files changed, 65 insertions(+), 58 deletions(-) diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 198119f24..aa9a5ae03 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -36,7 +36,12 @@ #include #include #include +#pragma warning(push) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" #include +#pragma warning(pop) +#pragma GCC diagnostic pop #include "vector_ref.h" #include "debugbreak.h" @@ -64,6 +69,7 @@ using u256 = boost::multiprecision::number>; using u160 = boost::multiprecision::number>; using s160 = boost::multiprecision::number>; +using u512 = boost::multiprecision::number>; using u256s = std::vector; using u160s = std::vector; using u256Set = std::set; diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 7a98950ea..20f9f5fdf 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -25,7 +25,7 @@ using namespace dev; using namespace dev::p2p; NodeTable::NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _listenPort): - m_node(Node(_alias.address(), _alias.pub(), bi::udp::endpoint())), + m_node(Node(_alias.pub(), bi::udp::endpoint())), m_secret(_alias.sec()), m_socket(new NodeSocket(_io, *this, _listenPort)), m_socketPtr(m_socket.get()), @@ -34,7 +34,11 @@ NodeTable::NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _listenPort): m_evictionCheckTimer(m_io) { for (unsigned i = 0; i < s_bins; i++) - m_state[i].distance = i, m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); + { + m_state[i].distance = i; + m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1); + } + m_socketPtr->connect(); doRefreshBuckets(boost::system::error_code()); } @@ -51,9 +55,9 @@ void NodeTable::join() doFindNode(m_node.id); } -std::list
NodeTable::nodes() const +std::list NodeTable::nodes() const { - std::list
nodes; + std::list nodes; Guard l(x_nodes); for (auto& i: m_nodes) nodes.push_back(i.second->id); @@ -70,20 +74,20 @@ list NodeTable::state() const return move(ret); } -NodeTable::NodeEntry NodeTable::operator[](Address _id) +NodeTable::NodeEntry NodeTable::operator[](NodeId _id) { Guard l(x_nodes); return *m_nodes[_id]; } -void NodeTable::requestNeighbors(NodeEntry const& _node, Address _target) const +void NodeTable::requestNeighbors(NodeEntry const& _node, NodeId _target) const { FindNode p(_node.endpoint.udp, _target); p.sign(m_secret); m_socketPtr->send(p); } -void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptr>> _tried) +void NodeTable::doFindNode(NodeId _node, unsigned _round, std::shared_ptr>> _tried) { if (!m_socketPtr->isOpen() || _round == s_maxSteps) return; @@ -131,7 +135,7 @@ void NodeTable::doFindNode(Address _node, unsigned _round, std::shared_ptr> NodeTable::findNearest(Address _target) +std::vector> NodeTable::findNearest(NodeId _target) { // send s_alpha FindNode packets to nodes we know, closest to target static unsigned lastBin = s_bins - 1; @@ -234,26 +238,24 @@ void NodeTable::evict(std::shared_ptr _leastSeen, std::shared_ptr node; { Guard l(x_nodes); - auto n = m_nodes.find(id); + auto n = m_nodes.find(_pubk); if (n == m_nodes.end()) { - node.reset(new NodeEntry(m_node, id, _pubk, _endpoint)); - m_nodes[id] = node; -// clog(NodeTableMessageSummary) << "Adding node to cache: " << id; + node.reset(new NodeEntry(m_node, _pubk, _endpoint)); + m_nodes[_pubk] = node; +// clog(NodeTableMessageSummary) << "Adding node to cache: " << _pubk; } else { node = n->second; -// clog(NodeTableMessageSummary) << "Found node in cache: " << id; +// clog(NodeTableMessageSummary) << "Found node in cache: " << _pubk; } } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 314775955..5466a3ae3 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -50,6 +50,7 @@ namespace p2p * @todo expiration and sha3(id) 'to' for messages which are replies (prevents replay) * @todo std::shared_ptr m_cachedPingPacket; * @todo std::shared_ptr m_cachedFindSelfPacket; + * @todo store root node in table? * * [Networking] * @todo TCP endpoints @@ -68,7 +69,7 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this friend struct Neighbors; using NodeSocket = UDPSocket; using TimePoint = std::chrono::steady_clock::time_point; - using EvictionTimeout = std::pair,Address>; + using EvictionTimeout = std::pair,NodeId>; ///< First NodeId may be evicted and replaced with second NodeId. struct NodeDefaultEndpoint { @@ -78,14 +79,13 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this struct Node { - Node(Address _id, Public _pubk, NodeDefaultEndpoint _udp): id(_id), pubk(_pubk), endpoint(_udp) {} - Node(Address _id, Public _pubk, bi::udp::endpoint _udp): Node(_id, _pubk, NodeDefaultEndpoint(_udp)) {} + Node(Public _pubk, NodeDefaultEndpoint _udp): id(_pubk), endpoint(_udp) {} + Node(Public _pubk, bi::udp::endpoint _udp): Node(_pubk, NodeDefaultEndpoint(_udp)) {} - virtual Address const& address() const { return id; } - virtual Public const& publicKey() const { return pubk; } + virtual NodeId const& address() const { return id; } + virtual Public const& publicKey() const { return id; } - Address id; - Public pubk; + NodeId id; NodeDefaultEndpoint endpoint; }; @@ -95,8 +95,8 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this */ struct NodeEntry: public Node { - NodeEntry(Node _src, Address _id, Public _pubk, NodeDefaultEndpoint _gw): Node(_id, _pubk, _gw), distance(dist(_src.id,_id)) {} - NodeEntry(Node _src, Address _id, Public _pubk, bi::udp::endpoint _udp): Node(_id, _pubk, NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_id)) {} + NodeEntry(Node _src, Public _pubk, NodeDefaultEndpoint _gw): Node(_pubk, _gw), distance(dist(_src.id,_pubk)) {} + NodeEntry(Node _src, Public _pubk, bi::udp::endpoint _udp): Node(_pubk, NodeDefaultEndpoint(_udp)), distance(dist(_src.id,_pubk)) {} const unsigned distance; ///< Node's distance from _src (see constructor). }; @@ -114,7 +114,7 @@ public: /// Constants for Kademlia, mostly derived from address space. - static unsigned const s_addressByteSize = sizeof(Node::id); ///< Size of address type in bytes. + static unsigned const s_addressByteSize = sizeof(NodeId); ///< Size of address type in bytes. static unsigned const s_bits = 8 * s_addressByteSize; ///< Denoted by n in [Kademlia]. static unsigned const s_bins = s_bits - 1; ///< Size of m_state (excludes root, which is us). static unsigned const s_maxSteps = boost::static_log2::value; ///< Max iterations of discovery. (doFindNode) @@ -130,23 +130,23 @@ public: std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] - static unsigned dist(Address const& _a, Address const& _b) { u160 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } + static unsigned dist(NodeId const& _a, NodeId const& _b) { u512 d = _a ^ _b; unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; } void join(); - NodeEntry root() const { return NodeEntry(m_node, m_node.address(), m_node.publicKey(), m_node.endpoint.udp); } - std::list
nodes() const; + NodeEntry root() const { return NodeEntry(m_node, m_node.publicKey(), m_node.endpoint.udp); } + std::list nodes() const; std::list state() const; - NodeEntry operator[](Address _id); + NodeEntry operator[](NodeId _id); protected: /// Repeatedly sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to s_maxSteps rounds. - void doFindNode(Address _node, unsigned _round = 0, std::shared_ptr>> _tried = std::shared_ptr>>()); + void doFindNode(NodeId _node, unsigned _round = 0, std::shared_ptr>> _tried = std::shared_ptr>>()); /// Returns nodes nearest to target. - std::vector> findNearest(Address _target); + std::vector> findNearest(NodeId _target); void ping(bi::udp::endpoint _to) const; @@ -180,13 +180,13 @@ private: protected: #endif /// Sends FindNeighbor packet. See doFindNode. - void requestNeighbors(NodeEntry const& _node, Address _target) const; + void requestNeighbors(NodeEntry const& _node, NodeId _target) const; Node m_node; ///< This node. Secret m_secret; ///< This nodes secret key. mutable Mutex x_nodes; ///< Mutable for thread-safe copy in nodes() const. - std::map> m_nodes; ///< Address -> Node table (most common lookup path) + std::map> m_nodes; ///< NodeId -> Node table (most common lookup path) mutable Mutex x_state; std::array m_state; ///< State table of binned nodes. @@ -233,9 +233,9 @@ inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) */ struct PingNode: RLPXDatagram { - using RLPXDatagram::RLPXDatagram; - PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_ep), ipAddress(_src), port(_srcPort), expiration(futureFromEpoch(_expiration)) {} - + PingNode(bi::udp::endpoint _ep): RLPXDatagram(_ep) {} + PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _expiration = std::chrono::seconds(60)): RLPXDatagram(_ep), ipAddress(_src), port(_srcPort), expiration(futureFromEpoch(_expiration)) {} + std::string ipAddress; unsigned port; unsigned expiration; @@ -257,7 +257,7 @@ struct PingNode: RLPXDatagram */ struct Pong: RLPXDatagram { - using RLPXDatagram::RLPXDatagram; + Pong(bi::udp::endpoint _ep): RLPXDatagram(_ep) {} h256 replyTo; // hash of rlp of PingNode unsigned expiration; @@ -275,20 +275,20 @@ struct Pong: RLPXDatagram * Minimum Encoded Size: 21 bytes * Maximum Encoded Size: 30 bytes * - * target: Address of node. The responding node will send back nodes closest to the target. + * 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 { using RLPXDatagram::RLPXDatagram; - FindNode(bi::udp::endpoint _ep, Address _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_ep), target(_target), expiration(futureFromEpoch(_expiration)) {} + FindNode(bi::udp::endpoint _ep, NodeId _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_ep), target(_target), expiration(futureFromEpoch(_expiration)) {} - h160 target; + h512 target; unsigned expiration; - + void streamRLP(RLPStream& _s) const { _s.appendList(2); _s << target << expiration; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash(); expiration = r[1].toInt(); } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); target = r[0].toHash(); expiration = r[1].toInt(); } }; /** @@ -317,7 +317,7 @@ struct Neighbors: RLPXDatagram }; using RLPXDatagram::RLPXDatagram; - Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to) + Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to) { auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size(); for (auto i = _offset; i < limit; i++) @@ -328,15 +328,6 @@ struct Neighbors: RLPXDatagram node.node = _nearest[i]->publicKey(); nodes.push_back(node); } - -// for (auto& n: _nearest) -// { -// Node node; -// node.ipAddress = n->endpoint.udp.address().to_string(); // 16 -// node.port = n->endpoint.udp.port(); // 3 -// node.node = n->publicKey();// 67 -// nodes.push_back(node); -// } } std::list nodes; diff --git a/libp2p/UDP.cpp b/libp2p/UDP.cpp index 8b60196e9..b1f87e409 100644 --- a/libp2p/UDP.cpp +++ b/libp2p/UDP.cpp @@ -37,7 +37,7 @@ h256 RLPXDatagramFace::sign(Secret const& _k) bytesConstRef packetHash(&data[0], h256::size); bytesConstRef signedPayload(&data[h256::size], Signature::size + rlp.size()); bytesConstRef payloadSig(&data[h256::size], Signature::size); - bytesConstRef payload(&data[h256::size+Signature::size], rlp.size()); + bytesConstRef payload(&data[h256::size + Signature::size], rlp.size()); sig.ref().copyTo(payloadSig); rlp.copyTo(payload); diff --git a/libp2p/UDP.h b/libp2p/UDP.h index cc8e58b9f..ac4afb0b1 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -60,10 +60,8 @@ protected: * @todo compact templates * @todo make data private/functional (see UDPDatagram) */ -//template struct RLPXDatagramFace: public UDPDatagram { -// static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } static uint64_t futureFromEpoch(std::chrono::milliseconds _ms) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _ms).time_since_epoch()).count(); } static uint64_t futureFromEpoch(std::chrono::seconds _sec) { return std::chrono::duration_cast((std::chrono::system_clock::now() + _sec).time_since_epoch()).count(); } static Public authenticate(bytesConstRef _sig, bytesConstRef _rlp); @@ -74,12 +72,12 @@ struct RLPXDatagramFace: public UDPDatagram virtual void streamRLP(RLPStream&) const =0; virtual void interpretRLP(bytesConstRef _bytes) =0; }; - + template struct RLPXDatagram: public RLPXDatagramFace { - static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } using RLPXDatagramFace::RLPXDatagramFace; + static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } }; /** diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 7d89f853c..cef3cc0a7 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -21,4 +21,8 @@ */ #define BOOST_TEST_MODULE EthereumTests +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" #include +#pragma warning(pop) +#pragma GCC diagnostic pop diff --git a/test/net.cpp b/test/net.cpp index 8a2e2af78..0a008d6eb 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -171,6 +171,12 @@ BOOST_AUTO_TEST_CASE(test_findnode_neighbors) // into the same list of nearest nodes. } +BOOST_AUTO_TEST_CASE(test_windows_template) +{ + bi::udp::endpoint ep; + PingNode p(ep); +} + BOOST_AUTO_TEST_CASE(kademlia) { // Not yet a 'real' test. From 4ba309d9ac4ac98e075a7dfc9a2609f5278cab4a Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 5 Jan 2015 17:43:06 +0100 Subject: [PATCH 27/54] code review fixes. remove std::, fix improper naming of class members, camelCase ivars for readability. --- libp2p/Host.cpp | 18 +++++++++--------- libp2p/NodeTable.cpp | 38 +++++++++++++++++++------------------- libp2p/UDP.h | 28 ++++++++++++++-------------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 4a99ac90f..d1c3cd19b 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -272,15 +272,15 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) // if user supplied address is a public address then we use it // if user supplied address is private, and localnetworking is enabled, we use it - bi::address reqpublicaddr(bi::address(_publicAddress.empty() ? bi::address() : bi::address::from_string(_publicAddress))); - bi::tcp::endpoint reqpublic(reqpublicaddr, m_listenPort); - bool isprivate = isPrivateAddress(reqpublicaddr); - bool ispublic = !isprivate && !isLocalHostAddress(reqpublicaddr); - if (!reqpublicaddr.is_unspecified() && (ispublic || (isprivate && m_netPrefs.localNetworking))) + bi::address reqPublicAddr(bi::address(_publicAddress.empty() ? bi::address() : bi::address::from_string(_publicAddress))); + bi::tcp::endpoint reqPublic(reqPublicAddr, m_listenPort); + bool isprivate = isPrivateAddress(reqPublicAddr); + bool ispublic = !isprivate && !isLocalHostAddress(reqPublicAddr); + if (!reqPublicAddr.is_unspecified() && (ispublic || (isprivate && m_netPrefs.localNetworking))) { - if (!m_peerAddresses.count(reqpublicaddr)) - m_peerAddresses.insert(reqpublicaddr); - m_tcpPublic = reqpublic; + if (!m_peerAddresses.count(reqPublicAddr)) + m_peerAddresses.insert(reqPublicAddr); + m_tcpPublic = reqPublic; return; } @@ -307,7 +307,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) } // or if no address provided, use private ipv4 address if local networking is enabled - if (reqpublicaddr.is_unspecified()) + if (reqPublicAddr.is_unspecified()) if (m_netPrefs.localNetworking) for (auto addr: m_peerAddresses) if (addr.is_v4() && isPrivateAddress(addr)) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 20f9f5fdf..bcaea2555 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -55,13 +55,13 @@ void NodeTable::join() doFindNode(m_node.id); } -std::list NodeTable::nodes() const +list NodeTable::nodes() const { - std::list nodes; + list nodes; Guard l(x_nodes); for (auto& i: m_nodes) nodes.push_back(i.second->id); - return std::move(nodes); + return move(nodes); } list NodeTable::state() const @@ -87,7 +87,7 @@ void NodeTable::requestNeighbors(NodeEntry const& _node, NodeId _target) const m_socketPtr->send(p); } -void NodeTable::doFindNode(NodeId _node, unsigned _round, std::shared_ptr>> _tried) +void NodeTable::doFindNode(NodeId _node, unsigned _round, shared_ptr>> _tried) { if (!m_socketPtr->isOpen() || _round == s_maxSteps) return; @@ -99,10 +99,10 @@ void NodeTable::doFindNode(NodeId _node, unsigned _round, std::shared_ptr>()); + _tried.reset(new set>()); auto nearest = findNearest(_node); - std::list> tried; + list> tried; for (unsigned i = 0; i < nearest.size() && tried.size() < s_alpha; i++) if (!_tried->count(nearest[i])) { @@ -135,14 +135,14 @@ void NodeTable::doFindNode(NodeId _node, unsigned _round, std::shared_ptr> NodeTable::findNearest(NodeId _target) +vector> NodeTable::findNearest(NodeId _target) { // send s_alpha FindNode packets to nodes we know, closest to target static unsigned lastBin = s_bins - 1; unsigned head = dist(m_node.id, _target); unsigned tail = head == 0 ? lastBin : (head - 1) % s_bins; - std::map>> found; + map>> found; unsigned count = 0; // if d is 0, then we roll look forward, if last, we reverse, else, spread from d @@ -202,11 +202,11 @@ std::vector> NodeTable::findNearest(NodeId tail--; } - std::vector> ret; + vector> ret; for (auto& nodes: found) for (auto n: nodes.second) ret.push_back(n); - return std::move(ret); + return move(ret); } void NodeTable::ping(bi::udp::endpoint _to) const @@ -222,7 +222,7 @@ void NodeTable::ping(NodeEntry* _n) const ping(_n->endpoint.udp); } -void NodeTable::evict(std::shared_ptr _leastSeen, std::shared_ptr _new) +void NodeTable::evict(shared_ptr _leastSeen, shared_ptr _new) { if (!m_socketPtr->isOpen()) return; @@ -242,7 +242,7 @@ void NodeTable::noteNode(Public const& _pubk, bi::udp::endpoint const& _endpoint if (_pubk == m_node.address()) return; - std::shared_ptr node; + shared_ptr node; { Guard l(x_nodes); auto n = m_nodes.find(_pubk); @@ -264,13 +264,13 @@ void NodeTable::noteNode(Public const& _pubk, bi::udp::endpoint const& _endpoint noteNode(node); } -void NodeTable::noteNode(std::shared_ptr _n) +void NodeTable::noteNode(shared_ptr _n) { - std::shared_ptr contested; + shared_ptr contested; { NodeBucket& s = bucket(_n.get()); Guard l(x_state); - s.nodes.remove_if([&_n](std::weak_ptr n) + s.nodes.remove_if([&_n](weak_ptr n) { if (n.lock() == _n) return true; @@ -294,12 +294,12 @@ void NodeTable::noteNode(std::shared_ptr _n) evict(contested, _n); } -void NodeTable::dropNode(std::shared_ptr _n) +void NodeTable::dropNode(shared_ptr _n) { NodeBucket &s = bucket(_n.get()); { Guard l(x_state); - s.nodes.remove_if([&_n](std::weak_ptr n) { return n.lock() == _n; }); + s.nodes.remove_if([&_n](weak_ptr n) { return n.lock() == _n; }); } Guard l(x_nodes); m_nodes.erase(_n->id); @@ -365,7 +365,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes // clog(NodeTableMessageSummary) << "Received FindNode from " << _from.address().to_string() << ":" << _from.port(); FindNode in = FindNode::fromBytesConstRef(_from, rlpBytes); - std::vector> nearest = findNearest(in.target); + vector> nearest = findNearest(in.target); static unsigned const nlimit = (m_socketPtr->maxDatagramSize - 11) / 86; for (unsigned offset = 0; offset < nearest.size(); offset += nlimit) { @@ -412,7 +412,7 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) return; bool evictionsRemain = false; - std::list> drop; + list> drop; { Guard le(x_evictions); Guard ln(x_nodes); diff --git a/libp2p/UDP.h b/libp2p/UDP.h index ac4afb0b1..0c878a3de 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -142,13 +142,13 @@ protected: bi::udp::endpoint m_endpoint; ///< Endpoint which we listen to. Mutex x_sendQ; - std::deque sendQ; ///< Queue for egress data. - std::array recvData; ///< Buffer for ingress data. - bi::udp::endpoint recvEndpoint; ///< Endpoint data was received from. + std::deque m_sendQ; ///< Queue for egress data. + std::array m_recvData; ///< Buffer for ingress data. + bi::udp::endpoint m_recvEndpoint; ///< Endpoint data was received from. bi::udp::socket m_socket; ///< Boost asio udp socket. Mutex x_socketError; ///< Mutex for error which can be set from host or IO thread. - boost::system::error_code socketError; ///< Set when shut down due to error. + boost::system::error_code m_socketError; ///< Set when shut down due to error. }; template @@ -163,7 +163,7 @@ void UDPSocket::connect() // clear write queue so reconnect doesn't send stale messages Guard l(x_sendQ); - sendQ.clear(); + m_sendQ.clear(); m_closed = false; doRead(); @@ -176,8 +176,8 @@ bool UDPSocket::send(UDPDatagram const& _datagram) return false; Guard l(x_sendQ); - sendQ.push_back(_datagram); - if (sendQ.size() == 1) + m_sendQ.push_back(_datagram); + if (m_sendQ.size() == 1) doWrite(); return true; @@ -190,13 +190,13 @@ void UDPSocket::doRead() return; auto self(UDPSocket::shared_from_this()); - m_socket.async_receive_from(boost::asio::buffer(recvData), recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) + m_socket.async_receive_from(boost::asio::buffer(m_recvData), m_recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) { if (_ec) return disconnectWithError(_ec); assert(_len); - m_host.onReceived(this, recvEndpoint, bytesConstRef(recvData.data(), _len)); + m_host.onReceived(this, m_recvEndpoint, bytesConstRef(m_recvData.data(), _len)); doRead(); }); } @@ -207,7 +207,7 @@ void UDPSocket::doWrite() if (m_closed) return; - const UDPDatagram& datagram = sendQ[0]; + const UDPDatagram& datagram = m_sendQ[0]; auto self(UDPSocket::shared_from_this()); m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.endpoint(), [this, self](boost::system::error_code _ec, std::size_t) { @@ -216,8 +216,8 @@ void UDPSocket::doWrite() else { Guard l(x_sendQ); - sendQ.pop_front(); - if (sendQ.empty()) + m_sendQ.pop_front(); + if (m_sendQ.empty()) return; } doWrite(); @@ -235,9 +235,9 @@ void UDPSocket::disconnectWithError(boost::system::erro { // disconnect-operation following prior non-zero errors are ignored Guard l(x_socketError); - if (socketError != boost::system::error_code()) + if (m_socketError != boost::system::error_code()) return; - socketError = _ec; + m_socketError = _ec; } // TODO: (if non-zero error) schedule high-priority writes From e9f4a7a43927dc3adc88307293ee31da8f6d69d3 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 5 Jan 2015 22:01:23 +0100 Subject: [PATCH 28/54] code review --- libp2p/NodeTable.cpp | 13 +++++++------ libp2p/NodeTable.h | 26 ++++++++++---------------- libp2p/UDP.h | 11 +++++------ test/net.cpp | 12 ++++++------ 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index bcaea2555..f6ab0f949 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -80,7 +80,7 @@ NodeTable::NodeEntry NodeTable::operator[](NodeId _id) return *m_nodes[_id]; } -void NodeTable::requestNeighbors(NodeEntry const& _node, NodeId _target) const +void NodeTable::requestNeighbours(NodeEntry const& _node, NodeId _target) const { FindNode p(_node.endpoint.udp, _target); p.sign(m_secret); @@ -312,7 +312,7 @@ NodeTable::NodeBucket& NodeTable::bucket(NodeEntry const* _n) void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) { - // h256 + Signature + RLP (smallest possible packet is empty neighbors packet which is 3 bytes) + // h256 + Signature + RLP (smallest possible packet is empty neighbours packet which is 3 bytes) if (_packet.size() < h256::size + Signature::size + 3) { clog(NodeTableMessageSummary) << "Invalid Message size from " << _from.address().to_string() << ":" << _from.port(); @@ -341,7 +341,8 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes noteNode(nodeid, _from); try { - switch (itemCount) { + switch (itemCount) + { case 1: { // clog(NodeTableMessageSummary) << "Received Pong from " << _from.address().to_string() << ":" << _from.port(); @@ -355,8 +356,8 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes case 2: if (rlp[0].isList()) { - Neighbors in = Neighbors::fromBytesConstRef(_from, rlpBytes); -// clog(NodeTableMessageSummary) << "Received " << in.nodes.size() << " Neighbors from " << _from.address().to_string() << ":" << _from.port(); + Neighbours in = Neighbours::fromBytesConstRef(_from, rlpBytes); +// clog(NodeTableMessageSummary) << "Received " << in.nodes.size() << " Neighbours from " << _from.address().to_string() << ":" << _from.port(); for (auto n: in.nodes) noteNode(n.node, bi::udp::endpoint(bi::address::from_string(n.ipAddress), n.port)); } @@ -369,7 +370,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes static unsigned const nlimit = (m_socketPtr->maxDatagramSize - 11) / 86; for (unsigned offset = 0; offset < nearest.size(); offset += nlimit) { - Neighbors out(_from, nearest, offset, nlimit); + Neighbours out(_from, nearest, offset, nlimit); out.sign(m_secret); m_socketPtr->send(out); } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 5466a3ae3..8f15cb958 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -49,7 +49,7 @@ namespace p2p * @todo Pong to include ip:port where ping was received * @todo expiration and sha3(id) 'to' for messages which are replies (prevents replay) * @todo std::shared_ptr m_cachedPingPacket; - * @todo std::shared_ptr m_cachedFindSelfPacket; + * @todo std::shared_ptr m_cachedFindSelfPacket; * @todo store root node in table? * * [Networking] @@ -66,7 +66,7 @@ namespace p2p */ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this { - friend struct Neighbors; + friend struct Neighbours; using NodeSocket = UDPSocket; using TimePoint = std::chrono::steady_clock::time_point; using EvictionTimeout = std::pair,NodeId>; ///< First NodeId may be evicted and replaced with second NodeId. @@ -180,7 +180,7 @@ private: protected: #endif /// Sends FindNeighbor packet. See doFindNode. - void requestNeighbors(NodeEntry const& _node, NodeId _target) const; + void requestNeighbours(NodeEntry const& _node, NodeId _target) const; Node m_node; ///< This node. Secret m_secret; ///< This nodes secret key. @@ -269,7 +269,7 @@ struct Pong: RLPXDatagram /** * FindNode Packet: Request k-nodes, closest to the target. * FindNode is cached and regenerated after expiration - t, where t is timeout. - * FindNode implicitly results in finding neighbors of a given node. + * FindNode implicitly results in finding neighbours of a given node. * * RLP Encoded Items: 2 * Minimum Encoded Size: 21 bytes @@ -299,7 +299,7 @@ struct FindNode: RLPXDatagram * * @todo nonce: Should be replaced with expiration. */ -struct Neighbors: RLPXDatagram +struct Neighbours: RLPXDatagram { struct Node { @@ -308,16 +308,12 @@ struct Neighbors: RLPXDatagram std::string ipAddress; unsigned port; 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(); node = h512(_r[2].toBytes()); - } + 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(); node = h512(_r[2].toBytes()); } }; - using RLPXDatagram::RLPXDatagram; - Neighbors(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to) + using RLPXDatagram::RLPXDatagram; + Neighbours(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to) { auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size(); for (auto i = _offset; i < limit; i++) @@ -334,9 +330,7 @@ struct Neighbors: RLPXDatagram unsigned expiration = 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(); - } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); expiration = r[1].toInt(); } }; struct NodeTableWarn: public LogChannel { static const char* name() { return "!P!"; } static const int verbosity = 0; }; diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 0c878a3de..96e7fd99e 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -128,7 +128,6 @@ public: void disconnect() { disconnectWithError(boost::asio::error::connection_reset); } protected: - void doRead(); void doWrite(); @@ -152,7 +151,7 @@ protected: }; template -void UDPSocket::connect() +void UDPSocket::connect() { bool expect = false; if (!m_started.compare_exchange_strong(expect, true)) @@ -170,7 +169,7 @@ void UDPSocket::connect() } template -bool UDPSocket::send(UDPDatagram const& _datagram) +bool UDPSocket::send(UDPDatagram const& _datagram) { if (m_closed) return false; @@ -184,7 +183,7 @@ bool UDPSocket::send(UDPDatagram const& _datagram) } template -void UDPSocket::doRead() +void UDPSocket::doRead() { if (m_closed) return; @@ -202,7 +201,7 @@ void UDPSocket::doRead() } template -void UDPSocket::doWrite() +void UDPSocket::doWrite() { if (m_closed) return; @@ -225,7 +224,7 @@ void UDPSocket::doWrite() } template -void UDPSocket::disconnectWithError(boost::system::error_code _ec) +void UDPSocket::disconnectWithError(boost::system::error_code _ec) { // If !started and already stopped, shutdown has already occured. (EOF or Operation canceled) if (!m_started && m_closed && !m_socket.is_open() /* todo: veirfy this logic*/) diff --git a/test/net.cpp b/test/net.cpp index 0a008d6eb..6952c282a 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -134,16 +134,16 @@ public: bool success = false; }; -BOOST_AUTO_TEST_CASE(test_neighbors_packet) +BOOST_AUTO_TEST_CASE(test_neighbours_packet) { KeyPair k = KeyPair::create(); std::vector> testNodes(TestNodeTable::createTestNodes(16)); bi::udp::endpoint to(boost::asio::ip::address::from_string("127.0.0.1"), 30000); - Neighbors out(to); + Neighbours out(to); for (auto n: testNodes) { - Neighbors::Node node; + Neighbours::Node node; node.ipAddress = boost::asio::ip::address::from_string("127.0.0.1").to_string(); node.port = n.second; node.node = n.first.pub(); @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(test_neighbors_packet) bytesConstRef packet(out.data.data(), out.data.size()); bytesConstRef rlpBytes(packet.cropped(97, packet.size() - 97)); - Neighbors in = Neighbors::fromBytesConstRef(to, rlpBytes); + Neighbours in = Neighbours::fromBytesConstRef(to, rlpBytes); int count = 0; for (auto n: in.nodes) { @@ -164,10 +164,10 @@ BOOST_AUTO_TEST_CASE(test_neighbors_packet) } } -BOOST_AUTO_TEST_CASE(test_findnode_neighbors) +BOOST_AUTO_TEST_CASE(test_findnode_neighbours) { // Executing findNode should result in a list which is serialized - // into Neighbors packet. Neighbors packet should then be deserialized + // into Neighbours packet. Neighbours packet should then be deserialized // into the same list of nearest nodes. } From 3d449785ded5c2c50d5678a189ab8fa88ed05c0e Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 6 Jan 2015 11:30:52 +0100 Subject: [PATCH 29/54] Blockhash correction and some tests --- libevm/ExtVMFace.h | 2 +- libevm/VM.h | 2 +- test/vmBlockInfoTestFiller.json | 90 +++++++++++++++++++++++++++++++-- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index a4ec80ed8..bf1b180e9 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -148,7 +148,7 @@ public: virtual void revert() {} /// Hash of a block if within the last 256 blocks, or h256() otherwise. - h256 prevhash(u256 _number) { return _number < currentBlock.number && _number > (std::max(257, currentBlock.number) - 257) ? lastHashes[(unsigned)(currentBlock.number - 1 - _number)] : h256(); } // TODO: CHECK!!! + h256 blockhash(u256 _number) { return _number < currentBlock.number && _number >= (std::max(256, currentBlock.number) - 256) ? lastHashes[(unsigned)(currentBlock.number - 1 - _number)] : h256(); } // TODO: CHECK!!! /// Get the code at the given location in code ROM. byte getCode(u256 _n) const { return _n < code.size() ? code[(size_t)_n] : 0; } diff --git a/libevm/VM.h b/libevm/VM.h index eab248b44..cc9556c26 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -559,7 +559,7 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st m_stack.push_back(_ext.gasPrice); break; case Instruction::BLOCKHASH: - m_stack.back() = (u256)_ext.prevhash(m_stack.back()); + m_stack.back() = (u256)_ext.blockhash(m_stack.back()); break; case Instruction::COINBASE: m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress); diff --git a/test/vmBlockInfoTestFiller.json b/test/vmBlockInfoTestFiller.json index 1ebedc12b..15c270173 100644 --- a/test/vmBlockInfoTestFiller.json +++ b/test/vmBlockInfoTestFiller.json @@ -1,8 +1,92 @@ { - "prevhash": { + "blockhashNotExistingBlock": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", - "currentNumber" : "0", + "currentNumber" : "1", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BLOCKHASH 2) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "blockhashMyBlock": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "1", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BLOCKHASH 1) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "blockhash258Block": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "258", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BLOCKHASH 1) }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "blockhash257Block": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "257", "currentGasLimit" : "1000000", "currentDifficulty" : "256", "currentTimestamp" : 1, @@ -12,7 +96,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ [[ 0 ]] (PREVHASH) }", + "code" : "{ [[ 0 ]] (BLOCKHASH 0) }", "storage": {} } }, From 00416dd1180b9516ae72a53a6475d6b56a1056b5 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 6 Jan 2015 13:48:23 +0100 Subject: [PATCH 30/54] removed evmjit submodule --- evmjit | 1 - 1 file changed, 1 deletion(-) delete mode 160000 evmjit diff --git a/evmjit b/evmjit deleted file mode 160000 index 3df5a125f..000000000 --- a/evmjit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3df5a125fa0baa579528abce80402118cad803fd From 49653da6078481d542ff6899080c818d844c68bb Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 6 Jan 2015 13:53:09 +0100 Subject: [PATCH 31/54] Squashed 'evmjit/' content from commit 334deeb git-subtree-dir: evmjit git-subtree-split: 334deeb4c6ca93a26f58979b609f6386042128ff --- .gitignore | 28 ++++++++++++++++++++++++++++ LICENSE | 22 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b8bd0267b --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d7e0e57ac --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + From 29c9178ff40adc093ef84f81fe3d995144f87627 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 6 Jan 2015 13:59:00 +0100 Subject: [PATCH 32/54] Squashed 'evmjit/' changes from 334deeb..533531b 533531b Create helper/external functions on demand (env_balance for now) e12fa12 Suicide rework ae6e709 Disalbe bswap constant folding ec30ce4 Object cache 5be8c59 Byte swap for constants 3df5a12 Do not compile LLVM module if machine code available in memory 2b31cba Memory manager cleanup 6a22491 Clean up ExecutionEngine 28a0623 Clean up ExecutionEngine de02425 Clean up ExecutionEngine ece7fe7 Clean up ExecutionEngine fe90c6f Clean up ExecutionEngine 9d9e73a Using one ExecutionEngine to cache compiled contracts 713b33e Remove global private memory pointers f8ffdfa Remove global privite pointers to runtime instance and runtime data 6643af5 Use code hash as main function name e3245e1 Remove addtional cost param from commitCostBlock(). Count additional cost manually. [#81461534] 90cb4a7 More aggresive gas counting optimization: do not commit cost before LOG. [#81461534] 5d5259e Do not auto-commit cost block c5de54d More aggresive gas counting optimization: do not commit cost before SSTORE. c5ebca6 More aggresive gas counting optimization: do not commit cost before memory access. 69930b9 Use sub "no-wrap" 6acbfe0 Remove unused function eaed9c3 Read push data using llvm::APInt 30f0a7a Type usage and other cleanups 4254b3f Fix memory copy [Delivers #84703344] 43e08ea Handle create/call depth limit in CREATE instruction 20190b1 Add -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS needed by LLVM (reverted from commit 66d5a2b5cdf1361dcf0205b191dd12be090ed224) 75fa672 Fix compilation with LLVM pre-3.5 (Ubuntu 14.04) (reverted from commit 6094aa30add22d56bee145bbb4a7d440c08db5c8) 6094aa3 Fix compilation with LLVM pre-3.5 (Ubuntu 14.04) 66d5a2b Add -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS needed by LLVM 1b49024 Workaround for Ubuntu broken LLVM package (llvm-3.5-dev does not have correct cmake files) ed63ced Disable rtti for Cache as LLVM has not rtti 232f9fe Refactor cmake files 8287c60 Own cached objects 6543cc4 Execution Engine cleanups (reverted from commit bd85efa299e661228a1ac6283b5fad14e09f88d9) 98b265b Merge commit '2332595c9c0d315dc9ecde22d236ae477b931863' into develop 2b83b75 Object cache wit memory leaks de22a0a Execution Engine cleanups 2332595 Change the name of a module to some hash (for caching) bd85efa Execution Engine cleanups b37ce8e Gas counting for CREATE instruction 0a64245 Fix checking memory requirements with size 0 a492760 Using llvm::ExecutionEngine::getFunctionAddress() instead of getPointerToFunction(). Cleanups. dd63bb3 Remove TODO comment 8f3edbc Disable cache. It's broken. 49bd78b SHA3 gas cost updated 76d30b8 Count additional gas cost for memory copies. Some GasMeter improvments. 077cf7b Count additional cost for EXP exponent c2699b3 Comment about MCJIT and caching pointers to entry functions 91cd37e Remove logs 47d92e9 Remove dummy int argument in entry function 2e1c90f Change the way entry function is called. 4a9d08d Change the way entry function is called. 70279f8 Changes in setjmps dbf8174 Update usage of ExtVMFace dc4bc0e Jit can handle call stack up to 1024 45fb3f5 Move some data from stack to JitVM object to save stack space. 6e318f8 Check current call depth 70348d9 Fix cache key and do not compile to LLVM module when it is not needed da02a18 Cache key is contract code. Does not work 7c9cf6e Trying to cache compiled contracts with pointer to code as a key (it's not a good idea) 2a7111c ExecBundle - all information needed to execute a JIT-compiled contracts b3a1734 Disable JIT logs b1b94de Opcodes update c9f5694 CMakeLists.txt cleanup (in progress) 63d6997 added missing b07f25d Detect endianness without boost help b176574 Remove usage of boost::lexical_cast 5f4c1c7 Fix evmcc compilation - provide fake runtime data e3ccc03 Fix MSVC warning 9452da9 Merge remote-tracking branch 'origin/develop' into develop 2fd2446 Simplify ExecutionEngine interface. It is possible to pass raw code instead of LLVM module. 46d1926 fixed gcc warnings (missing field initializers, unused functions) bd40486 Moving ext_calldataload helper function to evmjit library (does not depend on Env) 2b9b530 Cleanups c6fcdbc Arith performace tests adb5a68 A bit of work in evmcc. Not ready yet. b987258 A TODO comment 5362d50 SDIV & SMOD fixed 58d2bfb External code access in Env reimplemented 4366542 LOGs reimplemented 63719d2 env_call updated 69dae9a env_create updated 6f84f3d Rename sload, sstore & sha3 Env functions 408fda8 Old code removed 116ce60 Remove Env* param from env_sha3 function (not needed) 4fc4e76 Change the way memory is passed to sha3 Env function cc6bb83 Simplify JIT logs 259d1d2 Old files removed ddfe85b Update VM gas counter after execution 2b4430e Change Env functions first argument type to opaque Env* 969f1b5 The way runtime data is passed to jit'ed contract changed b8e0ad1 Fix exporting Env callback symbols 7db676c Client side Env updated a0d0f85 JIT VM updated 3298e10 Remove mock declaration 49b82cd Move EXP implementation from Ext to Arith256 8672c4b Updating Ext interface bb6e603 Updating ExecutionEngine interface 0509b3b Changing Runtime interface aa77158 Separate runtime classes 68648a2 Separating EVM JIT from CPP client (WIP) 99c7113 CMake scripts 5f61bdc Starting cmake scripts dc82664 C Interface draft 1 94e41d8 correct calculation of LOG cost fdd5275 fixed bug in implementation of LOG 30eea6e Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc f72cced resolved circular dependency between libevm and libevmjit c2c96e6 Merge remote-tracking branch 'origin/develop-evmcc' into develop-evmcc bf6a4d4 output gas updated in vmPerformanceTest.json d77ee40 EVM code updated in vmPerformanceTests 3517bfc fixed libevmjit dependencies in cmake files f7d6554 Move mock of ExtVMFace to evmcc tool 89e6d2f made _ext a mandatory arg of Runtime::run() aa7fc79 libevmface -> libevmcore: updated cmake files 320bdf5 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 86334f5 Handle bytecode as bytes 2bd4d6c Update Instruction.h location c907c71 codegen for LOG instructions [Delivers #81700490] cdcbb6b removed references to Memory::dump() 47e654c Merge branch 'develop' into develop-evmcc 7305fb3 fixed bug in llvm ir computing required memory size 4b4265c Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc ae9f57f (1) Handling evmcc options with boost::program_options. (2) Writing out .ll and .bc files cb87052 unused var removed 9a0f9d3 1) JUMP/I semantics updated. 2) Members of BasicBlock::LocalStack pulled out to BasicBlock c91c5df Rename BNOT -> NOT, NOT -> ISZERO 29432be Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc ee0e2d3 added struct for compiler options 172fc1c Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 8f7b5d6 removed *.orig file 6f99869 code cleanup and coding-standardization 3fc508f Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc a44a7ab Use llvm.longjmp intrinsic for longjmp [Delivers #81792986] b6b4f5f Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 8ee32d9 Coding style: use "o_" prefix for output function arguments 40561b1 code cleanup & coding-standardization aaaf543 Rename Type::i256 -> Type::Word b5abb70 Use common builder in GasMeter and Memory 33cc50d Empty lines removal 72398d2 Handle unsigned integer overflow in Memory::require() [Delivers #81773288] 350b004 Change Constant::get to support negative values 272c568 Remove Memory::require(size) interface [#81773288] 273b0f6 Deprecate Memory::require(size) function. Risk of unsigned integer overflow. 439561a Use readPushData() in basic block analysis bfb9660 Use readPushData() in instruction compilation 1008c70 Create dedicated function for pushdata reading 4ff7ba0 Fix u256 to APInt conversion 65af01c removed accidentally added *.orig file d32daf6 New performance tests [Delivers #81578852] 895d0aa Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 7760b31 Cleaning up warnings and build dependencies [#81588646] 2d6aa46 CMakeLists updated, should now build without LLVM when EVMJIT is not enabled [#81588646] 43093d6 Fix MSIZE and memory resize step [Delivers #81777708] 85f67a5 Improve PUSH compilation 2bbbf6c Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 3725432 Fix case where JUMPI is the last instruction 2524c72 Codegen for SIGNEXTEND [Delivers #81700414] 007641a SIGEXTEND: first try [#81700414] d778640 Fix BNOT instruction [Delivers #81700198] dd75da2 BNOT instruction [Delivers #81700198] e4a77c1 Increase refund counter if deleting a storage item [Delivers #81575908] 22e4d16 Remove old code af0530b Got rid of some gcc warnings c6cf723 Fix u256 to APInt conversion e6b4761 Allow creating LLVM constants directly from u256 33f1253 Update gas counting for SSTORE, no refunding yet [#81575908] e24c9c2 Improve Compiler code formatting f06445f Improve VM code formatting c03d36a Improve Ext code formatting bc1ef19 Improve GasMeter code formatting 3212b2b Improve ExecutionEngine code formatting de67937 Improve BasicBlock code formatting 64513d5 Improve Memory code formatting 3ee33cc Improve code formatting 1e8a091 Improve Arith256 code formatting 211d3c0 Change #include setjmp fa6d4c6 Improve Stack code formatting 7e3a9f4 Remove done FIXME tasks ce889fc Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 4c9fed9 turned on stack optimization by default in jit compiler 664de37 json test file for performance testing 89d4a1c Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 887bac9 Use clog for JIT logs 6e8d1ce added inlcude of setjmp.h required for jmp_buf type ac478d2 added dependency on libethereum to evmcc a3871e5 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc cecf567 MULMOD/ADDMOD implemented in separate functions [FIXES #80566276] 65eaa13 Remove global Runtime pointer 25ccd49 Changed semantics of JUMPDEST so that *the next* instruction is a jump destination 6bf994d 1. Indenting spaces converted to tabs 2. Options changed: -G --> -g 4684c6f Using call helper 2493ef5 Call helper 6da6f3d Handle bad instructions (BadInstruction exception) [#81563132] ac38bf9 Add raiseException helper to RuntimeManager [#81563132] 31c9dd3 Ignore opOp callback silently 5a923d6 Ignore opOp callback silently c7ba567 Merge branch 'develop' into develop-evmcc 0da6823 Fix EXTCODECOPY 0eb8311 Restore correct memory access in Ext functions 05bbe53 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc d5ddbfa Added missing changes [#79450108] 6a16efa Get Runtime pointer from main function argument if available e273299 Change some runtime names fd7b6da Move return data reference to Runtime [#81470252] 6d428d8 Fix ReturnData::Index::_size 64e3748 Move jmpbuf to Runtime [#81470252] 68ca696 256-bit arithmetic implemented via calls to boost::multiprecision library. [#79450108] 4bcd092 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc a90ebe6 Print compilation/execution times 734dd31 Using RuntimeData pointer in ext functions [#81470252] a076ced Old bswap intrinsic removed 3cba473 Moving CODE data from Ext to Runtime [#81470252] 83b24b6 Moving CALLDATA data from Ext to Runtime [#81470252] 916f5ab Group instructions that access runtime data [#81470252] 088a4ef Moving the rest word-size data from Ext to Runtime [#81470252] bfe1216 Moving ORIGIN, CALLER & CALLVALUE data from Ext to Runtime [#81470252] 5c1e344 Add name to data indices [#81470252] 399cf28 Moving ADDRESS data from Ext to Runtime [#81470252] 2b898fc Introducing RuntimeHelper - a compiler helper that depends on runtime data [#81470252] 9ca2663 Use Type::RuntimePtr instead of RuntimeData::getType->getPointerTo() 3670e32 Access stack structure through runtime structure [#81470252] 693c4b3 Add Type::RuntimePtr predefined LLVM type c388a81 Access memory structure through runtime structure [#81470252] b579c70 Place current gas counter value in RuntimeData 097b7ae Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 8da55ff Export runtime data to global variable in main function a60843e Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 547ca38 Propagation of values between basic blocks' local stacks [#80895676] 6c2a120 Introducing RuntimeData struct - a data that will be provided to running program 6580a9b Visual Studio build fix 0dae894 new test cases d281396 Refactored local stack [#81180320] d41828f added option to set initial gas fc7a46b Change the way VMs are created (mostly for tracking where are created) dcb739e Better assert condition 16de530 Try not to use JIT in any interactive mode 48108f5 Implement VMFace with jit::VM 701d99e Fixed the order in which phi nodes are created (was incorrect) [#80895676] aaf0f7d Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 044e838 Fixed bug in phi node rewriting [#80895676] 170ca15 Null gas in case of out-of-gas exception [Delivers #81118624] 54c636a Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc f28f8cc Add support for Big Endian architectures [Delivers #79877740] 0757523 Use Endianness in Ext [#79877740] 920ea2e Comment: storage uses native endianness [#79877740] 05741c2 Handle endianness for CALLDATALOAD correctly [#79877740] 5a8ba36 added assert in linkBasicBlocks() 8909c15 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 080cf20 Handling pop() from the empty EVM stack. [#80895676] 49b0769 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc d95083a Remove unreachable basic blocks before "linking" a6ce4ba Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 1cf3549 minor changes in the compiler driver a4416e5 Remove unreachable basic blocks before "linking" c4eb835 added missing CMakeLists.txt 937fbaa Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 0a9e0f5 Propagation of values between basic blocks (and the stack): initial implementation (probably buggy, but simple cases work). [#80895676] 8ba533f Merge branch 'develop' into develop-evmcc c7eac0d Fix SHA3 instruction :) 5db2038 Handle endianness of MSTORE & MLOAD [#79877740] 51fc9ad Fix compiling empty bytecode c97ca24 Throw exception if EVM program is not jitable instead of terminating to make tests going 150162e Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc f83e23a Another round of fixing ExtVM interface 54e0824 Updated cmake files after moving the jit compiler to a lib. 58c5a4f Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 607458b BYTE reimplementation afe0254 Endianness handler b13f1ac Check if pushed item is a word b01a75a Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 52ba848 Add support for direct jump outside the code - terminates with STOP 6c3af96 Limit debug output de8deab Fix for invalid jump table basic block 0febd6a Do not try to add additional cost (call instruction) to cost-block f31f3bc Using gas provided by test engine and fix for creating fake ExtVMFace. dfac5a0 Using ExtVM provided by test engine f0928f5 Tests moved to test/ folder dd6fbda Fix not allocated arg8 for CALL 15714ce Fix GasMeter not nulling cost call 0f4c8eb VM execution wrapper with similar interface as libevm/VM 9105fb1 Merge branch 'develop' into develop-evmcc 7df24fa Move JIT compiler project to library 34afb85 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc f062fd0 Dumping CFG to .dot: showing indirect jumps with dashed lines dcafa47 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 72a6fe4 InsertPointGuard definition moved to CompilerHelper.h 8eea475 Get IR module from IR builder on demand f233122 Added dumping of CFG to a .dot file [Delivers #80816506] c83739e Get IR module from IR builder 5e13d59 Introducing CompilerHelper - a base class for... compiler helper classes like Memory, GasMeter, etc. bb51b34 Cleanup block terminator generation 1463897 Basic block compilation in separated function e094ba9 Placing IRBuilder in Compiler class (for future refactoring) 5586ff5 Some changes about final/stop block 07f6bbf Removing unnecessary `dev` name prefixes 11bf67b Eliminating some `using namespace` a2da7c9 Change namespace from `evmcc` to `dev::eth::jit` f1ea6c9 Macros grouping PUSH, DUP and SWAP switch cases a36a432 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 67ee0b6 Unused rt_exit() function removed ec2013d Working longjmp solution 00a872e * codegen for CODECALL * fixes for EXTCODECOPY 59a392c Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 564a593 Codegen for EXTCODESIZE & EXTCODECOPY [Delivers #80644732] 9007b74 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 7fcf24d Trying to implement "exceptions" with longjmp (does not work on Windows 64bit at least) 727691f Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc e22238f Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc c57de78 Use longjmp to return OutOfGas code from main function (WIP) dbb3e81 Try to throw an OutOfGas exception when out of gas. The exception cannot be handled. 04d6ffb Some LLVM function attribute fun e11a9bb Change a bit the implementation of GAS instruction (fix) 00e5afc Change a bit the implementation of GAS instruction 0d679f0 Count gas cost for SSTORE instruction df93292 Codegen for CODECOPY. [Delivers #80644732] 1c70f3d Prealloc memory and count gas for CALL instruction d56f60f Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 7d67476 Prealloc memory and count gas for SHA3 and CREATE instructions 42b14ae Fixes in CALLDATACOPY [Delivers #80644732] 9d9e160 InsertPointGuard helper class for IRBuilder 7203120 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 0d97405 Initial implementation for CALLDATACOPY [Delivers #80644732] 82cba7a Use mem.require in mload, mstore & mstore8 0931289 Convenient Memory::require() overload ab556c6 Private mem.require function that preallocates memory and counts gas fee 01ca987 Give back an amount of gas not used by CALL instructions fb87a0b Count gas for CALL instructions 07131e4 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 475b85d Fixes in inline asm in some jump tests. 7494fdb Use Constant::get() interface to create LLVM constants f95999c Allocate memory and count gas for RETURN instruction 7a89751 Change the way compiled program returns 8fb6de0 Define constants and return codes 6caff31 Implemented ADDMOD, MULMOD and GAS [Delivers #80566276] 9b3c446 Codegen for GAS 3a25969 Report error if static-analysed stack is too small 0c259cd Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc a2a496e test cases for JUMPS/phi nodes 6131cca Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 8a52283 Do not check gas cost of value 0 efe23ad Do not commit gas costs on JUMPDEST instruction 7c78ecb Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 293f0de Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 9bdfd59 Change basic block linking order to get better results (still not perfect) 56a17a0 test cases for JUMP e4cf741 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 6e2bcef Implemented indirect JUMPI and fixes for JUMPs to invalid PCs 2c3d0cc test cases for JUMP(I) d5f7de4 Always commit cost blocks 39ba3ae Use external counter for gas (external linkage global variable) 4eb65a8 Fix block cost counting (counter not reset) 5470faf Count gas for additional memory d6e6a5f MSIZE test 0128f09 Wrong resize condition fixed 4f87144 mload function added as implementation of MLOAD 5f1ea8f store8 function added as implementation of MSTORE8 f5a0975 Use mem.store as implementation of MSTORE f825a60 Generate mem.store function and dependencies (currently unused) 7a7d4e3 Fix for finding jumpTableBlock d6915b4 Renames & comments ad43b92 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc fd7069e Indirect jump: jump table generated in separate bblock. 09a5899 Fix stack swap or dup not generating PHI nodes 973e0a2 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 52d1ceb test cases for jumps 5dd3037 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc cfb226b Group gas counting into block of instructions called cost-block. dab29a9 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 6778940 test cases for JUMP 2b36803 Implementing JUMP/JUMPDEST (work in progress) e42217d Disable gas checking for now (does not work and makes IR code hard to read) 04cf0cf Generate gas checking function 97644d6 Cleanup LLVM types usage b7f31af Cleanup LLVM types usage 28d6dd7 Decrement global gas value by instruction step cost d3f59f6 Introducing GasMeter 7f88487 EXP instruction. dafa5bc Make the Stack an integral part of BasicBlock 58c5950 JUMP test in assembly bb19540 Cleanup Compiler b273b86 Provide end instruction to basic block 16ea3bf Improve basic blocks linking implementation 7a3f3f9 Remove external stack 1cbb9d5 Stack cleanups a338b88 Cleanups: move basic block linking to separated function 577438e Correct the order of basic blocks e52d421 Link basic blocks with phi functions 0961908 Introducing BasicBlock class. cc51bfd Pop entry state of basic block from external stack, push exit state to external stack. 33e36ce Basic stack implementation for basic block. Values on stack are not preserved between basic blocks (jumps) 3cba3a2 SHA3 instruction f230c82 SUICIDE instruction 3f57858 CALL instruction. Still needs gas counting and callback support. 3942b2e CREATE instruction. Still needs some work with ExtVM interface. a1a195b Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc f075b7b - fixes for RETURN and STOP - codegen for SLT, SGT, EQ and NEG b519b73 Stack functions simplified 7bee86a Runtime class that inits and keeps stack, memory and ExtVM interface 320add0 BYTE instruction implementation (with bug on BYTE 0) 710b734 TODO: implement BYTE 48897f4 Bitwise operators: AND, OR, XOR c002d9b PC instructions 20cd698 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 09a9f10 Block Information instructions: PREVHASH, COINBASE, TIMESTAMP, NUMBER, DIFFICULTY, GASLIMIT 9289508 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 48d4294 - implemented LT, GT - new tests for jumps and comparisons 954300f Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc fbe97fd BALANCE 8b625fa fixed implementation of NOT fa76d7a fixes for JUMP/JUMPI (generating final basic block) implementation of NOT ad2c4c4 fixed implementation of JUMPI (cond casted to bool) 1d17da1 Added Module* constructor argument to Memory and Ext. b730bdc Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 0b8ba6d initial implementation of JUMP/JUMPI (untested) edf192b Print returned memory to standard output d073af7 Merge branch 'develop-evmcc' of https://github.com/imapp-pl/ethereum into develop-evmcc 748ee0f Implementing RETURN and STOP instructions eea755c fixed bug in memory handling e1abb20 Merge branch 'develop-evmcc' of github.com:imapp-pl/ethereum into develop-evmcc 00f9939 Implemented MLOAD, MSTORE, MSTORE8 and MSIZE b9cda13 Push call data on stack - CALLDATALOAD fcde2f3 Accessing Ext static data: CALLER, ORIGIN, CALLVALUE, CALLDATASIZE, GASPRICE ac795c4 Send Ext static data to running contract. ADDRESS instruction 9ba6a7c Improve stack binary interface 8cd4326 Set/get values in Ext store - SSTORE & SLOAD 0013656 added ethcore to libs required by evmcc 15b9452 Merge branch 'develop-evmcc' of git@github.com:imapp-pl/ethereum.git into develop-evmcc 68f15f9 initial implementation of memory f3a347b Starting ExtVM binary interface 78b188c Fix stack set/get bad function signature 58e03d5 Basic arithmetic, limited precision for MUL, DIV and MOD. e53c0a4 SWAP* instructions 3e01d18 POP and DUP* instructions 4266ce5 PUSH instruction implementation d8430db Output compilation (option -c) result to standard output by default e8ff67c Stack interface and implementation bbc3aa4 evmcc's CMakeLists updated: * added std=c++14 for gcc (required for std::make_unique) * added required llvm libnames dfa141a Staring with Stack helper f124be6 Implementation of ExecutionEngine with LLVM MCJIT e33fdea Remove public LLVM dependency in Compiler 507ba06 ExecutionEngine stub and -i program option for interpreting EVM Code f5eda1f Moving Compiler to separated files REVERT: 334deeb Initial commit git-subtree-dir: evmjit git-subtree-split: 533531bd07cb2d95f3fa017231484e91196d87a6 --- .gitignore | 28 - CMakeLists.txt | 27 + LICENSE | 22 - evmcc/CMakeLists.txt | 18 + evmcc/evmcc.cpp | 211 ++++ evmcc/test/arith/addmod.evm | 1 + evmcc/test/arith/addmod.lll | 12 + evmcc/test/arith/arith1.evm | 1 + evmcc/test/arith/arith1.lll | 37 + evmcc/test/arith/arith_bnot.evm | 1 + evmcc/test/arith/arith_bnot.lll | 14 + evmcc/test/arith/div.evm | 1 + evmcc/test/arith/div.lll | 10 + evmcc/test/arith/fib1.evm | 1 + evmcc/test/arith/fib1.lll | 57 ++ evmcc/test/arith/mul.evm | 1 + evmcc/test/arith/mul.lll | 13 + evmcc/test/arith/mulmod.evm | 1 + evmcc/test/arith/mulmod.lll | 12 + evmcc/test/except/badinst1.evm | 1 + evmcc/test/ext/calldatacopy1.evm | 1 + evmcc/test/ext/calldatacopy1.lll | 13 + evmcc/test/ext/calldatacopy2.evm | 1 + evmcc/test/ext/calldatacopy2.lll | 13 + evmcc/test/ext/codecopy1.evm | 1 + evmcc/test/ext/codecopy1.lll | 13 + evmcc/test/ext/codecopy2.evm | 1 + evmcc/test/ext/codecopy2.lll | 13 + evmcc/test/ext/codecopy3.evm | 1 + evmcc/test/ext/codecopy3.lll | 13 + evmcc/test/ext/ext_test.evm | 1 + evmcc/test/ext/ext_test.lll | 55 + evmcc/test/ext/extcodecopy1.evm | 1 + evmcc/test/ext/extcodecopy1.lll | 11 + evmcc/test/ext/store_delete.evm | 1 + evmcc/test/ext/store_delete.lll | 9 + evmcc/test/ext/store_test.evm | 1 + evmcc/test/ext/store_test.lll | 14 + evmcc/test/jump/ackermann.ethel | 7 + evmcc/test/jump/ackermann.evm | 1 + evmcc/test/jump/badindirect1.evm | 1 + evmcc/test/jump/badindirect1.lll | 9 + evmcc/test/jump/badindirect2.evm | 1 + evmcc/test/jump/badindirect2.lll | 12 + evmcc/test/jump/badjump1.evm | 1 + evmcc/test/jump/badjump1.lll | 6 + evmcc/test/jump/badjump2.evm | 1 + evmcc/test/jump/badjump2.lll | 9 + evmcc/test/jump/call1.ethel | 5 + evmcc/test/jump/call1.evm | 1 + evmcc/test/jump/call2.ethel | 5 + evmcc/test/jump/call2.evm | 1 + evmcc/test/jump/fac.ethel | 5 + evmcc/test/jump/fac.evm | 1 + evmcc/test/jump/fac_tail.ethel | 5 + evmcc/test/jump/fac_tail.evm | 1 + evmcc/test/jump/fib1.ethel | 6 + evmcc/test/jump/fib1.evm | 1 + evmcc/test/jump/for1.evm | 1 + evmcc/test/jump/for1.lll | 3 + evmcc/test/jump/for2.evm | 1 + evmcc/test/jump/for2.lll | 3 + evmcc/test/jump/if1.ethel | 1 + evmcc/test/jump/if1.evm | 1 + evmcc/test/jump/if2.ethel | 1 + evmcc/test/jump/if2.evm | 1 + evmcc/test/jump/indirect1.evm | 1 + evmcc/test/jump/indirect1.lll | 13 + evmcc/test/jump/indirect2.evm | 1 + evmcc/test/jump/indirect2.lll | 19 + evmcc/test/jump/indirect3.evm | 1 + evmcc/test/jump/indirect3.lll | 14 + evmcc/test/jump/indirect4.evm | 1 + evmcc/test/jump/indirect4.lll | 15 + evmcc/test/jump/jump1.evm | 1 + evmcc/test/jump/jump1.lll | 11 + evmcc/test/jump/jump2.evm | 1 + evmcc/test/jump/jump2.lll | 10 + evmcc/test/jump/jump3.evm | 1 + evmcc/test/jump/jump3.lll | 10 + evmcc/test/jump/jump4.evm | 1 + evmcc/test/jump/jump4.lll | 17 + evmcc/test/jump/jump5.evm | 1 + evmcc/test/jump/jump5.lll | 16 + evmcc/test/jump/jump6.evm | 1 + evmcc/test/jump/jump6.lll | 32 + evmcc/test/jump/jumpi_at_the_end.evm | 1 + evmcc/test/jump/jumpi_at_the_end.lll | 1 + evmcc/test/jump/loop1.evm | 1 + evmcc/test/jump/loop1.lll | 27 + evmcc/test/jump/loop2.evm | 1 + evmcc/test/jump/loop2.lll | 28 + evmcc/test/jump/rec1.ethel | 4 + evmcc/test/jump/rec1.evm | 1 + evmcc/test/jump/when1.asm | 10 + evmcc/test/jump/when1.evm | 1 + evmcc/test/jump/when1.lll | 2 + evmcc/test/kv.evm | 1 + evmcc/test/kv.lll | 10 + evmcc/test/mem/byte.evm | 1 + evmcc/test/mem/byte.lll | 105 ++ evmcc/test/mem/mem2.evm | 1 + evmcc/test/mem/mem2.lll | 15 + evmcc/test/mem/memtest1.evm | 1 + evmcc/test/mem/memtest1.lll | 18 + evmcc/test/mem/mstore1.evm | 1 + evmcc/test/mem/mstore1.lll | 6 + evmcc/test/ret/return1.evm | 1 + evmcc/test/ret/return1.lll | 6 + evmcc/test/ret/return2.evm | 1 + evmcc/test/ret/return2.lll | 6 + evmcc/test/ret/return_test.evm | 1 + evmcc/test/ret/return_test.lll | 15 + evmcc/test/stack/oos.evm | 1 + evmcc/test/stack/oos.lll | 11 + evmcc/test/stack/push_test.evm | 1 + evmcc/test/stack/push_test.lll | 35 + evmcc/test/stack/stack_test.evm | 1 + evmcc/test/stack/stack_test.lll | 8 + evmcc/test/stack/stackjump.evm | 1 + evmcc/test/stack/stackjump.lll | 3 + evmcc/test/stack/swap.evm | 1 + evmcc/test/stack/swap.lll | 31 + evmcc/test/stack/swapswap.evm | 1 + evmcc/test/stack/swapswap.lll | 32 + evmcc/test/stack/test.evm | 1 + .../test/vmtests/vmArithPerformanceTest.json | 260 +++++ evmcc/test/vmtests/vmPerformanceTest.json | 214 ++++ evmcc/test/vmtests/vm_jump.json | 41 + libevmjit-cpp/CMakeLists.txt | 16 + libevmjit-cpp/Env.cpp | 125 +++ libevmjit-cpp/JitVM.cpp | 69 ++ libevmjit-cpp/JitVM.h | 28 + libevmjit/Arith256.cpp | 194 ++++ libevmjit/Arith256.h | 49 + libevmjit/BasicBlock.cpp | 380 +++++++ libevmjit/BasicBlock.h | 117 +++ libevmjit/CMakeLists.txt | 24 + libevmjit/Cache.cpp | 60 ++ libevmjit/Cache.h | 41 + libevmjit/Common.h | 49 + libevmjit/Compiler.cpp | 949 ++++++++++++++++++ libevmjit/Compiler.h | 91 ++ libevmjit/CompilerHelper.cpp | 46 + libevmjit/CompilerHelper.h | 83 ++ libevmjit/Endianness.cpp | 38 + libevmjit/Endianness.h | 24 + libevmjit/ExecutionEngine.cpp | 127 +++ libevmjit/ExecutionEngine.h | 26 + libevmjit/Ext.cpp | 178 ++++ libevmjit/Ext.h | 69 ++ libevmjit/GasMeter.cpp | 222 ++++ libevmjit/GasMeter.h | 64 ++ libevmjit/Instruction.h | 235 +++++ libevmjit/Memory.cpp | 237 +++++ libevmjit/Memory.h | 46 + libevmjit/Runtime.cpp | 52 + libevmjit/Runtime.h | 62 ++ libevmjit/RuntimeData.h | 52 + libevmjit/RuntimeManager.cpp | 199 ++++ libevmjit/RuntimeManager.h | 51 + libevmjit/Stack.cpp | 133 +++ libevmjit/Stack.h | 43 + libevmjit/Type.cpp | 67 ++ libevmjit/Type.h | 54 + libevmjit/Utils.cpp | 66 ++ libevmjit/Utils.h | 24 + libevmjit/interface.c | 30 + 168 files changed, 6168 insertions(+), 50 deletions(-) delete mode 100644 .gitignore create mode 100644 CMakeLists.txt delete mode 100644 LICENSE create mode 100644 evmcc/CMakeLists.txt create mode 100644 evmcc/evmcc.cpp create mode 100644 evmcc/test/arith/addmod.evm create mode 100644 evmcc/test/arith/addmod.lll create mode 100644 evmcc/test/arith/arith1.evm create mode 100644 evmcc/test/arith/arith1.lll create mode 100644 evmcc/test/arith/arith_bnot.evm create mode 100644 evmcc/test/arith/arith_bnot.lll create mode 100644 evmcc/test/arith/div.evm create mode 100644 evmcc/test/arith/div.lll create mode 100644 evmcc/test/arith/fib1.evm create mode 100644 evmcc/test/arith/fib1.lll create mode 100644 evmcc/test/arith/mul.evm create mode 100644 evmcc/test/arith/mul.lll create mode 100644 evmcc/test/arith/mulmod.evm create mode 100644 evmcc/test/arith/mulmod.lll create mode 100644 evmcc/test/except/badinst1.evm create mode 100644 evmcc/test/ext/calldatacopy1.evm create mode 100644 evmcc/test/ext/calldatacopy1.lll create mode 100644 evmcc/test/ext/calldatacopy2.evm create mode 100644 evmcc/test/ext/calldatacopy2.lll create mode 100644 evmcc/test/ext/codecopy1.evm create mode 100644 evmcc/test/ext/codecopy1.lll create mode 100644 evmcc/test/ext/codecopy2.evm create mode 100644 evmcc/test/ext/codecopy2.lll create mode 100644 evmcc/test/ext/codecopy3.evm create mode 100644 evmcc/test/ext/codecopy3.lll create mode 100644 evmcc/test/ext/ext_test.evm create mode 100644 evmcc/test/ext/ext_test.lll create mode 100644 evmcc/test/ext/extcodecopy1.evm create mode 100644 evmcc/test/ext/extcodecopy1.lll create mode 100644 evmcc/test/ext/store_delete.evm create mode 100644 evmcc/test/ext/store_delete.lll create mode 100644 evmcc/test/ext/store_test.evm create mode 100644 evmcc/test/ext/store_test.lll create mode 100644 evmcc/test/jump/ackermann.ethel create mode 100644 evmcc/test/jump/ackermann.evm create mode 100644 evmcc/test/jump/badindirect1.evm create mode 100644 evmcc/test/jump/badindirect1.lll create mode 100644 evmcc/test/jump/badindirect2.evm create mode 100644 evmcc/test/jump/badindirect2.lll create mode 100644 evmcc/test/jump/badjump1.evm create mode 100644 evmcc/test/jump/badjump1.lll create mode 100644 evmcc/test/jump/badjump2.evm create mode 100644 evmcc/test/jump/badjump2.lll create mode 100644 evmcc/test/jump/call1.ethel create mode 100644 evmcc/test/jump/call1.evm create mode 100644 evmcc/test/jump/call2.ethel create mode 100644 evmcc/test/jump/call2.evm create mode 100644 evmcc/test/jump/fac.ethel create mode 100644 evmcc/test/jump/fac.evm create mode 100644 evmcc/test/jump/fac_tail.ethel create mode 100644 evmcc/test/jump/fac_tail.evm create mode 100644 evmcc/test/jump/fib1.ethel create mode 100644 evmcc/test/jump/fib1.evm create mode 100644 evmcc/test/jump/for1.evm create mode 100644 evmcc/test/jump/for1.lll create mode 100644 evmcc/test/jump/for2.evm create mode 100644 evmcc/test/jump/for2.lll create mode 100644 evmcc/test/jump/if1.ethel create mode 100644 evmcc/test/jump/if1.evm create mode 100644 evmcc/test/jump/if2.ethel create mode 100644 evmcc/test/jump/if2.evm create mode 100644 evmcc/test/jump/indirect1.evm create mode 100644 evmcc/test/jump/indirect1.lll create mode 100644 evmcc/test/jump/indirect2.evm create mode 100644 evmcc/test/jump/indirect2.lll create mode 100644 evmcc/test/jump/indirect3.evm create mode 100644 evmcc/test/jump/indirect3.lll create mode 100644 evmcc/test/jump/indirect4.evm create mode 100644 evmcc/test/jump/indirect4.lll create mode 100644 evmcc/test/jump/jump1.evm create mode 100644 evmcc/test/jump/jump1.lll create mode 100644 evmcc/test/jump/jump2.evm create mode 100644 evmcc/test/jump/jump2.lll create mode 100644 evmcc/test/jump/jump3.evm create mode 100644 evmcc/test/jump/jump3.lll create mode 100644 evmcc/test/jump/jump4.evm create mode 100644 evmcc/test/jump/jump4.lll create mode 100644 evmcc/test/jump/jump5.evm create mode 100644 evmcc/test/jump/jump5.lll create mode 100644 evmcc/test/jump/jump6.evm create mode 100644 evmcc/test/jump/jump6.lll create mode 100644 evmcc/test/jump/jumpi_at_the_end.evm create mode 100644 evmcc/test/jump/jumpi_at_the_end.lll create mode 100644 evmcc/test/jump/loop1.evm create mode 100644 evmcc/test/jump/loop1.lll create mode 100644 evmcc/test/jump/loop2.evm create mode 100644 evmcc/test/jump/loop2.lll create mode 100644 evmcc/test/jump/rec1.ethel create mode 100644 evmcc/test/jump/rec1.evm create mode 100644 evmcc/test/jump/when1.asm create mode 100644 evmcc/test/jump/when1.evm create mode 100644 evmcc/test/jump/when1.lll create mode 100644 evmcc/test/kv.evm create mode 100644 evmcc/test/kv.lll create mode 100644 evmcc/test/mem/byte.evm create mode 100644 evmcc/test/mem/byte.lll create mode 100644 evmcc/test/mem/mem2.evm create mode 100644 evmcc/test/mem/mem2.lll create mode 100644 evmcc/test/mem/memtest1.evm create mode 100644 evmcc/test/mem/memtest1.lll create mode 100644 evmcc/test/mem/mstore1.evm create mode 100644 evmcc/test/mem/mstore1.lll create mode 100644 evmcc/test/ret/return1.evm create mode 100644 evmcc/test/ret/return1.lll create mode 100644 evmcc/test/ret/return2.evm create mode 100644 evmcc/test/ret/return2.lll create mode 100644 evmcc/test/ret/return_test.evm create mode 100644 evmcc/test/ret/return_test.lll create mode 100644 evmcc/test/stack/oos.evm create mode 100644 evmcc/test/stack/oos.lll create mode 100644 evmcc/test/stack/push_test.evm create mode 100644 evmcc/test/stack/push_test.lll create mode 100644 evmcc/test/stack/stack_test.evm create mode 100644 evmcc/test/stack/stack_test.lll create mode 100644 evmcc/test/stack/stackjump.evm create mode 100644 evmcc/test/stack/stackjump.lll create mode 100644 evmcc/test/stack/swap.evm create mode 100644 evmcc/test/stack/swap.lll create mode 100644 evmcc/test/stack/swapswap.evm create mode 100644 evmcc/test/stack/swapswap.lll create mode 100644 evmcc/test/stack/test.evm create mode 100644 evmcc/test/vmtests/vmArithPerformanceTest.json create mode 100644 evmcc/test/vmtests/vmPerformanceTest.json create mode 100644 evmcc/test/vmtests/vm_jump.json create mode 100644 libevmjit-cpp/CMakeLists.txt create mode 100644 libevmjit-cpp/Env.cpp create mode 100644 libevmjit-cpp/JitVM.cpp create mode 100644 libevmjit-cpp/JitVM.h create mode 100644 libevmjit/Arith256.cpp create mode 100644 libevmjit/Arith256.h create mode 100644 libevmjit/BasicBlock.cpp create mode 100644 libevmjit/BasicBlock.h create mode 100644 libevmjit/CMakeLists.txt create mode 100644 libevmjit/Cache.cpp create mode 100644 libevmjit/Cache.h create mode 100644 libevmjit/Common.h create mode 100644 libevmjit/Compiler.cpp create mode 100644 libevmjit/Compiler.h create mode 100644 libevmjit/CompilerHelper.cpp create mode 100644 libevmjit/CompilerHelper.h create mode 100644 libevmjit/Endianness.cpp create mode 100644 libevmjit/Endianness.h create mode 100644 libevmjit/ExecutionEngine.cpp create mode 100644 libevmjit/ExecutionEngine.h create mode 100644 libevmjit/Ext.cpp create mode 100644 libevmjit/Ext.h create mode 100644 libevmjit/GasMeter.cpp create mode 100644 libevmjit/GasMeter.h create mode 100644 libevmjit/Instruction.h create mode 100644 libevmjit/Memory.cpp create mode 100644 libevmjit/Memory.h create mode 100644 libevmjit/Runtime.cpp create mode 100644 libevmjit/Runtime.h create mode 100644 libevmjit/RuntimeData.h create mode 100644 libevmjit/RuntimeManager.cpp create mode 100644 libevmjit/RuntimeManager.h create mode 100644 libevmjit/Stack.cpp create mode 100644 libevmjit/Stack.h create mode 100644 libevmjit/Type.cpp create mode 100644 libevmjit/Type.h create mode 100644 libevmjit/Utils.cpp create mode 100644 libevmjit/Utils.h create mode 100644 libevmjit/interface.c diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b8bd0267b..000000000 --- a/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..26a8010d1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(evmjit) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# LLVM +if(LLVM_DIR) # local LLVM build + find_package(LLVM REQUIRED CONFIG) + message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") + message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + # TODO: bitwriter is needed only for evmcc + llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter) +else() + # Workaround for Ubuntu broken LLVM package + message(STATUS "Using llvm-3.5-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") + execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS) + message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}") + set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm") +endif() + +# Boost +find_package(Boost REQUIRED) + +add_subdirectory(libevmjit) +add_subdirectory(libevmjit-cpp) +add_subdirectory(evmcc) \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d7e0e57ac..000000000 --- a/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/evmcc/CMakeLists.txt b/evmcc/CMakeLists.txt new file mode 100644 index 000000000..4ffbf5fb5 --- /dev/null +++ b/evmcc/CMakeLists.txt @@ -0,0 +1,18 @@ +set(TARGET_NAME evmcc) + +set(SOURCES + evmcc.cpp +) +source_group("" FILES ${SOURCES}) + +add_executable(${TARGET_NAME} ${SOURCES}) +set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "tools") + +include_directories(../..) +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(${TARGET_NAME} ethereum) +target_link_libraries(${TARGET_NAME} ${Boost_PROGRAM_OPTIONS_LIBRARIES}) + +install(TARGETS ${TARGET_NAME} DESTINATION bin ) \ No newline at end of file diff --git a/evmcc/evmcc.cpp b/evmcc/evmcc.cpp new file mode 100644 index 000000000..3c43ab78b --- /dev/null +++ b/evmcc/evmcc.cpp @@ -0,0 +1,211 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +void parseProgramOptions(int _argc, char** _argv, boost::program_options::variables_map& _varMap) +{ + namespace opt = boost::program_options; + + opt::options_description explicitOpts("Allowed options"); + explicitOpts.add_options() + ("help,h", "show usage information") + ("compile,c", "compile the code to LLVM IR") + ("interpret,i", "compile the code to LLVM IR and execute") + ("gas,g", opt::value(), "set initial gas for execution") + ("disassemble,d", "dissassemble the code") + ("dump-cfg", "dump control flow graph to graphviz file") + ("dont-optimize", "turn off optimizations") + ("optimize-stack", "optimize stack use between basic blocks (default: on)") + ("rewrite-switch", "rewrite LLVM switch to branches (default: on)") + ("output-ll", opt::value(), "dump generated LLVM IR to file") + ("output-bc", opt::value(), "dump generated LLVM bitcode to file") + ("show-logs", "output LOG statements to stderr") + ("verbose,V", "enable verbose output"); + + opt::options_description implicitOpts("Input files"); + implicitOpts.add_options() + ("input-file", opt::value(), "input file"); + + opt::options_description allOpts(""); + allOpts.add(explicitOpts).add(implicitOpts); + + opt::positional_options_description inputOpts; + inputOpts.add("input-file", 1); + + const char* errorMsg = nullptr; + try + { + auto parser = opt::command_line_parser(_argc, _argv).options(allOpts).positional(inputOpts); + opt::store(parser.run(), _varMap); + opt::notify(_varMap); + } + catch (boost::program_options::error& err) + { + errorMsg = err.what(); + } + + if (!errorMsg && _varMap.count("input-file") == 0) + errorMsg = "missing input file name"; + + if (_varMap.count("disassemble") == 0 + && _varMap.count("compile") == 0 + && _varMap.count("interpret") == 0) + { + errorMsg = "at least one of -c, -i, -d is required"; + } + + if (errorMsg || _varMap.count("help")) + { + if (errorMsg) + std::cerr << "Error: " << errorMsg << std::endl; + + std::cout << "Usage: " << _argv[0] << " input-file " << std::endl + << explicitOpts << std::endl; + std::exit(errorMsg ? 1 : 0); + } +} + +int main(int argc, char** argv) +{ + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + + boost::program_options::variables_map options; + parseProgramOptions(argc, argv, options); + + auto inputFile = options["input-file"].as(); + std::ifstream ifs(inputFile); + if (!ifs.is_open()) + { + std::cerr << "cannot open input file " << inputFile << std::endl; + exit(1); + } + + std::string src((std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + + boost::algorithm::trim(src); + + using namespace dev; + + bytes bytecode = fromHex(src); + + if (options.count("disassemble")) + { + std::string assembly = eth::disassemble(bytecode); + std::cout << assembly << std::endl; + } + + if (options.count("compile") || options.count("interpret")) + { + size_t initialGas = 10000; + + if (options.count("gas")) + initialGas = options["gas"].as(); + + auto compilationStartTime = std::chrono::high_resolution_clock::now(); + + eth::jit::Compiler::Options compilerOptions; + compilerOptions.dumpCFG = options.count("dump-cfg") > 0; + bool optimize = options.count("dont-optimize") == 0; + compilerOptions.optimizeStack = optimize || options.count("optimize-stack") > 0; + compilerOptions.rewriteSwitchToBranches = optimize || options.count("rewrite-switch") > 0; + + auto compiler = eth::jit::Compiler(compilerOptions); + auto module = compiler.compile(bytecode, "main"); + + auto compilationEndTime = std::chrono::high_resolution_clock::now(); + + module->dump(); + + if (options.count("output-ll")) + { + auto outputFile = options["output-ll"].as(); + std::ofstream ofs(outputFile); + if (!ofs.is_open()) + { + std::cerr << "cannot open output file " << outputFile << std::endl; + exit(1); + } + llvm::raw_os_ostream ros(ofs); + module->print(ros, nullptr); + ofs.close(); + } + + if (options.count("output-bc")) + { + auto outputFile = options["output-bc"].as(); + std::ofstream ofs(outputFile); + if (!ofs.is_open()) + { + std::cerr << "cannot open output file " << outputFile << std::endl; + exit(1); + } + llvm::raw_os_ostream ros(ofs); + llvm::WriteBitcodeToFile(module.get(), ros); + ros.flush(); + ofs.close(); + } + + if (options.count("verbose")) + { + std::cerr << "*** Compilation time: " + << std::chrono::duration_cast(compilationEndTime - compilationStartTime).count() + << std::endl; + } + + if (options.count("interpret")) + { + using namespace eth::jit; + + ExecutionEngine engine; + eth::jit::u256 gas = initialGas; + + // Create random runtime data + RuntimeData data; + data.set(RuntimeData::Gas, gas); + data.set(RuntimeData::Address, (u160)Address(1122334455667788)); + data.set(RuntimeData::Caller, (u160)Address(0xfacefacefaceface)); + data.set(RuntimeData::Origin, (u160)Address(101010101010101010)); + data.set(RuntimeData::CallValue, 0xabcd); + data.set(RuntimeData::CallDataSize, 3); + data.set(RuntimeData::GasPrice, 1003); + data.set(RuntimeData::PrevHash, 1003); + data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015)); + data.set(RuntimeData::TimeStamp, 1005); + data.set(RuntimeData::Number, 1006); + data.set(RuntimeData::Difficulty, 16); + data.set(RuntimeData::GasLimit, 1008); + data.set(RuntimeData::CodeSize, bytecode.size()); + data.callData = (uint8_t*)"abc"; + data.code = bytecode.data(); + + // BROKEN: env_* functions must be implemented & RuntimeData struct created + // TODO: Do not compile module again + auto result = engine.run(bytecode, &data, nullptr); + return static_cast(result); + } + } + + return 0; +} diff --git a/evmcc/test/arith/addmod.evm b/evmcc/test/arith/addmod.evm new file mode 100644 index 000000000..4ca71e065 --- /dev/null +++ b/evmcc/test/arith/addmod.evm @@ -0,0 +1 @@ +60646107b760271460005560006001f2 diff --git a/evmcc/test/arith/addmod.lll b/evmcc/test/arith/addmod.lll new file mode 100644 index 000000000..11a6b2cb9 --- /dev/null +++ b/evmcc/test/arith/addmod.lll @@ -0,0 +1,12 @@ +;; Should return (1975 + 39) `mod` 100 = 14 = 0x0e +(asm +100 +1975 +39 +ADDMOD +0 +MSTORE8 +0 +1 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/arith/arith1.evm b/evmcc/test/arith/arith1.evm new file mode 100644 index 000000000..c7a029f52 --- /dev/null +++ b/evmcc/test/arith/arith1.evm @@ -0,0 +1 @@ +60016001900160070260050160029004600490066021900560150160030260059007600303600960110860005460086000f2 diff --git a/evmcc/test/arith/arith1.lll b/evmcc/test/arith/arith1.lll new file mode 100644 index 000000000..4757a7420 --- /dev/null +++ b/evmcc/test/arith/arith1.lll @@ -0,0 +1,37 @@ + +(asm +1 +1 +SWAP1 +ADD ;; 2 +7 +MUL ;; 14 +5 +ADD ;; 19 +2 +SWAP1 +DIV ;; 9 +4 +SWAP1 +MOD ;; 1 +33 +SWAP1 +SDIV;; 0 +21 +ADD ;; 21 +3 +MUL ;; 63 +5 +SWAP1 +SMOD;; 3 +3 +SUB ;; 0 +9 +17 +EXP ;; 17^9 +0 +MSTORE +8 +0 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/arith/arith_bnot.evm b/evmcc/test/arith/arith_bnot.evm new file mode 100644 index 000000000..4cfaf8f55 --- /dev/null +++ b/evmcc/test/arith/arith_bnot.evm @@ -0,0 +1 @@ +6201e2406000546000530960005460206000f2 diff --git a/evmcc/test/arith/arith_bnot.lll b/evmcc/test/arith/arith_bnot.lll new file mode 100644 index 000000000..a83b05a9a --- /dev/null +++ b/evmcc/test/arith/arith_bnot.lll @@ -0,0 +1,14 @@ + +(asm +123456 +0 +MSTORE +0 +MLOAD +BNOT +0 +MSTORE +32 +0 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/arith/div.evm b/evmcc/test/arith/div.evm new file mode 100644 index 000000000..b68d5d202 --- /dev/null +++ b/evmcc/test/arith/div.evm @@ -0,0 +1 @@ +60027ffedcba9876543210fedcba9876543210fedcba9876543210fedcba98765432100460005460206000f2 diff --git a/evmcc/test/arith/div.lll b/evmcc/test/arith/div.lll new file mode 100644 index 000000000..72c22bfdc --- /dev/null +++ b/evmcc/test/arith/div.lll @@ -0,0 +1,10 @@ +(asm +0x2 +0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210 +DIV +0 +MSTORE +32 +0 +RETURN +) diff --git a/evmcc/test/arith/fib1.evm b/evmcc/test/arith/fib1.evm new file mode 100644 index 000000000..4c141314e --- /dev/null +++ b/evmcc/test/arith/fib1.evm @@ -0,0 +1 @@ +60016001818101818101818101818101818101818101818101818101818101818101818101818101818101818101818101818101818101 diff --git a/evmcc/test/arith/fib1.lll b/evmcc/test/arith/fib1.lll new file mode 100644 index 000000000..286bed275 --- /dev/null +++ b/evmcc/test/arith/fib1.lll @@ -0,0 +1,57 @@ +;; Fibbonacci unrolled + +(asm +1 +1 +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +) \ No newline at end of file diff --git a/evmcc/test/arith/mul.evm b/evmcc/test/arith/mul.evm new file mode 100644 index 000000000..7e8afd268 --- /dev/null +++ b/evmcc/test/arith/mul.evm @@ -0,0 +1 @@ +7001234567890abcdef0fedcba09876543217001234567890abcdef0fedcba09876543217001234567890abcdef0fedcba0987654321020260005460206000f2 diff --git a/evmcc/test/arith/mul.lll b/evmcc/test/arith/mul.lll new file mode 100644 index 000000000..b0fa343bb --- /dev/null +++ b/evmcc/test/arith/mul.lll @@ -0,0 +1,13 @@ +(asm +0x1234567890abcdef0fedcba0987654321 +0x1234567890abcdef0fedcba0987654321 +0x1234567890abcdef0fedcba0987654321 +MUL +MUL +0 +MSTORE +32 +0 +RETURN +;; 47d0817e4167b1eb4f9fc722b133ef9d7d9a6fb4c2c1c442d000107a5e419561 +) diff --git a/evmcc/test/arith/mulmod.evm b/evmcc/test/arith/mulmod.evm new file mode 100644 index 000000000..e34a06154 --- /dev/null +++ b/evmcc/test/arith/mulmod.evm @@ -0,0 +1 @@ +6064601b60251560005560006001f2 diff --git a/evmcc/test/arith/mulmod.lll b/evmcc/test/arith/mulmod.lll new file mode 100644 index 000000000..5e87f0843 --- /dev/null +++ b/evmcc/test/arith/mulmod.lll @@ -0,0 +1,12 @@ +;; Should return (27 * 37) `mod` 100 = 99 = 0x63 +(asm +100 +27 +37 +MULMOD +0 +MSTORE8 +0 +1 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/except/badinst1.evm b/evmcc/test/except/badinst1.evm new file mode 100644 index 000000000..69aadac5e --- /dev/null +++ b/evmcc/test/except/badinst1.evm @@ -0,0 +1 @@ +4a diff --git a/evmcc/test/ext/calldatacopy1.evm b/evmcc/test/ext/calldatacopy1.evm new file mode 100644 index 000000000..f20019651 --- /dev/null +++ b/evmcc/test/ext/calldatacopy1.evm @@ -0,0 +1 @@ +60326000600a37600053600a6014f2 diff --git a/evmcc/test/ext/calldatacopy1.lll b/evmcc/test/ext/calldatacopy1.lll new file mode 100644 index 000000000..3d2ae0a78 --- /dev/null +++ b/evmcc/test/ext/calldatacopy1.lll @@ -0,0 +1,13 @@ +(asm +50 ;; byte count +0 ;; source index in calldata array +10 ;; dest index in memory +CALLDATACOPY + +0 +MLOAD ;; to dump memory + +10 +20 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/ext/calldatacopy2.evm b/evmcc/test/ext/calldatacopy2.evm new file mode 100644 index 000000000..e8eea8da7 --- /dev/null +++ b/evmcc/test/ext/calldatacopy2.evm @@ -0,0 +1 @@ +606464e8d4a510006000376000536000600af2 diff --git a/evmcc/test/ext/calldatacopy2.lll b/evmcc/test/ext/calldatacopy2.lll new file mode 100644 index 000000000..6bbea48d8 --- /dev/null +++ b/evmcc/test/ext/calldatacopy2.lll @@ -0,0 +1,13 @@ +(asm +100 ;; byte count +1000000000000 ;; source index in calldata array +0 ;; dest index in memory +CALLDATACOPY + +0 +MLOAD ;; to dump memory + +0 +10 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/ext/codecopy1.evm b/evmcc/test/ext/codecopy1.evm new file mode 100644 index 000000000..d286f9232 --- /dev/null +++ b/evmcc/test/ext/codecopy1.evm @@ -0,0 +1 @@ +60146000600a39600053600a6014f2 diff --git a/evmcc/test/ext/codecopy1.lll b/evmcc/test/ext/codecopy1.lll new file mode 100644 index 000000000..85a02b5d7 --- /dev/null +++ b/evmcc/test/ext/codecopy1.lll @@ -0,0 +1,13 @@ +(asm +20 ;; byte count +0 ;; source index in code array +10 ;; dest index in memory +CODECOPY + +0 +MLOAD ;; to dump memory + +10 +20 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/ext/codecopy2.evm b/evmcc/test/ext/codecopy2.evm new file mode 100644 index 000000000..71cd92525 --- /dev/null +++ b/evmcc/test/ext/codecopy2.evm @@ -0,0 +1 @@ +606464e8d4a510006000396000536000600af2 diff --git a/evmcc/test/ext/codecopy2.lll b/evmcc/test/ext/codecopy2.lll new file mode 100644 index 000000000..dcbbcaa46 --- /dev/null +++ b/evmcc/test/ext/codecopy2.lll @@ -0,0 +1,13 @@ +(asm +100 ;; byte count +1000000000000 ;; source index in code array +0 ;; dest index in memory +CODECOPY + +0 +MLOAD ;; to dump memory + +0 +10 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/ext/codecopy3.evm b/evmcc/test/ext/codecopy3.evm new file mode 100644 index 000000000..e4b6a9253 --- /dev/null +++ b/evmcc/test/ext/codecopy3.evm @@ -0,0 +1 @@ +3860006000396000536000600af2 diff --git a/evmcc/test/ext/codecopy3.lll b/evmcc/test/ext/codecopy3.lll new file mode 100644 index 000000000..80d9982c6 --- /dev/null +++ b/evmcc/test/ext/codecopy3.lll @@ -0,0 +1,13 @@ +(asm +CODESIZE ;; byte count +0 ;; source index in code array +0 ;; dest index in memory +CODECOPY + +0 +MLOAD ;; to dump memory + +0 +10 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/ext/ext_test.evm b/evmcc/test/ext/ext_test.evm new file mode 100644 index 000000000..580bd9675 --- /dev/null +++ b/evmcc/test/ext/ext_test.evm @@ -0,0 +1 @@ +5a3031333234363a4041424344455a36600035602635601335387f1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff600054602060006000f06020600060206000600030610bb8f1600053611000545b60200260002030ff60016002f2 diff --git a/evmcc/test/ext/ext_test.lll b/evmcc/test/ext/ext_test.lll new file mode 100644 index 000000000..3287ae95f --- /dev/null +++ b/evmcc/test/ext/ext_test.lll @@ -0,0 +1,55 @@ + +(asm +PC +ADDRESS +BALANCE +CALLER +ORIGIN +CALLVALUE +CALLDATASIZE +GASPRICE +PREVHASH +COINBASE +TIMESTAMP +NUMBER +DIFFICULTY +GASLIMIT +PC +CALLDATASIZE +0 +CALLDATALOAD +38 +CALLDATALOAD +19 +CALLDATALOAD +CODESIZE +0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff +0 +MSTORE +32 +0 +0 +CREATE +32 +0 +32 +0 +0 +ADDRESS +3000 +CALL +0 +MLOAD +4096 +MSTORE +MSIZE +32 +MUL +0 +SHA3 +ADDRESS +SUICIDE +1 +2 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/ext/extcodecopy1.evm b/evmcc/test/ext/extcodecopy1.evm new file mode 100644 index 000000000..6132b52d8 --- /dev/null +++ b/evmcc/test/ext/extcodecopy1.evm @@ -0,0 +1 @@ +60c86000600a303c60005360006020f2 diff --git a/evmcc/test/ext/extcodecopy1.lll b/evmcc/test/ext/extcodecopy1.lll new file mode 100644 index 000000000..c37054574 --- /dev/null +++ b/evmcc/test/ext/extcodecopy1.lll @@ -0,0 +1,11 @@ +(asm +200 ;; byte count +0 ;; source index in code array +10 ;; dest index in memory +ADDRESS +EXTCODECOPY + +0 MLOAD ;; to dump memory + +0 32 RETURN +) \ No newline at end of file diff --git a/evmcc/test/ext/store_delete.evm b/evmcc/test/ext/store_delete.evm new file mode 100644 index 000000000..d6acae03d --- /dev/null +++ b/evmcc/test/ext/store_delete.evm @@ -0,0 +1 @@ +6104d26063576000606357 diff --git a/evmcc/test/ext/store_delete.lll b/evmcc/test/ext/store_delete.lll new file mode 100644 index 000000000..3d8f0f23a --- /dev/null +++ b/evmcc/test/ext/store_delete.lll @@ -0,0 +1,9 @@ + +(asm +1234 +99 +SSTORE +0 +99 +SSTORE +) \ No newline at end of file diff --git a/evmcc/test/ext/store_test.evm b/evmcc/test/ext/store_test.evm new file mode 100644 index 000000000..54c9419b5 --- /dev/null +++ b/evmcc/test/ext/store_test.evm @@ -0,0 +1 @@ +607b607c60015760005760015660005603 diff --git a/evmcc/test/ext/store_test.lll b/evmcc/test/ext/store_test.lll new file mode 100644 index 000000000..c40471c40 --- /dev/null +++ b/evmcc/test/ext/store_test.lll @@ -0,0 +1,14 @@ + +(asm +123 +124 +1 +SSTORE +0 +SSTORE +1 +SLOAD +0 +SLOAD +SUB +) \ No newline at end of file diff --git a/evmcc/test/jump/ackermann.ethel b/evmcc/test/jump/ackermann.ethel new file mode 100644 index 000000000..971fd2b8d --- /dev/null +++ b/evmcc/test/jump/ackermann.ethel @@ -0,0 +1,7 @@ +let A m n = + if m == 0 then n+1 + else if n == 0 then A (m-1) 1 + else A (m-1) (A (m) (n-1)) + +return A 3 8 + diff --git a/evmcc/test/jump/ackermann.evm b/evmcc/test/jump/ackermann.evm new file mode 100644 index 000000000..964844045 --- /dev/null +++ b/evmcc/test/jump/ackermann.evm @@ -0,0 +1 @@ +6009600360086012585d60005460206000f26000820e6047596000810e603859603460018303603084600185036012585d6012585d60445860436001830360016012585d604b5860018101905090509058 \ No newline at end of file diff --git a/evmcc/test/jump/badindirect1.evm b/evmcc/test/jump/badindirect1.evm new file mode 100644 index 000000000..b2a8aad67 --- /dev/null +++ b/evmcc/test/jump/badindirect1.evm @@ -0,0 +1 @@ +601b602502585d diff --git a/evmcc/test/jump/badindirect1.lll b/evmcc/test/jump/badindirect1.lll new file mode 100644 index 000000000..d6291be68 --- /dev/null +++ b/evmcc/test/jump/badindirect1.lll @@ -0,0 +1,9 @@ +;; Indirect jump out of code + +(asm +27 +37 +MUL +JUMP +JUMPDEST +) \ No newline at end of file diff --git a/evmcc/test/jump/badindirect2.evm b/evmcc/test/jump/badindirect2.evm new file mode 100644 index 000000000..22217523d --- /dev/null +++ b/evmcc/test/jump/badindirect2.evm @@ -0,0 +1 @@ +60016003600302596000600058 diff --git a/evmcc/test/jump/badindirect2.lll b/evmcc/test/jump/badindirect2.lll new file mode 100644 index 000000000..53a6294f7 --- /dev/null +++ b/evmcc/test/jump/badindirect2.lll @@ -0,0 +1,12 @@ +;; Indirect jump into data + +(asm +1 ;; 0 +3 +3 +MUL ;; 6 +JUMPI ;; 7 +0 ;; 8 +0 +JUMP +) \ No newline at end of file diff --git a/evmcc/test/jump/badjump1.evm b/evmcc/test/jump/badjump1.evm new file mode 100644 index 000000000..5c11a8661 --- /dev/null +++ b/evmcc/test/jump/badjump1.evm @@ -0,0 +1 @@ +6103e758 diff --git a/evmcc/test/jump/badjump1.lll b/evmcc/test/jump/badjump1.lll new file mode 100644 index 000000000..1834a62ef --- /dev/null +++ b/evmcc/test/jump/badjump1.lll @@ -0,0 +1,6 @@ +;; Direct jump out of code. + +(asm +999 +JUMP +) \ No newline at end of file diff --git a/evmcc/test/jump/badjump2.evm b/evmcc/test/jump/badjump2.evm new file mode 100644 index 000000000..900a1c15a --- /dev/null +++ b/evmcc/test/jump/badjump2.evm @@ -0,0 +1 @@ +6004586000600058 diff --git a/evmcc/test/jump/badjump2.lll b/evmcc/test/jump/badjump2.lll new file mode 100644 index 000000000..ce61276d7 --- /dev/null +++ b/evmcc/test/jump/badjump2.lll @@ -0,0 +1,9 @@ +;; Direct jump into data + +(asm +4 ;; 0 0-3 +JUMP ;; 2 +0 ;; 3 3-4 +0 ;; 5 4-7 +JUMP ;; 6 +) \ No newline at end of file diff --git a/evmcc/test/jump/call1.ethel b/evmcc/test/jump/call1.ethel new file mode 100644 index 000000000..414ad0124 --- /dev/null +++ b/evmcc/test/jump/call1.ethel @@ -0,0 +1,5 @@ +let f n = n + 1 + +return f 2 + + diff --git a/evmcc/test/jump/call1.evm b/evmcc/test/jump/call1.evm new file mode 100644 index 000000000..252aaf778 --- /dev/null +++ b/evmcc/test/jump/call1.evm @@ -0,0 +1 @@ +600760026010585d60005460206000f28060010190509058 \ No newline at end of file diff --git a/evmcc/test/jump/call2.ethel b/evmcc/test/jump/call2.ethel new file mode 100644 index 000000000..bdeb9b734 --- /dev/null +++ b/evmcc/test/jump/call2.ethel @@ -0,0 +1,5 @@ +let f a b = a + b + +return f 2 3 + + diff --git a/evmcc/test/jump/call2.evm b/evmcc/test/jump/call2.evm new file mode 100644 index 000000000..6832e044d --- /dev/null +++ b/evmcc/test/jump/call2.evm @@ -0,0 +1 @@ +6009600260036012585d60005460206000f2818101905090509058 \ No newline at end of file diff --git a/evmcc/test/jump/fac.ethel b/evmcc/test/jump/fac.ethel new file mode 100644 index 000000000..8bfe94dd6 --- /dev/null +++ b/evmcc/test/jump/fac.ethel @@ -0,0 +1,5 @@ +let fac n = + if n == 0 then 1 + else n * fac (n-1) + +return fac 60 \ No newline at end of file diff --git a/evmcc/test/jump/fac.evm b/evmcc/test/jump/fac.evm new file mode 100644 index 000000000..04cd3e4f4 --- /dev/null +++ b/evmcc/test/jump/fac.evm @@ -0,0 +1 @@ +6007603c6010585d60005460206000f26000810e6026596020600182036010585d8102602858600190509058 \ No newline at end of file diff --git a/evmcc/test/jump/fac_tail.ethel b/evmcc/test/jump/fac_tail.ethel new file mode 100644 index 000000000..9ce5ecac7 --- /dev/null +++ b/evmcc/test/jump/fac_tail.ethel @@ -0,0 +1,5 @@ +let fac a n = + if n == 0 then a + else fac (a*n) (n-1) + +return fac 1 60 \ No newline at end of file diff --git a/evmcc/test/jump/fac_tail.evm b/evmcc/test/jump/fac_tail.evm new file mode 100644 index 000000000..8384d94e4 --- /dev/null +++ b/evmcc/test/jump/fac_tail.evm @@ -0,0 +1 @@ +60096001603c6012585d60005460206000f26000810e6029596025818302600183036012585d602a5881905090509058 \ No newline at end of file diff --git a/evmcc/test/jump/fib1.ethel b/evmcc/test/jump/fib1.ethel new file mode 100644 index 000000000..81b869f41 --- /dev/null +++ b/evmcc/test/jump/fib1.ethel @@ -0,0 +1,6 @@ +let fib n = + if n < 3 then 1 + else fib (n-1) + fib (n-2) + +return fib 10 + diff --git a/evmcc/test/jump/fib1.evm b/evmcc/test/jump/fib1.evm new file mode 100644 index 000000000..5042a192f --- /dev/null +++ b/evmcc/test/jump/fib1.evm @@ -0,0 +1 @@ +6007600a6010585d60005460206000f26003810a602f596020600282036010585d602a600183036010585d01603158600190509058 \ No newline at end of file diff --git a/evmcc/test/jump/for1.evm b/evmcc/test/jump/for1.evm new file mode 100644 index 000000000..f8e65cbf2 --- /dev/null +++ b/evmcc/test/jump/for1.evm @@ -0,0 +1 @@ +600a60805460006080530b0f60255960a0536080530160a054600160805303608054600558 diff --git a/evmcc/test/jump/for1.lll b/evmcc/test/jump/for1.lll new file mode 100644 index 000000000..419fc9b54 --- /dev/null +++ b/evmcc/test/jump/for1.lll @@ -0,0 +1,3 @@ +(for [i]:10 (> @i 0) [i](- @i 1) + [j](+ @i @j) +) diff --git a/evmcc/test/jump/for2.evm b/evmcc/test/jump/for2.evm new file mode 100644 index 000000000..628297778 --- /dev/null +++ b/evmcc/test/jump/for2.evm @@ -0,0 +1 @@ +6000608054600a6080530a0f60255960a0536080530160a054600160805301608054600558 diff --git a/evmcc/test/jump/for2.lll b/evmcc/test/jump/for2.lll new file mode 100644 index 000000000..de17d65ac --- /dev/null +++ b/evmcc/test/jump/for2.lll @@ -0,0 +1,3 @@ +(for [i]:0 (< @i 10) [i](+ @i 1) + [j](+ @i @j) +) diff --git a/evmcc/test/jump/if1.ethel b/evmcc/test/jump/if1.ethel new file mode 100644 index 000000000..85c3e126b --- /dev/null +++ b/evmcc/test/jump/if1.ethel @@ -0,0 +1 @@ +return if 0 then 1 else 2 \ No newline at end of file diff --git a/evmcc/test/jump/if1.evm b/evmcc/test/jump/if1.evm new file mode 100644 index 000000000..51fbe04bd --- /dev/null +++ b/evmcc/test/jump/if1.evm @@ -0,0 +1 @@ +60006300000010596002630000001258600160005460206000f2 \ No newline at end of file diff --git a/evmcc/test/jump/if2.ethel b/evmcc/test/jump/if2.ethel new file mode 100644 index 000000000..2a58d6365 --- /dev/null +++ b/evmcc/test/jump/if2.ethel @@ -0,0 +1 @@ +return if 1 then 1 else 2 \ No newline at end of file diff --git a/evmcc/test/jump/if2.evm b/evmcc/test/jump/if2.evm new file mode 100644 index 000000000..6d823b374 --- /dev/null +++ b/evmcc/test/jump/if2.evm @@ -0,0 +1 @@ +60016300000010596002630000001258600160005460206000f2 \ No newline at end of file diff --git a/evmcc/test/jump/indirect1.evm b/evmcc/test/jump/indirect1.evm new file mode 100644 index 000000000..ab6928304 --- /dev/null +++ b/evmcc/test/jump/indirect1.evm @@ -0,0 +1 @@ +600460030158005d6001600054 diff --git a/evmcc/test/jump/indirect1.lll b/evmcc/test/jump/indirect1.lll new file mode 100644 index 000000000..1ee7dc347 --- /dev/null +++ b/evmcc/test/jump/indirect1.lll @@ -0,0 +1,13 @@ +;; Indirect JUMP + +(asm +4 ;; 0 +3 ;; 2 +ADD ;; 4 +JUMP ;; 5 +STOP ;; 6 +JUMPDEST ;; 7 +1 +0 +MSTORE +) \ No newline at end of file diff --git a/evmcc/test/jump/indirect2.evm b/evmcc/test/jump/indirect2.evm new file mode 100644 index 000000000..e9697eaa1 --- /dev/null +++ b/evmcc/test/jump/indirect2.evm @@ -0,0 +1 @@ +600860060158005d6001600054005d600260005400 diff --git a/evmcc/test/jump/indirect2.lll b/evmcc/test/jump/indirect2.lll new file mode 100644 index 000000000..f2f068630 --- /dev/null +++ b/evmcc/test/jump/indirect2.lll @@ -0,0 +1,19 @@ +;; Indirect JUMP + +(asm +8 ;; 0 +6 ;; 2 +ADD ;; 4 +JUMP ;; 5 --> 14 +STOP ;; 6 +JUMPDEST ;; 7 +1 ;; 8 +0 ;; 10 +MSTORE ;; 12 +STOP ;; 13 +JUMPDEST ;; 14 +2 +0 +MSTORE +STOP +) \ No newline at end of file diff --git a/evmcc/test/jump/indirect3.evm b/evmcc/test/jump/indirect3.evm new file mode 100644 index 000000000..1fb0a356c --- /dev/null +++ b/evmcc/test/jump/indirect3.evm @@ -0,0 +1 @@ +6001600460050159005d6001600054 diff --git a/evmcc/test/jump/indirect3.lll b/evmcc/test/jump/indirect3.lll new file mode 100644 index 000000000..d6a679f9a --- /dev/null +++ b/evmcc/test/jump/indirect3.lll @@ -0,0 +1,14 @@ +;; Indirect JUMP + +(asm +1 ;; 0 +4 ;; 2 +5 ;; 4 +ADD ;; 6 +JUMPI ;; 7 +STOP ;; 8 +JUMPDEST ;; 9 +1 +0 +MSTORE +) \ No newline at end of file diff --git a/evmcc/test/jump/indirect4.evm b/evmcc/test/jump/indirect4.evm new file mode 100644 index 000000000..f0e31a8f4 --- /dev/null +++ b/evmcc/test/jump/indirect4.evm @@ -0,0 +1 @@ +60006007600501596001600054005d00 diff --git a/evmcc/test/jump/indirect4.lll b/evmcc/test/jump/indirect4.lll new file mode 100644 index 000000000..7fbe0b833 --- /dev/null +++ b/evmcc/test/jump/indirect4.lll @@ -0,0 +1,15 @@ +;; Indirect JUMP + +(asm +0 ;; 0 +7 ;; 2 +5 ;; 4 +ADD ;; 6 +JUMPI ;; 7 +1 ;; 8 +0 ;; 9 +MSTORE ;; 10 +STOP ;; 11 +JUMPDEST ;; 12 +STOP +) \ No newline at end of file diff --git a/evmcc/test/jump/jump1.evm b/evmcc/test/jump/jump1.evm new file mode 100644 index 000000000..0df9b4036 --- /dev/null +++ b/evmcc/test/jump/jump1.evm @@ -0,0 +1 @@ +600458006001600154 diff --git a/evmcc/test/jump/jump1.lll b/evmcc/test/jump/jump1.lll new file mode 100644 index 000000000..33119edb3 --- /dev/null +++ b/evmcc/test/jump/jump1.lll @@ -0,0 +1,11 @@ +;; Direct JUMP. +;; output: memory[1] == 1 + +(asm +4 ;; 0 +JUMP ;; 2 +STOP ;; 3 +1 ;; 4 +1 ;; 6 +MSTORE ;; 8 +) \ No newline at end of file diff --git a/evmcc/test/jump/jump2.evm b/evmcc/test/jump/jump2.evm new file mode 100644 index 000000000..35d75941d --- /dev/null +++ b/evmcc/test/jump/jump2.evm @@ -0,0 +1 @@ +6008586001600154 diff --git a/evmcc/test/jump/jump2.lll b/evmcc/test/jump/jump2.lll new file mode 100644 index 000000000..a70d50ecb --- /dev/null +++ b/evmcc/test/jump/jump2.lll @@ -0,0 +1,10 @@ +;; Direct JUMP to the end of code. +;; output: memory should have size 0. + +(asm +8 ;; 0 +JUMP ;; 2 +1 ;; 3 +1 ;; 5 +MSTORE ;; 7 +) \ No newline at end of file diff --git a/evmcc/test/jump/jump3.evm b/evmcc/test/jump/jump3.evm new file mode 100644 index 000000000..599d4a764 --- /dev/null +++ b/evmcc/test/jump/jump3.evm @@ -0,0 +1 @@ +602a586001600154 diff --git a/evmcc/test/jump/jump3.lll b/evmcc/test/jump/jump3.lll new file mode 100644 index 000000000..bc897e30c --- /dev/null +++ b/evmcc/test/jump/jump3.lll @@ -0,0 +1,10 @@ +;; Direct JUMP past the end of code. +;; output: memory should have size 0. + +(asm +42 +JUMP +1 +1 +MSTORE +) \ No newline at end of file diff --git a/evmcc/test/jump/jump4.evm b/evmcc/test/jump/jump4.evm new file mode 100644 index 000000000..41713f43e --- /dev/null +++ b/evmcc/test/jump/jump4.evm @@ -0,0 +1 @@ +600b6009580000600558005d6001600154 diff --git a/evmcc/test/jump/jump4.lll b/evmcc/test/jump/jump4.lll new file mode 100644 index 000000000..131baee2d --- /dev/null +++ b/evmcc/test/jump/jump4.lll @@ -0,0 +1,17 @@ +;; Direct JUMP. +;; output: memory[1] = 1 + +(asm +11 ;; 0 +9 ;; 2 +JUMP ;; 4 --> 9 +STOP ;; 5 +STOP ;; 6 +5 ;; 7 +JUMP ;; 9 --> 11 +STOP ;; 10 +JUMPDEST +1 ;; 11 +1 +MSTORE +) \ No newline at end of file diff --git a/evmcc/test/jump/jump5.evm b/evmcc/test/jump/jump5.evm new file mode 100644 index 000000000..c36d9615b --- /dev/null +++ b/evmcc/test/jump/jump5.evm @@ -0,0 +1 @@ +6005600e585d600160015400600f5800 diff --git a/evmcc/test/jump/jump5.lll b/evmcc/test/jump/jump5.lll new file mode 100644 index 000000000..d28b7d4ac --- /dev/null +++ b/evmcc/test/jump/jump5.lll @@ -0,0 +1,16 @@ +;; Direct JUMP. +;; output: memory[1] = 1 + +(asm +5 ;; 0 +14 ;; 2 +JUMP ;; 4 --> 14 +JUMPDEST ;; 5 +1 ;; 6 +1 ;; 8 +MSTORE ;; 10 +STOP ;; 11 +15 ;; 12 +JUMP ;; 14 --> 5 +STOP ;; 15 +) \ No newline at end of file diff --git a/evmcc/test/jump/jump6.evm b/evmcc/test/jump/jump6.evm new file mode 100644 index 000000000..029db7191 --- /dev/null +++ b/evmcc/test/jump/jump6.evm @@ -0,0 +1 @@ +600358600f600d58006014600758005d6001600154005d600260025400 diff --git a/evmcc/test/jump/jump6.lll b/evmcc/test/jump/jump6.lll new file mode 100644 index 000000000..1116aa663 --- /dev/null +++ b/evmcc/test/jump/jump6.lll @@ -0,0 +1,32 @@ +;; Direct JUMP. +;; output: memory[1] = 1 + +;; 0, 2 --> 3 .. 7 --> 13 -*-> 15 .. 19 + +(asm +3 ;; 0 +JUMP ;; 2 + +15 ;; 3 <- start +13 ;; 5 +JUMP ;; 7 <- b +STOP ;; 8 + +20 ;; 9 +7 ;; 11 + +JUMP ;; 13 <- a +STOP ;; 14 + +JUMPDEST ;; 15 <- c +1 ;; 16 +1 ;; 18 +MSTORE ;; 19 +STOP ;; 20 + +JUMPDEST ;; 21 <- d +2 ;; 22 +2 ;; 24 +MSTORE ;; 26 +STOP ;; 27 +) \ No newline at end of file diff --git a/evmcc/test/jump/jumpi_at_the_end.evm b/evmcc/test/jump/jumpi_at_the_end.evm new file mode 100644 index 000000000..2d7411761 --- /dev/null +++ b/evmcc/test/jump/jumpi_at_the_end.evm @@ -0,0 +1 @@ +600a6000545d6000536001900380600054600659 diff --git a/evmcc/test/jump/jumpi_at_the_end.lll b/evmcc/test/jump/jumpi_at_the_end.lll new file mode 100644 index 000000000..263ada6a7 --- /dev/null +++ b/evmcc/test/jump/jumpi_at_the_end.lll @@ -0,0 +1 @@ +(asm 10 0 MSTORE JUMPDEST 0 MLOAD 1 SWAP1 SUB DUP1 0 MSTORE 6 JUMPI) \ No newline at end of file diff --git a/evmcc/test/jump/loop1.evm b/evmcc/test/jump/loop1.evm new file mode 100644 index 000000000..7724d6308 --- /dev/null +++ b/evmcc/test/jump/loop1.evm @@ -0,0 +1 @@ +600a600181038060025960005460015460025400 diff --git a/evmcc/test/jump/loop1.lll b/evmcc/test/jump/loop1.lll new file mode 100644 index 000000000..0044ec1fb --- /dev/null +++ b/evmcc/test/jump/loop1.lll @@ -0,0 +1,27 @@ +;; Produces 1 2 3 4 5 6 7 8 9 10 on the stack and exits + +(asm +10 + +;; 2 +1 +DUP2 +SUB +DUP1 +2 +JUMPI + +;; stack = 1 2 3 4 5 6 7 8 9 10 +0 +MSTORE +1 +MSTORE +2 +MSTORE +;;3 +;;MSTORE + +STOP +) + + diff --git a/evmcc/test/jump/loop2.evm b/evmcc/test/jump/loop2.evm new file mode 100644 index 000000000..faffa4e5b --- /dev/null +++ b/evmcc/test/jump/loop2.evm @@ -0,0 +1 @@ +600a80600190038060025960005460015460025400 diff --git a/evmcc/test/jump/loop2.lll b/evmcc/test/jump/loop2.lll new file mode 100644 index 000000000..9996c52ba --- /dev/null +++ b/evmcc/test/jump/loop2.lll @@ -0,0 +1,28 @@ +;; Produces 1 2 3 4 5 6 7 8 9 10 on the stack and exits + +(asm +10 + +;; 2 +DUP1 +1 +SWAP1 +SUB +DUP1 +2 +JUMPI + +;; stack = 1 2 3 4 5 6 7 8 9 10 +0 +MSTORE +1 +MSTORE +2 +MSTORE +;;3 +;;MSTORE + +STOP +) + + diff --git a/evmcc/test/jump/rec1.ethel b/evmcc/test/jump/rec1.ethel new file mode 100644 index 000000000..f83c8e81e --- /dev/null +++ b/evmcc/test/jump/rec1.ethel @@ -0,0 +1,4 @@ +let f n = + if n == 0 then 2 else f (n-1) + +return f 10 diff --git a/evmcc/test/jump/rec1.evm b/evmcc/test/jump/rec1.evm new file mode 100644 index 000000000..2ae62aff6 --- /dev/null +++ b/evmcc/test/jump/rec1.evm @@ -0,0 +1 @@ +6007600a6010585d60005460206000f26000810e6024596020600182036010585d602658600290509058 \ No newline at end of file diff --git a/evmcc/test/jump/when1.asm b/evmcc/test/jump/when1.asm new file mode 100644 index 000000000..01d41c266 --- /dev/null +++ b/evmcc/test/jump/when1.asm @@ -0,0 +1,10 @@ +.code: + PUSH 1 + NOT + PUSH [tag0] + JUMPI + PUSH 13 + PUSH 128 + MSTORE +tag0: + diff --git a/evmcc/test/jump/when1.evm b/evmcc/test/jump/when1.evm new file mode 100644 index 000000000..303b02623 --- /dev/null +++ b/evmcc/test/jump/when1.evm @@ -0,0 +1 @@ +60010f600b59600d608054 diff --git a/evmcc/test/jump/when1.lll b/evmcc/test/jump/when1.lll new file mode 100644 index 000000000..990a6e64a --- /dev/null +++ b/evmcc/test/jump/when1.lll @@ -0,0 +1,2 @@ +(when (> 1 0) [i] 13) + \ No newline at end of file diff --git a/evmcc/test/kv.evm b/evmcc/test/kv.evm new file mode 100644 index 000000000..55141ea59 --- /dev/null +++ b/evmcc/test/kv.evm @@ -0,0 +1 @@ +33604557602a8060106000396000f200604556330e0f602a59366080530a0f602a59602060805301356080533557604060805301608054600958 diff --git a/evmcc/test/kv.lll b/evmcc/test/kv.lll new file mode 100644 index 000000000..c62d9fa70 --- /dev/null +++ b/evmcc/test/kv.lll @@ -0,0 +1,10 @@ +{ + [[69]] (caller) + (return 0 (lll + (when (= (caller) @@69) + (for {} (< @i (calldatasize)) [i](+ @i 64) + [[ (calldataload @i) ]] (calldataload (+ @i 32)) + ) + ) + 0)) +} diff --git a/evmcc/test/mem/byte.evm b/evmcc/test/mem/byte.evm new file mode 100644 index 000000000..ab63431ee --- /dev/null +++ b/evmcc/test/mem/byte.evm @@ -0,0 +1 @@ +7f112233445566778899001122334455667788990011223344556677889900aabb6000137f112233445566778899001122334455667788990011223344556677889900aabb6001137f112233445566778899001122334455667788990011223344556677889900aabb6002137f112233445566778899001122334455667788990011223344556677889900aabb6003137f112233445566778899001122334455667788990011223344556677889900aabb6004137f112233445566778899001122334455667788990011223344556677889900aabb6005137f112233445566778899001122334455667788990011223344556677889900aabb6006137f112233445566778899001122334455667788990011223344556677889900aabb6007137f112233445566778899001122334455667788990011223344556677889900aabb6008137f112233445566778899001122334455667788990011223344556677889900aabb6009137f112233445566778899001122334455667788990011223344556677889900aabb600a137f112233445566778899001122334455667788990011223344556677889900aabb600b137f112233445566778899001122334455667788990011223344556677889900aabb600c137f112233445566778899001122334455667788990011223344556677889900aabb600d137f112233445566778899001122334455667788990011223344556677889900aabb600e137f112233445566778899001122334455667788990011223344556677889900aabb600f137f112233445566778899001122334455667788990011223344556677889900aabb6010137f112233445566778899001122334455667788990011223344556677889900aabb6011137f112233445566778899001122334455667788990011223344556677889900aabb6012137f112233445566778899001122334455667788990011223344556677889900aabb6013137f112233445566778899001122334455667788990011223344556677889900aabb6014137f112233445566778899001122334455667788990011223344556677889900aabb6015137f112233445566778899001122334455667788990011223344556677889900aabb6016137f112233445566778899001122334455667788990011223344556677889900aabb6017137f112233445566778899001122334455667788990011223344556677889900aabb6018137f112233445566778899001122334455667788990011223344556677889900aabb6019137f112233445566778899001122334455667788990011223344556677889900aabb601a137f112233445566778899001122334455667788990011223344556677889900aabb601b137f112233445566778899001122334455667788990011223344556677889900aabb601c137f112233445566778899001122334455667788990011223344556677889900aabb601d137f112233445566778899001122334455667788990011223344556677889900aabb601e137f112233445566778899001122334455667788990011223344556677889900aabb601f137f112233445566778899001122334455667788990011223344556677889900aabb6020137f112233445566778899001122334455667788990011223344556677889900aabb6107de13 diff --git a/evmcc/test/mem/byte.lll b/evmcc/test/mem/byte.lll new file mode 100644 index 000000000..95b0f99dc --- /dev/null +++ b/evmcc/test/mem/byte.lll @@ -0,0 +1,105 @@ + +(asm +0x112233445566778899001122334455667788990011223344556677889900aabb +0 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +1 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +2 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +3 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +4 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +5 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +6 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +7 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +8 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +9 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +10 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +11 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +12 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +13 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +14 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +15 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +16 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +17 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +18 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +19 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +20 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +21 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +22 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +23 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +24 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +25 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +26 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +27 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +28 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +29 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +30 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +31 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +32 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +2014 +BYTE +) \ No newline at end of file diff --git a/evmcc/test/mem/mem2.evm b/evmcc/test/mem/mem2.evm new file mode 100644 index 000000000..49cc6e8b1 --- /dev/null +++ b/evmcc/test/mem/mem2.evm @@ -0,0 +1 @@ +6001610d805b01556504409585d6df620493e05462061a80535b01 diff --git a/evmcc/test/mem/mem2.lll b/evmcc/test/mem/mem2.lll new file mode 100644 index 000000000..5345ee47c --- /dev/null +++ b/evmcc/test/mem/mem2.lll @@ -0,0 +1,15 @@ + +(asm ;; [] +1 +3456 +MSIZE +ADD +MSTORE8 ;; [02] +4675432994527 +300000 +MSTORE +400000 +MLOAD +MSIZE +ADD +) \ No newline at end of file diff --git a/evmcc/test/mem/memtest1.evm b/evmcc/test/mem/memtest1.evm new file mode 100644 index 000000000..0506bf928 --- /dev/null +++ b/evmcc/test/mem/memtest1.evm @@ -0,0 +1 @@ +6002600055600360015560005360015301600254 diff --git a/evmcc/test/mem/memtest1.lll b/evmcc/test/mem/memtest1.lll new file mode 100644 index 000000000..4b4389ad8 --- /dev/null +++ b/evmcc/test/mem/memtest1.lll @@ -0,0 +1,18 @@ + +(asm ;; [] +2 +0 +MSTORE8 ;; [02] +3 +1 +MSTORE8 ;; [02 03] +0 +MLOAD ;; [2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] +1 +MLOAD ;; [2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ;; 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] +ADD +2 +MSTORE ;; [2 3 5 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ;; 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] +) \ No newline at end of file diff --git a/evmcc/test/mem/mstore1.evm b/evmcc/test/mem/mstore1.evm new file mode 100644 index 000000000..ba6141ab1 --- /dev/null +++ b/evmcc/test/mem/mstore1.evm @@ -0,0 +1 @@ +6001600054 diff --git a/evmcc/test/mem/mstore1.lll b/evmcc/test/mem/mstore1.lll new file mode 100644 index 000000000..2d2ca32b5 --- /dev/null +++ b/evmcc/test/mem/mstore1.lll @@ -0,0 +1,6 @@ + +(asm ;; [] +1 +0 +MSTORE ;; [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1] +) \ No newline at end of file diff --git a/evmcc/test/ret/return1.evm b/evmcc/test/ret/return1.evm new file mode 100644 index 000000000..8092cb007 --- /dev/null +++ b/evmcc/test/ret/return1.evm @@ -0,0 +1 @@ +600160805460006080530b601b59600160005460206000f2602a58602760005460206000f26002608054 diff --git a/evmcc/test/ret/return1.lll b/evmcc/test/ret/return1.lll new file mode 100644 index 000000000..159d15ca3 --- /dev/null +++ b/evmcc/test/ret/return1.lll @@ -0,0 +1,6 @@ +;; code should return 39 +;; i should remain 1 +{ + [i] 1 + ( if (> @i 0) { (return 39) [i] 2 } (return 1) ) +} \ No newline at end of file diff --git a/evmcc/test/ret/return2.evm b/evmcc/test/ret/return2.evm new file mode 100644 index 000000000..e29da7664 --- /dev/null +++ b/evmcc/test/ret/return2.evm @@ -0,0 +1 @@ +6001620f4240f2 diff --git a/evmcc/test/ret/return2.lll b/evmcc/test/ret/return2.lll new file mode 100644 index 000000000..f5ee68f6e --- /dev/null +++ b/evmcc/test/ret/return2.lll @@ -0,0 +1,6 @@ + +(asm +1 +1000000 +RETURN ;; return 1 byte from index 1M +) \ No newline at end of file diff --git a/evmcc/test/ret/return_test.evm b/evmcc/test/ret/return_test.evm new file mode 100644 index 000000000..977cf7c19 --- /dev/null +++ b/evmcc/test/ret/return_test.evm @@ -0,0 +1 @@ +60016064546002608454600360a45460606064f2 diff --git a/evmcc/test/ret/return_test.lll b/evmcc/test/ret/return_test.lll new file mode 100644 index 000000000..c87a2d812 --- /dev/null +++ b/evmcc/test/ret/return_test.lll @@ -0,0 +1,15 @@ + +(asm +1 +100 +MSTORE +2 +132 +MSTORE +3 +164 +MSTORE +96 +100 +RETURN +) \ No newline at end of file diff --git a/evmcc/test/stack/oos.evm b/evmcc/test/stack/oos.evm new file mode 100644 index 000000000..ea2f1c890 --- /dev/null +++ b/evmcc/test/stack/oos.evm @@ -0,0 +1 @@ +60018194505050509150 diff --git a/evmcc/test/stack/oos.lll b/evmcc/test/stack/oos.lll new file mode 100644 index 000000000..5394b06ba --- /dev/null +++ b/evmcc/test/stack/oos.lll @@ -0,0 +1,11 @@ +(asm ;; x . v y z +1 ;; 1 x . v y z +DUP2 ;; x 1 x . v y z +SWAP5 ;; y 1 x . v x z +POP ;; 1 x . v x z +POP ;; x . v x z +POP ;; . v x z +POP ;; v x z +SWAP2 ;; z x v +POP ;; x v +) diff --git a/evmcc/test/stack/push_test.evm b/evmcc/test/stack/push_test.evm new file mode 100644 index 000000000..d624cee1d --- /dev/null +++ b/evmcc/test/stack/push_test.evm @@ -0,0 +1 @@ +60656107d26204a0c763026921f4640bc5588eb165372d0f1dca6e661ba1d901961c71670c55f7bc23038e3868056bc75e2d630fffff69021e19e0c9bab24000016a085d1c6e8050f0ea1c71bd6b0688be36543f3c36e638e37a6c03d41f73d55d0d482ae55555376dc76810d0fe03c91964d31c71c6f46e615dd0360c07d931663b14e38e38b16f2da3f99955a3adcf27ebb1caaaaaaa6e7014ccba6a8bb1ed35bd86bf065c71c71c2b7109491c5d4781b79c9009de6bfb8e38e38de8720414a0f6fdec81304d4c563e740bffffffffa573118427b3b4a05bc8a8a4de8459868000000000017406eb15e7331e727940d4ac54b7cdca1c71c71c71bd750567a91c9fefc96ebaa626a22f98c5e638e38e38e37a76032abd16c5b68006e15d5aa307e383f4e55555555555377701a6427bdc4f0d58eab5f48a3ec67f64e21c71c71c71c6f478080dd0a0c9b9ff2c2a0c740b06853a0a980ee38e38e38e38b17903c679cb5e8f2f9cb3b5d6652b0e7334f746faaaaaaaaaaaaa6e7a01b873815917ebb2bf3b890a1af495d6235bae3c71c71c71c71c2b7b07ae4cca96e1a55dfa49c85ad3c3e60e426b92fb8e38e38e38e38de87c036018bf074e292bcc7d6c8bea0f9699443046178bffffffffffffffa57d0e7d34c64a9c85d4460dbbca87196b61618a4bd2168000000000000000017e05b901f48a5b994d6572502bc4ea43140486666416aa1c71c71c71c71c71bd7f047889870c178fc477414ea231d70467a388fffe31b4e638e38e38e38e38e37a diff --git a/evmcc/test/stack/push_test.lll b/evmcc/test/stack/push_test.lll new file mode 100644 index 000000000..832daaec1 --- /dev/null +++ b/evmcc/test/stack/push_test.lll @@ -0,0 +1,35 @@ + +(asm +101 ;; PUSH1 +2002 ;; PUSH2 +303303 ;; PUSH3 +40444404 ;; PUSH4 +50555555505 ;; PUSH5 +60666666666606 +7777777777777777 +888888888888888888 +99999999999999999999 +10000000000000000000001 +10111111111111111111111101 +2022222222222222222222222202 +303333333333333333333333333303 +4044444444444444444444444444444404 +505555555555555555555555555555555505 +60666666666666666666666666666666666606 +7077777777777777777777777777777777777707 +808888888888888888888888888888888888888808 +90999999999999999999999999999999999999999909 +100000000000000000000000000000000000000000000001 +10111111111111111111111111111111111111111111111101 +2022222222222222222222222222222222222222222222222202 +303333333333333333333333333333333333333333333333333303 +40444444444444444444444444444444444444444444444444444404 +50555555555555555555555555555555555555555555555555555555505 +6066666666666666666666666666666666666666666666666666666666606 +707777777777777777777777777777777777777777777777777777777777707 +808888888888888888888888888888888888888888888888888888888888888808 +90999999999999999999999999999999999999999999999999999999999999999909 +100000000000000000000000000000000000000000000000000000000000000000000001 +10111111111111111111111111111111111111111111111111111111111111111111111101 +2022222222222222222222222222222222222222222222222222222222222222222222222202 ;; PUSH32 +) \ No newline at end of file diff --git a/evmcc/test/stack/stack_test.evm b/evmcc/test/stack/stack_test.evm new file mode 100644 index 000000000..02417c967 --- /dev/null +++ b/evmcc/test/stack/stack_test.evm @@ -0,0 +1 @@ +65372d0f1dca6e661925338e3e5c2b808280848184505050505050506104576108ae81819290 diff --git a/evmcc/test/stack/stack_test.lll b/evmcc/test/stack/stack_test.lll new file mode 100644 index 000000000..fdf83594c --- /dev/null +++ b/evmcc/test/stack/stack_test.lll @@ -0,0 +1,8 @@ + +(asm +123 +SSTORE +SLOAD +123 +SUB +) \ No newline at end of file diff --git a/evmcc/test/stack/stackjump.evm b/evmcc/test/stack/stackjump.evm new file mode 100644 index 000000000..baddec42e --- /dev/null +++ b/evmcc/test/stack/stackjump.evm @@ -0,0 +1 @@ +600460066009601358600a036000545b6000f260005401600958 \ No newline at end of file diff --git a/evmcc/test/stack/stackjump.lll b/evmcc/test/stack/stackjump.lll new file mode 100644 index 000000000..f5da5e733 --- /dev/null +++ b/evmcc/test/stack/stackjump.lll @@ -0,0 +1,3 @@ +(asm +0x4 0x6 0x9 0x13 JUMP 0xa SUB 0x0 MSTORE MSIZE 0x0 RETURN 0x0 MSTORE ADD 0x9 JUMP +) diff --git a/evmcc/test/stack/swap.evm b/evmcc/test/stack/swap.evm new file mode 100644 index 000000000..d17f0ee09 --- /dev/null +++ b/evmcc/test/stack/swap.evm @@ -0,0 +1 @@ +600560026001600c59505000906001601559505000036000546001601ff2 diff --git a/evmcc/test/stack/swap.lll b/evmcc/test/stack/swap.lll new file mode 100644 index 000000000..90dee585d --- /dev/null +++ b/evmcc/test/stack/swap.lll @@ -0,0 +1,31 @@ +(asm +5 ;; 0 +2 ;; 2 +1 ;; 4 +12 ;; 6 +JUMPI ;; 8 + +POP ;; 9 +POP ;; 10 +STOP ;; 11 + +;; stack = 2,1 +SWAP1 ;; 12 +1 ;; 13 +21 ;; 15 +JUMPI ;; 17 + +POP ;; 18 +POP ;; 19 +STOP ;; 20 + +;; stack = 1,2 +SUB ;; 21 +0 +MSTORE +1 +31 +RETURN ;; returns 03 +) + + diff --git a/evmcc/test/stack/swapswap.evm b/evmcc/test/stack/swapswap.evm new file mode 100644 index 000000000..fb4f1bf75 --- /dev/null +++ b/evmcc/test/stack/swapswap.evm @@ -0,0 +1 @@ +600260056001600c5950500090906001601659505000036000546001601ff2 diff --git a/evmcc/test/stack/swapswap.lll b/evmcc/test/stack/swapswap.lll new file mode 100644 index 000000000..1fedf726e --- /dev/null +++ b/evmcc/test/stack/swapswap.lll @@ -0,0 +1,32 @@ +(asm +2 ;; 0 +5 ;; 2 +1 ;; 4 +12 ;; 6 +JUMPI ;; 8 + +POP ;; 9 +POP ;; 10 +STOP ;; 11 + +;; stack = 2,1 +SWAP1 ;; 12 +SWAP1 ;; 13 +1 ;; 14 +22 ;; 16 +JUMPI ;; 18 + +POP ;; 19 +POP ;; 20 +STOP ;; 21 + +;; stack = 2,1 +SUB ;; 22 +0 +MSTORE +1 +31 +RETURN ;; returns 03 +) + + diff --git a/evmcc/test/stack/test.evm b/evmcc/test/stack/test.evm new file mode 100644 index 000000000..ea2f1c890 --- /dev/null +++ b/evmcc/test/stack/test.evm @@ -0,0 +1 @@ +60018194505050509150 diff --git a/evmcc/test/vmtests/vmArithPerformanceTest.json b/evmcc/test/vmtests/vmArithPerformanceTest.json new file mode 100644 index 000000000..d9017517f --- /dev/null +++ b/evmcc/test/vmtests/vmArithPerformanceTest.json @@ -0,0 +1,260 @@ +{ + "arith-1" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x600a60005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "999538", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x600a60005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x600a60005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "nonce" : "0", + "storage" : { } + } + } + } + + , + + "arith-2" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x606460005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "995488", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x606460005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x606460005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "nonce" : "0", + "storage" : { } + } + } + } + + , + + "arith-3" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x6103e860005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "954988", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6103e860005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6103e860005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "nonce" : "0", + "storage" : { } + } + } + } + + , + + "arith-4" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x61271060005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "549988", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x61271060005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x61271060005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "nonce" : "0", + "storage" : { } + } + } + } + + + , + + + "arith-5" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x620186a060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "data" : "0x", + "gas" : "10000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "5499988", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620186a060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620186a060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "nonce" : "0", + "storage" : { } + } + } + } + +, + + "arith-6" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x620f424060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "data" : "0x", + "gas" : "100000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "54999988", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620f424060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620f424060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "nonce" : "0", + "storage" : { } + } + } + } + +} diff --git a/evmcc/test/vmtests/vmPerformanceTest.json b/evmcc/test/vmtests/vmPerformanceTest.json new file mode 100644 index 000000000..604e45993 --- /dev/null +++ b/evmcc/test/vmtests/vmPerformanceTest.json @@ -0,0 +1,214 @@ +{ + "mulmodloop" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x60015b68010000000000000000908060010109600356", + "data" : "0x", + "gas" : "10000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "0", + "out" : "0x0", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x60015b68010000000000000000908060010109600356", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x60015b68010000000000000000908060010109600356", + "nonce" : "0", + "storage" : { } + } + } + }, + + + "for-1e06" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x620f42406080525b6000608051111560285760a0516080510160a0526001608051036080526007565b", + "data" : "0x", + "gas" : "30000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "6999982", + "out" : "0x00", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620f42406080525b6000608051111560285760a0516080510160a0526001608051036080526007565b", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620f42406080525b6000608051111560285760a0516080510160a0526001608051036080526007565b", + "nonce" : "0", + "storage" : { } + } + } + }, + + "fib25" : { + "callcreates" : [ + ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x6007601e6010565b60005260206000f25b600381106030576021600282036010565b602b600183036010565b016033565b60015b90509056", + "data" : "0x", + "gas" : "40000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "5886377", + "out" : "0x00000000000000000000000000000000000000000000000000000000000cb228", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6007601e6010565b60005260206000f25b600381106030576021600282036010565b602b600183036010565b016033565b60015b90509056", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6007601e6010565b60005260206000f25b600381106030576021600282036010565b602b600183036010565b016033565b60015b90509056", + "nonce" : "0", + "storage" : { + } + } + } + }, + + "ackermann37" : { + "callcreates" : [ + ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "20000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x6009600360076012565b60005260206000f25b60008214604a5760008114603957603560018303603184600185036012565b6012565b6046565b60456001830360016012565b5b604f565b600181015b905090509056", + "data" : "0x", + "gas" : "20000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "913456", + "out" : "0x00000000000000000000000000000000000000000000000000000000000003fd", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6009600360076012565b60005260206000f25b60008214604a5760008114603957603560018303603184600185036012565b6012565b6046565b60456001830360016012565b5b604f565b600181015b905090509056", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6009600360076012565b60005260206000f25b60008214604a5760008114603957603560018303603184600185036012565b6012565b6046565b60456001830360016012565b5b604f565b600181015b905090509056", + "nonce" : "0", + "storage" : { + } + } + } + }, + + "jumptable100" : { + "callcreates" : [ + ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x6103e85b60019003806000146104605761001a600160005256005b610025600260005256005b610030600360005256005b61003b600460005256005b610046600560005256005b610051600660005256005b61005c600760005256005b610067600860005256005b610072600960005256005b61007d600a60005256005b610088600b60005256005b610093600c60005256005b61009e600d60005256005b6100a9600e60005256005b6100b4600f60005256005b6100bf601060005256005b6100ca601160005256005b6100d5601260005256005b6100e0601360005256005b6100eb601460005256005b6100f6601560005256005b610101601660005256005b61010c601760005256005b610117601860005256005b610122601960005256005b61012d601a60005256005b610138601b60005256005b610143601c60005256005b61014e601d60005256005b610159601e60005256005b610164601f60005256005b61016f602060005256005b61017a602160005256005b610185602260005256005b610190602360005256005b61019b602460005256005b6101a6602560005256005b6101b1602660005256005b6101bc602760005256005b6101c7602860005256005b6101d2602960005256005b6101dd602a60005256005b6101e8602b60005256005b6101f3602c60005256005b6101fe602d60005256005b610209602e60005256005b610214602f60005256005b61021f603060005256005b61022a603160005256005b610235603260005256005b610240603360005256005b61024b603460005256005b610256603560005256005b610261603660005256005b61026c603760005256005b610277603860005256005b610282603960005256005b61028d603a60005256005b610298603b60005256005b6102a3603c60005256005b6102ae603d60005256005b6102b9603e60005256005b6102c4603f60005256005b6102cf604060005256005b6102da604160005256005b6102e5604260005256005b6102f0604360005256005b6102fb604460005256005b610306604560005256005b610311604660005256005b61031c604760005256005b610327604860005256005b610332604960005256005b61033d604a60005256005b610348604b60005256005b610353604c60005256005b61035e604d60005256005b610369604e60005256005b610374604f60005256005b61037f605060005256005b61038a605160005256005b610395605260005256005b6103a0605360005256005b6103ab605460005256005b6103b6605560005256005b6103c1605660005256005b6103cc605760005256005b6103d7605860005256005b6103e2605960005256005b6103ed605a60005256005b6103f8605b60005256005b610403605c60005256005b61040e605d60005256005b610419605e60005256005b610424605f60005256005b61042f606060005256005b61043a606160005256005b610445606260005256005b610450606360005256005b61045b606460005256005b610003565b60206000f2", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "389596", + "out" : "0x0000000000000000000000000000000000000000000000000000000000000064", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6103e85b60019003806000146104605761001a600160005256005b610025600260005256005b610030600360005256005b61003b600460005256005b610046600560005256005b610051600660005256005b61005c600760005256005b610067600860005256005b610072600960005256005b61007d600a60005256005b610088600b60005256005b610093600c60005256005b61009e600d60005256005b6100a9600e60005256005b6100b4600f60005256005b6100bf601060005256005b6100ca601160005256005b6100d5601260005256005b6100e0601360005256005b6100eb601460005256005b6100f6601560005256005b610101601660005256005b61010c601760005256005b610117601860005256005b610122601960005256005b61012d601a60005256005b610138601b60005256005b610143601c60005256005b61014e601d60005256005b610159601e60005256005b610164601f60005256005b61016f602060005256005b61017a602160005256005b610185602260005256005b610190602360005256005b61019b602460005256005b6101a6602560005256005b6101b1602660005256005b6101bc602760005256005b6101c7602860005256005b6101d2602960005256005b6101dd602a60005256005b6101e8602b60005256005b6101f3602c60005256005b6101fe602d60005256005b610209602e60005256005b610214602f60005256005b61021f603060005256005b61022a603160005256005b610235603260005256005b610240603360005256005b61024b603460005256005b610256603560005256005b610261603660005256005b61026c603760005256005b610277603860005256005b610282603960005256005b61028d603a60005256005b610298603b60005256005b6102a3603c60005256005b6102ae603d60005256005b6102b9603e60005256005b6102c4603f60005256005b6102cf604060005256005b6102da604160005256005b6102e5604260005256005b6102f0604360005256005b6102fb604460005256005b610306604560005256005b610311604660005256005b61031c604760005256005b610327604860005256005b610332604960005256005b61033d604a60005256005b610348604b60005256005b610353604c60005256005b61035e604d60005256005b610369604e60005256005b610374604f60005256005b61037f605060005256005b61038a605160005256005b610395605260005256005b6103a0605360005256005b6103ab605460005256005b6103b6605560005256005b6103c1605660005256005b6103cc605760005256005b6103d7605860005256005b6103e2605960005256005b6103ed605a60005256005b6103f8605b60005256005b610403605c60005256005b61040e605d60005256005b610419605e60005256005b610424605f60005256005b61042f606060005256005b61043a606160005256005b610445606260005256005b610450606360005256005b61045b606460005256005b610003565b60206000f2", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6103e85b60019003806000146104605761001a600160005256005b610025600260005256005b610030600360005256005b61003b600460005256005b610046600560005256005b610051600660005256005b61005c600760005256005b610067600860005256005b610072600960005256005b61007d600a60005256005b610088600b60005256005b610093600c60005256005b61009e600d60005256005b6100a9600e60005256005b6100b4600f60005256005b6100bf601060005256005b6100ca601160005256005b6100d5601260005256005b6100e0601360005256005b6100eb601460005256005b6100f6601560005256005b610101601660005256005b61010c601760005256005b610117601860005256005b610122601960005256005b61012d601a60005256005b610138601b60005256005b610143601c60005256005b61014e601d60005256005b610159601e60005256005b610164601f60005256005b61016f602060005256005b61017a602160005256005b610185602260005256005b610190602360005256005b61019b602460005256005b6101a6602560005256005b6101b1602660005256005b6101bc602760005256005b6101c7602860005256005b6101d2602960005256005b6101dd602a60005256005b6101e8602b60005256005b6101f3602c60005256005b6101fe602d60005256005b610209602e60005256005b610214602f60005256005b61021f603060005256005b61022a603160005256005b610235603260005256005b610240603360005256005b61024b603460005256005b610256603560005256005b610261603660005256005b61026c603760005256005b610277603860005256005b610282603960005256005b61028d603a60005256005b610298603b60005256005b6102a3603c60005256005b6102ae603d60005256005b6102b9603e60005256005b6102c4603f60005256005b6102cf604060005256005b6102da604160005256005b6102e5604260005256005b6102f0604360005256005b6102fb604460005256005b610306604560005256005b610311604660005256005b61031c604760005256005b610327604860005256005b610332604960005256005b61033d604a60005256005b610348604b60005256005b610353604c60005256005b61035e604d60005256005b610369604e60005256005b610374604f60005256005b61037f605060005256005b61038a605160005256005b610395605260005256005b6103a0605360005256005b6103ab605460005256005b6103b6605560005256005b6103c1605660005256005b6103cc605760005256005b6103d7605860005256005b6103e2605960005256005b6103ed605a60005256005b6103f8605b60005256005b610403605c60005256005b61040e605d60005256005b610419605e60005256005b610424605f60005256005b61042f606060005256005b61043a606160005256005b610445606260005256005b610450606360005256005b61045b606460005256005b610003565b60206000f2", + "nonce" : "0", + "storage" : { + } + } + } + }, + +} diff --git a/evmcc/test/vmtests/vm_jump.json b/evmcc/test/vmtests/vm_jump.json new file mode 100644 index 000000000..6b63edeae --- /dev/null +++ b/evmcc/test/vmtests/vm_jump.json @@ -0,0 +1,41 @@ +{ + "jumpi_at_the_end" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "(asm 10 0 MSTORE JUMPDEST 0 MLOAD 1 SWAP1 SUB DUP1 0 MSTORE 6 JUMPI)", + "data" : "0x", + "gas" : "1000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "895", + "out" : "0x0", + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "(asm 10 0 MSTORE JUMPDEST 0 MLOAD 1 SWAP1 SUB DUP1 0 MSTORE 6 JUMPI)", + "nonce" : "0", + "storage" : {} + } + }, + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "(asm 10 0 MSTORE JUMPDEST 0 MLOAD 1 SWAP1 SUB DUP1 0 MSTORE 6 JUMPI)", + "nonce" : "0", + "storage" : {} + } + } + } +} diff --git a/libevmjit-cpp/CMakeLists.txt b/libevmjit-cpp/CMakeLists.txt new file mode 100644 index 000000000..25be95177 --- /dev/null +++ b/libevmjit-cpp/CMakeLists.txt @@ -0,0 +1,16 @@ +set(TARGET_NAME evmjit-cpp) + +set(SOURCES + Env.cpp + JitVM.cpp JitVM.h +) +source_group("" FILES ${SOURCES}) + +add_library(${TARGET_NAME} ${SOURCES}) +set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") + +include_directories(../..) +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(${TARGET_NAME} evmjit) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp new file mode 100644 index 000000000..1dcd38162 --- /dev/null +++ b/libevmjit-cpp/Env.cpp @@ -0,0 +1,125 @@ + +#include +#include +#include + +#include + +extern "C" +{ + #ifdef _MSC_VER + #define EXPORT __declspec(dllexport) + #else + #define EXPORT + #endif + + using namespace dev; + using namespace dev::eth; + using jit::i256; + using jit::eth2llvm; + + EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) + { + auto index = llvm2eth(*_index); + auto value = _env->store(index); // Interface uses native endianness + *o_value = eth2llvm(value); + } + + EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value) + { + auto index = llvm2eth(*_index); + auto value = llvm2eth(*_value); + + if (value == 0 && _env->store(index) != 0) // If delete + _env->sub.refunds += c_sstoreRefundGas; // Increase refund counter + + _env->setStore(index, value); // Interface uses native endianness + } + + EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value) + { + auto u = _env->balance(right160(*_address)); + *o_value = eth2llvm(u); + } + + EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) + { + if (_env->depth == 1024) + jit::terminate(jit::ReturnCode::OutOfGas); + + assert(_env->depth < 1024); + + auto endowment = llvm2eth(*_endowment); + + if (_env->balance(_env->myAddress) >= endowment) + { + _env->subBalance(endowment); + auto gas = llvm2eth(*io_gas); + OnOpFunc onOp {}; // TODO: Handle that thing + h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, onOp), h256::AlignRight); + *io_gas = eth2llvm(gas); + *o_address = address; + } + else + *o_address = {}; + } + + EXPORT bool env_call(ExtVMFace* _env, i256* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) + { + if (_env->depth == 1024) + jit::terminate(jit::ReturnCode::OutOfGas); + + assert(_env->depth < 1024); + + auto value = llvm2eth(*_value); + if (_env->balance(_env->myAddress) >= value) + { + _env->subBalance(value); + auto receiveAddress = right160(*_receiveAddress); + auto inRef = bytesConstRef{_inBeg, _inSize}; + auto outRef = bytesConstRef{_outBeg, _outSize}; + OnOpFunc onOp {}; // TODO: Handle that thing + auto codeAddress = right160(*_codeAddress); + auto gas = llvm2eth(*io_gas); + auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, onOp, {}, codeAddress); + *io_gas = eth2llvm(gas); + return ret; + } + + return false; + } + + EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) + { + auto hash = sha3({_begin, _size}); + *o_hash = hash; + } + + EXPORT byte const* env_getExtCode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) + { + auto addr = right160(*_addr256); + auto& code = _env->codeAt(addr); + *o_size = code.size(); + return code.data(); + } + + EXPORT void env_log(ExtVMFace* _env, byte* _beg, uint64_t _size, h256* _topic1, h256* _topic2, h256* _topic3, h256* _topic4) + { + dev::h256s topics; + + if (_topic1) + topics.push_back(*_topic1); + + if (_topic2) + topics.push_back(*_topic2); + + if (_topic3) + topics.push_back(*_topic3); + + if (_topic4) + topics.push_back(*_topic4); + + _env->log(std::move(topics), {_beg, _size}); + } +} + diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp new file mode 100644 index 000000000..815aa6332 --- /dev/null +++ b/libevmjit-cpp/JitVM.cpp @@ -0,0 +1,69 @@ + +#include "JitVM.h" +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) +{ + using namespace jit; + + m_data.set(RuntimeData::Gas, m_gas); + m_data.set(RuntimeData::Address, fromAddress(_ext.myAddress)); + m_data.set(RuntimeData::Caller, fromAddress(_ext.caller)); + m_data.set(RuntimeData::Origin, fromAddress(_ext.origin)); + m_data.set(RuntimeData::CallValue, _ext.value); + m_data.set(RuntimeData::CallDataSize, _ext.data.size()); + m_data.set(RuntimeData::GasPrice, _ext.gasPrice); + m_data.set(RuntimeData::PrevHash, _ext.previousBlock.hash); + m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress)); + m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp); + m_data.set(RuntimeData::Number, _ext.currentBlock.number); + m_data.set(RuntimeData::Difficulty, _ext.currentBlock.difficulty); + m_data.set(RuntimeData::GasLimit, _ext.currentBlock.gasLimit); + m_data.set(RuntimeData::CodeSize, _ext.code.size()); + m_data.callData = _ext.data.data(); + m_data.code = _ext.code.data(); + + auto env = reinterpret_cast(&_ext); + auto exitCode = m_engine.run(_ext.code, &m_data, env); + switch (exitCode) + { + case ReturnCode::Suicide: + _ext.suicide(right160(m_data.get(RuntimeData::SuicideDestAddress))); + break; + + case ReturnCode::BadJumpDestination: + BOOST_THROW_EXCEPTION(BadJumpDestination()); + case ReturnCode::OutOfGas: + BOOST_THROW_EXCEPTION(OutOfGas()); + case ReturnCode::StackTooSmall: + BOOST_THROW_EXCEPTION(StackTooSmall()); + case ReturnCode::BadInstruction: + BOOST_THROW_EXCEPTION(BadInstruction()); + default: + break; + } + + m_gas = llvm2eth(m_data.elems[RuntimeData::Gas]); + return {m_engine.returnData.data(), m_engine.returnData.size()}; // TODO: This all bytesConstRef is problematic, review. +} + +} +} + +namespace +{ + // MSVS linker ignores export symbols in Env.cpp if nothing points at least one of them + extern "C" void env_sload(); + void linkerWorkaround() + { + env_sload(); + (void)&linkerWorkaround; // suppress unused function warning from GCC + } +} diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h new file mode 100644 index 000000000..90855127e --- /dev/null +++ b/libevmjit-cpp/JitVM.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace dev +{ +namespace eth +{ + +class JitVM: public VMFace +{ + virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + + enum Kind: bool { Interpreter, JIT }; + static std::unique_ptr create(Kind, u256 _gas = 0); + +private: + friend class VMFactory; + explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} + + jit::RuntimeData m_data; + jit::ExecutionEngine m_engine; +}; + + +} +} diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp new file mode 100644 index 000000000..10d3e3449 --- /dev/null +++ b/libevmjit/Arith256.cpp @@ -0,0 +1,194 @@ +#include "Arith256.h" +#include "Runtime.h" +#include "Type.h" + +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Arith256::Arith256(llvm::IRBuilder<>& _builder) : + CompilerHelper(_builder) +{ + using namespace llvm; + + m_result = m_builder.CreateAlloca(Type::Word, nullptr, "arith.result"); + m_arg1 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg1"); + m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg2"); + m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg3"); + + using Linkage = GlobalValue::LinkageTypes; + + llvm::Type* arg2Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr}; + llvm::Type* arg3Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr}; + + m_mul = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mul", getModule()); + m_div = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_div", getModule()); + m_mod = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mod", getModule()); + m_sdiv = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_sdiv", getModule()); + m_smod = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_smod", getModule()); + m_exp = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_exp", getModule()); + m_addmod = Function::Create(FunctionType::get(Type::Void, arg3Types, false), Linkage::ExternalLinkage, "arith_addmod", getModule()); + m_mulmod = Function::Create(FunctionType::get(Type::Void, arg3Types, false), Linkage::ExternalLinkage, "arith_mulmod", getModule()); +} + +Arith256::~Arith256() +{} + +llvm::Value* Arith256::binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2) +{ + m_builder.CreateStore(_arg1, m_arg1); + m_builder.CreateStore(_arg2, m_arg2); + m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result); + return m_builder.CreateLoad(m_result); +} + +llvm::Value* Arith256::ternaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) +{ + m_builder.CreateStore(_arg1, m_arg1); + m_builder.CreateStore(_arg2, m_arg2); + m_builder.CreateStore(_arg3, m_arg3); + m_builder.CreateCall4(_op, m_arg1, m_arg2, m_arg3, m_result); + return m_builder.CreateLoad(m_result); +} + +llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) +{ + return binaryOp(m_mul, _arg1, _arg2); +} + +llvm::Value* Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) +{ + return binaryOp(m_div, _arg1, _arg2); +} + +llvm::Value* Arith256::mod(llvm::Value* _arg1, llvm::Value* _arg2) +{ + return binaryOp(m_mod, _arg1, _arg2); +} + +llvm::Value* Arith256::sdiv(llvm::Value* _arg1, llvm::Value* _arg2) +{ + return binaryOp(m_sdiv, _arg1, _arg2); +} + +llvm::Value* Arith256::smod(llvm::Value* _arg1, llvm::Value* _arg2) +{ + return binaryOp(m_smod, _arg1, _arg2); +} + +llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) +{ + return binaryOp(m_exp, _arg1, _arg2); +} + +llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) +{ + return ternaryOp(m_addmod, _arg1, _arg2, _arg3); +} + +llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) +{ + return ternaryOp(m_mulmod, _arg1, _arg2, _arg3); +} + +namespace +{ + using s256 = boost::multiprecision::int256_t; + + inline s256 u2s(u256 _u) + { + static const bigint c_end = (bigint)1 << 256; + static const u256 c_send = (u256)1 << 255; + if (_u < c_send) + return (s256)_u; + else + return (s256)-(c_end - _u); + } + + inline u256 s2u(s256 _u) + { + static const bigint c_end = (bigint)1 << 256; + if (_u >= 0) + return (u256)_u; + else + return (u256)(c_end + _u); + } +} + +} +} +} + + +extern "C" +{ + + using namespace dev::eth::jit; + + EXPORT void arith_mul(i256* _arg1, i256* _arg2, i256* o_result) + { + auto arg1 = llvm2eth(*_arg1); + auto arg2 = llvm2eth(*_arg2); + *o_result = eth2llvm(arg1 * arg2); + } + + EXPORT void arith_div(i256* _arg1, i256* _arg2, i256* o_result) + { + auto arg1 = llvm2eth(*_arg1); + auto arg2 = llvm2eth(*_arg2); + *o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 / arg2); + } + + EXPORT void arith_mod(i256* _arg1, i256* _arg2, i256* o_result) + { + auto arg1 = llvm2eth(*_arg1); + auto arg2 = llvm2eth(*_arg2); + *o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 % arg2); + } + + EXPORT void arith_sdiv(i256* _arg1, i256* _arg2, i256* o_result) + { + auto arg1 = llvm2eth(*_arg1); + auto arg2 = llvm2eth(*_arg2); + *o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) / u2s(arg2))); + } + + EXPORT void arith_smod(i256* _arg1, i256* _arg2, i256* o_result) + { + auto arg1 = llvm2eth(*_arg1); + auto arg2 = llvm2eth(*_arg2); + *o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) % u2s(arg2))); + } + + EXPORT void arith_exp(i256* _arg1, i256* _arg2, i256* o_result) + { + bigint left = llvm2eth(*_arg1); + bigint right = llvm2eth(*_arg2); + auto ret = static_cast(boost::multiprecision::powm(left, right, bigint(2) << 256)); + *o_result = eth2llvm(ret); + } + + EXPORT void arith_mulmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result) + { + auto arg1 = llvm2eth(*_arg1); + auto arg2 = llvm2eth(*_arg2); + auto arg3 = llvm2eth(*_arg3); + *o_result = eth2llvm(u256((bigint(arg1) * bigint(arg2)) % arg3)); + } + + EXPORT void arith_addmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result) + { + auto arg1 = llvm2eth(*_arg1); + auto arg2 = llvm2eth(*_arg2); + auto arg3 = llvm2eth(*_arg3); + *o_result = eth2llvm(u256((bigint(arg1) + bigint(arg2)) % arg3)); + } + +} + + diff --git a/libevmjit/Arith256.h b/libevmjit/Arith256.h new file mode 100644 index 000000000..57bc061de --- /dev/null +++ b/libevmjit/Arith256.h @@ -0,0 +1,49 @@ +#pragma once + +#include "CompilerHelper.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class Arith256 : public CompilerHelper +{ +public: + Arith256(llvm::IRBuilder<>& _builder); + virtual ~Arith256(); + + llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* div(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* mod(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* sdiv(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* smod(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); + llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); + +private: + llvm::Value* binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* ternaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); + + llvm::Function* m_mul; + llvm::Function* m_div; + llvm::Function* m_mod; + llvm::Function* m_sdiv; + llvm::Function* m_smod; + llvm::Function* m_exp; + llvm::Function* m_mulmod; + llvm::Function* m_addmod; + + llvm::Value* m_arg1; + llvm::Value* m_arg2; + llvm::Value* m_arg3; + llvm::Value* m_result; +}; + + +} +} +} diff --git a/libevmjit/BasicBlock.cpp b/libevmjit/BasicBlock.cpp new file mode 100644 index 000000000..d233ea744 --- /dev/null +++ b/libevmjit/BasicBlock.cpp @@ -0,0 +1,380 @@ + +#include "BasicBlock.h" + +#include + +#include +#include +#include +#include +#include + +#include "Type.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +const char* BasicBlock::NamePrefix = "Instr."; + +BasicBlock::BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) : + m_beginInstIdx(_beginInstIdx), + m_endInstIdx(_endInstIdx), + m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {NamePrefix, std::to_string(_beginInstIdx)}, _mainFunc)), + m_stack(*this), + m_builder(_builder) +{} + +BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) : + m_beginInstIdx(0), + m_endInstIdx(0), + m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)), + m_stack(*this), + m_builder(_builder) +{} + +BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) : + m_bblock(_owner) +{} + +void BasicBlock::LocalStack::push(llvm::Value* _value) +{ + m_bblock.m_currentStack.push_back(_value); + m_bblock.m_tosOffset += 1; +} + +llvm::Value* BasicBlock::LocalStack::pop() +{ + auto result = get(0); + + if (m_bblock.m_currentStack.size() > 0) + m_bblock.m_currentStack.pop_back(); + + m_bblock.m_tosOffset -= 1; + return result; +} + +/** + * Pushes a copy of _index-th element (tos is 0-th elem). + */ +void BasicBlock::LocalStack::dup(size_t _index) +{ + auto val = get(_index); + push(val); +} + +/** + * Swaps tos with _index-th element (tos is 0-th elem). + * _index must be > 0. + */ +void BasicBlock::LocalStack::swap(size_t _index) +{ + assert(_index > 0); + auto val = get(_index); + auto tos = get(0); + set(_index, tos); + set(0, val); +} + +std::vector::iterator BasicBlock::LocalStack::getItemIterator(size_t _index) +{ + auto& currentStack = m_bblock.m_currentStack; + if (_index < currentStack.size()) + return currentStack.end() - _index - 1; + + // Need to map more elements from the EVM stack + auto nNewItems = 1 + _index - currentStack.size(); + currentStack.insert(currentStack.begin(), nNewItems, nullptr); + + return currentStack.end() - _index - 1; +} + +llvm::Value* BasicBlock::LocalStack::get(size_t _index) +{ + auto& initialStack = m_bblock.m_initialStack; + auto itemIter = getItemIterator(_index); + + if (*itemIter == nullptr) + { + // Need to fetch a new item from the EVM stack + assert(static_cast(_index) >= m_bblock.m_tosOffset); + size_t initialIdx = _index - m_bblock.m_tosOffset; + if (initialIdx >= initialStack.size()) + { + auto nNewItems = 1 + initialIdx - initialStack.size(); + initialStack.insert(initialStack.end(), nNewItems, nullptr); + } + + assert(initialStack[initialIdx] == nullptr); + // Create a dummy value. + std::string name = "get_" + std::to_string(_index); + initialStack[initialIdx] = m_bblock.m_builder.CreatePHI(Type::Word, 0, std::move(name)); + *itemIter = initialStack[initialIdx]; + } + + return *itemIter; +} + +void BasicBlock::LocalStack::set(size_t _index, llvm::Value* _word) +{ + auto itemIter = getItemIterator(_index); + *itemIter = _word; +} + + + + + +void BasicBlock::synchronizeLocalStack(Stack& _evmStack) +{ + auto blockTerminator = m_llvmBB->getTerminator(); + assert(blockTerminator != nullptr); + m_builder.SetInsertPoint(blockTerminator); + + auto currIter = m_currentStack.begin(); + auto endIter = m_currentStack.end(); + + // Update (emit set()) changed values + for (int idx = m_currentStack.size() - 1 - m_tosOffset; + currIter < endIter && idx >= 0; + ++currIter, --idx) + { + assert(static_cast(idx) < m_initialStack.size()); + if (*currIter != m_initialStack[idx]) // value needs update + _evmStack.set(static_cast(idx), *currIter); + } + + if (m_tosOffset < 0) + { + // Pop values + _evmStack.pop(static_cast(-m_tosOffset)); + } + + // Push new values + for (; currIter < endIter; ++currIter) + { + assert(*currIter != nullptr); + _evmStack.push(*currIter); + } + + // Emit get() for all (used) values from the initial stack + for (size_t idx = 0; idx < m_initialStack.size(); ++idx) + { + auto val = m_initialStack[idx]; + if (val == nullptr) + continue; + + assert(llvm::isa(val)); + llvm::PHINode* phi = llvm::cast(val); + if (! phi->use_empty()) + { + // Insert call to get() just before the PHI node and replace + // the uses of PHI with the uses of this new instruction. + m_builder.SetInsertPoint(phi); + auto newVal = _evmStack.get(idx); + phi->replaceAllUsesWith(newVal); + } + phi->eraseFromParent(); + } + + // Reset the stack + m_initialStack.erase(m_initialStack.begin(), m_initialStack.end()); + m_currentStack.erase(m_currentStack.begin(), m_currentStack.end()); + m_tosOffset = 0; +} + +void BasicBlock::linkLocalStacks(std::vector basicBlocks, llvm::IRBuilder<>& _builder) +{ + struct BBInfo + { + BasicBlock& bblock; + std::vector predecessors; + size_t inputItems; + size_t outputItems; + std::vector phisToRewrite; + + BBInfo(BasicBlock& _bblock) : + bblock(_bblock), + predecessors(), + inputItems(0), + outputItems(0) + { + auto& initialStack = bblock.m_initialStack; + for (auto it = initialStack.begin(); + it != initialStack.end() && *it != nullptr; + ++it, ++inputItems); + + //if (bblock.localStack().m_tosOffset > 0) + // outputItems = bblock.localStack().m_tosOffset; + auto& exitStack = bblock.m_currentStack; + for (auto it = exitStack.rbegin(); + it != exitStack.rend() && *it != nullptr; + ++it, ++outputItems); + } + }; + + std::map cfg; + + // Create nodes in cfg + for (auto bb : basicBlocks) + cfg.emplace(bb->llvm(), *bb); + + // Create edges in cfg: for each bb info fill the list + // of predecessor infos. + for (auto& pair : cfg) + { + auto bb = pair.first; + auto& info = pair.second; + + for (auto predIt = llvm::pred_begin(bb); predIt != llvm::pred_end(bb); ++predIt) + { + auto predInfoEntry = cfg.find(*predIt); + if (predInfoEntry != cfg.end()) + info.predecessors.push_back(&predInfoEntry->second); + } + } + + // Iteratively compute inputs and outputs of each block, until reaching fixpoint. + bool valuesChanged = true; + while (valuesChanged) + { + if (getenv("EVMCC_DEBUG_BLOCKS")) + { + for (auto& pair : cfg) + std::cerr << pair.second.bblock.llvm()->getName().str() + << ": in " << pair.second.inputItems + << ", out " << pair.second.outputItems + << "\n"; + } + + valuesChanged = false; + for (auto& pair : cfg) + { + auto& info = pair.second; + + if (info.predecessors.empty()) + info.inputItems = 0; // no consequences for other blocks, so leave valuesChanged false + + for (auto predInfo : info.predecessors) + { + if (predInfo->outputItems < info.inputItems) + { + info.inputItems = predInfo->outputItems; + valuesChanged = true; + } + else if (predInfo->outputItems > info.inputItems) + { + predInfo->outputItems = info.inputItems; + valuesChanged = true; + } + } + } + } + + // Propagate values between blocks. + for (auto& entry : cfg) + { + auto& info = entry.second; + auto& bblock = info.bblock; + + llvm::BasicBlock::iterator fstNonPhi(bblock.llvm()->getFirstNonPHI()); + auto phiIter = bblock.m_initialStack.begin(); + for (size_t index = 0; index < info.inputItems; ++index, ++phiIter) + { + assert(llvm::isa(*phiIter)); + auto phi = llvm::cast(*phiIter); + + for (auto predIt : info.predecessors) + { + auto& predExitStack = predIt->bblock.m_currentStack; + auto value = *(predExitStack.end() - 1 - index); + phi->addIncoming(value, predIt->bblock.llvm()); + } + + // Move phi to the front + if (llvm::BasicBlock::iterator(phi) != bblock.llvm()->begin()) + { + phi->removeFromParent(); + _builder.SetInsertPoint(bblock.llvm(), bblock.llvm()->begin()); + _builder.Insert(phi); + } + } + + // The items pulled directly from predecessors block must be removed + // from the list of items that has to be popped from the initial stack. + auto& initialStack = bblock.m_initialStack; + initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems); + // Initial stack shrinks, so the size difference grows: + bblock.m_tosOffset += info.inputItems; + } + + // We must account for the items that were pushed directly to successor + // blocks and thus should not be on the list of items to be pushed onto + // to EVM stack + for (auto& entry : cfg) + { + auto& info = entry.second; + auto& bblock = info.bblock; + + auto& exitStack = bblock.m_currentStack; + exitStack.erase(exitStack.end() - info.outputItems, exitStack.end()); + bblock.m_tosOffset -= info.outputItems; + } +} + +void BasicBlock::dump() +{ + dump(std::cerr, false); +} + +void BasicBlock::dump(std::ostream& _out, bool _dotOutput) +{ + llvm::raw_os_ostream out(_out); + + out << (_dotOutput ? "" : "Initial stack:\n"); + for (auto val : m_initialStack) + { + if (val == nullptr) + out << " ?"; + else if (llvm::isa(val)) + out << *val; + else + out << " " << *val; + + out << (_dotOutput ? "\\l" : "\n"); + } + + out << (_dotOutput ? "| " : "Instructions:\n"); + for (auto ins = m_llvmBB->begin(); ins != m_llvmBB->end(); ++ins) + out << *ins << (_dotOutput ? "\\l" : "\n"); + + if (! _dotOutput) + out << "Current stack (offset = " << m_tosOffset << "):\n"; + else + out << "|"; + + for (auto val = m_currentStack.rbegin(); val != m_currentStack.rend(); ++val) + { + if (*val == nullptr) + out << " ?"; + else if (llvm::isa(*val)) + out << **val; + else + out << " " << **val; + out << (_dotOutput ? "\\l" : "\n"); + } + + if (! _dotOutput) + out << " ...\n----------------------------------------\n"; +} + + + + +} +} +} + diff --git a/libevmjit/BasicBlock.h b/libevmjit/BasicBlock.h new file mode 100644 index 000000000..f0643f342 --- /dev/null +++ b/libevmjit/BasicBlock.h @@ -0,0 +1,117 @@ +#pragma once + +#include + +#include + +#include "Stack.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +using ProgramCounter = uint64_t; // TODO: Rename + +class BasicBlock +{ +public: + class LocalStack + { + public: + /// Pushes value on stack + void push(llvm::Value* _value); + + /// Pops and returns top value + llvm::Value* pop(); + + /// Duplicates _index'th value on stack + void dup(size_t _index); + + /// Swaps _index'th value on stack with a value on stack top. + /// @param _index Index of value to be swaped. Must be > 0. + void swap(size_t _index); + + private: + LocalStack(BasicBlock& _owner); + LocalStack(LocalStack const&) = delete; + void operator=(LocalStack const&) = delete; + friend BasicBlock; + + /// Gets _index'th value from top (counting from 0) + llvm::Value* get(size_t _index); + + /// Sets _index'th value from top (counting from 0) + void set(size_t _index, llvm::Value* _value); + + std::vector::iterator getItemIterator(size_t _index); + + private: + BasicBlock& m_bblock; + }; + + /// Basic block name prefix. The rest is beging instruction index. + static const char* NamePrefix; + + explicit BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder); + explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder); + + BasicBlock(const BasicBlock&) = delete; + void operator=(const BasicBlock&) = delete; + + operator llvm::BasicBlock*() { return m_llvmBB; } + llvm::BasicBlock* llvm() { return m_llvmBB; } + + ProgramCounter begin() { return m_beginInstIdx; } + ProgramCounter end() { return m_endInstIdx; } + + LocalStack& localStack() { return m_stack; } + + /// Optimization: propagates values between local stacks in basic blocks + /// to avoid excessive pushing/popping on the EVM stack. + static void linkLocalStacks(std::vector _basicBlocks, llvm::IRBuilder<>& _builder); + + /// Synchronize current local stack with the EVM stack. + void synchronizeLocalStack(Stack& _evmStack); + + /// Prints local stack and block instructions to stderr. + /// Useful for calling in a debugger session. + void dump(); + void dump(std::ostream& os, bool _dotOutput = false); + +private: + ProgramCounter const m_beginInstIdx; + ProgramCounter const m_endInstIdx; + + llvm::BasicBlock* const m_llvmBB; + + /// Basic black state vector (stack) - current/end values and their positions on stack + /// @internal Must be AFTER m_llvmBB + LocalStack m_stack; + + llvm::IRBuilder<>& m_builder; + + /// This stack contains LLVM values that correspond to items found at + /// the EVM stack when the current basic block starts executing. + /// Location 0 corresponds to the top of the EVM stack, location 1 is + /// the item below the top and so on. The stack grows as the code + /// accesses more items on the EVM stack but once a value is put on + /// the stack, it will never be replaced. + std::vector m_initialStack = {}; + + /// This stack tracks the contents of the EVM stack as the basic block + /// executes. It may grow on both sides, as the code pushes items on + /// top of the stack or changes existing items. + std::vector m_currentStack = {}; + + /// How many items higher is the current stack than the initial one. + /// May be negative. + int m_tosOffset = 0; +}; + +} +} +} + diff --git a/libevmjit/CMakeLists.txt b/libevmjit/CMakeLists.txt new file mode 100644 index 000000000..7c35169a7 --- /dev/null +++ b/libevmjit/CMakeLists.txt @@ -0,0 +1,24 @@ +set(TARGET_NAME evmjit) + +file(GLOB SOURCES "*.cpp") +file(GLOB HEADERS "*.h") +source_group("" FILES ${HEADERS}) +source_group("" FILES ${SOURCES}) + +if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # Disable rtti for Cache as LLVM has no rtti + set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti) +endif () + +add_library(${TARGET_NAME} ${SOURCES} ${HEADERS}) +set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") + +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(${TARGET_NAME} ${LLVM_LIBS}) + +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") + +#install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +#install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp new file mode 100644 index 000000000..a887d91e9 --- /dev/null +++ b/libevmjit/Cache.cpp @@ -0,0 +1,60 @@ +#include "Cache.h" +#include +#include +#include +#include +#include +#include +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +//#define LOG(...) std::cerr << "CACHE " +#define LOG(...) std::ostream(nullptr) + +ObjectCache* Cache::getObjectCache() +{ + static ObjectCache objectCache; + return &objectCache; +} + + +void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) +{ + auto&& id = _module->getModuleIdentifier(); + llvm::SmallString<256> cachePath; + llvm::sys::path::system_temp_directory(false, cachePath); + llvm::sys::path::append(cachePath, "evm_objs"); + + if (llvm::sys::fs::create_directory(cachePath.str())) + return; // TODO: Add log + + llvm::sys::path::append(cachePath, id); + + std::string error; + llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); + cacheFile << _object->getBuffer(); +} + +llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) +{ + auto&& id = _module->getModuleIdentifier(); + llvm::SmallString<256> cachePath; + llvm::sys::path::system_temp_directory(false, cachePath); + llvm::sys::path::append(cachePath, "evm_objs", id); + + if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) + return llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); + else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) + std::cerr << r.getError().message(); // TODO: Add log + return nullptr; +} + +} +} +} diff --git a/libevmjit/Cache.h b/libevmjit/Cache.h new file mode 100644 index 000000000..80fe47ade --- /dev/null +++ b/libevmjit/Cache.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class ObjectCache : public llvm::ObjectCache +{ +public: + /// notifyObjectCompiled - Provides a pointer to compiled code for Module M. + virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) final override; + + /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that + /// contains the object which corresponds with Module M, or 0 if an object is + /// not available. The caller owns both the MemoryBuffer returned by this + /// and the memory it references. + virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override; + +private: + std::unordered_map> m_map; +}; + + +class Cache +{ +public: + static ObjectCache* getObjectCache(); +}; + +} +} +} diff --git a/libevmjit/Common.h b/libevmjit/Common.h new file mode 100644 index 000000000..d98cc0acb --- /dev/null +++ b/libevmjit/Common.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +using byte = uint8_t; +using bytes = std::vector; +using u256 = boost::multiprecision::uint256_t; +using bigint = boost::multiprecision::cpp_int; + +struct NoteChannel {}; // FIXME: Use some log library? + +enum class ReturnCode +{ + Stop = 0, + Return = 1, + Suicide = 2, + + BadJumpDestination = 101, + OutOfGas = 102, + StackTooSmall = 103, + BadInstruction = 104, + + LLVMConfigError = 201, + LLVMCompileError = 202, + LLVMLinkError = 203, +}; + +/// Representation of 256-bit value binary compatible with LLVM i256 +// TODO: Replace with h256 +struct i256 +{ + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; +}; +static_assert(sizeof(i256) == 32, "Wrong i265 size"); + +} +} +} diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp new file mode 100644 index 000000000..48dc50d60 --- /dev/null +++ b/libevmjit/Compiler.cpp @@ -0,0 +1,949 @@ + +#include "Compiler.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include "Instruction.h" +#include "Type.h" +#include "Memory.h" +#include "Stack.h" +#include "Ext.h" +#include "GasMeter.h" +#include "Utils.h" +#include "Endianness.h" +#include "Arith256.h" +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Compiler::Compiler(Options const& _options): + m_options(_options), + m_builder(llvm::getGlobalContext()) +{ + Type::init(m_builder.getContext()); +} + +void Compiler::createBasicBlocks(bytes const& _bytecode) +{ + std::set splitPoints; // Sorted collections of instruction indices where basic blocks start/end + + std::map directJumpTargets; + std::vector indirectJumpTargets; + boost::dynamic_bitset<> validJumpTargets(std::max(_bytecode.size(), size_t(1))); + + splitPoints.insert(0); // First basic block + validJumpTargets[0] = true; + + for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr) + { + ProgramCounter currentPC = curr - _bytecode.begin(); + validJumpTargets[currentPC] = true; + + auto inst = Instruction(*curr); + switch (inst) + { + + case Instruction::ANY_PUSH: + { + auto val = readPushData(curr, _bytecode.end()); + auto next = curr + 1; + if (next == _bytecode.end()) + break; + + auto nextInst = Instruction(*next); + if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI) + { + // Create a block for the JUMP target. + ProgramCounter targetPC = val.ult(_bytecode.size()) ? val.getZExtValue() : _bytecode.size(); + splitPoints.insert(targetPC); + + ProgramCounter jumpPC = (next - _bytecode.begin()); + directJumpTargets[jumpPC] = targetPC; + } + break; + } + + case Instruction::JUMPDEST: + { + // A basic block starts here. + splitPoints.insert(currentPC); + indirectJumpTargets.push_back(currentPC); + break; + } + + case Instruction::JUMP: + case Instruction::JUMPI: + case Instruction::RETURN: + case Instruction::STOP: + case Instruction::SUICIDE: + { + // Create a basic block starting at the following instruction. + if (curr + 1 < _bytecode.end()) + splitPoints.insert(currentPC + 1); + break; + } + + default: + break; + } + } + + // Remove split points generated from jumps out of code or into data. + for (auto it = splitPoints.cbegin(); it != splitPoints.cend();) + { + if (*it > _bytecode.size() || !validJumpTargets[*it]) + it = splitPoints.erase(it); + else + ++it; + } + + for (auto it = splitPoints.cbegin(); it != splitPoints.cend();) + { + auto beginInstIdx = *it; + ++it; + auto endInstIdx = it != splitPoints.cend() ? *it : _bytecode.size(); + basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginInstIdx), std::forward_as_tuple(beginInstIdx, endInstIdx, m_mainFunc, m_builder)); + } + + m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); + m_badJumpBlock = std::unique_ptr(new BasicBlock("BadJumpBlock", m_mainFunc, m_builder)); + m_jumpTableBlock = std::unique_ptr(new BasicBlock("JumpTableBlock", m_mainFunc, m_builder)); + + for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it) + { + if (it->second >= _bytecode.size()) + { + // Jumping out of code means STOP + m_directJumpTargets[it->first] = m_stopBB; + continue; + } + + auto blockIter = basicBlocks.find(it->second); + if (blockIter != basicBlocks.end()) + { + m_directJumpTargets[it->first] = blockIter->second.llvm(); + } + else + { + clog(JIT) << "Bad JUMP at PC " << it->first + << ": " << it->second << " is not a valid PC"; + m_directJumpTargets[it->first] = m_badJumpBlock->llvm(); + } + } + + for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it) + m_indirectJumpTargets.push_back(&basicBlocks.find(*it)->second); +} + +std::unique_ptr Compiler::compile(bytes const& _bytecode, std::string const& _id) +{ + auto compilationStartTime = std::chrono::high_resolution_clock::now(); + auto module = std::unique_ptr(new llvm::Module(_id, m_builder.getContext())); + + // Create main function + auto mainFuncType = llvm::FunctionType::get(Type::MainReturn, Type::RuntimePtr, false); + m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get()); + m_mainFunc->getArgumentList().front().setName("rt"); + + // Create the basic blocks. + auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc); + m_builder.SetInsertPoint(entryBlock); + + createBasicBlocks(_bytecode); + + // Init runtime structures. + RuntimeManager runtimeManager(m_builder); + GasMeter gasMeter(m_builder, runtimeManager); + Memory memory(runtimeManager, gasMeter); + Ext ext(runtimeManager, memory); + Stack stack(m_builder, runtimeManager); + Arith256 arith(m_builder); + + m_builder.CreateBr(basicBlocks.begin()->second); + + for (auto basicBlockPairIt = basicBlocks.begin(); basicBlockPairIt != basicBlocks.end(); ++basicBlockPairIt) + { + auto& basicBlock = basicBlockPairIt->second; + auto iterCopy = basicBlockPairIt; + ++iterCopy; + auto nextBasicBlock = (iterCopy != basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; + compileBasicBlock(basicBlock, _bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); + } + + // Code for special blocks: + // TODO: move to separate function. + m_builder.SetInsertPoint(m_stopBB); + m_builder.CreateRet(Constant::get(ReturnCode::Stop)); + + m_builder.SetInsertPoint(m_badJumpBlock->llvm()); + m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination)); + + m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); + if (m_indirectJumpTargets.size() > 0) + { + auto dest = m_jumpTableBlock->localStack().pop(); + auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(), + m_indirectJumpTargets.size()); + for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it) + { + auto& bb = *it; + auto dest = Constant::get(bb->begin()); + switchInstr->addCase(dest, bb->llvm()); + } + } + else + m_builder.CreateBr(m_badJumpBlock->llvm()); + + removeDeadBlocks(); + + dumpCFGifRequired("blocks-init.dot"); + + if (m_options.optimizeStack) + { + std::vector blockList; + for (auto& entry : basicBlocks) + blockList.push_back(&entry.second); + + if (m_jumpTableBlock) + blockList.push_back(m_jumpTableBlock.get()); + + BasicBlock::linkLocalStacks(blockList, m_builder); + + dumpCFGifRequired("blocks-opt.dot"); + } + + for (auto& entry : basicBlocks) + entry.second.synchronizeLocalStack(stack); + if (m_jumpTableBlock) + m_jumpTableBlock->synchronizeLocalStack(stack); + + dumpCFGifRequired("blocks-sync.dot"); + + if (m_jumpTableBlock && m_options.rewriteSwitchToBranches) + { + llvm::FunctionPassManager fpManager(module.get()); + fpManager.add(llvm::createLowerSwitchPass()); + fpManager.doInitialization(); + fpManager.run(*m_mainFunc); + } + + auto compilationEndTime = std::chrono::high_resolution_clock::now(); + clog(JIT) << "JIT: " << std::chrono::duration_cast(compilationEndTime - compilationStartTime).count(); + return module; +} + + +void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, RuntimeManager& _runtimeManager, + Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock) +{ + if (!_nextBasicBlock) // this is the last block in the code + _nextBasicBlock = m_stopBB; + + m_builder.SetInsertPoint(_basicBlock.llvm()); + auto& stack = _basicBlock.localStack(); + + for (auto currentPC = _basicBlock.begin(); currentPC != _basicBlock.end(); ++currentPC) + { + auto inst = static_cast(_bytecode[currentPC]); + + _gasMeter.count(inst); + + switch (inst) + { + + case Instruction::ADD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto result = m_builder.CreateAdd(lhs, rhs); + stack.push(result); + break; + } + + case Instruction::SUB: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto result = m_builder.CreateSub(lhs, rhs); + stack.push(result); + break; + } + + case Instruction::MUL: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.mul(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::DIV: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.div(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::SDIV: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.sdiv(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::MOD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.mod(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::SMOD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.smod(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::EXP: + { + auto base = stack.pop(); + auto exponent = stack.pop(); + _gasMeter.countExp(exponent); + auto ret = _arith.exp(base, exponent); + stack.push(ret); + break; + } + + case Instruction::NOT: + { + auto value = stack.pop(); + auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot"); + stack.push(ret); + break; + } + + case Instruction::LT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpULT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::GT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpUGT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::SLT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpSLT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::SGT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpSGT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::EQ: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpEQ(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::ISZERO: + { + auto top = stack.pop(); + auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero"); + auto result = m_builder.CreateZExt(iszero, Type::Word); + stack.push(result); + break; + } + + case Instruction::AND: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateAnd(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::OR: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateOr(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::XOR: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateXor(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::BYTE: + { + const auto byteNum = stack.pop(); + auto value = stack.pop(); + + value = Endianness::toBE(m_builder, value); + auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); + auto byte = m_builder.CreateExtractElement(bytes, byteNum, "byte"); + value = m_builder.CreateZExt(byte, Type::Word); + + auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32)); + value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0)); + stack.push(value); + break; + } + + case Instruction::ADDMOD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto mod = stack.pop(); + auto res = _arith.addmod(lhs, rhs, mod); + stack.push(res); + break; + } + + case Instruction::MULMOD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto mod = stack.pop(); + auto res = _arith.mulmod(lhs, rhs, mod); + stack.push(res); + break; + } + + case Instruction::SIGNEXTEND: + { + auto idx = stack.pop(); + auto word = stack.pop(); + + auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); + auto k32 = m_builder.CreateZExt(k32_, Type::Word); + auto k32x8 = m_builder.CreateMul(k32, Constant::get(8), "kx8"); + + // test for word >> (k * 8 + 7) + auto bitpos = m_builder.CreateAdd(k32x8, Constant::get(7), "bitpos"); + auto bitval = m_builder.CreateLShr(word, bitpos, "bitval"); + auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); + + auto mask_ = m_builder.CreateShl(Constant::get(1), bitpos); + auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); + + auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask"); + auto val1 = m_builder.CreateOr(word, negmask); + auto val0 = m_builder.CreateAnd(word, mask); + + auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::Word, 30)); + auto result = m_builder.CreateSelect(kInRange, + m_builder.CreateSelect(bittest, val1, val0), + word); + stack.push(result); + break; + } + + case Instruction::SHA3: + { + auto inOff = stack.pop(); + auto inSize = stack.pop(); + _memory.require(inOff, inSize); + _gasMeter.countSha3Data(inSize); + auto hash = _ext.sha3(inOff, inSize); + stack.push(hash); + break; + } + + case Instruction::POP: + { + auto val = stack.pop(); + static_cast(val); + // Generate a dummy use of val to make sure that a get(0) will be emitted at this point, + // so that StackTooSmall will be thrown + // m_builder.CreateICmpEQ(val, val, "dummy"); + break; + } + + case Instruction::ANY_PUSH: + { + auto curr = _bytecode.begin() + currentPC; // TODO: replace currentPC with iterator + auto value = readPushData(curr, _bytecode.end()); + currentPC = curr - _bytecode.begin(); + + stack.push(Constant::get(value)); + break; + } + + case Instruction::ANY_DUP: + { + auto index = static_cast(inst) - static_cast(Instruction::DUP1); + stack.dup(index); + break; + } + + case Instruction::ANY_SWAP: + { + auto index = static_cast(inst) - static_cast(Instruction::SWAP1) + 1; + stack.swap(index); + break; + } + + case Instruction::MLOAD: + { + auto addr = stack.pop(); + auto word = _memory.loadWord(addr); + stack.push(word); + break; + } + + case Instruction::MSTORE: + { + auto addr = stack.pop(); + auto word = stack.pop(); + _memory.storeWord(addr, word); + break; + } + + case Instruction::MSTORE8: + { + auto addr = stack.pop(); + auto word = stack.pop(); + _memory.storeByte(addr, word); + break; + } + + case Instruction::MSIZE: + { + auto word = _memory.getSize(); + stack.push(word); + break; + } + + case Instruction::SLOAD: + { + auto index = stack.pop(); + auto value = _ext.sload(index); + stack.push(value); + break; + } + + case Instruction::SSTORE: + { + auto index = stack.pop(); + auto value = stack.pop(); + _gasMeter.countSStore(_ext, index, value); + _ext.sstore(index, value); + break; + } + + case Instruction::JUMP: + case Instruction::JUMPI: + { + // Generate direct jump iff: + // 1. this is not the first instruction in the block + // 2. m_directJumpTargets[currentPC] is defined (meaning that the previous instruction is a PUSH) + // Otherwise generate a indirect jump (a switch). + llvm::BasicBlock* targetBlock = nullptr; + if (currentPC != _basicBlock.begin()) + { + auto pairIter = m_directJumpTargets.find(currentPC); + if (pairIter != m_directJumpTargets.end()) + targetBlock = pairIter->second; + } + + if (inst == Instruction::JUMP) + { + if (targetBlock) + { + // The target address is computed at compile time, + // just pop it without looking... + stack.pop(); + m_builder.CreateBr(targetBlock); + } + else + m_builder.CreateBr(m_jumpTableBlock->llvm()); + } + else // JUMPI + { + stack.swap(1); + auto val = stack.pop(); + auto zero = Constant::get(0); + auto cond = m_builder.CreateICmpNE(val, zero, "nonzero"); + + if (targetBlock) + { + stack.pop(); + m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock); + } + else + m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), _nextBasicBlock); + } + + break; + } + + case Instruction::JUMPDEST: + { + // Nothing to do + break; + } + + case Instruction::PC: + { + auto value = Constant::get(currentPC); + stack.push(value); + break; + } + + case Instruction::GAS: + { + _gasMeter.commitCostBlock(); + stack.push(_runtimeManager.getGas()); + break; + } + + case Instruction::ADDRESS: + case Instruction::CALLER: + case Instruction::ORIGIN: + case Instruction::CALLVALUE: + case Instruction::CALLDATASIZE: + case Instruction::CODESIZE: + case Instruction::GASPRICE: + case Instruction::PREVHASH: + case Instruction::COINBASE: + case Instruction::TIMESTAMP: + case Instruction::NUMBER: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + { + // Pushes an element of runtime data on stack + stack.push(_runtimeManager.get(inst)); + break; + } + + case Instruction::BALANCE: + { + auto address = stack.pop(); + auto value = _ext.balance(address); + stack.push(value); + break; + } + + case Instruction::EXTCODESIZE: + { + auto addr = stack.pop(); + auto codeRef = _ext.getExtCode(addr); + stack.push(codeRef.size); + break; + } + + case Instruction::CALLDATACOPY: + { + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto srcPtr = _runtimeManager.getCallData(); + auto srcSize = _runtimeManager.get(RuntimeData::CallDataSize); + + _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::CODECOPY: + { + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234 + auto srcSize = _runtimeManager.get(RuntimeData::CodeSize); + + _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::EXTCODECOPY: + { + auto addr = stack.pop(); + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto codeRef = _ext.getExtCode(addr); + + _memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::CALLDATALOAD: + { + auto index = stack.pop(); + auto value = _ext.calldataload(index); + stack.push(value); + break; + } + + case Instruction::CREATE: + { + auto endowment = stack.pop(); + auto initOff = stack.pop(); + auto initSize = stack.pop(); + _memory.require(initOff, initSize); + + _gasMeter.commitCostBlock(); + + auto gas = _runtimeManager.getGas(); + auto address = _ext.create(gas, endowment, initOff, initSize); + _runtimeManager.setGas(gas); + stack.push(address); + break; + } + + case Instruction::CALL: + case Instruction::CALLCODE: + { + auto gas = stack.pop(); + auto codeAddress = stack.pop(); + auto value = stack.pop(); + auto inOff = stack.pop(); + auto inSize = stack.pop(); + auto outOff = stack.pop(); + auto outSize = stack.pop(); + + _gasMeter.commitCostBlock(); + + // Require memory for in and out buffers + _memory.require(outOff, outSize); // Out buffer first as we guess it will be after the in one + _memory.require(inOff, inSize); + + auto receiveAddress = codeAddress; + if (inst == Instruction::CALLCODE) + receiveAddress = _runtimeManager.get(RuntimeData::Address); + + _gasMeter.count(gas); + auto ret = _ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress); + _gasMeter.giveBack(gas); + stack.push(ret); + break; + } + + case Instruction::RETURN: + { + auto index = stack.pop(); + auto size = stack.pop(); + + _memory.require(index, size); + _runtimeManager.registerReturnData(index, size); + + m_builder.CreateRet(Constant::get(ReturnCode::Return)); + break; + } + + case Instruction::SUICIDE: + { + _runtimeManager.registerSuicide(stack.pop()); + m_builder.CreateRet(Constant::get(ReturnCode::Suicide)); + break; + } + + + case Instruction::STOP: + { + m_builder.CreateRet(Constant::get(ReturnCode::Stop)); + break; + } + + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + auto beginIdx = stack.pop(); + auto numBytes = stack.pop(); + _memory.require(beginIdx, numBytes); + + // This will commit the current cost block + _gasMeter.countLogData(numBytes); + + std::array topics{{}}; + auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); + for (size_t i = 0; i < numTopics; ++i) + topics[i] = stack.pop(); + + _ext.log(beginIdx, numBytes, topics); + break; + } + + default: // Invalid instruction - runtime exception + { + _runtimeManager.raiseException(ReturnCode::BadInstruction); + } + + } + } + + _gasMeter.commitCostBlock(); + + // Block may have no terminator if the next instruction is a jump destination. + if (!_basicBlock.llvm()->getTerminator()) + m_builder.CreateBr(_nextBasicBlock); +} + + + +void Compiler::removeDeadBlocks() +{ + // Remove dead basic blocks + auto sthErased = false; + do + { + sthErased = false; + for (auto it = basicBlocks.begin(); it != basicBlocks.end();) + { + auto llvmBB = it->second.llvm(); + if (llvm::pred_begin(llvmBB) == llvm::pred_end(llvmBB)) + { + llvmBB->eraseFromParent(); + basicBlocks.erase(it++); + sthErased = true; + } + else + ++it; + } + } + while (sthErased); + + // Remove jump table block if no predecessors + if (llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm())) + { + m_jumpTableBlock->llvm()->eraseFromParent(); + m_jumpTableBlock.reset(); + } +} + +void Compiler::dumpCFGifRequired(std::string const& _dotfilePath) +{ + if (! m_options.dumpCFG) + return; + + // TODO: handle i/o failures + std::ofstream ofs(_dotfilePath); + dumpCFGtoStream(ofs); + ofs.close(); +} + +void Compiler::dumpCFGtoStream(std::ostream& _out) +{ + _out << "digraph BB {\n" + << " node [shape=record, fontname=Courier, fontsize=10];\n" + << " entry [share=record, label=\"entry block\"];\n"; + + std::vector blocks; + for (auto& pair : basicBlocks) + blocks.push_back(&pair.second); + if (m_jumpTableBlock) + blocks.push_back(m_jumpTableBlock.get()); + if (m_badJumpBlock) + blocks.push_back(m_badJumpBlock.get()); + + // std::map phiNodesPerBlock; + + // Output nodes + for (auto bb : blocks) + { + std::string blockName = bb->llvm()->getName(); + + std::ostringstream oss; + bb->dump(oss, true); + + _out << " \"" << blockName << "\" [shape=record, label=\" { " << blockName << "|" << oss.str() << "} \"];\n"; + } + + // Output edges + for (auto bb : blocks) + { + std::string blockName = bb->llvm()->getName(); + + auto end = llvm::pred_end(bb->llvm()); + for (llvm::pred_iterator it = llvm::pred_begin(bb->llvm()); it != end; ++it) + { + _out << " \"" << (*it)->getName().str() << "\" -> \"" << blockName << "\" [" + << ((m_jumpTableBlock.get() && *it == m_jumpTableBlock.get()->llvm()) ? "style = dashed, " : "") + << "];\n"; + } + } + + _out << "}\n"; +} + +void Compiler::dump() +{ + for (auto& entry : basicBlocks) + entry.second.dump(); + if (m_jumpTableBlock != nullptr) + m_jumpTableBlock->dump(); +} + +} +} +} + diff --git a/libevmjit/Compiler.h b/libevmjit/Compiler.h new file mode 100644 index 000000000..8e3bf357c --- /dev/null +++ b/libevmjit/Compiler.h @@ -0,0 +1,91 @@ + +#pragma once + +#include + +#include "Common.h" +#include "BasicBlock.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class Compiler +{ +public: + + struct Options + { + /// Optimize stack operations between basic blocks + bool optimizeStack; + + /// Rewrite switch instructions to sequences of branches + bool rewriteSwitchToBranches; + + /// Dump CFG as a .dot file for graphviz + bool dumpCFG; + + Options(): + optimizeStack(true), + rewriteSwitchToBranches(true), + dumpCFG(false) + {} + }; + + using ProgramCounter = uint64_t; + + Compiler(Options const& _options); + + std::unique_ptr compile(bytes const& _bytecode, std::string const& _id); + +private: + + void createBasicBlocks(bytes const& _bytecode); + + void compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); + + void removeDeadBlocks(); + + /// Dumps basic block graph in graphviz format to a file, if option dumpCFG is enabled. + void dumpCFGifRequired(std::string const& _dotfilePath); + + /// Dumps basic block graph in graphviz format to a stream. + void dumpCFGtoStream(std::ostream& _out); + + /// Dumps all basic blocks to stderr. Useful in a debugging session. + void dump(); + + /// Compiler options + Options const& m_options; + + /// Helper class for generating IR + llvm::IRBuilder<> m_builder; + + /// Maps a program counter pc to a basic block that starts at pc (if any). + std::map basicBlocks = {}; + + /// Maps a pc at which there is a JUMP or JUMPI to the target block of the jump. + std::map m_directJumpTargets = {}; + + /// A list of possible blocks to which there may be indirect jumps. + std::vector m_indirectJumpTargets = {}; + + /// Stop basic block - terminates execution with STOP code (0) + llvm::BasicBlock* m_stopBB = nullptr; + + /// Block with a jump table. + std::unique_ptr m_jumpTableBlock = nullptr; + + /// Default destination for indirect jumps. + std::unique_ptr m_badJumpBlock = nullptr; + + /// Main program function + llvm::Function* m_mainFunc = nullptr; +}; + +} +} +} diff --git a/libevmjit/CompilerHelper.cpp b/libevmjit/CompilerHelper.cpp new file mode 100644 index 000000000..badf9d889 --- /dev/null +++ b/libevmjit/CompilerHelper.cpp @@ -0,0 +1,46 @@ + +#include "CompilerHelper.h" + +#include +#include + +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +CompilerHelper::CompilerHelper(llvm::IRBuilder<>& _builder) : + m_builder(_builder) +{} + +llvm::Module* CompilerHelper::getModule() +{ + assert(m_builder.GetInsertBlock()); + assert(m_builder.GetInsertBlock()->getParent()); // BB must be in a function + return m_builder.GetInsertBlock()->getParent()->getParent(); +} + +llvm::Function* CompilerHelper::getMainFunction() +{ + // TODO: Rename or change semantics of getMainFunction() function + assert(m_builder.GetInsertBlock()); + auto mainFunc = m_builder.GetInsertBlock()->getParent(); + assert(mainFunc); + if (mainFunc == &mainFunc->getParent()->getFunctionList().front()) // Main function is the first one in module + return mainFunc; + return nullptr; +} + + +RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager): + CompilerHelper(_runtimeManager.getBuilder()), + m_runtimeManager(_runtimeManager) +{} + +} +} +} diff --git a/libevmjit/CompilerHelper.h b/libevmjit/CompilerHelper.h new file mode 100644 index 000000000..19315fe4a --- /dev/null +++ b/libevmjit/CompilerHelper.h @@ -0,0 +1,83 @@ + +#pragma once + +#include + + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class RuntimeManager; + +/// Base class for compiler helpers like Memory, GasMeter, etc. +class CompilerHelper +{ +protected: + CompilerHelper(llvm::IRBuilder<>& _builder); + + CompilerHelper(const CompilerHelper&) = delete; + void operator=(CompilerHelper) = delete; + + /// Reference to the IR module being compiled + llvm::Module* getModule(); + + /// Reference to the main module function + llvm::Function* getMainFunction(); + + /// Reference to parent compiler IR builder + llvm::IRBuilder<>& m_builder; + llvm::IRBuilder<>& getBuilder() { return m_builder; } + + template + llvm::CallInst* createCall(llvm::Function* _func, _Args*... _args) + { + llvm::Value* args[] = {_args...}; + return getBuilder().CreateCall(_func, args); + } + + friend class RuntimeHelper; +}; + + +/// Compiler helper that depends on runtime data +class RuntimeHelper : public CompilerHelper +{ +protected: + RuntimeHelper(RuntimeManager& _runtimeManager); + + RuntimeManager& getRuntimeManager() { return m_runtimeManager; } + +private: + RuntimeManager& m_runtimeManager; +}; + + +/// Saves the insert point of the IR builder and restores it when destructed +struct InsertPointGuard +{ + InsertPointGuard(llvm::IRBuilder<>& _builder) : + m_builder(_builder), + m_insertBB(m_builder.GetInsertBlock()), + m_insertPt(m_builder.GetInsertPoint()) + {} + + InsertPointGuard(const InsertPointGuard&) = delete; + void operator=(InsertPointGuard) = delete; + + ~InsertPointGuard() + { + m_builder.SetInsertPoint(m_insertBB, m_insertPt); + } + +private: + llvm::IRBuilder<>& m_builder; + llvm::BasicBlock* m_insertBB; + llvm::BasicBlock::iterator m_insertPt; +}; + +} +} +} diff --git a/libevmjit/Endianness.cpp b/libevmjit/Endianness.cpp new file mode 100644 index 000000000..db7edfdc9 --- /dev/null +++ b/libevmjit/Endianness.cpp @@ -0,0 +1,38 @@ + +#include "Endianness.h" + +#include + +#include "Type.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word) +{ + union tester + { + unsigned int x; + unsigned char isLE; + }; + + if (tester{1}.isLE) + { + // FIXME: Disabled because of problems with BYTE + //if (auto constant = llvm::dyn_cast(_word)) + // return _builder.getInt(constant->getValue().byteSwap()); + + // OPT: Cache func declaration? + auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word); + return _builder.CreateCall(bswapFunc, _word); + } + return _word; +} + +} +} +} diff --git a/libevmjit/Endianness.h b/libevmjit/Endianness.h new file mode 100644 index 000000000..8a1f41085 --- /dev/null +++ b/libevmjit/Endianness.h @@ -0,0 +1,24 @@ + +#pragma once + +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +struct Endianness +{ + static llvm::Value* toBE(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } + static llvm::Value* toNative(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } + +private: + static llvm::Value* bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word); +}; + +} +} +} diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp new file mode 100644 index 000000000..862586575 --- /dev/null +++ b/libevmjit/ExecutionEngine.cpp @@ -0,0 +1,127 @@ +#include "ExecutionEngine.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Runtime.h" +#include "Compiler.h" +#include "Cache.h" + +extern "C" void env_sha3(dev::eth::jit::byte const* _begin, uint64_t _size, std::array* o_hash); + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +namespace +{ +typedef ReturnCode(*EntryFuncPtr)(Runtime*); + +ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime) +{ + // That function uses long jumps to handle "execeptions". + // Do not create any non-POD objects here + + ReturnCode returnCode{}; + auto sj = setjmp(_runtime->getJmpBuf()); + if (sj == 0) + returnCode = _mainFunc(_runtime); + else + returnCode = static_cast(sj); + + return returnCode; +} + +std::string codeHash(bytes const& _code) +{ + std::array binHash; + env_sha3(_code.data(), _code.size(), &binHash); + + std::ostringstream os; + for (auto i: binHash) + os << std::hex << std::setfill('0') << std::setw(2) << (int)(std::make_unsigned::type)i; + + return os.str(); +} + +} + +ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env) +{ + static std::unique_ptr ee; // TODO: Use Managed Objects from LLVM? + + auto mainFuncName = codeHash(_code); + EntryFuncPtr entryFuncPtr{}; + Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls + + if (ee && (entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName))) + { + } + else + { + auto module = Compiler({}).compile(_code, mainFuncName); + //module->dump(); + if (!ee) + { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + + llvm::EngineBuilder builder(module.get()); + builder.setEngineKind(llvm::EngineKind::JIT); + builder.setUseMCJIT(true); + std::unique_ptr memoryManager(new llvm::SectionMemoryManager); + builder.setMCJITMemoryManager(memoryManager.get()); + builder.setOptLevel(llvm::CodeGenOpt::Default); + + auto triple = llvm::Triple(llvm::sys::getProcessTriple()); + if (triple.getOS() == llvm::Triple::OSType::Win32) + triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format + module->setTargetTriple(triple.str()); + + ee.reset(builder.create()); + if (!ee) + return ReturnCode::LLVMConfigError; + + module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module + memoryManager.release(); // and memory manager + + ee->setObjectCache(Cache::getObjectCache()); + entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); + } + else + { + if (!entryFuncPtr) + { + ee->addModule(module.get()); + module.release(); + entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); + } + } + } + assert(entryFuncPtr); + + auto executionStartTime = std::chrono::high_resolution_clock::now(); + + auto returnCode = runEntryFunc(entryFuncPtr, &runtime); + if (returnCode == ReturnCode::Return) + this->returnData = runtime.getReturnData(); + + auto executionEndTime = std::chrono::high_resolution_clock::now(); + clog(JIT) << " + " << std::chrono::duration_cast(executionEndTime - executionStartTime).count() << " ms\n"; + + return returnCode; +} + +} +} +} diff --git a/libevmjit/ExecutionEngine.h b/libevmjit/ExecutionEngine.h new file mode 100644 index 000000000..559701bba --- /dev/null +++ b/libevmjit/ExecutionEngine.h @@ -0,0 +1,26 @@ +#pragma once + +#include "RuntimeData.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class ExecutionEngine +{ +public: + ExecutionEngine() = default; + ExecutionEngine(ExecutionEngine const&) = delete; + void operator=(ExecutionEngine) = delete; + + ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env); + + bytes returnData; +}; + +} +} +} diff --git a/libevmjit/Ext.cpp b/libevmjit/Ext.cpp new file mode 100644 index 000000000..38adffd3f --- /dev/null +++ b/libevmjit/Ext.cpp @@ -0,0 +1,178 @@ + +#include "Ext.h" + +#include +#include +#include + +//#include +//#include + +#include "RuntimeManager.h" +#include "Memory.h" +#include "Type.h" +#include "Endianness.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): + RuntimeHelper(_runtimeManager), + m_memoryMan(_memoryMan) +{ + auto module = getModule(); + + m_args[0] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.index"); + m_args[1] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.value"); + m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg2"); + m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg3"); + m_arg4 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg4"); + m_arg5 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg5"); + m_arg6 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg6"); + m_arg7 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg7"); + m_arg8 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg8"); + m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); + + using Linkage = llvm::GlobalValue::LinkageTypes; + + llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr}; + + m_sload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sload", module); + m_sstore = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sstore", module); + + llvm::Type* sha3ArgsTypes[] = {Type::BytePtr, Type::Size, Type::WordPtr}; + m_sha3 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, sha3ArgsTypes, false), Linkage::ExternalLinkage, "env_sha3", module); + + llvm::Type* createArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr}; + m_create = llvm::Function::Create(llvm::FunctionType::get(Type::Void, createArgsTypes, false), Linkage::ExternalLinkage, "env_create", module); + + llvm::Type* callArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr}; + m_call = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, callArgsTypes, false), Linkage::ExternalLinkage, "env_call", module); + + llvm::Type* logArgsTypes[] = {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr}; + m_log = llvm::Function::Create(llvm::FunctionType::get(Type::Void, logArgsTypes, false), Linkage::ExternalLinkage, "env_log", module); + + llvm::Type* getExtCodeArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()}; + m_getExtCode = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, getExtCodeArgsTypes, false), Linkage::ExternalLinkage, "env_getExtCode", module); + + // Helper function, not client Env interface + llvm::Type* callDataLoadArgsTypes[] = {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr}; + m_calldataload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, callDataLoadArgsTypes, false), Linkage::ExternalLinkage, "ext_calldataload", module); +} + +llvm::Function* Ext::getBalanceFunc() +{ + if (!m_balance) + { + llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr}; + m_balance = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argsTypes, false), llvm::Function::ExternalLinkage, "env_balance", getModule()); + } + return m_balance; +} + +llvm::Value* Ext::sload(llvm::Value* _index) +{ + m_builder.CreateStore(_index, m_args[0]); + m_builder.CreateCall3(m_sload, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness + return m_builder.CreateLoad(m_args[1]); +} + +void Ext::sstore(llvm::Value* _index, llvm::Value* _value) +{ + m_builder.CreateStore(_index, m_args[0]); + m_builder.CreateStore(_value, m_args[1]); + m_builder.CreateCall3(m_sstore, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness +} + +llvm::Value* Ext::calldataload(llvm::Value* _index) +{ + m_builder.CreateStore(_index, m_args[0]); + createCall(m_calldataload, getRuntimeManager().getDataPtr(), m_args[0], m_args[1]); + auto ret = m_builder.CreateLoad(m_args[1]); + return Endianness::toNative(m_builder, ret); +} + +llvm::Value* Ext::balance(llvm::Value* _address) +{ + auto address = Endianness::toBE(m_builder, _address); + m_builder.CreateStore(address, m_args[0]); + createCall(getBalanceFunc(), getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); + return m_builder.CreateLoad(m_args[1]); +} + +llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) +{ + m_builder.CreateStore(_gas, m_args[0]); + m_builder.CreateStore(_endowment, m_arg2); + auto begin = m_memoryMan.getBytePtr(_initOff); + auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); + createCall(m_create, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, begin, size, m_args[1]); + _gas = m_builder.CreateLoad(m_args[0]); // Return gas + llvm::Value* address = m_builder.CreateLoad(m_args[1]); + address = Endianness::toNative(m_builder, address); + return address; +} + +llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) +{ + m_builder.CreateStore(_gas, m_args[0]); + auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress); + m_builder.CreateStore(receiveAddress, m_arg2); + m_builder.CreateStore(_value, m_arg3); + auto inBeg = m_memoryMan.getBytePtr(_inOff); + auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); + auto outBeg = m_memoryMan.getBytePtr(_outOff); + auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); + auto codeAddress = Endianness::toBE(m_builder, _codeAddress); + m_builder.CreateStore(codeAddress, m_arg8); + auto ret = createCall(m_call, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, m_arg3, inBeg, inSize, outBeg, outSize, m_arg8); + _gas = m_builder.CreateLoad(m_args[0]); // Return gas + return m_builder.CreateZExt(ret, Type::Word, "ret"); +} + +llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) +{ + auto begin = m_memoryMan.getBytePtr(_inOff); + auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); + createCall(m_sha3, begin, size, m_args[1]); + llvm::Value* hash = m_builder.CreateLoad(m_args[1]); + hash = Endianness::toNative(m_builder, hash); + return hash; +} + +MemoryRef Ext::getExtCode(llvm::Value* _addr) +{ + auto addr = Endianness::toBE(m_builder, _addr); + m_builder.CreateStore(addr, m_args[0]); + auto code = createCall(m_getExtCode, getRuntimeManager().getEnvPtr(), m_args[0], m_size); + auto codeSize = m_builder.CreateLoad(m_size); + auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word); + return {code, codeSize256}; +} + +void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array const& _topics) +{ + auto begin = m_memoryMan.getBytePtr(_memIdx); + auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size"); + llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, m_arg2, m_arg3, m_arg4, m_arg5}; + + auto topicArgPtr = &args[3]; + for (auto&& topic : _topics) + { + if (topic) + m_builder.CreateStore(Endianness::toBE(m_builder, topic), *topicArgPtr); + else + *topicArgPtr = llvm::ConstantPointerNull::get(Type::WordPtr); + ++topicArgPtr; + } + + m_builder.CreateCall(m_log, args); +} + +} +} +} diff --git a/libevmjit/Ext.h b/libevmjit/Ext.h new file mode 100644 index 000000000..be71dc1ff --- /dev/null +++ b/libevmjit/Ext.h @@ -0,0 +1,69 @@ + +#pragma once + +#include +#include "CompilerHelper.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + class Memory; + +struct MemoryRef +{ + llvm::Value* ptr; + llvm::Value* size; +}; + +class Ext : public RuntimeHelper +{ +public: + Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan); + + llvm::Value* sload(llvm::Value* _index); + void sstore(llvm::Value* _index, llvm::Value* _value); + + llvm::Value* balance(llvm::Value* _address); + llvm::Value* calldataload(llvm::Value* _index); + llvm::Value* create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); + llvm::Value* call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); + + llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); + MemoryRef getExtCode(llvm::Value* _addr); + + void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array const& _topics); + +private: + Memory& m_memoryMan; + + llvm::Value* m_args[2]; + llvm::Value* m_arg2; + llvm::Value* m_arg3; + llvm::Value* m_arg4; + llvm::Value* m_arg5; + llvm::Value* m_arg6; + llvm::Value* m_arg7; + llvm::Value* m_arg8; + llvm::Value* m_size; + llvm::Value* m_data = nullptr; + llvm::Function* m_sload; + llvm::Function* m_sstore; + llvm::Function* m_calldataload; + llvm::Function* m_balance = nullptr; + llvm::Function* m_create; + llvm::Function* m_call; + llvm::Function* m_sha3; + llvm::Function* m_getExtCode; + llvm::Function* m_log; + + llvm::Function* getBalanceFunc(); +}; + + +} +} +} + diff --git a/libevmjit/GasMeter.cpp b/libevmjit/GasMeter.cpp new file mode 100644 index 000000000..c31942a45 --- /dev/null +++ b/libevmjit/GasMeter.cpp @@ -0,0 +1,222 @@ + +#include "GasMeter.h" + +#include +#include +#include + +#include "Type.h" +#include "Ext.h" +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +namespace // Helper functions +{ + +uint64_t const c_stepGas = 1; +uint64_t const c_balanceGas = 20; +uint64_t const c_sha3Gas = 10; +uint64_t const c_sha3WordGas = 10; +uint64_t const c_sloadGas = 20; +uint64_t const c_sstoreSetGas = 300; +uint64_t const c_sstoreResetGas = 100; +uint64_t const c_sstoreRefundGas = 100; +uint64_t const c_createGas = 100; +uint64_t const c_createDataGas = 5; +uint64_t const c_callGas = 20; +uint64_t const c_expGas = 1; +uint64_t const c_expByteGas = 1; +uint64_t const c_memoryGas = 1; +uint64_t const c_txDataZeroGas = 1; +uint64_t const c_txDataNonZeroGas = 5; +uint64_t const c_txGas = 500; +uint64_t const c_logGas = 32; +uint64_t const c_logDataGas = 1; +uint64_t const c_logTopicGas = 32; +uint64_t const c_copyGas = 1; + +uint64_t getStepCost(Instruction inst) // TODO: Add this function to FeeSructure (pull request submitted) +{ + switch (inst) + { + default: // Assumes instruction code is valid + return c_stepGas; + + case Instruction::STOP: + case Instruction::SUICIDE: + case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore() + return 0; + + case Instruction::EXP: return c_expGas; + + case Instruction::SLOAD: return c_sloadGas; + + case Instruction::SHA3: return c_sha3Gas; + + case Instruction::BALANCE: return c_balanceGas; + + case Instruction::CALL: + case Instruction::CALLCODE: return c_callGas; + + case Instruction::CREATE: return c_createGas; + + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); + return c_logGas + numTopics * c_logTopicGas; + } + } +} + +} + +GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager) : + CompilerHelper(_builder), + m_runtimeManager(_runtimeManager) +{ + auto module = getModule(); + + llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Word}; + m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module); + InsertPointGuard guard(m_builder); + + auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); + auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); + auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); + + m_builder.SetInsertPoint(checkBB); + auto arg = m_gasCheckFunc->arg_begin(); + arg->setName("rt"); + ++arg; + arg->setName("cost"); + auto cost = arg; + auto gas = m_runtimeManager.getGas(); + auto isOutOfGas = m_builder.CreateICmpUGT(cost, gas, "isOutOfGas"); + m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB); + + m_builder.SetInsertPoint(outOfGasBB); + m_runtimeManager.raiseException(ReturnCode::OutOfGas); + m_builder.CreateUnreachable(); + + m_builder.SetInsertPoint(updateBB); + gas = m_builder.CreateSub(gas, cost); + m_runtimeManager.setGas(gas); + m_builder.CreateRetVoid(); +} + +void GasMeter::count(Instruction _inst) +{ + if (!m_checkCall) + { + // Create gas check call with mocked block cost at begining of current cost-block + m_checkCall = createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)); + } + + m_blockCost += getStepCost(_inst); +} + +void GasMeter::count(llvm::Value* _cost) +{ + createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), _cost); +} + +void GasMeter::countExp(llvm::Value* _exponent) +{ + // Additional cost is 1 per significant byte of exponent + // lz - leading zeros + // cost = ((256 - lz) + 7) / 8 + + // OPT: All calculations can be done on 32/64 bits + + auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); + auto lz = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); + auto sigBits = m_builder.CreateSub(Constant::get(256), lz); + auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, Constant::get(7)), Constant::get(8)); + count(sigBytes); +} + +void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) +{ + auto oldValue = _ext.sload(_index); + auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); + auto newValueIsZero = m_builder.CreateICmpEQ(_newValue, Constant::get(0), "newValueIsZero"); + auto oldValueIsntZero = m_builder.CreateICmpNE(oldValue, Constant::get(0), "oldValueIsntZero"); + auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); + auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); + auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete"); + auto cost = m_builder.CreateSelect(isInsert, Constant::get(c_sstoreSetGas), Constant::get(c_sstoreResetGas), "cost"); + cost = m_builder.CreateSelect(isDelete, Constant::get(0), cost, "cost"); + count(cost); +} + +void GasMeter::countLogData(llvm::Value* _dataLength) +{ + assert(m_checkCall); + assert(m_blockCost > 0); // LOGn instruction is already counted + static_assert(c_logDataGas == 1, "Log data gas cost has changed. Update GasMeter."); + count(_dataLength); +} + +void GasMeter::countSha3Data(llvm::Value* _dataLength) +{ + assert(m_checkCall); + assert(m_blockCost > 0); // SHA3 instruction is already counted + + // TODO: This round ups to 32 happens in many places + // FIXME: Overflow possible but Memory::require() also called. Probably 64-bit arith can be used. + static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter"); + auto words = m_builder.CreateUDiv(m_builder.CreateAdd(_dataLength, Constant::get(31)), Constant::get(32)); + auto cost = m_builder.CreateNUWMul(Constant::get(c_sha3WordGas), words); + count(cost); +} + +void GasMeter::giveBack(llvm::Value* _gas) +{ + m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); +} + +void GasMeter::commitCostBlock() +{ + // If any uncommited block + if (m_checkCall) + { + if (m_blockCost == 0) // Do not check 0 + { + m_checkCall->eraseFromParent(); // Remove the gas check call + m_checkCall = nullptr; + return; + } + + m_checkCall->setArgOperand(1, Constant::get(m_blockCost)); // Update block cost in gas check call + m_checkCall = nullptr; // End cost-block + m_blockCost = 0; + } + assert(m_blockCost == 0); +} + +void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords) +{ + static_assert(c_memoryGas == 1, "Memory gas cost has changed. Update GasMeter."); + count(_additionalMemoryInWords); +} + +void GasMeter::countCopy(llvm::Value* _copyWords) +{ + static_assert(c_copyGas == 1, "Copy gas cost has changed. Update GasMeter."); + count(_copyWords); +} + +} +} +} + diff --git a/libevmjit/GasMeter.h b/libevmjit/GasMeter.h new file mode 100644 index 000000000..56da6eb9f --- /dev/null +++ b/libevmjit/GasMeter.h @@ -0,0 +1,64 @@ + +#pragma once + +#include "CompilerHelper.h" +#include "Instruction.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class RuntimeManager; + +class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper +{ +public: + GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager); + + /// Count step cost of instruction + void count(Instruction _inst); + + /// Count additional cost + void count(llvm::Value* _cost); + + /// Calculate & count gas cost for SSTORE instruction + void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); + + /// Calculate & count additional gas cost for EXP instruction + void countExp(llvm::Value* _exponent); + + /// Count gas cost of LOG data + void countLogData(llvm::Value* _dataLength); + + /// Count gas cost of SHA3 data + void countSha3Data(llvm::Value* _dataLength); + + /// Finalize cost-block by checking gas needed for the block before the block + void commitCostBlock(); + + /// Give back an amount of gas not used by a call + void giveBack(llvm::Value* _gas); + + /// Generate code that checks the cost of additional memory used by program + void countMemory(llvm::Value* _additionalMemoryInWords); + + /// Count addional gas cost for memory copy + void countCopy(llvm::Value* _copyWords); + +private: + /// Cumulative gas cost of a block of instructions + /// @TODO Handle overflow + uint64_t m_blockCost = 0; + + llvm::CallInst* m_checkCall = nullptr; + llvm::Function* m_gasCheckFunc = nullptr; + + RuntimeManager& m_runtimeManager; +}; + +} +} +} + diff --git a/libevmjit/Instruction.h b/libevmjit/Instruction.h new file mode 100644 index 000000000..502c4b66e --- /dev/null +++ b/libevmjit/Instruction.h @@ -0,0 +1,235 @@ +#pragma once + +#include "Common.h" + +namespace llvm +{ + class APInt; +} + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +/// Virtual machine bytecode instruction. +enum class Instruction: uint8_t +{ + STOP = 0x00, ///< halts execution + ADD, ///< addition operation + MUL, ///< mulitplication operation + SUB, ///< subtraction operation + DIV, ///< integer division operation + SDIV, ///< signed integer division operation + MOD, ///< modulo remainder operation + SMOD, ///< signed modulo remainder operation + ADDMOD, ///< unsigned modular addition + MULMOD, ///< unsigned modular multiplication + EXP, ///< exponential operation + SIGNEXTEND, ///< extend length of signed integer + + LT = 0x10, ///< less-than comparision + GT, ///< greater-than comparision + SLT, ///< signed less-than comparision + SGT, ///< signed greater-than comparision + EQ, ///< equality comparision + ISZERO, ///< simple not operator + AND, ///< bitwise AND operation + OR, ///< bitwise OR operation + XOR, ///< bitwise XOR operation + NOT, ///< bitwise NOT opertation + BYTE, ///< retrieve single byte from word + + SHA3 = 0x20, ///< compute SHA3-256 hash + + ADDRESS = 0x30, ///< get address of currently executing account + BALANCE, ///< get balance of the given account + ORIGIN, ///< get execution origination address + CALLER, ///< get caller address + CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution + CALLDATALOAD, ///< get input data of current environment + CALLDATASIZE, ///< get size of input data in current environment + CALLDATACOPY, ///< copy input data in current environment to memory + CODESIZE, ///< get size of code running in current environment + CODECOPY, ///< copy code running in current environment to memory + GASPRICE, ///< get price of gas in current environment + EXTCODESIZE, ///< get external code size (from another contract) + EXTCODECOPY, ///< copy external code (from another contract) + + PREVHASH = 0x40, ///< get hash of most recent complete block + COINBASE, ///< get the block's coinbase address + TIMESTAMP, ///< get the block's timestamp + NUMBER, ///< get the block's number + DIFFICULTY, ///< get the block's difficulty + GASLIMIT, ///< get the block's gas limit + + POP = 0x50, ///< remove item from stack + MLOAD, ///< load word from memory + MSTORE, ///< save word to memory + MSTORE8, ///< save byte to memory + SLOAD, ///< load word from storage + SSTORE, ///< save word to storage + JUMP, ///< alter the program counter + JUMPI, ///< conditionally alter the program counter + PC, ///< get the program counter + MSIZE, ///< get the size of active memory + GAS, ///< get the amount of available gas + JUMPDEST, ///< set a potential jump destination + + PUSH1 = 0x60, ///< place 1 byte item on stack + PUSH2, ///< place 2 byte item on stack + PUSH3, ///< place 3 byte item on stack + PUSH4, ///< place 4 byte item on stack + PUSH5, ///< place 5 byte item on stack + PUSH6, ///< place 6 byte item on stack + PUSH7, ///< place 7 byte item on stack + PUSH8, ///< place 8 byte item on stack + PUSH9, ///< place 9 byte item on stack + PUSH10, ///< place 10 byte item on stack + PUSH11, ///< place 11 byte item on stack + PUSH12, ///< place 12 byte item on stack + PUSH13, ///< place 13 byte item on stack + PUSH14, ///< place 14 byte item on stack + PUSH15, ///< place 15 byte item on stack + PUSH16, ///< place 16 byte item on stack + PUSH17, ///< place 17 byte item on stack + PUSH18, ///< place 18 byte item on stack + PUSH19, ///< place 19 byte item on stack + PUSH20, ///< place 20 byte item on stack + PUSH21, ///< place 21 byte item on stack + PUSH22, ///< place 22 byte item on stack + PUSH23, ///< place 23 byte item on stack + PUSH24, ///< place 24 byte item on stack + PUSH25, ///< place 25 byte item on stack + PUSH26, ///< place 26 byte item on stack + PUSH27, ///< place 27 byte item on stack + PUSH28, ///< place 28 byte item on stack + PUSH29, ///< place 29 byte item on stack + PUSH30, ///< place 30 byte item on stack + PUSH31, ///< place 31 byte item on stack + PUSH32, ///< place 32 byte item on stack + + DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack + DUP2, ///< copies the second highest item in the stack to the top of the stack + DUP3, ///< copies the third highest item in the stack to the top of the stack + DUP4, ///< copies the 4th highest item in the stack to the top of the stack + DUP5, ///< copies the 5th highest item in the stack to the top of the stack + DUP6, ///< copies the 6th highest item in the stack to the top of the stack + DUP7, ///< copies the 7th highest item in the stack to the top of the stack + DUP8, ///< copies the 8th highest item in the stack to the top of the stack + DUP9, ///< copies the 9th highest item in the stack to the top of the stack + DUP10, ///< copies the 10th highest item in the stack to the top of the stack + DUP11, ///< copies the 11th highest item in the stack to the top of the stack + DUP12, ///< copies the 12th highest item in the stack to the top of the stack + DUP13, ///< copies the 13th highest item in the stack to the top of the stack + DUP14, ///< copies the 14th highest item in the stack to the top of the stack + DUP15, ///< copies the 15th highest item in the stack to the top of the stack + DUP16, ///< copies the 16th highest item in the stack to the top of the stack + + SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack + SWAP2, ///< swaps the highest and third highest value on the stack + SWAP3, ///< swaps the highest and 4th highest value on the stack + SWAP4, ///< swaps the highest and 5th highest value on the stack + SWAP5, ///< swaps the highest and 6th highest value on the stack + SWAP6, ///< swaps the highest and 7th highest value on the stack + SWAP7, ///< swaps the highest and 8th highest value on the stack + SWAP8, ///< swaps the highest and 9th highest value on the stack + SWAP9, ///< swaps the highest and 10th highest value on the stack + SWAP10, ///< swaps the highest and 11th highest value on the stack + SWAP11, ///< swaps the highest and 12th highest value on the stack + SWAP12, ///< swaps the highest and 13th highest value on the stack + SWAP13, ///< swaps the highest and 14th highest value on the stack + SWAP14, ///< swaps the highest and 15th highest value on the stack + SWAP15, ///< swaps the highest and 16th highest value on the stack + SWAP16, ///< swaps the highest and 17th highest value on the stack + + LOG0 = 0xa0, ///< Makes a log entry; no topics. + LOG1, ///< Makes a log entry; 1 topic. + LOG2, ///< Makes a log entry; 2 topics. + LOG3, ///< Makes a log entry; 3 topics. + LOG4, ///< Makes a log entry; 4 topics. + + CREATE = 0xf0, ///< create a new account with associated code + CALL, ///< message-call into an account + CALLCODE, ///< message-call with another account's code only + RETURN, ///< halt execution returning output data + SUICIDE = 0xff ///< halt execution and register account for later deletion +}; + +/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it +/// Reading out of bytecode means reading 0 +/// @param _curr is updates and points the last real byte read +llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); + +#define ANY_PUSH PUSH1: \ + case Instruction::PUSH2: \ + case Instruction::PUSH3: \ + case Instruction::PUSH4: \ + case Instruction::PUSH5: \ + case Instruction::PUSH6: \ + case Instruction::PUSH7: \ + case Instruction::PUSH8: \ + case Instruction::PUSH9: \ + case Instruction::PUSH10: \ + case Instruction::PUSH11: \ + case Instruction::PUSH12: \ + case Instruction::PUSH13: \ + case Instruction::PUSH14: \ + case Instruction::PUSH15: \ + case Instruction::PUSH16: \ + case Instruction::PUSH17: \ + case Instruction::PUSH18: \ + case Instruction::PUSH19: \ + case Instruction::PUSH20: \ + case Instruction::PUSH21: \ + case Instruction::PUSH22: \ + case Instruction::PUSH23: \ + case Instruction::PUSH24: \ + case Instruction::PUSH25: \ + case Instruction::PUSH26: \ + case Instruction::PUSH27: \ + case Instruction::PUSH28: \ + case Instruction::PUSH29: \ + case Instruction::PUSH30: \ + case Instruction::PUSH31: \ + case Instruction::PUSH32 + +#define ANY_DUP DUP1: \ + case Instruction::DUP2: \ + case Instruction::DUP3: \ + case Instruction::DUP4: \ + case Instruction::DUP5: \ + case Instruction::DUP6: \ + case Instruction::DUP7: \ + case Instruction::DUP8: \ + case Instruction::DUP9: \ + case Instruction::DUP10: \ + case Instruction::DUP11: \ + case Instruction::DUP12: \ + case Instruction::DUP13: \ + case Instruction::DUP14: \ + case Instruction::DUP15: \ + case Instruction::DUP16 + +#define ANY_SWAP SWAP1: \ + case Instruction::SWAP2: \ + case Instruction::SWAP3: \ + case Instruction::SWAP4: \ + case Instruction::SWAP5: \ + case Instruction::SWAP6: \ + case Instruction::SWAP7: \ + case Instruction::SWAP8: \ + case Instruction::SWAP9: \ + case Instruction::SWAP10: \ + case Instruction::SWAP11: \ + case Instruction::SWAP12: \ + case Instruction::SWAP13: \ + case Instruction::SWAP14: \ + case Instruction::SWAP15: \ + case Instruction::SWAP16 + +} +} +} diff --git a/libevmjit/Memory.cpp b/libevmjit/Memory.cpp new file mode 100644 index 000000000..c60a5e554 --- /dev/null +++ b/libevmjit/Memory.cpp @@ -0,0 +1,237 @@ +#include "Memory.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Type.h" +#include "Runtime.h" +#include "GasMeter.h" +#include "Endianness.h" +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): + RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed + m_gasMeter(_gasMeter) +{ + llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; + m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); + llvm::AttrBuilder attrBuilder; + attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); + m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); + + m_require = createRequireFunc(_gasMeter); + m_loadWord = createFunc(false, Type::Word, _gasMeter); + m_storeWord = createFunc(true, Type::Word, _gasMeter); + m_storeByte = createFunc(true, Type::Byte, _gasMeter); +} + +llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) +{ + llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); + auto rt = func->arg_begin(); + rt->setName("rt"); + auto offset = rt->getNextNode(); + offset->setName("offset"); + auto size = offset->getNextNode(); + size->setName("size"); + + auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); + auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); + auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); + auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); + + InsertPointGuard guard(m_builder); // Restores insert point at function exit + + // BB "Pre": Ignore checks with size 0 + m_builder.SetInsertPoint(preBB); + auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); + m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); + + // BB "Check" + m_builder.SetInsertPoint(checkBB); + auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); + auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); + auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); + auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); + auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); + auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); + auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); + m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? + + // BB "Resize" + m_builder.SetInsertPoint(resizeBB); + // Check gas first + uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); + auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); + auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); + auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); + wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); + wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); + sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); + auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k + auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); + _gasMeter.countMemory(newWords); + // Resize + m_builder.CreateStore(sizeRequired, sizePtr); + auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); + auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); + m_builder.CreateStore(newData, dataPtr); + m_builder.CreateBr(returnBB); + + // BB "Return" + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRetVoid(); + return func; +} + +llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) +{ + auto isWord = _valueType == Type::Word; + + llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; + llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; + auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; + auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); + auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); + + InsertPointGuard guard(m_builder); // Restores insert point at function exit + + m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); + auto rt = func->arg_begin(); + rt->setName("rt"); + auto index = rt->getNextNode(); + index->setName("index"); + + auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; + this->require(index, Constant::get(valueSize)); + auto ptr = getBytePtr(index); + if (isWord) + ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); + if (_isStore) + { + llvm::Value* value = index->getNextNode(); + value->setName("value"); + if (isWord) + value = Endianness::toBE(m_builder, value); + m_builder.CreateStore(value, ptr); + m_builder.CreateRetVoid(); + } + else + { + llvm::Value* ret = m_builder.CreateLoad(ptr); + ret = Endianness::toNative(m_builder, ret); + m_builder.CreateRet(ret); + } + + return func; +} + + +llvm::Value* Memory::loadWord(llvm::Value* _addr) +{ + return createCall(m_loadWord, getRuntimeManager().getRuntimePtr(), _addr); +} + +void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) +{ + createCall(m_storeWord, getRuntimeManager().getRuntimePtr(), _addr, _word); +} + +void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) +{ + auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); + createCall(m_storeByte, getRuntimeManager().getRuntimePtr(), _addr, byte); +} + +llvm::Value* Memory::getData() +{ + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); + return m_builder.CreateLoad(dataPtr, "data"); +} + +llvm::Value* Memory::getSize() +{ + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); + return m_builder.CreateLoad(sizePtr, "size"); +} + +llvm::Value* Memory::getBytePtr(llvm::Value* _index) +{ + return m_builder.CreateGEP(getData(), _index, "ptr"); +} + +void Memory::require(llvm::Value* _offset, llvm::Value* _size) +{ + createCall(m_require, getRuntimeManager().getRuntimePtr(), _offset, _size); +} + +void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, + llvm::Value* _destMemIdx, llvm::Value* _reqBytes) +{ + require(_destMemIdx, _reqBytes); + + // Additional copy cost + // TODO: This round ups to 32 happens in many places + auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); + m_gasMeter.countCopy(copyWords); + + // Algorithm: + // isOutsideData = idx256 >= size256 + // idx64 = trunc idx256 + // size64 = trunc size256 + // dataLeftSize = size64 - idx64 // safe if not isOutsideData + // reqBytes64 = trunc _reqBytes // require() handles large values + // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min + // bytesToCopy = select(isOutsideData, 0, bytesToCopy0) + + auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); + auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); + auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); + auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); + auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); + auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); + auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); + auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants + auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner); + + auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); + auto dst = m_builder.CreateGEP(getData(), _destMemIdx, "dst"); + m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); +} + +} +} +} + + +extern "C" +{ + using namespace dev::eth::jit; + + EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR + { + auto size = _size->a; // Trunc to 64-bit + auto& memory = _rt->getMemory(); + memory.resize(size); + return memory.data(); + } +} diff --git a/libevmjit/Memory.h b/libevmjit/Memory.h new file mode 100644 index 000000000..ed9c51805 --- /dev/null +++ b/libevmjit/Memory.h @@ -0,0 +1,46 @@ +#pragma once + +#include "CompilerHelper.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class GasMeter; + +class Memory : public RuntimeHelper +{ +public: + Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter); + + llvm::Value* loadWord(llvm::Value* _addr); + void storeWord(llvm::Value* _addr, llvm::Value* _word); + void storeByte(llvm::Value* _addr, llvm::Value* _byte); + llvm::Value* getData(); + llvm::Value* getSize(); + llvm::Value* getBytePtr(llvm::Value* _index); + void copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, + llvm::Value* _destMemIdx, llvm::Value* _byteCount); + + /// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory. + void require(llvm::Value* _offset, llvm::Value* _size); + +private: + GasMeter& m_gasMeter; + + llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter); + llvm::Function* createRequireFunc(GasMeter& _gasMeter); + + llvm::Function* m_resize; + llvm::Function* m_require; + llvm::Function* m_loadWord; + llvm::Function* m_storeWord; + llvm::Function* m_storeByte; +}; + +} +} +} + diff --git a/libevmjit/Runtime.cpp b/libevmjit/Runtime.cpp new file mode 100644 index 000000000..27c81ea86 --- /dev/null +++ b/libevmjit/Runtime.cpp @@ -0,0 +1,52 @@ + +#include "Runtime.h" + +#include +#include +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ +namespace +{ + jmp_buf_ref g_currJmpBuf; +} + +jmp_buf_ref Runtime::getCurrJmpBuf() +{ + return g_currJmpBuf; +} + +Runtime::Runtime(RuntimeData* _data, Env* _env): + m_data(*_data), + m_env(*_env), + m_currJmpBuf(m_jmpBuf), + m_prevJmpBuf(g_currJmpBuf) +{ + g_currJmpBuf = m_jmpBuf; +} + +Runtime::~Runtime() +{ + g_currJmpBuf = m_prevJmpBuf; +} + +bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy +{ + // TODO: Handle large indexes + auto offset = static_cast(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset])); + auto size = static_cast(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize])); + + assert(offset + size <= m_memory.size()); + // TODO: Handle invalid data access by returning empty ref + auto dataBeg = m_memory.begin() + offset; + return {dataBeg, dataBeg + size}; +} + +} +} +} diff --git a/libevmjit/Runtime.h b/libevmjit/Runtime.h new file mode 100644 index 000000000..e11dac319 --- /dev/null +++ b/libevmjit/Runtime.h @@ -0,0 +1,62 @@ + +#pragma once + +#include +#include + +#include "Instruction.h" +#include "CompilerHelper.h" +#include "Utils.h" +#include "Type.h" +#include "RuntimeData.h" + + +#ifdef _MSC_VER + #define EXPORT __declspec(dllexport) +#else + #define EXPORT +#endif + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +using StackImpl = std::vector; +using MemoryImpl = bytes; +using jmp_buf_ref = decltype(&std::jmp_buf{}[0]); + +class Runtime +{ +public: + Runtime(RuntimeData* _data, Env* _env); + ~Runtime(); + + Runtime(const Runtime&) = delete; + void operator=(const Runtime&) = delete; + + StackImpl& getStack() { return m_stack; } + MemoryImpl& getMemory() { return m_memory; } + Env* getEnvPtr() { return &m_env; } + + bytes getReturnData() const; + jmp_buf_ref getJmpBuf() { return m_jmpBuf; } + static jmp_buf_ref getCurrJmpBuf(); + +private: + RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. + Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. + jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract. + byte* m_memoryData = nullptr; + i256 m_memorySize = {}; + jmp_buf_ref m_prevJmpBuf; + std::jmp_buf m_jmpBuf; + StackImpl m_stack; + MemoryImpl m_memory; +}; + +} +} +} diff --git a/libevmjit/RuntimeData.h b/libevmjit/RuntimeData.h new file mode 100644 index 000000000..89987bdeb --- /dev/null +++ b/libevmjit/RuntimeData.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Utils.h" + + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +struct RuntimeData +{ + enum Index + { + Gas, + Address, + Caller, + Origin, + CallValue, + CallDataSize, + GasPrice, + PrevHash, + CoinBase, + TimeStamp, + Number, + Difficulty, + GasLimit, + CodeSize, + + _size, + + ReturnDataOffset = CallValue, // Reuse 2 fields for return data reference + ReturnDataSize = CallDataSize, + SuicideDestAddress = Address, ///< Suicide balance destination address + }; + + i256 elems[_size] = {}; + byte const* callData = nullptr; + byte const* code = nullptr; + + void set(Index _index, u256 _value) { elems[_index] = eth2llvm(_value); } + u256 get(Index _index) { return llvm2eth(elems[_index]); } +}; + +/// VM Environment (ExtVM) opaque type +struct Env; + +} +} +} diff --git a/libevmjit/RuntimeManager.cpp b/libevmjit/RuntimeManager.cpp new file mode 100644 index 000000000..14280f80f --- /dev/null +++ b/libevmjit/RuntimeManager.cpp @@ -0,0 +1,199 @@ + +#include "RuntimeManager.h" + +#include +#include +#include + +#include "RuntimeData.h" +#include "Instruction.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::StructType* RuntimeManager::getRuntimeDataType() +{ + static llvm::StructType* type = nullptr; + if (!type) + { + llvm::Type* elems[] = + { + llvm::ArrayType::get(Type::Word, RuntimeData::_size), // i256[] + Type::BytePtr, // callData + Type::BytePtr // code + }; + type = llvm::StructType::create(elems, "RuntimeData"); + } + return type; +} + +llvm::StructType* RuntimeManager::getRuntimeType() +{ + static llvm::StructType* type = nullptr; + if (!type) + { + llvm::Type* elems[] = + { + Type::RuntimeDataPtr, // data + Type::EnvPtr, // Env* + Type::BytePtr, // jmpbuf + Type::BytePtr, // memory data + Type::Word, // memory size + }; + type = llvm::StructType::create(elems, "Runtime"); + } + return type; +} + +namespace +{ +llvm::Twine getName(RuntimeData::Index _index) +{ + switch (_index) + { + default: return "data"; + case RuntimeData::Gas: return "gas"; + case RuntimeData::Address: return "address"; + case RuntimeData::Caller: return "caller"; + case RuntimeData::Origin: return "origin"; + case RuntimeData::CallValue: return "callvalue"; + case RuntimeData::CallDataSize: return "calldatasize"; + case RuntimeData::GasPrice: return "gasprice"; + case RuntimeData::PrevHash: return "prevhash"; + case RuntimeData::CoinBase: return "coinbase"; + case RuntimeData::TimeStamp: return "timestamp"; + case RuntimeData::Number: return "number"; + case RuntimeData::Difficulty: return "difficulty"; + case RuntimeData::GasLimit: return "gaslimit"; + case RuntimeData::CodeSize: return "codesize"; + } +} +} + +RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder): CompilerHelper(_builder) +{ + m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::longjmp); + + // Unpack data + auto rtPtr = getRuntimePtr(); + m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); + assert(m_dataPtr->getType() == Type::RuntimeDataPtr); + m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); + assert(m_envPtr->getType() == Type::EnvPtr); +} + +llvm::Value* RuntimeManager::getRuntimePtr() +{ + // Expect first argument of a function to be a pointer to Runtime + auto func = m_builder.GetInsertBlock()->getParent(); + auto rtPtr = &func->getArgumentList().front(); + assert(rtPtr->getType() == Type::RuntimePtr); + return rtPtr; +} + +llvm::Value* RuntimeManager::getDataPtr() +{ + if (getMainFunction()) + return m_dataPtr; + + auto rtPtr = getRuntimePtr(); + return m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); +} + +llvm::Value* RuntimeManager::getEnvPtr() +{ + assert(getMainFunction()); // Available only in main function + return m_envPtr; +} + +llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) +{ + llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(_index)}; + return m_builder.CreateInBoundsGEP(getDataPtr(), idxList, getName(_index) + "Ptr"); +} + +llvm::Value* RuntimeManager::get(RuntimeData::Index _index) +{ + return m_builder.CreateLoad(getPtr(_index), getName(_index)); +} + +void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) +{ + m_builder.CreateStore(_value, getPtr(_index)); +} + +void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) +{ + set(RuntimeData::ReturnDataOffset, _offset); + set(RuntimeData::ReturnDataSize, _size); +} + +void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress) +{ + set(RuntimeData::SuicideDestAddress, _balanceAddress); +} + +void RuntimeManager::raiseException(ReturnCode _returnCode) +{ + m_builder.CreateCall2(m_longjmp, getJmpBuf(), Constant::get(_returnCode)); +} + +llvm::Value* RuntimeManager::get(Instruction _inst) +{ + switch (_inst) + { + default: assert(false); return nullptr; + case Instruction::GAS: return get(RuntimeData::Gas); + case Instruction::ADDRESS: return get(RuntimeData::Address); + case Instruction::CALLER: return get(RuntimeData::Caller); + case Instruction::ORIGIN: return get(RuntimeData::Origin); + case Instruction::CALLVALUE: return get(RuntimeData::CallValue); + case Instruction::CALLDATASIZE: return get(RuntimeData::CallDataSize); + case Instruction::GASPRICE: return get(RuntimeData::GasPrice); + case Instruction::PREVHASH: return get(RuntimeData::PrevHash); + case Instruction::COINBASE: return get(RuntimeData::CoinBase); + case Instruction::TIMESTAMP: return get(RuntimeData::TimeStamp); + case Instruction::NUMBER: return get(RuntimeData::Number); + case Instruction::DIFFICULTY: return get(RuntimeData::Difficulty); + case Instruction::GASLIMIT: return get(RuntimeData::GasLimit); + case Instruction::CODESIZE: return get(RuntimeData::CodeSize); + } +} + +llvm::Value* RuntimeManager::getCallData() +{ + auto ptr = getBuilder().CreateStructGEP(getDataPtr(), 1, "calldataPtr"); + return getBuilder().CreateLoad(ptr, "calldata"); +} + +llvm::Value* RuntimeManager::getCode() +{ + auto ptr = getBuilder().CreateStructGEP(getDataPtr(), 2, "codePtr"); + return getBuilder().CreateLoad(ptr, "code"); +} + +llvm::Value* RuntimeManager::getJmpBuf() +{ + auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2, "jmpbufPtr"); + return getBuilder().CreateLoad(ptr, "jmpbuf"); +} + +llvm::Value* RuntimeManager::getGas() +{ + return get(RuntimeData::Gas); +} + +void RuntimeManager::setGas(llvm::Value* _gas) +{ + llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(RuntimeData::Gas)}; + auto ptr = m_builder.CreateInBoundsGEP(getDataPtr(), idxList, "gasPtr"); + m_builder.CreateStore(_gas, ptr); +} + +} +} +} diff --git a/libevmjit/RuntimeManager.h b/libevmjit/RuntimeManager.h new file mode 100644 index 000000000..ce60424ac --- /dev/null +++ b/libevmjit/RuntimeManager.h @@ -0,0 +1,51 @@ +#pragma once + +#include "CompilerHelper.h" +#include "Type.h" +#include "RuntimeData.h" +#include "Instruction.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class RuntimeManager: public CompilerHelper +{ +public: + RuntimeManager(llvm::IRBuilder<>& _builder); + + llvm::Value* getRuntimePtr(); + llvm::Value* getDataPtr(); + llvm::Value* getEnvPtr(); // TODO: Can we make it const? + + llvm::Value* get(RuntimeData::Index _index); + llvm::Value* get(Instruction _inst); + llvm::Value* getGas(); // TODO: Remove + llvm::Value* getCallData(); + llvm::Value* getCode(); + void setGas(llvm::Value* _gas); + + void registerReturnData(llvm::Value* _index, llvm::Value* _size); + void registerSuicide(llvm::Value* _balanceAddress); + + void raiseException(ReturnCode _returnCode); + + static llvm::StructType* getRuntimeType(); + static llvm::StructType* getRuntimeDataType(); + +private: + llvm::Value* getPtr(RuntimeData::Index _index); + void set(RuntimeData::Index _index, llvm::Value* _value); + llvm::Value* getJmpBuf(); + + llvm::Function* m_longjmp = nullptr; + llvm::Value* m_dataPtr = nullptr; + llvm::Value* m_envPtr = nullptr; +}; + +} +} +} diff --git a/libevmjit/Stack.cpp b/libevmjit/Stack.cpp new file mode 100644 index 000000000..52782999a --- /dev/null +++ b/libevmjit/Stack.cpp @@ -0,0 +1,133 @@ +#include "Stack.h" +#include "RuntimeManager.h" +#include "Runtime.h" +#include "Type.h" + +#include +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager): + CompilerHelper(_builder), + m_runtimeManager(_runtimeManager) +{ + m_arg = m_builder.CreateAlloca(Type::Word, nullptr, "stack.arg"); + + using namespace llvm; + using Linkage = GlobalValue::LinkageTypes; + + auto module = getModule(); + + llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr}; + m_push = Function::Create(FunctionType::get(Type::Void, pushArgTypes, false), Linkage::ExternalLinkage, "stack_push", module); + + llvm::Type* popArgTypes[] = {Type::RuntimePtr, Type::Size}; + m_pop = Function::Create(FunctionType::get(Type::Void, popArgTypes, false), Linkage::ExternalLinkage, "stack_pop", module); + + llvm::Type* getSetArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr}; + m_get = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_get", module); + m_set = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_set", module); +} + +llvm::Value* Stack::get(size_t _index) +{ + m_builder.CreateCall3(m_get, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _index, false), m_arg); + return m_builder.CreateLoad(m_arg); +} + +void Stack::set(size_t _index, llvm::Value* _value) +{ + m_builder.CreateStore(_value, m_arg); + m_builder.CreateCall3(m_set, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _index, false), m_arg); +} + +void Stack::pop(size_t _count) +{ + m_builder.CreateCall2(m_pop, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _count, false)); +} + +void Stack::push(llvm::Value* _value) +{ + m_builder.CreateStore(_value, m_arg); + m_builder.CreateCall2(m_push, m_runtimeManager.getRuntimePtr(), m_arg); +} + + +size_t Stack::maxStackSize = 0; + +} +} +} + +extern "C" +{ + using namespace dev::eth::jit; + + EXPORT void stack_pop(Runtime* _rt, uint64_t _count) + { + auto& stack = _rt->getStack(); + if (stack.size() < _count) + longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); + + stack.erase(stack.end() - _count, stack.end()); + } + + EXPORT void stack_push(Runtime* _rt, i256 const* _word) + { + auto& stack = _rt->getStack(); + stack.push_back(*_word); + + if (stack.size() > Stack::maxStackSize) + Stack::maxStackSize = stack.size(); + } + + EXPORT void stack_get(Runtime* _rt, uint64_t _index, i256* o_ret) + { + auto& stack = _rt->getStack(); + // TODO: encode _index and stack size in the return code + if (stack.size() <= _index) + longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); + + *o_ret = *(stack.rbegin() + _index); + } + + EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word) + { + auto& stack = _rt->getStack(); + // TODO: encode _index and stack size in the return code + if (stack.size() <= _index) + longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); + + *(stack.rbegin() + _index) = *_word; + } + + EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value) + { + // It asumes all indexes are less than 2^64 + + auto index = _index->a; + if (_index->b || _index->c || _index->d) // if bigger that 2^64 + index = std::numeric_limits::max(); // set max to fill with 0 leter + + auto data = _rtData->callData; + auto size = _rtData->elems[RuntimeData::CallDataSize].a; + for (auto i = 0; i < 32; ++i) + { + if (index < size) + { + o_value[i] = data[index]; + ++index; // increment only if in range + } + else + o_value[i] = 0; + } + } + +} // extern "C" + diff --git a/libevmjit/Stack.h b/libevmjit/Stack.h new file mode 100644 index 000000000..3e8881e4f --- /dev/null +++ b/libevmjit/Stack.h @@ -0,0 +1,43 @@ +#pragma once + +#include "CompilerHelper.h" + +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class RuntimeManager; + +class Stack : public CompilerHelper +{ +public: + Stack(llvm::IRBuilder<>& builder, RuntimeManager& runtimeManager); + + llvm::Value* get(size_t _index); + void set(size_t _index, llvm::Value* _value); + void pop(size_t _count); + void push(llvm::Value* _value); + + static size_t maxStackSize; + +private: + RuntimeManager& m_runtimeManager; + + llvm::Function* m_push; + llvm::Function* m_pop; + llvm::Function* m_get; + llvm::Function* m_set; + + llvm::Value* m_arg; +}; + + +} +} +} + + diff --git a/libevmjit/Type.cpp b/libevmjit/Type.cpp new file mode 100644 index 000000000..22ccea12e --- /dev/null +++ b/libevmjit/Type.cpp @@ -0,0 +1,67 @@ + +#include "Type.h" + +#include + +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::IntegerType* Type::Word; +llvm::PointerType* Type::WordPtr; +llvm::IntegerType* Type::lowPrecision; +llvm::IntegerType* Type::Bool; +llvm::IntegerType* Type::Size; +llvm::IntegerType* Type::Byte; +llvm::PointerType* Type::BytePtr; +llvm::Type* Type::Void; +llvm::IntegerType* Type::MainReturn; +llvm::PointerType* Type::EnvPtr; +llvm::PointerType* Type::RuntimeDataPtr; +llvm::PointerType* Type::RuntimePtr; + +void Type::init(llvm::LLVMContext& _context) +{ + if (!Word) // Do init only once + { + Word = llvm::Type::getIntNTy(_context, 256); + WordPtr = Word->getPointerTo(); + lowPrecision = llvm::Type::getInt64Ty(_context); + // TODO: Size should be architecture-dependent + Bool = llvm::Type::getInt1Ty(_context); + Size = llvm::Type::getInt64Ty(_context); + Byte = llvm::Type::getInt8Ty(_context); + BytePtr = Byte->getPointerTo(); + Void = llvm::Type::getVoidTy(_context); + MainReturn = llvm::Type::getInt32Ty(_context); + + EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); + RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); + RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); + } +} + +llvm::ConstantInt* Constant::get(int64_t _n) +{ + return llvm::ConstantInt::getSigned(Type::Word, _n); +} + +llvm::ConstantInt* Constant::get(llvm::APInt const& _n) +{ + return llvm::ConstantInt::get(Type::Word->getContext(), _n); +} + +llvm::ConstantInt* Constant::get(ReturnCode _returnCode) +{ + return llvm::ConstantInt::get(Type::MainReturn, static_cast(_returnCode)); +} + +} +} +} + diff --git a/libevmjit/Type.h b/libevmjit/Type.h new file mode 100644 index 000000000..d4804ee59 --- /dev/null +++ b/libevmjit/Type.h @@ -0,0 +1,54 @@ + +#pragma once + +#include +#include +#include "Common.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +struct Type +{ + static llvm::IntegerType* Word; + static llvm::PointerType* WordPtr; + + /// Type for doing low precision arithmetics where 256-bit precision is not supported by native target + /// @TODO: Use 64-bit for now. In 128-bit compiler-rt library functions are required + static llvm::IntegerType* lowPrecision; + + static llvm::IntegerType* Bool; + static llvm::IntegerType* Size; + + static llvm::IntegerType* Byte; + static llvm::PointerType* BytePtr; + + static llvm::Type* Void; + + /// Main function return type + static llvm::IntegerType* MainReturn; + + static llvm::PointerType* EnvPtr; + static llvm::PointerType* RuntimeDataPtr; + static llvm::PointerType* RuntimePtr; + + static void init(llvm::LLVMContext& _context); +}; + +struct Constant +{ + /// Returns word-size constant + static llvm::ConstantInt* get(int64_t _n); + static llvm::ConstantInt* get(llvm::APInt const& _n); + + static llvm::ConstantInt* get(ReturnCode _returnCode); +}; + +} +} +} + diff --git a/libevmjit/Utils.cpp b/libevmjit/Utils.cpp new file mode 100644 index 000000000..f1ffbf67f --- /dev/null +++ b/libevmjit/Utils.cpp @@ -0,0 +1,66 @@ + +#include +#include "Utils.h" +#include "Instruction.h" +#include "Runtime.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +u256 llvm2eth(i256 _i) +{ + u256 u = 0; + u |= _i.d; + u <<= 64; + u |= _i.c; + u <<= 64; + u |= _i.b; + u <<= 64; + u |= _i.a; + return u; +} + +i256 eth2llvm(u256 _u) +{ + i256 i; + u256 mask = 0xFFFFFFFFFFFFFFFF; + i.a = static_cast(_u & mask); + _u >>= 64; + i.b = static_cast(_u & mask); + _u >>= 64; + i.c = static_cast(_u & mask); + _u >>= 64; + i.d = static_cast(_u & mask); + return i; +} + +llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) +{ + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + llvm::APInt value(256, 0); + ++_curr; // Point the data + for (decltype(numBytes) i = 0; i < numBytes; ++i) + { + byte b = (_curr != _end) ? *_curr++ : 0; + value <<= 8; + value |= b; + } + --_curr; // Point the last real byte read + return value; +} + +void terminate(ReturnCode _returnCode) +{ + auto jmpBuf = Runtime::getCurrJmpBuf(); + std::longjmp(jmpBuf, static_cast(_returnCode)); +} + +} +} +} diff --git a/libevmjit/Utils.h b/libevmjit/Utils.h new file mode 100644 index 000000000..db0647fdf --- /dev/null +++ b/libevmjit/Utils.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Common.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +struct JIT: public NoteChannel { static const char* name() { return "JIT"; } }; + +//#define clog(CHANNEL) std::cerr +#define clog(CHANNEL) std::ostream(nullptr) + +u256 llvm2eth(i256); +i256 eth2llvm(u256); + +void terminate(ReturnCode _returnCode); + +} +} +} diff --git a/libevmjit/interface.c b/libevmjit/interface.c new file mode 100644 index 000000000..47589578b --- /dev/null +++ b/libevmjit/interface.c @@ -0,0 +1,30 @@ +#include + +// JIT object opaque type +typedef struct evm_jit evm_jit; + +// Contract execution return code +typedef int evm_jit_return_code; + +// Host-endian 256-bit integer type +typedef struct i256 i256; + +// Big-endian right aligned 256-bit hash +typedef struct h256 h256; + +// Runtime data struct - must be provided by external language (Go, C++, Python) +typedef struct evm_jit_rt evm_jit_rt; + +// Runtime callback functions - implementations must be provided by external language (Go, C++, Python) +void evm_jit_rt_sload(evm_jit_rt* _rt, i256* _index, i256* _ret); +void evm_jit_rt_sstore(evm_jit_rt* _rt, i256* _index, i256* _value); +void evm_jit_rt_balance(evm_jit_rt* _rt, h256* _address, i256* _ret); +// And so on... + +evm_jit* evm_jit_create(evm_jit_rt* _runtime_data); + +evm_jit_return_code evm_jit_execute(evm_jit* _jit); + +void evm_jit_get_return_data(evm_jit* _jit, char* _return_data_offset, size_t* _return_data_size); + +void evm_jit_destroy(evm_jit* _jit); From 8badd50826c56ab0730c153b8773551206e12e3c Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 6 Jan 2015 14:42:37 +0100 Subject: [PATCH 33/54] added more jumpdest tests --- test/vmIOandFlowOperationsTestFiller.json | 709 +++++++++++++++++++++- 1 file changed, 704 insertions(+), 5 deletions(-) diff --git a/test/vmIOandFlowOperationsTestFiller.json b/test/vmIOandFlowOperationsTestFiller.json index dce594e1e..073c8ac80 100644 --- a/test/vmIOandFlowOperationsTestFiller.json +++ b/test/vmIOandFlowOperationsTestFiller.json @@ -475,6 +475,174 @@ } }, + "jumpAfterStop": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6006560060015b6002600355", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "jumpiAfterStop": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60016008570060015b6002600355", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "jumpInsidePushWithoutJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60055661eeff", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "jumpInsidePushWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600456655b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "jumpifInsidePushWithoutJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600160075761eeff", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "jumpifInsidePushWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6001600657655b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "jump0_foreverOutOfGas": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -488,7 +656,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "0x600056", + "code" : "0x5b600056", "storage": {} } }, @@ -503,7 +671,7 @@ } }, - "jump0": { + "jump0_outOfBoundary": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -558,7 +726,35 @@ } }, - "jump0_jumpdest1": { + "jump0_withoutJumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236007566001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "jump0_AfterJumpdest": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -614,7 +810,7 @@ } }, - "jump0_jumpdest3": { + "jump0_AfterJumpdest3": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -754,7 +950,7 @@ } }, - "jumpi2": { + "jumpiOutsideBoundary": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -782,6 +978,509 @@ } }, + "DynamicJumpAfterStop": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6008600101560060015b6002600355", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpiAfterStop": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60016008600301570060015b6002600355", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpInsidePushWithoutJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60056003015661eeff", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpInsidePushWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600460030156655b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpifInsidePushWithoutJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x600160076003015761eeff", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpifInsidePushWithJumpDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6001600660030157655b6001600155", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJump0_foreverOutOfGas": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x5b600060000156", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DyanmicJump0_outOfBoundary": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236007600301566001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "DynamicJump0_jumpdest0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x602360076003015660015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJump0_withoutJumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236007600301566001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJump0_AfterJumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x602360086003015660015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJump0_jumpdest2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600a6008506003015660015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJump0_AfterJumpdest3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6023600b6008506003015660015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJump1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x620fffff620fffff0160030156", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpi0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x602360016009600301576001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpi1_jumpdest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60236001600a6003015760015b600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpi1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x602360006009600301576001600255", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "DynamicJumpiOutsideBoundary": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x60017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0600301576002600355", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "pc0": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From 418357cfbd1dfffde12c74444484c350ec256a1f Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 6 Jan 2015 15:05:13 +0100 Subject: [PATCH 34/54] eth --version display protocol version --- eth/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eth/main.cpp b/eth/main.cpp index d55766cfd..ffd8481ea 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -145,6 +145,8 @@ string credits(bool _interactive = false) void version() { cout << "eth version " << dev::Version << endl; + cout << "Network protocol version: " << dev::eth::c_protocolVersion << endl; + cout << "Client database version: " << dev::eth::c_databaseVersion << endl; cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; exit(0); } From 13a836987d41fb3fc039dc44e6cf7fd4cbf9bd24 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 6 Jan 2015 16:25:21 +0100 Subject: [PATCH 35/54] add zero memory expansion tests --- test/stSystemOperationsTestFiller.json | 117 +++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/test/stSystemOperationsTestFiller.json b/test/stSystemOperationsTestFiller.json index b79066368..47d18b7b1 100644 --- a/test/stSystemOperationsTestFiller.json +++ b/test/stSystemOperationsTestFiller.json @@ -33,6 +33,40 @@ } }, + "createNameRegistratorZeroMemExpansion": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "createNameRegistratorValueTooHigh": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -177,6 +211,48 @@ } }, + "CallToNameRegistratorZeorSizeMemExpansion": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa ) [[ 0 ]] (CALL 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0 0 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x60003554156009570060203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "CallToReturn1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -1132,6 +1208,47 @@ } }, + "callcodeToNameRegistratorZeroMemExpanion": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa ) [[ 0 ]] (CALLCODE 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0 0 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x60003554156009570060203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "1000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "TestNameRegistrator": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From 3cf6da492bbfc5f527895fb9563915dbd9854bdf Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 6 Jan 2015 16:32:01 +0100 Subject: [PATCH 36/54] add zero memory expansion tests for vm --- test/vmEnvironmentalInfoTestFiller.json | 94 ++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/test/vmEnvironmentalInfoTestFiller.json b/test/vmEnvironmentalInfoTestFiller.json index abeed9945..91b09cd33 100644 --- a/test/vmEnvironmentalInfoTestFiller.json +++ b/test/vmEnvironmentalInfoTestFiller.json @@ -478,6 +478,34 @@ } }, + "calldatacopyZeroMemExpansion": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (CALLDATACOPY 0 0 0 ) [[ 0 ]] @0}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + "calldatacopy_DataIndexTooHigh": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -674,7 +702,35 @@ } }, - "codecopy1": { + "codecopy0": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (CODECOPY 0 0 5 ) [[ 0 ]] @0 }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "codecopyZeroMemExpansion": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "currentNumber" : "0", @@ -687,7 +743,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (CODECOPY 0 0 (CODESIZE) ) [[ 0 ]] @0 }", + "code" : "{ (CODECOPY 0 0 0 ) [[ 0 ]] @0 }", "storage": {} } }, @@ -825,6 +881,40 @@ } }, + "extcodecopyZeroMemExpansion": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (EXTCODECOPY (CALLER) 0 0 0 ) ) [[ 0 ]] @0 }", + "storage": {} + }, + "cd1722f3947def4cf144679da39c4c32bdc35681" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] 5 }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "123456789", + "gas" : "100000000000" + } + }, + "extcodecopy0": { "env" : { From 506fe0f6e88f268192403dc39ab8bb72c0367a98 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 6 Jan 2015 16:51:10 +0100 Subject: [PATCH 37/54] define constructors for windows --- libp2p/NodeTable.h | 5 +++-- libp2p/UDP.h | 2 +- test/net.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 8f15cb958..e8385998e 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -46,6 +46,7 @@ namespace p2p * @todo makeRequired: exclude bucket from refresh if we have node as peer. * * [Optimization] + * @todo encapsulate doFindNode into NetworkAlgorithm (task) * @todo Pong to include ip:port where ping was received * @todo expiration and sha3(id) 'to' for messages which are replies (prevents replay) * @todo std::shared_ptr m_cachedPingPacket; @@ -281,7 +282,7 @@ struct Pong: RLPXDatagram */ struct FindNode: RLPXDatagram { - using RLPXDatagram::RLPXDatagram; + FindNode(bi::udp::endpoint _ep): RLPXDatagram(_ep) {} FindNode(bi::udp::endpoint _ep, NodeId _target, std::chrono::seconds _expiration = std::chrono::seconds(30)): RLPXDatagram(_ep), target(_target), expiration(futureFromEpoch(_expiration)) {} h512 target; @@ -312,7 +313,7 @@ struct Neighbours: RLPXDatagram void interpretRLP(RLP const& _r) { ipAddress = _r[0].toString(); port = _r[1].toInt(); node = h512(_r[2].toBytes()); } }; - using RLPXDatagram::RLPXDatagram; + Neighbours(bi::udp::endpoint _ep): RLPXDatagram(_ep) {} Neighbours(bi::udp::endpoint _to, std::vector> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): RLPXDatagram(_to) { auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size(); diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 96e7fd99e..6de783509 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -76,7 +76,7 @@ struct RLPXDatagramFace: public UDPDatagram template struct RLPXDatagram: public RLPXDatagramFace { - using RLPXDatagramFace::RLPXDatagramFace; + RLPXDatagram(bi::udp::endpoint const& _ep): RLPXDatagramFace(_ep) {} static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } }; diff --git a/test/net.cpp b/test/net.cpp index 6952c282a..946995130 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -51,7 +51,7 @@ protected: struct TestNodeTable: public NodeTable { /// Constructor - using NodeTable::NodeTable; + TestNodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _port = 30300): NodeTable(_io, _alias, _port) {} static std::vector> createTestNodes(unsigned _count) { From 2c168ffa142831aa537ad8669495ddfb1acaa562 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 6 Jan 2015 17:01:17 +0100 Subject: [PATCH 38/54] stl sleep_for, for windows --- test/net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/net.cpp b/test/net.cpp index 946995130..67c50dae8 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(test_udp_once) UDPDatagram d(bi::udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 30300), bytes({65,65,65,65})); TestUDPSocket a; a.m_socket->connect(); a.start(); a.m_socket->send(d); - sleep(1); + this_thread::sleep_for(chrono::seconds(1)); BOOST_REQUIRE_EQUAL(true, a.success); } From 3387d82a9c1b438dd298a3048c722d4d1b9fc29b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 6 Jan 2015 20:12:54 +0100 Subject: [PATCH 39/54] More efficient getLastHashes. --- libethereum/State.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index c6a6ce125..e0705859f 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1006,9 +1006,9 @@ LastHashes State::getLastHashes(BlockChain const& _bc) const ret.resize(256); if (c_protocolVersion > 49) { - unsigned n = (unsigned)m_previousBlock.number; - for (unsigned i = 0; i < 256; ++i) - ret[i] = _bc.numberHash(std::max(n, i) - i); + ret[0] = _bc.currentHash(); + for (unsigned i = 1; i < 256; ++i) + ret[i] = ret[i - 1] ? _bc.details(ret[i - 1]).parent : h256(); } return ret; } From 42fbfb818241f4db9193c84c69b1057f81b6300d Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 6 Jan 2015 20:57:33 +0100 Subject: [PATCH 40/54] Blockhash tests --- test/stBlockHashTestFiller.json | 103 ++++++++++++++++++++++++++++++++ test/state.cpp | 15 ++++- 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 test/stBlockHashTestFiller.json diff --git a/test/stBlockHashTestFiller.json b/test/stBlockHashTestFiller.json new file mode 100644 index 000000000..ccbff5d21 --- /dev/null +++ b/test/stBlockHashTestFiller.json @@ -0,0 +1,103 @@ +{ + "blockhash0" : { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "5", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BLOCKHASH 0) [[ 1 ]] (BLOCKHASH 5) [[ 2 ]] (BLOCKHASH 4) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "8500", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "blockhashOutOfRange" : { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "257", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BLOCKHASH 0) [[ 1 ]] (BLOCKHASH 257) [[ 2 ]] (BLOCKHASH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "8500", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "blockhashInRange" : { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "257", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BLOCKHASH 1) [[ 1 ]] (BLOCKHASH 2) [[ 2 ]] (BLOCKHASH 256) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "8500", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + } +} diff --git a/test/state.cpp b/test/state.cpp index 133468226..db8350e42 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -39,6 +39,14 @@ using namespace dev::eth; namespace dev { namespace test { +LastHashes lastHashes(u256 _currentBlockNumber) +{ + LastHashes ret; + for (u256 i = 1; i <= 256 && i <= _currentBlockNumber; ++i) + ret.push_back(sha3(toString(_currentBlockNumber - i))); + return ret; +} + void doStateTests(json_spirit::mValue& v, bool _fillin) @@ -62,7 +70,7 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) try { - theState.execute(LastHashes(), tx, &output); + theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx, &output); } catch (Exception const& _e) { @@ -157,6 +165,11 @@ BOOST_AUTO_TEST_CASE(stRefundTest) dev::test::executeTests("stRefundTest", "/StateTests", dev::test::doStateTests); } +BOOST_AUTO_TEST_CASE(stBlockHashTest) +{ + dev::test::executeTests("stBlockHashTest", "/StateTests", dev::test::doStateTests); +} + BOOST_AUTO_TEST_CASE(stCreateTest) { for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) From 132a3e7dd487bc1419bd12979665d058a5e1e5c7 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Wed, 7 Jan 2015 11:14:45 +0100 Subject: [PATCH 41/54] check address input greater then 2**160 --- test/stSystemOperationsTestFiller.json | 300 ++++++++++++++++++++++-- test/vmEnvironmentalInfoTestFiller.json | 210 +++++++++++++++++ 2 files changed, 494 insertions(+), 16 deletions(-) diff --git a/test/stSystemOperationsTestFiller.json b/test/stSystemOperationsTestFiller.json index 47d18b7b1..70ce448fc 100644 --- a/test/stSystemOperationsTestFiller.json +++ b/test/stSystemOperationsTestFiller.json @@ -12,7 +12,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 28) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 3 29) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -46,7 +46,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0 0) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 0 0) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -80,7 +80,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 1000 4 28) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 1000 3 29) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -114,7 +114,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 28) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 29) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -148,7 +148,41 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 0xfffffffffff) }", + "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 3 0xfffffffffff) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "balanceInputAddressTooBig": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BALANCE 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0baa ) }", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -187,7 +221,91 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "CallToNameRegistratorAddressTooBigLeft": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa ) [[ 0 ]] (CALL 1000 0xaa945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 64 64 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x6000355415600957005b60203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "CallToNameRegistratorAddressTooBigRight": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa ) [[ 0 ]] (CALL 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5aa 23 0 64 64 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -229,7 +347,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -440,7 +558,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -482,7 +600,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -523,7 +641,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -564,7 +682,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -606,7 +724,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -647,7 +765,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -926,6 +1044,74 @@ } }, + "suicideCallerAddresTooBigLeft": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[0]] (CALLER) (SUICIDE 0xaaa94f5374fce5edbc8e2a8697c15331677e6ebf0b)}", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "1000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "suicideCallerAddresTooBigRight": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[0]] (CALLER) (SUICIDE 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0baa)}", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "1000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "suicideOrigin": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -1185,7 +1371,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -1208,6 +1394,88 @@ } }, + "callcodeToNameRegistratorAddresTooBigLeft": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa ) [[ 0 ]] (CALLCODE 1000 0xaa945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 64 64 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x6000355415600957005b60203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "1000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "callcodeToNameRegistratorAddresTooBigRight": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa ) [[ 0 ]] (CALLCODE 1000 0x945304eb96065b2a98b57a48a06ae28d285a71b5aa 23 0 64 64 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x6000355415600957005b60203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "1000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "callcodeToNameRegistratorZeroMemExpanion": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -1226,7 +1494,7 @@ }, "945304eb96065b2a98b57a48a06ae28d285a71b5" : { "balance" : "23", - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "nonce" : "0", "storage" : { } @@ -1262,7 +1530,7 @@ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "1000000000000000000", "nonce" : 0, - "code" : "0x60003554156009570060203560003555", + "code" : "0x6000355415600957005b60203560003555", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { diff --git a/test/vmEnvironmentalInfoTestFiller.json b/test/vmEnvironmentalInfoTestFiller.json index 91b09cd33..43f4706cb 100644 --- a/test/vmEnvironmentalInfoTestFiller.json +++ b/test/vmEnvironmentalInfoTestFiller.json @@ -83,6 +83,91 @@ } }, + "balanceAddressInputTooBig": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BALANCE 0xcd1722f3947def4cf144679da39c4c32bdc35681aa )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "balanceAddressInputTooBigRightMyAddress": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BALANCE 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6aa )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "balanceAddressInputTooBigLeftMyAddress": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (BALANCE 0xaa0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "balance1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -786,6 +871,63 @@ } }, + "ExtCodeSizeAddressInputTooBigRightMyAddress": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXTCODESIZE 0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6aa )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "ExtCodeSizeAddressInputTooBigLeftMyAddress": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (EXTCODESIZE 0xaa0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 )}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "1000000000", + "gas" : "100000000000" + } + }, + + "extcodesize0": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -948,6 +1090,74 @@ "gasPrice" : "123456789", "gas" : "100000000000" } + }, + + "extcodecopy0AddressTooBigLeft": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (EXTCODECOPY 0xaacd1722f3947def4cf144679da39c4c32bdc35681 0 0 (EXTCODESIZE (CALLER) ) ) [[ 0 ]] @0 }", + "storage": {} + }, + "cd1722f3947def4cf144679da39c4c32bdc35681" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] 5 }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "123456789", + "gas" : "100000000000" + } + }, + + "extcodecopy0AddressTooBigRight": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (EXTCODECOPY 0xcd1722f3947def4cf144679da39c4c32bdc35681aa 0 0 (EXTCODESIZE (CALLER) ) ) [[ 0 ]] @0 }", + "storage": {} + }, + "cd1722f3947def4cf144679da39c4c32bdc35681" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] 5 }", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "0x1234567890abcdef01234567890abcdef", + "gasPrice" : "123456789", + "gas" : "100000000000" + } } } From 3871bac1d0ac3b0e7f8490fba646f159fa65d8b5 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 7 Jan 2015 11:26:23 +0100 Subject: [PATCH 42/54] Squashed 'libjsqrc/ethereumjs/' content from commit df4d784 git-subtree-dir: libjsqrc/ethereumjs git-subtree-split: df4d7846e58f98191b9811413a7be02f48a78aa3 --- .bowerrc | 5 + .editorconfig | 12 + .gitignore | 18 ++ .jshintrc | 50 +++++ .npmignore | 9 + .travis.yml | 11 + LICENSE | 14 ++ README.md | 79 +++++++ bower.json | 51 +++++ dist/ethereum.js | 20 ++ dist/ethereum.js.map | 29 +++ dist/ethereum.min.js | 1 + example/balance.html | 41 ++++ example/contract.html | 75 +++++++ example/node-app.js | 16 ++ gulpfile.js | 123 ++++++++++ index.js | 8 + index_qt.js | 5 + lib/abi.js | 265 ++++++++++++++++++++++ lib/autoprovider.js | 102 +++++++++ lib/contract.js | 65 ++++++ lib/httprpc.js | 94 ++++++++ lib/main.js | 508 ++++++++++++++++++++++++++++++++++++++++++ lib/qt.js | 45 ++++ lib/websocket.js | 77 +++++++ package.json | 67 ++++++ 26 files changed, 1790 insertions(+) create mode 100644 .bowerrc create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 .npmignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bower.json create mode 100644 dist/ethereum.js create mode 100644 dist/ethereum.js.map create mode 100644 dist/ethereum.min.js create mode 100644 example/balance.html create mode 100644 example/contract.html create mode 100644 example/node-app.js create mode 100644 gulpfile.js create mode 100644 index.js create mode 100644 index_qt.js create mode 100644 lib/abi.js create mode 100644 lib/autoprovider.js create mode 100644 lib/contract.js create mode 100644 lib/httprpc.js create mode 100644 lib/main.js create mode 100644 lib/qt.js create mode 100644 lib/websocket.js create mode 100644 package.json diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 000000000..c3a8813e8 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,5 @@ +{ + "directory": "example/js/", + "cwd": "./", + "analytics": false +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..60a2751d3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..399b6dc88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +*.swp +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store +ethereum/ethereum +ethereal/ethereal +example/js +node_modules +bower_components +npm-debug.log diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 000000000..c0ec5f89d --- /dev/null +++ b/.jshintrc @@ -0,0 +1,50 @@ +{ + "predef": [ + "console", + "require", + "equal", + "test", + "testBoth", + "testWithDefault", + "raises", + "deepEqual", + "start", + "stop", + "ok", + "strictEqual", + "module", + "expect", + "reject", + "impl" + ], + + "esnext": true, + "proto": true, + "node" : true, + "browser" : true, + "browserify" : true, + + "boss" : true, + "curly": false, + "debug": true, + "devel": true, + "eqeqeq": true, + "evil": true, + "forin": false, + "immed": false, + "laxbreak": false, + "newcap": true, + "noarg": true, + "noempty": false, + "nonew": false, + "nomen": false, + "onevar": false, + "plusplus": false, + "regexp": false, + "undef": true, + "sub": true, + "strict": false, + "white": false, + "shadow": true, + "eqnull": true +} \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..5bbffe4fd --- /dev/null +++ b/.npmignore @@ -0,0 +1,9 @@ +example/js +node_modules +test +.gitignore +.editorconfig +.travis.yml +.npmignore +component.json +testling.html \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..fafacbd5a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: node_js +node_js: + - "0.11" + - "0.10" +before_script: + - npm install + - npm install jshint +script: + - "jshint *.js lib" +after_script: + - npm run-script gulp diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..0f187b873 --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +This file is part of ethereum.js. + +ethereum.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +ethereum.js 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with ethereum.js. If not, see . \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..865b62c6b --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# Ethereum JavaScript API + +This is the Ethereum compatible JavaScript API using `Promise`s +which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js + +[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url] + + + +## Installation + +### Node.js + + npm install ethereum.js + +### For browser +Bower + + bower install ethereum.js + +Component + + component install ethereum/ethereum.js + +* Include `ethereum.min.js` in your html file. +* Include [es6-promise](https://github.com/jakearchibald/es6-promise) or another ES6-Shim if your browser doesn't support ECMAScript 6. + +## Usage +Require the library: + + var web3 = require('web3'); + +Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider) + + var web3.setProvider(new web3.providers.WebSocketProvider('ws://localhost:40404/eth')); + +There you go, now you can use it: + +``` +web3.eth.coinbase.then(function(result){ + console.log(result); + return web3.eth.balanceAt(result); +}).then(function(balance){ + console.log(web3.toDecimal(balance)); +}).catch(function(err){ + console.log(err); +}); +``` + + +For another example see `example/index.html`. + +## Building + +* `gulp build` + + +### Testing + +**Please note this repo is in it's early stage.** + +If you'd like to run a WebSocket ethereum node check out +[go-ethereum](https://github.com/ethereum/go-ethereum). + +To install ethereum and spawn a node: + +``` +go get github.com/ethereum/go-ethereum/ethereum +ethereum -ws -loglevel=4 +``` + +[npm-image]: https://badge.fury.io/js/ethereum.js.png +[npm-url]: https://npmjs.org/package/ethereum.js +[travis-image]: https://travis-ci.org/ethereum/ethereum.js.svg +[travis-url]: https://travis-ci.org/ethereum/ethereum.js +[dep-image]: https://david-dm.org/ethereum/ethereum.js.svg +[dep-url]: https://david-dm.org/ethereum/ethereum.js +[dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg +[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies \ No newline at end of file diff --git a/bower.json b/bower.json new file mode 100644 index 000000000..cedae9023 --- /dev/null +++ b/bower.json @@ -0,0 +1,51 @@ +{ + "name": "ethereum.js", + "namespace": "ethereum", + "version": "0.0.3", + "description": "Ethereum Compatible JavaScript API", + "main": ["./dist/ethereum.js", "./dist/ethereum.min.js"], + "dependencies": { + "es6-promise": "#master" + }, + "repository": { + "type": "git", + "url": "https://github.com/ethereum/ethereum.js.git" + }, + "homepage": "https://github.com/ethereum/ethereum.js", + "bugs": { + "url": "https://github.com/ethereum/ethereum.js/issues" + }, + "keywords": [ + "ethereum", + "javascript", + "API" + ], + "authors": [ + { + "name": "Marek Kotewicz", + "email": "marek@ethdev.com", + "homepage": "https://github.com/debris" + }, + { + "name": "Marian Oancea", + "email": "marian@ethdev.com", + "homepage": "https://github.com/cubedro" + } + ], + "license": "LGPL-3.0", + "ignore": [ + "example", + "lib", + "node_modules", + "package.json", + ".bowerrc", + ".editorconfig", + ".gitignore", + ".jshintrc", + ".npmignore", + ".travis.yml", + "gulpfile.js", + "index.js", + "**/*.txt" + ] +} \ No newline at end of file diff --git a/dist/ethereum.js b/dist/ethereum.js new file mode 100644 index 000000000..8abe6ad53 --- /dev/null +++ b/dist/ethereum.js @@ -0,0 +1,20 @@ +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;oi;i++)padding+=sizes[i]/8;return padding},setupInputTypes=function(){var prefixedType=function(prefix,calcPadding){return function(type,value){var padding,expected=prefix;return 0!==type.indexOf(expected)?!1:(padding=calcPadding(type,expected),value="number"==typeof value?value.toString(16):"string"==typeof value?web3.toHex(value):0===value.indexOf("0x")?value.substr(2):(+value).toString(16),padLeft(value,2*padding))}},namedType=function(name,padding,formatter){return function(type,value){return type!==name?!1:padLeft(formatter?formatter(value):value,2*padding)}},formatBool=function(value){return value?"0x1":"0x0"};return[prefixedType("uint",calcBitPadding),prefixedType("int",calcBitPadding),prefixedType("hash",calcBitPadding),prefixedType("string",calcBytePadding),prefixedType("real",calcRealPadding),prefixedType("ureal",calcRealPadding),namedType("address",20),namedType("bool",1,formatBool)]},inputTypes=setupInputTypes(),toAbiInput=function(json,methodName,params){var method,i,found,j,bytes="",index=findMethodIndex(json,methodName);if(-1!==index){for(bytes="0x"+padLeft(index.toString(16),2),method=json[index],i=0;ii&&(code=hex.charCodeAt(i),0!==code);i+=2)str+=String.fromCharCode(parseInt(hex.substr(i,2),16));return str},fromAscii:function(str,pad){pad=void 0===pad?32:pad;for(var hex=this.toHex(str);hex.length<2*pad;)hex+="00";return"0x"+hex},toDecimal:function(val){return hexToDec(val.substring(2))},fromDecimal:function(val){return"0x"+decToHex(val)},toEth:function(str){for(var s,replaceFunction,o,val="string"==typeof str?0===str.indexOf("0x")?parseInt(str.substr(2),16):parseInt(str):str,unit=0,units=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];val>3e3&&uniti;i++)padding+=sizes[i]/8;return padding},setupInputTypes=function(){var prefixedType=function(prefix,calcPadding){return function(type,value){var padding,expected=prefix;return 0!==type.indexOf(expected)?!1:(padding=calcPadding(type,expected),value=\"number\"==typeof value?value.toString(16):\"string\"==typeof value?web3.toHex(value):0===value.indexOf(\"0x\")?value.substr(2):(+value).toString(16),padLeft(value,2*padding))}},namedType=function(name,padding,formatter){return function(type,value){return type!==name?!1:padLeft(formatter?formatter(value):value,2*padding)}},formatBool=function(value){return value?\"0x1\":\"0x0\"};return[prefixedType(\"uint\",calcBitPadding),prefixedType(\"int\",calcBitPadding),prefixedType(\"hash\",calcBitPadding),prefixedType(\"string\",calcBytePadding),prefixedType(\"real\",calcRealPadding),prefixedType(\"ureal\",calcRealPadding),namedType(\"address\",20),namedType(\"bool\",1,formatBool)]},inputTypes=setupInputTypes(),toAbiInput=function(json,methodName,params){var method,i,found,j,bytes=\"\",index=findMethodIndex(json,methodName);if(-1!==index){for(bytes=\"0x\"+padLeft(index.toString(16),2),method=json[index],i=0;ii&&(code=hex.charCodeAt(i),0!==code);i+=2)str+=String.fromCharCode(parseInt(hex.substr(i,2),16));return str},fromAscii:function(str,pad){pad=void 0===pad?32:pad;for(var hex=this.toHex(str);hex.length<2*pad;)hex+=\"00\";return\"0x\"+hex},toDecimal:function(val){return hexToDec(val.substring(2))},fromDecimal:function(val){return\"0x\"+decToHex(val)},toEth:function(str){for(var s,replaceFunction,o,val=\"string\"==typeof str?0===str.indexOf(\"0x\")?parseInt(str.substr(2),16):parseInt(str):str,unit=0,units=[\"wei\",\"Kwei\",\"Mwei\",\"Gwei\",\"szabo\",\"finney\",\"ether\",\"grand\",\"Mether\",\"Gether\",\"Tether\",\"Pether\",\"Eether\",\"Zether\",\"Yether\",\"Nether\",\"Dether\",\"Vether\",\"Uether\"];val>3e3&&unito;o++)r+=n[o]/8;return r},l=function(){var t=function(t,e){return function(n,r){var o,i=t;return 0!==n.indexOf(i)?!1:(o=e(n,i),r="number"==typeof r?r.toString(16):"string"==typeof r?web3.toHex(r):0===r.indexOf("0x")?r.substr(2):(+r).toString(16),s(r,2*o))}},e=function(t,e,n){return function(r,o){return r!==t?!1:s(n?n(o):o,2*e)}},n=function(t){return t?"0x1":"0x0"};return[t("uint",a),t("int",a),t("hash",a),t("string",u),t("real",c),t("ureal",c),e("address",20),e("bool",1,n)]},h=l(),p=function(t,e,n){var r,o,a,u,c="",l=i(t,e);if(-1!==l){for(c="0x"+s(l.toString(16),2),r=t[l],o=0;or&&(e=t.charCodeAt(r),0!==e);r+=2)n+=String.fromCharCode(parseInt(t.substr(r,2),16));return n},fromAscii:function(t,e){e=void 0===e?32:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return m(t.substring(2))},fromDecimal:function(t){return"0x"+b(t)},toEth:function(t){for(var e,n,r,o="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,i=0,s=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];o>3e3&&i + + + + + + + + +

coinbase balance

+ +
+
+
+
+ + + diff --git a/example/contract.html b/example/contract.html new file mode 100644 index 000000000..44f0b03a1 --- /dev/null +++ b/example/contract.html @@ -0,0 +1,75 @@ + + + + + + + + + +

contract

+
+
+ +
+ +
+ + + diff --git a/example/node-app.js b/example/node-app.js new file mode 100644 index 000000000..f63fa9115 --- /dev/null +++ b/example/node-app.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +require('es6-promise').polyfill(); + +var web3 = require("../index.js"); + +web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080')); + +web3.eth.coinbase.then(function(result){ + console.log(result); + return web3.eth.balanceAt(result); +}).then(function(balance){ + console.log(web3.toDecimal(balance)); +}).catch(function(err){ + console.log(err); +}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 000000000..9e0717d8b --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,123 @@ +#!/usr/bin/env node + +'use strict'; + +var path = require('path'); + +var del = require('del'); +var gulp = require('gulp'); +var browserify = require('browserify'); +var jshint = require('gulp-jshint'); +var uglify = require('gulp-uglify'); +var rename = require('gulp-rename'); +var envify = require('envify/custom'); +var unreach = require('unreachable-branch-transform'); +var source = require('vinyl-source-stream'); +var exorcist = require('exorcist'); +var bower = require('bower'); + +var DEST = './dist/'; + +var build = function(src, dst) { + return browserify({ + debug: true, + insert_global_vars: false, + detectGlobals: false, + bundleExternal: false + }) + .require('./' + src + '.js', {expose: 'web3'}) + .add('./' + src + '.js') + .transform('envify', { + NODE_ENV: 'build' + }) + .transform('unreachable-branch-transform') + .transform('uglifyify', { + mangle: false, + compress: { + dead_code: false, + conditionals: true, + unused: false, + hoist_funs: true, + hoist_vars: true, + negate_iife: false + }, + beautify: true, + warnings: true + }) + .bundle() + .pipe(exorcist(path.join( DEST, dst + '.js.map'))) + .pipe(source(dst + '.js')) + .pipe(gulp.dest( DEST )); +}; + +var buildDev = function(src, dst) { + return browserify({ + debug: true, + insert_global_vars: false, + detectGlobals: false, + bundleExternal: false + }) + .require('./' + src + '.js', {expose: 'web3'}) + .add('./' + src + '.js') + .transform('envify', { + NODE_ENV: 'build' + }) + .transform('unreachable-branch-transform') + .bundle() + .pipe(exorcist(path.join( DEST, dst + '.js.map'))) + .pipe(source(dst + '.js')) + .pipe(gulp.dest( DEST )); +}; + +var uglifyFile = function(file) { + return gulp.src( DEST + file + '.js') + .pipe(uglify()) + .pipe(rename(file + '.min.js')) + .pipe(gulp.dest( DEST )); +}; + +gulp.task('bower', function(cb){ + bower.commands.install().on('end', function (installed){ + console.log(installed); + cb(); + }); +}); + +gulp.task('lint', function(){ + return gulp.src(['./*.js', './lib/*.js']) + .pipe(jshint()) + .pipe(jshint.reporter('default')); +}); + +gulp.task('clean', ['lint'], function(cb) { + del([ DEST ], cb); +}); + +gulp.task('build', ['clean'], function () { + return build('index', 'ethereum'); +}); + +gulp.task('buildQt', ['clean'], function () { + return build('index_qt', 'ethereum'); +}); + +gulp.task('buildDev', ['clean'], function () { + return buildDev('index', 'ethereum'); +}); + +gulp.task('uglify', ['build'], function(){ + return uglifyFile('ethereum'); +}); + +gulp.task('uglifyQt', ['buildQt'], function () { + return uglifyFile('ethereum'); +}); + +gulp.task('watch', function() { + gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']); +}); + +gulp.task('default', ['bower', 'lint', 'build', 'uglify']); +gulp.task('qt', ['bower', 'lint', 'buildQt', 'uglifyQt']); +gulp.task('dev', ['bower', 'lint', 'buildDev']); + diff --git a/index.js b/index.js new file mode 100644 index 000000000..c2de7e735 --- /dev/null +++ b/index.js @@ -0,0 +1,8 @@ +var web3 = require('./lib/main'); +web3.providers.WebSocketProvider = require('./lib/websocket'); +web3.providers.HttpRpcProvider = require('./lib/httprpc'); +web3.providers.QtProvider = require('./lib/qt'); +web3.providers.AutoProvider = require('./lib/autoprovider'); +web3.contract = require('./lib/contract'); + +module.exports = web3; diff --git a/index_qt.js b/index_qt.js new file mode 100644 index 000000000..d5e47597e --- /dev/null +++ b/index_qt.js @@ -0,0 +1,5 @@ +var web3 = require('./lib/main'); +web3.providers.QtProvider = require('./lib/qt'); +web3.contract = require('./lib/contract'); + +module.exports = web3; diff --git a/lib/abi.js b/lib/abi.js new file mode 100644 index 000000000..5a4d64515 --- /dev/null +++ b/lib/abi.js @@ -0,0 +1,265 @@ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file abi.js + * @authors: + * Marek Kotewicz + * Gav Wood + * @date 2014 + */ + +// TODO: is these line is supposed to be here? +if (process.env.NODE_ENV !== 'build') { + var web3 = require('./web3'); // jshint ignore:line +} + +// TODO: make these be actually accurate instead of falling back onto JS's doubles. +var hexToDec = function (hex) { + return parseInt(hex, 16).toString(); +}; + +var decToHex = function (dec) { + return parseInt(dec).toString(16); +}; + +var findIndex = function (array, callback) { + var end = false; + var i = 0; + for (; i < array.length && !end; i++) { + end = callback(array[i]); + } + return end ? i - 1 : -1; +}; + +var findMethodIndex = function (json, methodName) { + return findIndex(json, function (method) { + return method.name === methodName; + }); +}; + +var padLeft = function (string, chars) { + return new Array(chars - string.length + 1).join("0") + string; +}; + +var calcBitPadding = function (type, expected) { + var value = type.slice(expected.length); + if (value === "") { + return 32; + } + return parseInt(value) / 8; +}; + +var calcBytePadding = function (type, expected) { + var value = type.slice(expected.length); + if (value === "") { + return 32; + } + return parseInt(value); +}; + +var calcRealPadding = function (type, expected) { + var value = type.slice(expected.length); + if (value === "") { + return 32; + } + var sizes = value.split('x'); + for (var padding = 0, i = 0; i < sizes; i++) { + padding += (sizes[i] / 8); + } + return padding; +}; + +var setupInputTypes = function () { + + var prefixedType = function (prefix, calcPadding) { + return function (type, value) { + var expected = prefix; + if (type.indexOf(expected) !== 0) { + return false; + } + + var padding = calcPadding(type, expected); + if (typeof value === "number") + value = value.toString(16); + else if (typeof value === "string") + value = web3.toHex(value); + else if (value.indexOf('0x') === 0) + value = value.substr(2); + else + value = (+value).toString(16); + return padLeft(value, padding * 2); + }; + }; + + var namedType = function (name, padding, formatter) { + return function (type, value) { + if (type !== name) { + return false; + } + + return padLeft(formatter ? formatter(value) : value, padding * 2); + }; + }; + + var formatBool = function (value) { + return value ? '0x1' : '0x0'; + }; + + return [ + prefixedType('uint', calcBitPadding), + prefixedType('int', calcBitPadding), + prefixedType('hash', calcBitPadding), + prefixedType('string', calcBytePadding), + prefixedType('real', calcRealPadding), + prefixedType('ureal', calcRealPadding), + namedType('address', 20), + namedType('bool', 1, formatBool), + ]; +}; + +var inputTypes = setupInputTypes(); + +var toAbiInput = function (json, methodName, params) { + var bytes = ""; + var index = findMethodIndex(json, methodName); + + if (index === -1) { + return; + } + + bytes = "0x" + padLeft(index.toString(16), 2); + var method = json[index]; + + for (var i = 0; i < method.inputs.length; i++) { + var found = false; + for (var j = 0; j < inputTypes.length && !found; j++) { + found = inputTypes[j](method.inputs[i].type, params[i]); + } + if (!found) { + console.error('unsupported json type: ' + method.inputs[i].type); + } + bytes += found; + } + return bytes; +}; + +var setupOutputTypes = function () { + + var prefixedType = function (prefix, calcPadding) { + return function (type) { + var expected = prefix; + if (type.indexOf(expected) !== 0) { + return -1; + } + + var padding = calcPadding(type, expected); + return padding * 2; + }; + }; + + var namedType = function (name, padding) { + return function (type) { + return name === type ? padding * 2 : -1; + }; + }; + + var formatInt = function (value) { + return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value); + }; + + var formatHash = function (value) { + return "0x" + value; + }; + + var formatBool = function (value) { + return value === '1' ? true : false; + }; + + var formatString = function (value) { + return web3.toAscii(value); + }; + + return [ + { padding: prefixedType('uint', calcBitPadding), format: formatInt }, + { padding: prefixedType('int', calcBitPadding), format: formatInt }, + { padding: prefixedType('hash', calcBitPadding), format: formatHash }, + { padding: prefixedType('string', calcBytePadding), format: formatString }, + { padding: prefixedType('real', calcRealPadding), format: formatInt }, + { padding: prefixedType('ureal', calcRealPadding), format: formatInt }, + { padding: namedType('address', 20) }, + { padding: namedType('bool', 1), format: formatBool } + ]; +}; + +var outputTypes = setupOutputTypes(); + +var fromAbiOutput = function (json, methodName, output) { + var index = findMethodIndex(json, methodName); + + if (index === -1) { + return; + } + + output = output.slice(2); + + var result = []; + var method = json[index]; + for (var i = 0; i < method.outputs.length; i++) { + var padding = -1; + for (var j = 0; j < outputTypes.length && padding === -1; j++) { + padding = outputTypes[j].padding(method.outputs[i].type); + } + + if (padding === -1) { + // not found output parsing + continue; + } + var res = output.slice(0, padding); + var formatter = outputTypes[j - 1].format; + result.push(formatter ? formatter(res) : ("0x" + res)); + output = output.slice(padding); + } + + return result; +}; + +var inputParser = function (json) { + var parser = {}; + json.forEach(function (method) { + parser[method.name] = function () { + var params = Array.prototype.slice.call(arguments); + return toAbiInput(json, method.name, params); + }; + }); + + return parser; +}; + +var outputParser = function (json) { + var parser = {}; + json.forEach(function (method) { + parser[method.name] = function (output) { + return fromAbiOutput(json, method.name, output); + }; + }); + + return parser; +}; + +module.exports = { + inputParser: inputParser, + outputParser: outputParser +}; diff --git a/lib/autoprovider.js b/lib/autoprovider.js new file mode 100644 index 000000000..2f0a3e627 --- /dev/null +++ b/lib/autoprovider.js @@ -0,0 +1,102 @@ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file autoprovider.js + * @authors: + * Marek Kotewicz + * Marian Oancea + * @date 2014 + */ + +/* + * @brief if qt object is available, uses QtProvider, + * if not tries to connect over websockets + * if it fails, it uses HttpRpcProvider + */ + +// TODO: is these line is supposed to be here? +if (process.env.NODE_ENV !== 'build') { + var WebSocket = require('ws'); // jshint ignore:line + var web3 = require('./main.js'); // jshint ignore:line +} + +var AutoProvider = function (userOptions) { + if (web3.haveProvider()) { + return; + } + + // before we determine what provider we are, we have to cache request + this.sendQueue = []; + this.onmessageQueue = []; + + if (navigator.qt) { + this.provider = new web3.providers.QtProvider(); + return; + } + + userOptions = userOptions || {}; + var options = { + httprpc: userOptions.httprpc || 'http://localhost:8080', + websockets: userOptions.websockets || 'ws://localhost:40404/eth' + }; + + var self = this; + var closeWithSuccess = function (success) { + ws.close(); + if (success) { + self.provider = new web3.providers.WebSocketProvider(options.websockets); + } else { + self.provider = new web3.providers.HttpRpcProvider(options.httprpc); + self.poll = self.provider.poll.bind(self.provider); + } + self.sendQueue.forEach(function (payload) { + self.provider(payload); + }); + self.onmessageQueue.forEach(function (handler) { + self.provider.onmessage = handler; + }); + }; + + var ws = new WebSocket(options.websockets); + + ws.onopen = function() { + closeWithSuccess(true); + }; + + ws.onerror = function() { + closeWithSuccess(false); + }; +}; + +AutoProvider.prototype.send = function (payload) { + if (this.provider) { + this.provider.send(payload); + return; + } + this.sendQueue.push(payload); +}; + +Object.defineProperty(AutoProvider.prototype, 'onmessage', { + set: function (handler) { + if (this.provider) { + this.provider.onmessage = handler; + return; + } + this.onmessageQueue.push(handler); + } +}); + +module.exports = AutoProvider; diff --git a/lib/contract.js b/lib/contract.js new file mode 100644 index 000000000..b10339003 --- /dev/null +++ b/lib/contract.js @@ -0,0 +1,65 @@ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file contract.js + * @authors: + * Marek Kotewicz + * @date 2014 + */ + +// TODO: is these line is supposed to be here? +if (process.env.NODE_ENV !== 'build') { + var web3 = require('./web3'); // jshint ignore:line +} + +var abi = require('./abi'); + +var contract = function (address, desc) { + var inputParser = abi.inputParser(desc); + var outputParser = abi.outputParser(desc); + + var contract = {}; + + desc.forEach(function (method) { + contract[method.name] = function () { + var params = Array.prototype.slice.call(arguments); + var parsed = inputParser[method.name].apply(null, params); + + var onSuccess = function (result) { + return outputParser[method.name](result); + }; + + return { + call: function (extra) { + extra = extra || {}; + extra.to = address; + extra.data = parsed; + return web3.eth.call(extra).then(onSuccess); + }, + transact: function (extra) { + extra = extra || {}; + extra.to = address; + extra.data = parsed; + return web3.eth.transact(extra).then(onSuccess); + } + }; + }; + }); + + return contract; +}; + +module.exports = contract; diff --git a/lib/httprpc.js b/lib/httprpc.js new file mode 100644 index 000000000..d315201f1 --- /dev/null +++ b/lib/httprpc.js @@ -0,0 +1,94 @@ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file httprpc.js + * @authors: + * Marek Kotewicz + * Marian Oancea + * @date 2014 + */ + +// TODO: is these line is supposed to be here? +if (process.env.NODE_ENV !== 'build') { + var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line +} + +var HttpRpcProvider = function (host) { + this.handlers = []; + this.host = host; +}; + +function formatJsonRpcObject(object) { + return { + jsonrpc: '2.0', + method: object.call, + params: object.args, + id: object._id + }; +} + +function formatJsonRpcMessage(message) { + var object = JSON.parse(message); + + return { + _id: object.id, + data: object.result, + error: object.error + }; +} + +HttpRpcProvider.prototype.sendRequest = function (payload, cb) { + var data = formatJsonRpcObject(payload); + + var request = new XMLHttpRequest(); + request.open("POST", this.host, true); + request.send(JSON.stringify(data)); + request.onreadystatechange = function () { + if (request.readyState === 4 && cb) { + cb(request); + } + }; +}; + +HttpRpcProvider.prototype.send = function (payload) { + var self = this; + this.sendRequest(payload, function (request) { + self.handlers.forEach(function (handler) { + handler.call(self, formatJsonRpcMessage(request.responseText)); + }); + }); +}; + +HttpRpcProvider.prototype.poll = function (payload, id) { + var self = this; + this.sendRequest(payload, function (request) { + var parsed = JSON.parse(request.responseText); + if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) { + return; + } + self.handlers.forEach(function (handler) { + handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); + }); + }); +}; + +Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { + set: function (handler) { + this.handlers.push(handler); + } +}); + +module.exports = HttpRpcProvider; diff --git a/lib/main.js b/lib/main.js new file mode 100644 index 000000000..b240bdae2 --- /dev/null +++ b/lib/main.js @@ -0,0 +1,508 @@ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file main.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Gav Wood + * @date 2014 + */ + +function flattenPromise (obj) { + if (obj instanceof Promise) { + return Promise.resolve(obj); + } + + if (obj instanceof Array) { + return new Promise(function (resolve) { + var promises = obj.map(function (o) { + return flattenPromise(o); + }); + + return Promise.all(promises).then(function (res) { + for (var i = 0; i < obj.length; i++) { + obj[i] = res[i]; + } + resolve(obj); + }); + }); + } + + if (obj instanceof Object) { + return new Promise(function (resolve) { + var keys = Object.keys(obj); + var promises = keys.map(function (key) { + return flattenPromise(obj[key]); + }); + + return Promise.all(promises).then(function (res) { + for (var i = 0; i < keys.length; i++) { + obj[keys[i]] = res[i]; + } + resolve(obj); + }); + }); + } + + return Promise.resolve(obj); +} + +var web3Methods = function () { + return [ + { name: 'sha3', call: 'web3_sha3' } + ]; +}; + +var ethMethods = function () { + var blockCall = function (args) { + return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; + }; + + var transactionCall = function (args) { + return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; + }; + + var uncleCall = function (args) { + return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; + }; + + var methods = [ + { name: 'balanceAt', call: 'eth_balanceAt' }, + { name: 'stateAt', call: 'eth_stateAt' }, + { name: 'storageAt', call: 'eth_storageAt' }, + { name: 'countAt', call: 'eth_countAt'}, + { name: 'codeAt', call: 'eth_codeAt' }, + { name: 'transact', call: 'eth_transact' }, + { name: 'call', call: 'eth_call' }, + { name: 'block', call: blockCall }, + { name: 'transaction', call: transactionCall }, + { name: 'uncle', call: uncleCall }, + { name: 'compilers', call: 'eth_compilers' }, + { name: 'lll', call: 'eth_lll' }, + { name: 'solidity', call: 'eth_solidity' }, + { name: 'serpent', call: 'eth_serpent' }, + { name: 'logs', call: 'eth_logs' } + ]; + return methods; +}; + +var ethProperties = function () { + return [ + { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, + { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, + { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, + { name: 'gasPrice', getter: 'eth_gasPrice' }, + { name: 'account', getter: 'eth_account' }, + { name: 'accounts', getter: 'eth_accounts' }, + { name: 'peerCount', getter: 'eth_peerCount' }, + { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, + { name: 'number', getter: 'eth_number'} + ]; +}; + +var dbMethods = function () { + return [ + { name: 'put', call: 'db_put' }, + { name: 'get', call: 'db_get' }, + { name: 'putString', call: 'db_putString' }, + { name: 'getString', call: 'db_getString' } + ]; +}; + +var shhMethods = function () { + return [ + { name: 'post', call: 'shh_post' }, + { name: 'newIdentity', call: 'shh_newIdentity' }, + { name: 'haveIdentity', call: 'shh_haveIdentity' }, + { name: 'newGroup', call: 'shh_newGroup' }, + { name: 'addToGroup', call: 'shh_addToGroup' } + ]; +}; + +var ethWatchMethods = function () { + var newFilter = function (args) { + return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; + }; + + return [ + { name: 'newFilter', call: newFilter }, + { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, + { name: 'getMessages', call: 'eth_filterLogs' } + ]; +}; + +var shhWatchMethods = function () { + return [ + { name: 'newFilter', call: 'shh_newFilter' }, + { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, + { name: 'getMessage', call: 'shh_getMessages' } + ]; +}; + +var setupMethods = function (obj, methods) { + methods.forEach(function (method) { + obj[method.name] = function () { + return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { + var call = typeof method.call === "function" ? method.call(args) : method.call; + return {call: call, args: args}; + }).then(function (request) { + return new Promise(function (resolve, reject) { + web3.provider.send(request, function (err, result) { + if (!err) { + resolve(result); + return; + } + reject(err); + }); + }); + }).catch(function(err) { + console.error(err); + }); + }; + }); +}; + +var setupProperties = function (obj, properties) { + properties.forEach(function (property) { + var proto = {}; + proto.get = function () { + return new Promise(function(resolve, reject) { + web3.provider.send({call: property.getter}, function(err, result) { + if (!err) { + resolve(result); + return; + } + reject(err); + }); + }); + }; + if (property.setter) { + proto.set = function (val) { + return flattenPromise([val]).then(function (args) { + return new Promise(function (resolve) { + web3.provider.send({call: property.setter, args: args}, function (err, result) { + if (!err) { + resolve(result); + return; + } + reject(err); + }); + }); + }).catch(function (err) { + console.error(err); + }); + }; + } + Object.defineProperty(obj, property.name, proto); + }); +}; + +// TODO: import from a dependency, don't duplicate. +var hexToDec = function (hex) { + return parseInt(hex, 16).toString(); +}; + +var decToHex = function (dec) { + return parseInt(dec).toString(16); +}; + + +var web3 = { + _callbacks: {}, + _events: {}, + providers: {}, + + toHex: function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return hex; + }, + + toAscii: function(hex) { + // Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') + i = 2; + for(; i < l; i+=2) { + var code = hex.charCodeAt(i); + if(code === 0) { + break; + } + + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + + return str; + }, + + fromAscii: function(str, pad) { + pad = pad === undefined ? 32 : pad; + var hex = this.toHex(str); + while(hex.length < pad*2) + hex += "00"; + return "0x" + hex; + }, + + toDecimal: function (val) { + return hexToDec(val.substring(2)); + }, + + fromDecimal: function (val) { + return "0x" + decToHex(val); + }, + + toEth: function(str) { + var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; + var unit = 0; + var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ]; + while (val > 3000 && unit < units.length - 1) + { + val /= 1000; + unit++; + } + var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); + var replaceFunction = function($0, $1, $2) { + return $1 + ',' + $2; + }; + + while (true) { + var o = s; + s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); + if (o === s) + break; + } + return s + ' ' + units[unit]; + }, + + eth: { + prototype: Object(), // jshint ignore:line + watch: function (params) { + return new Filter(params, ethWatch); + } + }, + + db: { + prototype: Object() // jshint ignore:line + }, + + shh: { + prototype: Object(), // jshint ignore:line + watch: function (params) { + return new Filter(params, shhWatch); + } + }, + + on: function(event, id, cb) { + if(web3._events[event] === undefined) { + web3._events[event] = {}; + } + + web3._events[event][id] = cb; + return this; + }, + + off: function(event, id) { + if(web3._events[event] !== undefined) { + delete web3._events[event][id]; + } + + return this; + }, + + trigger: function(event, id, data) { + var callbacks = web3._events[event]; + if (!callbacks || !callbacks[id]) { + return; + } + var cb = callbacks[id]; + cb(data); + } +}; + +setupMethods(web3, web3Methods()); +setupMethods(web3.eth, ethMethods()); +setupProperties(web3.eth, ethProperties()); +setupMethods(web3.db, dbMethods()); +setupMethods(web3.shh, shhMethods()); + +var ethWatch = { + changed: 'eth_changed' +}; +setupMethods(ethWatch, ethWatchMethods()); +var shhWatch = { + changed: 'shh_changed' +}; +setupMethods(shhWatch, shhWatchMethods()); + +var ProviderManager = function() { + this.queued = []; + this.polls = []; + this.ready = false; + this.provider = undefined; + this.id = 1; + + var self = this; + var poll = function () { + if (self.provider && self.provider.poll) { + self.polls.forEach(function (data) { + data.data._id = self.id; + self.id++; + self.provider.poll(data.data, data.id); + }); + } + setTimeout(poll, 12000); + }; + poll(); +}; + +ProviderManager.prototype.send = function(data, cb) { + data._id = this.id; + if (cb) { + web3._callbacks[data._id] = cb; + } + + data.args = data.args || []; + this.id++; + + if(this.provider !== undefined) { + this.provider.send(data); + } else { + console.warn("provider is not set"); + this.queued.push(data); + } +}; + +ProviderManager.prototype.set = function(provider) { + if(this.provider !== undefined && this.provider.unload !== undefined) { + this.provider.unload(); + } + + this.provider = provider; + this.ready = true; +}; + +ProviderManager.prototype.sendQueued = function() { + for(var i = 0; this.queued.length; i++) { + // Resend + this.send(this.queued[i]); + } +}; + +ProviderManager.prototype.installed = function() { + return this.provider !== undefined; +}; + +ProviderManager.prototype.startPolling = function (data, pollId) { + if (!this.provider || !this.provider.poll) { + return; + } + this.polls.push({data: data, id: pollId}); +}; + +ProviderManager.prototype.stopPolling = function (pollId) { + for (var i = this.polls.length; i--;) { + var poll = this.polls[i]; + if (poll.id === pollId) { + this.polls.splice(i, 1); + } + } +}; + +web3.provider = new ProviderManager(); + +web3.setProvider = function(provider) { + provider.onmessage = messageHandler; + web3.provider.set(provider); + web3.provider.sendQueued(); +}; + +web3.haveProvider = function() { + return !!web3.provider.provider; +}; + +var Filter = function(options, impl) { + this.impl = impl; + this.callbacks = []; + + var self = this; + this.promise = impl.newFilter(options); + this.promise.then(function (id) { + self.id = id; + web3.on(impl.changed, id, self.trigger.bind(self)); + web3.provider.startPolling({call: impl.changed, args: [id]}, id); + }); +}; + +Filter.prototype.arrived = function(callback) { + this.changed(callback); +}; + +Filter.prototype.changed = function(callback) { + var self = this; + this.promise.then(function(id) { + self.callbacks.push(callback); + }); +}; + +Filter.prototype.trigger = function(messages) { + for(var i = 0; i < this.callbacks.length; i++) { + this.callbacks[i].call(this, messages); + } +}; + +Filter.prototype.uninstall = function() { + var self = this; + this.promise.then(function (id) { + self.impl.uninstallFilter(id); + web3.provider.stopPolling(id); + web3.off(impl.changed, id); + }); +}; + +Filter.prototype.messages = function() { + var self = this; + return this.promise.then(function (id) { + return self.impl.getMessages(id); + }); +}; + +Filter.prototype.logs = function () { + return this.messages(); +}; + +function messageHandler(data) { + if(data._event !== undefined) { + web3.trigger(data._event, data._id, data.data); + return; + } + + if(data._id) { + var cb = web3._callbacks[data._id]; + if (cb) { + cb.call(this, data.error, data.data); + delete web3._callbacks[data._id]; + } + } +} + +module.exports = web3; diff --git a/lib/qt.js b/lib/qt.js new file mode 100644 index 000000000..f02239547 --- /dev/null +++ b/lib/qt.js @@ -0,0 +1,45 @@ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file qt.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * @date 2014 + */ + +var QtProvider = function() { + this.handlers = []; + + var self = this; + navigator.qt.onmessage = function (message) { + self.handlers.forEach(function (handler) { + handler.call(self, JSON.parse(message.data)); + }); + }; +}; + +QtProvider.prototype.send = function(payload) { + navigator.qt.postMessage(JSON.stringify(payload)); +}; + +Object.defineProperty(QtProvider.prototype, "onmessage", { + set: function(handler) { + this.handlers.push(handler); + } +}); + +module.exports = QtProvider; diff --git a/lib/websocket.js b/lib/websocket.js new file mode 100644 index 000000000..ddb44aed5 --- /dev/null +++ b/lib/websocket.js @@ -0,0 +1,77 @@ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file websocket.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * @date 2014 + */ + +// TODO: is these line is supposed to be here? +if (process.env.NODE_ENV !== 'build') { + var WebSocket = require('ws'); // jshint ignore:line +} + +var WebSocketProvider = function(host) { + // onmessage handlers + this.handlers = []; + // queue will be filled with messages if send is invoked before the ws is ready + this.queued = []; + this.ready = false; + + this.ws = new WebSocket(host); + + var self = this; + this.ws.onmessage = function(event) { + for(var i = 0; i < self.handlers.length; i++) { + self.handlers[i].call(self, JSON.parse(event.data), event); + } + }; + + this.ws.onopen = function() { + self.ready = true; + + for(var i = 0; i < self.queued.length; i++) { + // Resend + self.send(self.queued[i]); + } + }; +}; + +WebSocketProvider.prototype.send = function(payload) { + if(this.ready) { + var data = JSON.stringify(payload); + + this.ws.send(data); + } else { + this.queued.push(payload); + } +}; + +WebSocketProvider.prototype.onMessage = function(handler) { + this.handlers.push(handler); +}; + +WebSocketProvider.prototype.unload = function() { + this.ws.close(); +}; +Object.defineProperty(WebSocketProvider.prototype, "onmessage", { + set: function(provider) { this.onMessage(provider); } +}); + +module.exports = WebSocketProvider; diff --git a/package.json b/package.json new file mode 100644 index 000000000..24141ea2e --- /dev/null +++ b/package.json @@ -0,0 +1,67 @@ +{ + "name": "ethereum.js", + "namespace": "ethereum", + "version": "0.0.5", + "description": "Ethereum Compatible JavaScript API", + "main": "./index.js", + "directories": { + "lib": "./lib" + }, + "dependencies": { + "es6-promise": "*", + "ws": "*", + "xmlhttprequest": "*" + }, + "devDependencies": { + "bower": ">=1.3.0", + "browserify": ">=6.0", + "del": ">=0.1.1", + "envify": "^3.0.0", + "exorcist": "^0.1.6", + "gulp": ">=3.4.0", + "gulp-jshint": ">=1.5.0", + "gulp-rename": ">=1.2.0", + "gulp-uglify": ">=1.0.0", + "jshint": ">=2.5.0", + "uglifyify": "^2.6.0", + "unreachable-branch-transform": "^0.1.0", + "vinyl-source-stream": "^1.0.0" + }, + "scripts": { + "build": "gulp", + "watch": "gulp watch", + "lint": "gulp lint" + }, + "repository": { + "type": "git", + "url": "https://github.com/ethereum/ethereum.js.git" + }, + "homepage": "https://github.com/ethereum/ethereum.js", + "bugs": { + "url": "https://github.com/ethereum/ethereum.js/issues" + }, + "keywords": [ + "ethereum", + "javascript", + "API" + ], + "author": "ethdev.com", + "authors": [ + { + "name": "Jeffery Wilcke", + "email": "jeff@ethdev.com", + "url": "https://github.com/obscuren" + }, + { + "name": "Marek Kotewicz", + "email": "marek@ethdev.com", + "url": "https://github.com/debris" + }, + { + "name": "Marian Oancea", + "email": "marian@ethdev.com", + "url": "https://github.com/cubedro" + } + ], + "license": "LGPL-3.0" +} From f95a8be58b62cea0ba2997737d90b32a5eeabe40 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Wed, 7 Jan 2015 11:29:44 +0100 Subject: [PATCH 43/54] checked --- libevm/ExtVMFace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index bf1b180e9..84cfe0a94 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -148,7 +148,7 @@ public: virtual void revert() {} /// Hash of a block if within the last 256 blocks, or h256() otherwise. - h256 blockhash(u256 _number) { return _number < currentBlock.number && _number >= (std::max(256, currentBlock.number) - 256) ? lastHashes[(unsigned)(currentBlock.number - 1 - _number)] : h256(); } // TODO: CHECK!!! + h256 blockhash(u256 _number) { return _number < currentBlock.number && _number >= (std::max(256, currentBlock.number) - 256) ? lastHashes[(unsigned)(currentBlock.number - 1 - _number)] : h256(); } /// Get the code at the given location in code ROM. byte getCode(u256 _n) const { return _n < code.size() ? code[(size_t)_n] : 0; } From fb2419d4bee639cbca102050d27885498cd50fe7 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 7 Jan 2015 11:43:51 +0100 Subject: [PATCH 44/54] removed ethereum.js --- libjsqrc/ethereum.js | 1119 ------------------------------------------ libjsqrc/js.qrc | 2 +- 2 files changed, 1 insertion(+), 1120 deletions(-) delete mode 100644 libjsqrc/ethereum.js diff --git a/libjsqrc/ethereum.js b/libjsqrc/ethereum.js deleted file mode 100644 index 08bb5014c..000000000 --- a/libjsqrc/ethereum.js +++ /dev/null @@ -1,1119 +0,0 @@ -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. -*/ -/** @file abi.js - * @authors: - * Marek Kotewicz - * Gav Wood - * @date 2014 - */ - -// TODO: make these be actually accurate instead of falling back onto JS's doubles. -var hexToDec = function (hex) { - return parseInt(hex, 16).toString(); -}; - -var decToHex = function (dec) { - return parseInt(dec).toString(16); -}; - -var findIndex = function (array, callback) { - var end = false; - var i = 0; - for (; i < array.length && !end; i++) { - end = callback(array[i]); - } - return end ? i - 1 : -1; -}; - -var findMethodIndex = function (json, methodName) { - return findIndex(json, function (method) { - return method.name === methodName; - }); -}; - -var padLeft = function (string, chars) { - return Array(chars - string.length + 1).join("0") + string; -}; - -var setupInputTypes = function () { - var prefixedType = function (prefix) { - return function (type, value) { - var expected = prefix; - if (type.indexOf(expected) !== 0) { - return false; - } - - var padding = parseInt(type.slice(expected.length)) / 8; - if (typeof value === "number") - value = value.toString(16); - else if (value.indexOf('0x') === 0) - value = value.substr(2); - else - value = (+value).toString(16); - return padLeft(value, padding * 2); - }; - }; - - var namedType = function (name, padding, formatter) { - return function (type, value) { - if (type !== name) { - return false; - } - - return padLeft(formatter ? formatter(value) : value, padding * 2); - }; - }; - - var formatBool = function (value) { - return value ? '0x1' : '0x0'; - }; - - return [ - prefixedType('uint'), - prefixedType('int'), - prefixedType('hash'), - namedType('address', 20), - namedType('bool', 1, formatBool), - ]; -}; - -var inputTypes = setupInputTypes(); - -var toAbiInput = function (json, methodName, params) { - var bytes = ""; - var index = findMethodIndex(json, methodName); - - if (index === -1) { - return; - } - - bytes = "0x" + padLeft(index.toString(16), 2); - var method = json[index]; - - for (var i = 0; i < method.inputs.length; i++) { - var found = false; - for (var j = 0; j < inputTypes.length && !found; j++) { - found = inputTypes[j](method.inputs[i].type, params[i]); - } - if (!found) { - console.error('unsupported json type: ' + method.inputs[i].type); - } - bytes += found; - } - return bytes; -}; - -var setupOutputTypes = function () { - var prefixedType = function (prefix) { - return function (type) { - var expected = prefix; - if (type.indexOf(expected) !== 0) { - return -1; - } - - var padding = parseInt(type.slice(expected.length)) / 8; - return padding * 2; - }; - }; - - var namedType = function (name, padding) { - return function (type) { - return name === type ? padding * 2 : -1; - }; - }; - - var formatInt = function (value) { - return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value); - }; - - var formatHash = function (value) { - return "0x" + value; - }; - - var formatBool = function (value) { - return value === '1' ? true : false; - }; - - return [ - { padding: prefixedType('uint'), format: formatInt }, - { padding: prefixedType('int'), format: formatInt }, - { padding: prefixedType('hash'), format: formatHash }, - { padding: namedType('address', 20) }, - { padding: namedType('bool', 1), format: formatBool } - ]; -}; - -var outputTypes = setupOutputTypes(); - -var fromAbiOutput = function (json, methodName, output) { - var index = findMethodIndex(json, methodName); - - if (index === -1) { - return; - } - - output = output.slice(2); - - var result = []; - var method = json[index]; - for (var i = 0; i < method.outputs.length; i++) { - var padding = -1; - for (var j = 0; j < outputTypes.length && padding === -1; j++) { - padding = outputTypes[j].padding(method.outputs[i].type); - } - - if (padding === -1) { - // not found output parsing - continue; - } - var res = output.slice(0, padding); - var formatter = outputTypes[j - 1].format; - result.push(formatter ? formatter(res) : ("0x" + res)); - output = output.slice(padding); - } - - return result; -}; - -var inputParser = function (json) { - var parser = {}; - json.forEach(function (method) { - parser[method.name] = function () { - var params = Array.prototype.slice.call(arguments); - return toAbiInput(json, method.name, params); - }; - }); - - return parser; -}; - -var outputParser = function (json) { - var parser = {}; - json.forEach(function (method) { - parser[method.name] = function (output) { - return fromAbiOutput(json, method.name, output); - }; - }); - - return parser; -}; - -module.exports = { - inputParser: inputParser, - outputParser: outputParser -}; - - -},{}],2:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file autoprovider.js - * @authors: - * Marek Kotewicz - * Marian Oancea - * @date 2014 - */ - -/* - * @brief if qt object is available, uses QtProvider, - * if not tries to connect over websockets - * if it fails, it uses HttpRpcProvider - */ -if ("build" !== 'build') {/* - var WebSocket = require('ws'); // jshint ignore:line - var web3 = require('./main.js'); // jshint ignore:line -*/} - -var AutoProvider = function (userOptions) { - if (web3.haveProvider()) { - return; - } - - // before we determine what provider we are, we have to cache request - this.sendQueue = []; - this.onmessageQueue = []; - - if (navigator.qt) { - this.provider = new web3.providers.QtProvider(); - return; - } - - userOptions = userOptions || {}; - var options = { - httprpc: userOptions.httprpc || 'http://localhost:8080', - websockets: userOptions.websockets || 'ws://localhost:40404/eth' - }; - - var self = this; - var closeWithSuccess = function (success) { - ws.close(); - if (success) { - self.provider = new web3.providers.WebSocketProvider(options.websockets); - } else { - self.provider = new web3.providers.HttpRpcProvider(options.httprpc); - self.poll = self.provider.poll.bind(self.provider); - } - self.sendQueue.forEach(function (payload) { - self.provider(payload); - }); - self.onmessageQueue.forEach(function (handler) { - self.provider.onmessage = handler; - }); - }; - - var ws = new WebSocket(options.websockets); - - ws.onopen = function() { - closeWithSuccess(true); - }; - - ws.onerror = function() { - closeWithSuccess(false); - }; -}; - -AutoProvider.prototype.send = function (payload) { - if (this.provider) { - this.provider.send(payload); - return; - } - this.sendQueue.push(payload); -}; - -Object.defineProperty(AutoProvider.prototype, 'onmessage', { - set: function (handler) { - if (this.provider) { - this.provider.onmessage = handler; - return; - } - this.onmessageQueue.push(handler); - } -}); - -module.exports = AutoProvider; - -},{}],3:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file contract.js - * @authors: - * Marek Kotewicz - * @date 2014 - */ - -if ("build" !== 'build') {/* - var web3 = require('./web3'); // jshint ignore:line -*/} -var abi = require('./abi'); - -var contract = function (address, desc) { - var inputParser = abi.inputParser(desc); - var outputParser = abi.outputParser(desc); - - var contract = {}; - - desc.forEach(function (method) { - contract[method.name] = function () { - var params = Array.prototype.slice.call(arguments); - var parsed = inputParser[method.name].apply(null, params); - - var onSuccess = function (result) { - return outputParser[method.name](result); - }; - - return { - call: function (extra) { - extra = extra || {}; - extra.to = address; - extra.data = parsed; - return web3.eth.call(extra).then(onSuccess); - }, - transact: function (extra) { - extra = extra || {}; - extra.to = address; - extra.data = parsed; - return web3.eth.transact(extra).then(onSuccess); - } - }; - }; - }); - - return contract; -}; - -module.exports = contract; - -},{"./abi":1}],4:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file httprpc.js - * @authors: - * Marek Kotewicz - * Marian Oancea - * @date 2014 - */ - -if ("build" !== "build") {/* - var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line -*/} - -var HttpRpcProvider = function (host) { - this.handlers = []; - this.host = host; -}; - -function formatJsonRpcObject(object) { - return { - jsonrpc: '2.0', - method: object.call, - params: object.args, - id: object._id - }; -} - -function formatJsonRpcMessage(message) { - var object = JSON.parse(message); - - return { - _id: object.id, - data: object.result, - error: object.error - }; -} - -HttpRpcProvider.prototype.sendRequest = function (payload, cb) { - var data = formatJsonRpcObject(payload); - - var request = new XMLHttpRequest(); - request.open("POST", this.host, true); - request.send(JSON.stringify(data)); - request.onreadystatechange = function () { - if (request.readyState === 4 && cb) { - cb(request); - } - }; -}; - -HttpRpcProvider.prototype.send = function (payload) { - var self = this; - this.sendRequest(payload, function (request) { - self.handlers.forEach(function (handler) { - handler.call(self, formatJsonRpcMessage(request.responseText)); - }); - }); -}; - -HttpRpcProvider.prototype.poll = function (payload, id) { - var self = this; - this.sendRequest(payload, function (request) { - var parsed = JSON.parse(request.responseText); - if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) { - return; - } - self.handlers.forEach(function (handler) { - handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); - }); - }); -}; - -Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { - set: function (handler) { - this.handlers.push(handler); - } -}); - -module.exports = HttpRpcProvider; - -},{}],5:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file main.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * Marian Oancea - * Gav Wood - * @date 2014 - */ - -function flattenPromise (obj) { - if (obj instanceof Promise) { - return Promise.resolve(obj); - } - - if (obj instanceof Array) { - return new Promise(function (resolve) { - var promises = obj.map(function (o) { - return flattenPromise(o); - }); - - return Promise.all(promises).then(function (res) { - for (var i = 0; i < obj.length; i++) { - obj[i] = res[i]; - } - resolve(obj); - }); - }); - } - - if (obj instanceof Object) { - return new Promise(function (resolve) { - var keys = Object.keys(obj); - var promises = keys.map(function (key) { - return flattenPromise(obj[key]); - }); - - return Promise.all(promises).then(function (res) { - for (var i = 0; i < keys.length; i++) { - obj[keys[i]] = res[i]; - } - resolve(obj); - }); - }); - } - - return Promise.resolve(obj); -} - -var web3Methods = function () { - return [ - { name: 'sha3', call: 'web3_sha3' } - ]; -}; - -var ethMethods = function () { - var blockCall = function (args) { - return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; - }; - - var transactionCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; - }; - - var uncleCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; - }; - - var methods = [ - { name: 'balanceAt', call: 'eth_balanceAt' }, - { name: 'stateAt', call: 'eth_stateAt' }, - { name: 'storageAt', call: 'eth_storageAt' }, - { name: 'countAt', call: 'eth_countAt'}, - { name: 'codeAt', call: 'eth_codeAt' }, - { name: 'transact', call: 'eth_transact' }, - { name: 'call', call: 'eth_call' }, - { name: 'block', call: blockCall }, - { name: 'transaction', call: transactionCall }, - { name: 'uncle', call: uncleCall }, - { name: 'compilers', call: 'eth_compilers' }, - { name: 'lll', call: 'eth_lll' }, - { name: 'solidity', call: 'eth_solidity' }, - { name: 'serpent', call: 'eth_serpent' }, - { name: 'logs', call: 'eth_logs' } - ]; - return methods; -}; - -var ethProperties = function () { - return [ - { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, - { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, - { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, - { name: 'gasPrice', getter: 'eth_gasPrice' }, - { name: 'account', getter: 'eth_account' }, - { name: 'accounts', getter: 'eth_accounts' }, - { name: 'peerCount', getter: 'eth_peerCount' }, - { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, - { name: 'number', getter: 'eth_number'} - ]; -}; - -var dbMethods = function () { - return [ - { name: 'put', call: 'db_put' }, - { name: 'get', call: 'db_get' }, - { name: 'putString', call: 'db_putString' }, - { name: 'getString', call: 'db_getString' } - ]; -}; - -var shhMethods = function () { - return [ - { name: 'post', call: 'shh_post' }, - { name: 'newIdentity', call: 'shh_newIdentity' }, - { name: 'haveIdentity', call: 'shh_haveIdentity' }, - { name: 'newGroup', call: 'shh_newGroup' }, - { name: 'addToGroup', call: 'shh_addToGroup' } - ]; -}; - -var ethWatchMethods = function () { - var newFilter = function (args) { - return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; - }; - - return [ - { name: 'newFilter', call: newFilter }, - { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, - { name: 'getMessages', call: 'eth_filterLogs' } - ]; -}; - -var shhWatchMethods = function () { - return [ - { name: 'newFilter', call: 'shh_newFilter' }, - { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, - { name: 'getMessage', call: 'shh_getMessages' } - ]; -}; - -var setupMethods = function (obj, methods) { - methods.forEach(function (method) { - obj[method.name] = function () { - return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { - var call = typeof method.call === "function" ? method.call(args) : method.call; - return {call: call, args: args}; - }).then(function (request) { - return new Promise(function (resolve, reject) { - web3.provider.send(request, function (err, result) { - if (!err) { - resolve(result); - return; - } - reject(err); - }); - }); - }).catch(function(err) { - console.error(err); - }); - }; - }); -}; - -var setupProperties = function (obj, properties) { - properties.forEach(function (property) { - var proto = {}; - proto.get = function () { - return new Promise(function(resolve, reject) { - web3.provider.send({call: property.getter}, function(err, result) { - if (!err) { - resolve(result); - return; - } - reject(err); - }); - }); - }; - if (property.setter) { - proto.set = function (val) { - return flattenPromise([val]).then(function (args) { - return new Promise(function (resolve) { - web3.provider.send({call: property.setter, args: args}, function (err, result) { - if (!err) { - resolve(result); - return; - } - reject(err); - }); - }); - }).catch(function (err) { - console.error(err); - }); - }; - } - Object.defineProperty(obj, property.name, proto); - }); -}; - -// TODO: import from a dependency, don't duplicate. -var hexToDec = function (hex) { - return parseInt(hex, 16).toString(); -}; - -var decToHex = function (dec) { - return parseInt(dec).toString(16); -}; - - -var web3 = { - _callbacks: {}, - _events: {}, - providers: {}, - - toAscii: function(hex) { - // Find termination - var str = ""; - var i = 0, l = hex.length; - if (hex.substring(0, 2) === '0x') - i = 2; - for(; i < l; i+=2) { - var code = hex.charCodeAt(i); - if(code === 0) { - break; - } - - str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); - } - - return str; - }, - - fromAscii: function(str, pad) { - pad = pad === undefined ? 32 : pad; - var hex = this.toHex(str); - while(hex.length < pad*2) - hex += "00"; - return "0x" + hex; - }, - - toDecimal: function (val) { - return hexToDec(val.substring(2)); - }, - - fromDecimal: function (val) { - return "0x" + decToHex(val); - }, - - toEth: function(str) { - var val = typeof str === "string" ? str.indexOf('0x') == 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; - var unit = 0; - var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ]; - while (val > 3000 && unit < units.length - 1) - { - val /= 1000; - unit++; - } - var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); - while (true) { - var o = s; - s = s.replace(/(\d)(\d\d\d[\.\,])/, function($0, $1, $2) { return $1 + ',' + $2; }); - if (o == s) - break; - } - return s + ' ' + units[unit]; - }, - - eth: { - prototype: Object(), // jshint ignore:line - watch: function (params) { - return new Filter(params, ethWatch); - } - }, - - db: { - prototype: Object() // jshint ignore:line - }, - - shh: { - prototype: Object(), // jshint ignore:line - watch: function (params) { - return new Filter(params, shhWatch); - } - }, - - on: function(event, id, cb) { - if(web3._events[event] === undefined) { - web3._events[event] = {}; - } - - web3._events[event][id] = cb; - return this; - }, - - off: function(event, id) { - if(web3._events[event] !== undefined) { - delete web3._events[event][id]; - } - - return this; - }, - - trigger: function(event, id, data) { - var callbacks = web3._events[event]; - if (!callbacks || !callbacks[id]) { - return; - } - var cb = callbacks[id]; - cb(data); - } -}; - -setupMethods(web3, web3Methods()); -setupMethods(web3.eth, ethMethods()); -setupProperties(web3.eth, ethProperties()); -setupMethods(web3.db, dbMethods()); -setupMethods(web3.shh, shhMethods()); - -var ethWatch = { - changed: 'eth_changed' -}; -setupMethods(ethWatch, ethWatchMethods()); -var shhWatch = { - changed: 'shh_changed' -}; -setupMethods(shhWatch, shhWatchMethods()); - -var ProviderManager = function() { - this.queued = []; - this.polls = []; - this.ready = false; - this.provider = undefined; - this.id = 1; - - var self = this; - var poll = function () { - if (self.provider && self.provider.poll) { - self.polls.forEach(function (data) { - data.data._id = self.id; - self.id++; - self.provider.poll(data.data, data.id); - }); - } - setTimeout(poll, 12000); - }; - poll(); -}; - -ProviderManager.prototype.send = function(data, cb) { - data._id = this.id; - if (cb) { - web3._callbacks[data._id] = cb; - } - - data.args = data.args || []; - this.id++; - - if(this.provider !== undefined) { - this.provider.send(data); - } else { - console.warn("provider is not set"); - this.queued.push(data); - } -}; - -ProviderManager.prototype.set = function(provider) { - if(this.provider !== undefined && this.provider.unload !== undefined) { - this.provider.unload(); - } - - this.provider = provider; - this.ready = true; -}; - -ProviderManager.prototype.sendQueued = function() { - for(var i = 0; this.queued.length; i++) { - // Resend - this.send(this.queued[i]); - } -}; - -ProviderManager.prototype.installed = function() { - return this.provider !== undefined; -}; - -ProviderManager.prototype.startPolling = function (data, pollId) { - if (!this.provider || !this.provider.poll) { - return; - } - this.polls.push({data: data, id: pollId}); -}; - -ProviderManager.prototype.stopPolling = function (pollId) { - for (var i = this.polls.length; i--;) { - var poll = this.polls[i]; - if (poll.id === pollId) { - this.polls.splice(i, 1); - } - } -}; - -web3.provider = new ProviderManager(); - -web3.setProvider = function(provider) { - provider.onmessage = messageHandler; - web3.provider.set(provider); - web3.provider.sendQueued(); -}; - -web3.haveProvider = function() { - return !!web3.provider.provider; -}; - -var Filter = function(options, impl) { - this.impl = impl; - this.callbacks = []; - - var self = this; - this.promise = impl.newFilter(options); - this.promise.then(function (id) { - self.id = id; - web3.on(impl.changed, id, self.trigger.bind(self)); - web3.provider.startPolling({call: impl.changed, args: [id]}, id); - }); -}; - -Filter.prototype.arrived = function(callback) { - this.changed(callback); -}; - -Filter.prototype.changed = function(callback) { - var self = this; - this.promise.then(function(id) { - self.callbacks.push(callback); - }); -}; - -Filter.prototype.trigger = function(messages) { - for(var i = 0; i < this.callbacks.length; i++) { - this.callbacks[i].call(this, messages); - } -}; - -Filter.prototype.uninstall = function() { - var self = this; - this.promise.then(function (id) { - self.impl.uninstallFilter(id); - web3.provider.stopPolling(id); - web3.off(impl.changed, id); - }); -}; - -Filter.prototype.messages = function() { - var self = this; - return this.promise.then(function (id) { - return self.impl.getMessages(id); - }); -}; - -Filter.prototype.logs = function () { - return this.messages(); -}; - -function messageHandler(data) { - if(data._event !== undefined) { - web3.trigger(data._event, data._id, data.data); - return; - } - - if(data._id) { - var cb = web3._callbacks[data._id]; - if (cb) { - cb.call(this, data.error, data.data); - delete web3._callbacks[data._id]; - } - } -} - -module.exports = web3; - - -},{}],6:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file qt.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * @date 2014 - */ - -var QtProvider = function() { - this.handlers = []; - - var self = this; - navigator.qt.onmessage = function (message) { - self.handlers.forEach(function (handler) { - handler.call(self, JSON.parse(message.data)); - }); - }; -}; - -QtProvider.prototype.send = function(payload) { - navigator.qt.postMessage(JSON.stringify(payload)); -}; - -Object.defineProperty(QtProvider.prototype, "onmessage", { - set: function(handler) { - this.handlers.push(handler); - } -}); - -module.exports = QtProvider; - -},{}],7:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file websocket.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * Marian Oancea - * @date 2014 - */ - -if ("build" !== "build") {/* - var WebSocket = require('ws'); // jshint ignore:line -*/} - -var WebSocketProvider = function(host) { - // onmessage handlers - this.handlers = []; - // queue will be filled with messages if send is invoked before the ws is ready - this.queued = []; - this.ready = false; - - this.ws = new WebSocket(host); - - var self = this; - this.ws.onmessage = function(event) { - for(var i = 0; i < self.handlers.length; i++) { - self.handlers[i].call(self, JSON.parse(event.data), event); - } - }; - - this.ws.onopen = function() { - self.ready = true; - - for(var i = 0; i < self.queued.length; i++) { - // Resend - self.send(self.queued[i]); - } - }; -}; - -WebSocketProvider.prototype.send = function(payload) { - if(this.ready) { - var data = JSON.stringify(payload); - - this.ws.send(data); - } else { - this.queued.push(payload); - } -}; - -WebSocketProvider.prototype.onMessage = function(handler) { - this.handlers.push(handler); -}; - -WebSocketProvider.prototype.unload = function() { - this.ws.close(); -}; -Object.defineProperty(WebSocketProvider.prototype, "onmessage", { - set: function(provider) { this.onMessage(provider); } -}); - -module.exports = WebSocketProvider; - -},{}],"web3":[function(require,module,exports){ -var web3 = require('./lib/main'); -web3.providers.WebSocketProvider = require('./lib/websocket'); -web3.providers.HttpRpcProvider = require('./lib/httprpc'); -web3.providers.QtProvider = require('./lib/qt'); -web3.providers.AutoProvider = require('./lib/autoprovider'); -web3.contract = require('./lib/contract'); - -module.exports = web3; - -},{"./lib/autoprovider":2,"./lib/contract":3,"./lib/httprpc":4,"./lib/main":5,"./lib/qt":6,"./lib/websocket":7}]},{},[]) - - -//# sourceMappingURL=ethereum.js.map diff --git a/libjsqrc/js.qrc b/libjsqrc/js.qrc index 56cab75f1..eee8f6652 100644 --- a/libjsqrc/js.qrc +++ b/libjsqrc/js.qrc @@ -2,6 +2,6 @@ es6-promise-2.0.0.js setup.js - ethereum.js + ethereumjs/dist/ethereum.js From e855cf9426ddacf95b078098d438a78f98997bde Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Wed, 7 Jan 2015 12:14:33 +0100 Subject: [PATCH 45/54] memory test: high offset, zero size -> zero gas cost --- test/stSystemOperationsTestFiller.json | 69 ++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/test/stSystemOperationsTestFiller.json b/test/stSystemOperationsTestFiller.json index 70ce448fc..757c8a235 100644 --- a/test/stSystemOperationsTestFiller.json +++ b/test/stSystemOperationsTestFiller.json @@ -33,6 +33,75 @@ } }, + "createNameRegistratorZeroMem": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 3 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "createNameRegistratorZeroMem2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 23 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "createNameRegistratorZeroMemExpansion": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From 67debe5ea5df33baa53793f536fcbfd522559b1e Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Wed, 7 Jan 2015 12:36:15 +0100 Subject: [PATCH 46/54] jump onto jump, dynamic and static jump with same destination --- test/vmIOandFlowOperationsTestFiller.json | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/vmIOandFlowOperationsTestFiller.json b/test/vmIOandFlowOperationsTestFiller.json index 073c8ac80..efbd0d683 100644 --- a/test/vmIOandFlowOperationsTestFiller.json +++ b/test/vmIOandFlowOperationsTestFiller.json @@ -671,6 +671,62 @@ } }, + "jumpOntoJump": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x565b600056", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + + "jumpDynamicJumpSameDest": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x6002600401565b600360005260206000f3600656", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "10000" + } + }, + "jump0_outOfBoundary": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From 7963e26aeb39d4ecb24edf1b206b2ef0757a0c70 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 7 Jan 2015 14:15:51 +0100 Subject: [PATCH 47/54] Squashed 'libjsqrc/ethereumjs/' changes from df4d784..5208bb3 5208bb3 gulpfile modifications, default build set to dev git-subtree-dir: libjsqrc/ethereumjs git-subtree-split: 5208bb32f147bd00ba36ca684faba31c0cc7fc9a --- dist/ethereum.js | 1180 +++++++++++++++++++++++++++++++++++++++++- dist/ethereum.js.map | 18 +- dist/ethereum.min.js | 2 +- gulpfile.js | 81 ++- 4 files changed, 1213 insertions(+), 68 deletions(-) diff --git a/dist/ethereum.js b/dist/ethereum.js index 8abe6ad53..e496c30f4 100644 --- a/dist/ethereum.js +++ b/dist/ethereum.js @@ -1,19 +1,1183 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;oi;i++)padding+=sizes[i]/8;return padding},setupInputTypes=function(){var prefixedType=function(prefix,calcPadding){return function(type,value){var padding,expected=prefix;return 0!==type.indexOf(expected)?!1:(padding=calcPadding(type,expected),value="number"==typeof value?value.toString(16):"string"==typeof value?web3.toHex(value):0===value.indexOf("0x")?value.substr(2):(+value).toString(16),padLeft(value,2*padding))}},namedType=function(name,padding,formatter){return function(type,value){return type!==name?!1:padLeft(formatter?formatter(value):value,2*padding)}},formatBool=function(value){return value?"0x1":"0x0"};return[prefixedType("uint",calcBitPadding),prefixedType("int",calcBitPadding),prefixedType("hash",calcBitPadding),prefixedType("string",calcBytePadding),prefixedType("real",calcRealPadding),prefixedType("ureal",calcRealPadding),namedType("address",20),namedType("bool",1,formatBool)]},inputTypes=setupInputTypes(),toAbiInput=function(json,methodName,params){var method,i,found,j,bytes="",index=findMethodIndex(json,methodName);if(-1!==index){for(bytes="0x"+padLeft(index.toString(16),2),method=json[index],i=0;i. +*/ +/** @file abi.js + * @authors: + * Marek Kotewicz + * Gav Wood + * @date 2014 + */ + +// TODO: is these line is supposed to be here? +if ("build" !== 'build') {/* + var web3 = require('./web3'); // jshint ignore:line +*/} + +// TODO: make these be actually accurate instead of falling back onto JS's doubles. +var hexToDec = function (hex) { + return parseInt(hex, 16).toString(); +}; + +var decToHex = function (dec) { + return parseInt(dec).toString(16); +}; + +var findIndex = function (array, callback) { + var end = false; + var i = 0; + for (; i < array.length && !end; i++) { + end = callback(array[i]); + } + return end ? i - 1 : -1; +}; + +var findMethodIndex = function (json, methodName) { + return findIndex(json, function (method) { + return method.name === methodName; + }); +}; + +var padLeft = function (string, chars) { + return new Array(chars - string.length + 1).join("0") + string; +}; + +var calcBitPadding = function (type, expected) { + var value = type.slice(expected.length); + if (value === "") { + return 32; + } + return parseInt(value) / 8; +}; + +var calcBytePadding = function (type, expected) { + var value = type.slice(expected.length); + if (value === "") { + return 32; + } + return parseInt(value); +}; + +var calcRealPadding = function (type, expected) { + var value = type.slice(expected.length); + if (value === "") { + return 32; + } + var sizes = value.split('x'); + for (var padding = 0, i = 0; i < sizes; i++) { + padding += (sizes[i] / 8); + } + return padding; +}; + +var setupInputTypes = function () { + + var prefixedType = function (prefix, calcPadding) { + return function (type, value) { + var expected = prefix; + if (type.indexOf(expected) !== 0) { + return false; + } + + var padding = calcPadding(type, expected); + if (typeof value === "number") + value = value.toString(16); + else if (typeof value === "string") + value = web3.toHex(value); + else if (value.indexOf('0x') === 0) + value = value.substr(2); + else + value = (+value).toString(16); + return padLeft(value, padding * 2); + }; + }; + + var namedType = function (name, padding, formatter) { + return function (type, value) { + if (type !== name) { + return false; + } + + return padLeft(formatter ? formatter(value) : value, padding * 2); + }; + }; + + var formatBool = function (value) { + return value ? '0x1' : '0x0'; + }; + + return [ + prefixedType('uint', calcBitPadding), + prefixedType('int', calcBitPadding), + prefixedType('hash', calcBitPadding), + prefixedType('string', calcBytePadding), + prefixedType('real', calcRealPadding), + prefixedType('ureal', calcRealPadding), + namedType('address', 20), + namedType('bool', 1, formatBool), + ]; +}; + +var inputTypes = setupInputTypes(); + +var toAbiInput = function (json, methodName, params) { + var bytes = ""; + var index = findMethodIndex(json, methodName); + + if (index === -1) { + return; + } + + bytes = "0x" + padLeft(index.toString(16), 2); + var method = json[index]; + + for (var i = 0; i < method.inputs.length; i++) { + var found = false; + for (var j = 0; j < inputTypes.length && !found; j++) { + found = inputTypes[j](method.inputs[i].type, params[i]); + } + if (!found) { + console.error('unsupported json type: ' + method.inputs[i].type); + } + bytes += found; + } + return bytes; +}; + +var setupOutputTypes = function () { + + var prefixedType = function (prefix, calcPadding) { + return function (type) { + var expected = prefix; + if (type.indexOf(expected) !== 0) { + return -1; + } + + var padding = calcPadding(type, expected); + return padding * 2; + }; + }; + + var namedType = function (name, padding) { + return function (type) { + return name === type ? padding * 2 : -1; + }; + }; + + var formatInt = function (value) { + return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value); + }; + + var formatHash = function (value) { + return "0x" + value; + }; + + var formatBool = function (value) { + return value === '1' ? true : false; + }; + + var formatString = function (value) { + return web3.toAscii(value); + }; + + return [ + { padding: prefixedType('uint', calcBitPadding), format: formatInt }, + { padding: prefixedType('int', calcBitPadding), format: formatInt }, + { padding: prefixedType('hash', calcBitPadding), format: formatHash }, + { padding: prefixedType('string', calcBytePadding), format: formatString }, + { padding: prefixedType('real', calcRealPadding), format: formatInt }, + { padding: prefixedType('ureal', calcRealPadding), format: formatInt }, + { padding: namedType('address', 20) }, + { padding: namedType('bool', 1), format: formatBool } + ]; +}; + +var outputTypes = setupOutputTypes(); + +var fromAbiOutput = function (json, methodName, output) { + var index = findMethodIndex(json, methodName); + + if (index === -1) { + return; + } + + output = output.slice(2); + + var result = []; + var method = json[index]; + for (var i = 0; i < method.outputs.length; i++) { + var padding = -1; + for (var j = 0; j < outputTypes.length && padding === -1; j++) { + padding = outputTypes[j].padding(method.outputs[i].type); + } + + if (padding === -1) { + // not found output parsing + continue; + } + var res = output.slice(0, padding); + var formatter = outputTypes[j - 1].format; + result.push(formatter ? formatter(res) : ("0x" + res)); + output = output.slice(padding); + } + + return result; +}; + +var inputParser = function (json) { + var parser = {}; + json.forEach(function (method) { + parser[method.name] = function () { + var params = Array.prototype.slice.call(arguments); + return toAbiInput(json, method.name, params); + }; + }); + + return parser; +}; + +var outputParser = function (json) { + var parser = {}; + json.forEach(function (method) { + parser[method.name] = function (output) { + return fromAbiOutput(json, method.name, output); + }; + }); + + return parser; +}; + +module.exports = { + inputParser: inputParser, + outputParser: outputParser +}; + },{}],2:[function(require,module,exports){ -var AutoProvider=function(userOptions){var options,self,closeWithSuccess,ws;if(!web3.haveProvider()){if(this.sendQueue=[],this.onmessageQueue=[],navigator.qt)return void(this.provider=new web3.providers.QtProvider);userOptions=userOptions||{},options={httprpc:userOptions.httprpc||"http://localhost:8080",websockets:userOptions.websockets||"ws://localhost:40404/eth"},self=this,closeWithSuccess=function(success){ws.close(),success?self.provider=new web3.providers.WebSocketProvider(options.websockets):(self.provider=new web3.providers.HttpRpcProvider(options.httprpc),self.poll=self.provider.poll.bind(self.provider)),self.sendQueue.forEach(function(payload){self.provider(payload)}),self.onmessageQueue.forEach(function(handler){self.provider.onmessage=handler})},ws=new WebSocket(options.websockets),ws.onopen=function(){closeWithSuccess(!0)},ws.onerror=function(){closeWithSuccess(!1)}}};AutoProvider.prototype.send=function(payload){return this.provider?void this.provider.send(payload):void this.sendQueue.push(payload)},Object.defineProperty(AutoProvider.prototype,"onmessage",{set:function(handler){return this.provider?void(this.provider.onmessage=handler):void this.onmessageQueue.push(handler)}}),module.exports=AutoProvider; +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file autoprovider.js + * @authors: + * Marek Kotewicz + * Marian Oancea + * @date 2014 + */ + +/* + * @brief if qt object is available, uses QtProvider, + * if not tries to connect over websockets + * if it fails, it uses HttpRpcProvider + */ + +// TODO: is these line is supposed to be here? +if ("build" !== 'build') {/* + var WebSocket = require('ws'); // jshint ignore:line + var web3 = require('./main.js'); // jshint ignore:line +*/} + +var AutoProvider = function (userOptions) { + if (web3.haveProvider()) { + return; + } + + // before we determine what provider we are, we have to cache request + this.sendQueue = []; + this.onmessageQueue = []; + + if (navigator.qt) { + this.provider = new web3.providers.QtProvider(); + return; + } + + userOptions = userOptions || {}; + var options = { + httprpc: userOptions.httprpc || 'http://localhost:8080', + websockets: userOptions.websockets || 'ws://localhost:40404/eth' + }; + + var self = this; + var closeWithSuccess = function (success) { + ws.close(); + if (success) { + self.provider = new web3.providers.WebSocketProvider(options.websockets); + } else { + self.provider = new web3.providers.HttpRpcProvider(options.httprpc); + self.poll = self.provider.poll.bind(self.provider); + } + self.sendQueue.forEach(function (payload) { + self.provider(payload); + }); + self.onmessageQueue.forEach(function (handler) { + self.provider.onmessage = handler; + }); + }; + + var ws = new WebSocket(options.websockets); + + ws.onopen = function() { + closeWithSuccess(true); + }; + + ws.onerror = function() { + closeWithSuccess(false); + }; +}; + +AutoProvider.prototype.send = function (payload) { + if (this.provider) { + this.provider.send(payload); + return; + } + this.sendQueue.push(payload); +}; + +Object.defineProperty(AutoProvider.prototype, 'onmessage', { + set: function (handler) { + if (this.provider) { + this.provider.onmessage = handler; + return; + } + this.onmessageQueue.push(handler); + } +}); + +module.exports = AutoProvider; + },{}],3:[function(require,module,exports){ -var abi,contract;abi=require("./abi"),contract=function(address,desc){var inputParser=abi.inputParser(desc),outputParser=abi.outputParser(desc),contract={};return desc.forEach(function(method){contract[method.name]=function(){var params=Array.prototype.slice.call(arguments),parsed=inputParser[method.name].apply(null,params),onSuccess=function(result){return outputParser[method.name](result)};return{call:function(extra){return extra=extra||{},extra.to=address,extra.data=parsed,web3.eth.call(extra).then(onSuccess)},transact:function(extra){return extra=extra||{},extra.to=address,extra.data=parsed,web3.eth.transact(extra).then(onSuccess)}}}}),contract},module.exports=contract; +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file contract.js + * @authors: + * Marek Kotewicz + * @date 2014 + */ + +// TODO: is these line is supposed to be here? +if ("build" !== 'build') {/* + var web3 = require('./web3'); // jshint ignore:line +*/} + +var abi = require('./abi'); + +var contract = function (address, desc) { + var inputParser = abi.inputParser(desc); + var outputParser = abi.outputParser(desc); + + var contract = {}; + + desc.forEach(function (method) { + contract[method.name] = function () { + var params = Array.prototype.slice.call(arguments); + var parsed = inputParser[method.name].apply(null, params); + + var onSuccess = function (result) { + return outputParser[method.name](result); + }; + + return { + call: function (extra) { + extra = extra || {}; + extra.to = address; + extra.data = parsed; + return web3.eth.call(extra).then(onSuccess); + }, + transact: function (extra) { + extra = extra || {}; + extra.to = address; + extra.data = parsed; + return web3.eth.transact(extra).then(onSuccess); + } + }; + }; + }); + + return contract; +}; + +module.exports = contract; + },{"./abi":1}],4:[function(require,module,exports){ -function formatJsonRpcObject(object){return{jsonrpc:"2.0",method:object.call,params:object.args,id:object._id}}function formatJsonRpcMessage(message){var object=JSON.parse(message);return{_id:object.id,data:object.result,error:object.error}}var HttpRpcProvider=function(host){this.handlers=[],this.host=host};HttpRpcProvider.prototype.sendRequest=function(payload,cb){var data=formatJsonRpcObject(payload),request=new XMLHttpRequest;request.open("POST",this.host,!0),request.send(JSON.stringify(data)),request.onreadystatechange=function(){4===request.readyState&&cb&&cb(request)}},HttpRpcProvider.prototype.send=function(payload){var self=this;this.sendRequest(payload,function(request){self.handlers.forEach(function(handler){handler.call(self,formatJsonRpcMessage(request.responseText))})})},HttpRpcProvider.prototype.poll=function(payload,id){var self=this;this.sendRequest(payload,function(request){var parsed=JSON.parse(request.responseText);!parsed.error&&(parsed.result instanceof Array?0!==parsed.result.length:parsed.result)&&self.handlers.forEach(function(handler){handler.call(self,{_event:payload.call,_id:id,data:parsed.result})})})},Object.defineProperty(HttpRpcProvider.prototype,"onmessage",{set:function(handler){this.handlers.push(handler)}}),module.exports=HttpRpcProvider; +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file httprpc.js + * @authors: + * Marek Kotewicz + * Marian Oancea + * @date 2014 + */ + +// TODO: is these line is supposed to be here? +if ("build" !== 'build') {/* + var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line +*/} + +var HttpRpcProvider = function (host) { + this.handlers = []; + this.host = host; +}; + +function formatJsonRpcObject(object) { + return { + jsonrpc: '2.0', + method: object.call, + params: object.args, + id: object._id + }; +} + +function formatJsonRpcMessage(message) { + var object = JSON.parse(message); + + return { + _id: object.id, + data: object.result, + error: object.error + }; +} + +HttpRpcProvider.prototype.sendRequest = function (payload, cb) { + var data = formatJsonRpcObject(payload); + + var request = new XMLHttpRequest(); + request.open("POST", this.host, true); + request.send(JSON.stringify(data)); + request.onreadystatechange = function () { + if (request.readyState === 4 && cb) { + cb(request); + } + }; +}; + +HttpRpcProvider.prototype.send = function (payload) { + var self = this; + this.sendRequest(payload, function (request) { + self.handlers.forEach(function (handler) { + handler.call(self, formatJsonRpcMessage(request.responseText)); + }); + }); +}; + +HttpRpcProvider.prototype.poll = function (payload, id) { + var self = this; + this.sendRequest(payload, function (request) { + var parsed = JSON.parse(request.responseText); + if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) { + return; + } + self.handlers.forEach(function (handler) { + handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); + }); + }); +}; + +Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { + set: function (handler) { + this.handlers.push(handler); + } +}); + +module.exports = HttpRpcProvider; + },{}],5:[function(require,module,exports){ -function flattenPromise(obj){return obj instanceof Promise?Promise.resolve(obj):obj instanceof Array?new Promise(function(resolve){var promises=obj.map(function(o){return flattenPromise(o)});return Promise.all(promises).then(function(res){for(var i=0;ii&&(code=hex.charCodeAt(i),0!==code);i+=2)str+=String.fromCharCode(parseInt(hex.substr(i,2),16));return str},fromAscii:function(str,pad){pad=void 0===pad?32:pad;for(var hex=this.toHex(str);hex.length<2*pad;)hex+="00";return"0x"+hex},toDecimal:function(val){return hexToDec(val.substring(2))},fromDecimal:function(val){return"0x"+decToHex(val)},toEth:function(str){for(var s,replaceFunction,o,val="string"==typeof str?0===str.indexOf("0x")?parseInt(str.substr(2),16):parseInt(str):str,unit=0,units=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];val>3e3&&unit. +*/ +/** @file main.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Gav Wood + * @date 2014 + */ + +function flattenPromise (obj) { + if (obj instanceof Promise) { + return Promise.resolve(obj); + } + + if (obj instanceof Array) { + return new Promise(function (resolve) { + var promises = obj.map(function (o) { + return flattenPromise(o); + }); + + return Promise.all(promises).then(function (res) { + for (var i = 0; i < obj.length; i++) { + obj[i] = res[i]; + } + resolve(obj); + }); + }); + } + + if (obj instanceof Object) { + return new Promise(function (resolve) { + var keys = Object.keys(obj); + var promises = keys.map(function (key) { + return flattenPromise(obj[key]); + }); + + return Promise.all(promises).then(function (res) { + for (var i = 0; i < keys.length; i++) { + obj[keys[i]] = res[i]; + } + resolve(obj); + }); + }); + } + + return Promise.resolve(obj); +} + +var web3Methods = function () { + return [ + { name: 'sha3', call: 'web3_sha3' } + ]; +}; + +var ethMethods = function () { + var blockCall = function (args) { + return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; + }; + + var transactionCall = function (args) { + return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; + }; + + var uncleCall = function (args) { + return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; + }; + + var methods = [ + { name: 'balanceAt', call: 'eth_balanceAt' }, + { name: 'stateAt', call: 'eth_stateAt' }, + { name: 'storageAt', call: 'eth_storageAt' }, + { name: 'countAt', call: 'eth_countAt'}, + { name: 'codeAt', call: 'eth_codeAt' }, + { name: 'transact', call: 'eth_transact' }, + { name: 'call', call: 'eth_call' }, + { name: 'block', call: blockCall }, + { name: 'transaction', call: transactionCall }, + { name: 'uncle', call: uncleCall }, + { name: 'compilers', call: 'eth_compilers' }, + { name: 'lll', call: 'eth_lll' }, + { name: 'solidity', call: 'eth_solidity' }, + { name: 'serpent', call: 'eth_serpent' }, + { name: 'logs', call: 'eth_logs' } + ]; + return methods; +}; + +var ethProperties = function () { + return [ + { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, + { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, + { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, + { name: 'gasPrice', getter: 'eth_gasPrice' }, + { name: 'account', getter: 'eth_account' }, + { name: 'accounts', getter: 'eth_accounts' }, + { name: 'peerCount', getter: 'eth_peerCount' }, + { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, + { name: 'number', getter: 'eth_number'} + ]; +}; + +var dbMethods = function () { + return [ + { name: 'put', call: 'db_put' }, + { name: 'get', call: 'db_get' }, + { name: 'putString', call: 'db_putString' }, + { name: 'getString', call: 'db_getString' } + ]; +}; + +var shhMethods = function () { + return [ + { name: 'post', call: 'shh_post' }, + { name: 'newIdentity', call: 'shh_newIdentity' }, + { name: 'haveIdentity', call: 'shh_haveIdentity' }, + { name: 'newGroup', call: 'shh_newGroup' }, + { name: 'addToGroup', call: 'shh_addToGroup' } + ]; +}; + +var ethWatchMethods = function () { + var newFilter = function (args) { + return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; + }; + + return [ + { name: 'newFilter', call: newFilter }, + { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, + { name: 'getMessages', call: 'eth_filterLogs' } + ]; +}; + +var shhWatchMethods = function () { + return [ + { name: 'newFilter', call: 'shh_newFilter' }, + { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, + { name: 'getMessage', call: 'shh_getMessages' } + ]; +}; + +var setupMethods = function (obj, methods) { + methods.forEach(function (method) { + obj[method.name] = function () { + return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { + var call = typeof method.call === "function" ? method.call(args) : method.call; + return {call: call, args: args}; + }).then(function (request) { + return new Promise(function (resolve, reject) { + web3.provider.send(request, function (err, result) { + if (!err) { + resolve(result); + return; + } + reject(err); + }); + }); + }).catch(function(err) { + console.error(err); + }); + }; + }); +}; + +var setupProperties = function (obj, properties) { + properties.forEach(function (property) { + var proto = {}; + proto.get = function () { + return new Promise(function(resolve, reject) { + web3.provider.send({call: property.getter}, function(err, result) { + if (!err) { + resolve(result); + return; + } + reject(err); + }); + }); + }; + if (property.setter) { + proto.set = function (val) { + return flattenPromise([val]).then(function (args) { + return new Promise(function (resolve) { + web3.provider.send({call: property.setter, args: args}, function (err, result) { + if (!err) { + resolve(result); + return; + } + reject(err); + }); + }); + }).catch(function (err) { + console.error(err); + }); + }; + } + Object.defineProperty(obj, property.name, proto); + }); +}; + +// TODO: import from a dependency, don't duplicate. +var hexToDec = function (hex) { + return parseInt(hex, 16).toString(); +}; + +var decToHex = function (dec) { + return parseInt(dec).toString(16); +}; + + +var web3 = { + _callbacks: {}, + _events: {}, + providers: {}, + + toHex: function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return hex; + }, + + toAscii: function(hex) { + // Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') + i = 2; + for(; i < l; i+=2) { + var code = hex.charCodeAt(i); + if(code === 0) { + break; + } + + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + + return str; + }, + + fromAscii: function(str, pad) { + pad = pad === undefined ? 32 : pad; + var hex = this.toHex(str); + while(hex.length < pad*2) + hex += "00"; + return "0x" + hex; + }, + + toDecimal: function (val) { + return hexToDec(val.substring(2)); + }, + + fromDecimal: function (val) { + return "0x" + decToHex(val); + }, + + toEth: function(str) { + var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; + var unit = 0; + var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ]; + while (val > 3000 && unit < units.length - 1) + { + val /= 1000; + unit++; + } + var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); + var replaceFunction = function($0, $1, $2) { + return $1 + ',' + $2; + }; + + while (true) { + var o = s; + s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); + if (o === s) + break; + } + return s + ' ' + units[unit]; + }, + + eth: { + prototype: Object(), // jshint ignore:line + watch: function (params) { + return new Filter(params, ethWatch); + } + }, + + db: { + prototype: Object() // jshint ignore:line + }, + + shh: { + prototype: Object(), // jshint ignore:line + watch: function (params) { + return new Filter(params, shhWatch); + } + }, + + on: function(event, id, cb) { + if(web3._events[event] === undefined) { + web3._events[event] = {}; + } + + web3._events[event][id] = cb; + return this; + }, + + off: function(event, id) { + if(web3._events[event] !== undefined) { + delete web3._events[event][id]; + } + + return this; + }, + + trigger: function(event, id, data) { + var callbacks = web3._events[event]; + if (!callbacks || !callbacks[id]) { + return; + } + var cb = callbacks[id]; + cb(data); + } +}; + +setupMethods(web3, web3Methods()); +setupMethods(web3.eth, ethMethods()); +setupProperties(web3.eth, ethProperties()); +setupMethods(web3.db, dbMethods()); +setupMethods(web3.shh, shhMethods()); + +var ethWatch = { + changed: 'eth_changed' +}; +setupMethods(ethWatch, ethWatchMethods()); +var shhWatch = { + changed: 'shh_changed' +}; +setupMethods(shhWatch, shhWatchMethods()); + +var ProviderManager = function() { + this.queued = []; + this.polls = []; + this.ready = false; + this.provider = undefined; + this.id = 1; + + var self = this; + var poll = function () { + if (self.provider && self.provider.poll) { + self.polls.forEach(function (data) { + data.data._id = self.id; + self.id++; + self.provider.poll(data.data, data.id); + }); + } + setTimeout(poll, 12000); + }; + poll(); +}; + +ProviderManager.prototype.send = function(data, cb) { + data._id = this.id; + if (cb) { + web3._callbacks[data._id] = cb; + } + + data.args = data.args || []; + this.id++; + + if(this.provider !== undefined) { + this.provider.send(data); + } else { + console.warn("provider is not set"); + this.queued.push(data); + } +}; + +ProviderManager.prototype.set = function(provider) { + if(this.provider !== undefined && this.provider.unload !== undefined) { + this.provider.unload(); + } + + this.provider = provider; + this.ready = true; +}; + +ProviderManager.prototype.sendQueued = function() { + for(var i = 0; this.queued.length; i++) { + // Resend + this.send(this.queued[i]); + } +}; + +ProviderManager.prototype.installed = function() { + return this.provider !== undefined; +}; + +ProviderManager.prototype.startPolling = function (data, pollId) { + if (!this.provider || !this.provider.poll) { + return; + } + this.polls.push({data: data, id: pollId}); +}; + +ProviderManager.prototype.stopPolling = function (pollId) { + for (var i = this.polls.length; i--;) { + var poll = this.polls[i]; + if (poll.id === pollId) { + this.polls.splice(i, 1); + } + } +}; + +web3.provider = new ProviderManager(); + +web3.setProvider = function(provider) { + provider.onmessage = messageHandler; + web3.provider.set(provider); + web3.provider.sendQueued(); +}; + +web3.haveProvider = function() { + return !!web3.provider.provider; +}; + +var Filter = function(options, impl) { + this.impl = impl; + this.callbacks = []; + + var self = this; + this.promise = impl.newFilter(options); + this.promise.then(function (id) { + self.id = id; + web3.on(impl.changed, id, self.trigger.bind(self)); + web3.provider.startPolling({call: impl.changed, args: [id]}, id); + }); +}; + +Filter.prototype.arrived = function(callback) { + this.changed(callback); +}; + +Filter.prototype.changed = function(callback) { + var self = this; + this.promise.then(function(id) { + self.callbacks.push(callback); + }); +}; + +Filter.prototype.trigger = function(messages) { + for(var i = 0; i < this.callbacks.length; i++) { + this.callbacks[i].call(this, messages); + } +}; + +Filter.prototype.uninstall = function() { + var self = this; + this.promise.then(function (id) { + self.impl.uninstallFilter(id); + web3.provider.stopPolling(id); + web3.off(impl.changed, id); + }); +}; + +Filter.prototype.messages = function() { + var self = this; + return this.promise.then(function (id) { + return self.impl.getMessages(id); + }); +}; + +Filter.prototype.logs = function () { + return this.messages(); +}; + +function messageHandler(data) { + if(data._event !== undefined) { + web3.trigger(data._event, data._id, data.data); + return; + } + + if(data._id) { + var cb = web3._callbacks[data._id]; + if (cb) { + cb.call(this, data.error, data.data); + delete web3._callbacks[data._id]; + } + } +} + +module.exports = web3; + },{}],6:[function(require,module,exports){ -var QtProvider=function(){this.handlers=[];var self=this;navigator.qt.onmessage=function(message){self.handlers.forEach(function(handler){handler.call(self,JSON.parse(message.data))})}};QtProvider.prototype.send=function(payload){navigator.qt.postMessage(JSON.stringify(payload))},Object.defineProperty(QtProvider.prototype,"onmessage",{set:function(handler){this.handlers.push(handler)}}),module.exports=QtProvider; +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file qt.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * @date 2014 + */ + +var QtProvider = function() { + this.handlers = []; + + var self = this; + navigator.qt.onmessage = function (message) { + self.handlers.forEach(function (handler) { + handler.call(self, JSON.parse(message.data)); + }); + }; +}; + +QtProvider.prototype.send = function(payload) { + navigator.qt.postMessage(JSON.stringify(payload)); +}; + +Object.defineProperty(QtProvider.prototype, "onmessage", { + set: function(handler) { + this.handlers.push(handler); + } +}); + +module.exports = QtProvider; + },{}],7:[function(require,module,exports){ -var WebSocketProvider=function(host){this.handlers=[],this.queued=[],this.ready=!1,this.ws=new WebSocket(host);var self=this;this.ws.onmessage=function(event){for(var i=0;i. +*/ +/** @file websocket.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * @date 2014 + */ + +// TODO: is these line is supposed to be here? +if ("build" !== 'build') {/* + var WebSocket = require('ws'); // jshint ignore:line +*/} + +var WebSocketProvider = function(host) { + // onmessage handlers + this.handlers = []; + // queue will be filled with messages if send is invoked before the ws is ready + this.queued = []; + this.ready = false; + + this.ws = new WebSocket(host); + + var self = this; + this.ws.onmessage = function(event) { + for(var i = 0; i < self.handlers.length; i++) { + self.handlers[i].call(self, JSON.parse(event.data), event); + } + }; + + this.ws.onopen = function() { + self.ready = true; + + for(var i = 0; i < self.queued.length; i++) { + // Resend + self.send(self.queued[i]); + } + }; +}; + +WebSocketProvider.prototype.send = function(payload) { + if(this.ready) { + var data = JSON.stringify(payload); + + this.ws.send(data); + } else { + this.queued.push(payload); + } +}; + +WebSocketProvider.prototype.onMessage = function(handler) { + this.handlers.push(handler); +}; + +WebSocketProvider.prototype.unload = function() { + this.ws.close(); +}; +Object.defineProperty(WebSocketProvider.prototype, "onmessage", { + set: function(provider) { this.onMessage(provider); } +}); + +module.exports = WebSocketProvider; + },{}],"web3":[function(require,module,exports){ -var web3=require("./lib/main");web3.providers.WebSocketProvider=require("./lib/websocket"),web3.providers.HttpRpcProvider=require("./lib/httprpc"),web3.providers.QtProvider=require("./lib/qt"),web3.providers.AutoProvider=require("./lib/autoprovider"),web3.contract=require("./lib/contract"),module.exports=web3; +var web3 = require('./lib/main'); +web3.providers.WebSocketProvider = require('./lib/websocket'); +web3.providers.HttpRpcProvider = require('./lib/httprpc'); +web3.providers.QtProvider = require('./lib/qt'); +web3.providers.AutoProvider = require('./lib/autoprovider'); +web3.contract = require('./lib/contract'); + +module.exports = web3; + },{"./lib/autoprovider":2,"./lib/contract":3,"./lib/httprpc":4,"./lib/main":5,"./lib/qt":6,"./lib/websocket":7}]},{},["web3"]) diff --git a/dist/ethereum.js.map b/dist/ethereum.js.map index f15143fac..b54a294d7 100644 --- a/dist/ethereum.js.map +++ b/dist/ethereum.js.map @@ -12,18 +12,18 @@ "index.js" ], "names": [], - "mappings": "AAAA;ACAA;;ACAA;;ACAA;;ACAA;;ACAA;;ACAA;;ACAA;;ACAA", + "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5fA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", "file": "generated.js", "sourceRoot": "", "sourcesContent": [ "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;oi;i++)padding+=sizes[i]/8;return padding},setupInputTypes=function(){var prefixedType=function(prefix,calcPadding){return function(type,value){var padding,expected=prefix;return 0!==type.indexOf(expected)?!1:(padding=calcPadding(type,expected),value=\"number\"==typeof value?value.toString(16):\"string\"==typeof value?web3.toHex(value):0===value.indexOf(\"0x\")?value.substr(2):(+value).toString(16),padLeft(value,2*padding))}},namedType=function(name,padding,formatter){return function(type,value){return type!==name?!1:padLeft(formatter?formatter(value):value,2*padding)}},formatBool=function(value){return value?\"0x1\":\"0x0\"};return[prefixedType(\"uint\",calcBitPadding),prefixedType(\"int\",calcBitPadding),prefixedType(\"hash\",calcBitPadding),prefixedType(\"string\",calcBytePadding),prefixedType(\"real\",calcRealPadding),prefixedType(\"ureal\",calcRealPadding),namedType(\"address\",20),namedType(\"bool\",1,formatBool)]},inputTypes=setupInputTypes(),toAbiInput=function(json,methodName,params){var method,i,found,j,bytes=\"\",index=findMethodIndex(json,methodName);if(-1!==index){for(bytes=\"0x\"+padLeft(index.toString(16),2),method=json[index],i=0;ii&&(code=hex.charCodeAt(i),0!==code);i+=2)str+=String.fromCharCode(parseInt(hex.substr(i,2),16));return str},fromAscii:function(str,pad){pad=void 0===pad?32:pad;for(var hex=this.toHex(str);hex.length<2*pad;)hex+=\"00\";return\"0x\"+hex},toDecimal:function(val){return hexToDec(val.substring(2))},fromDecimal:function(val){return\"0x\"+decToHex(val)},toEth:function(str){for(var s,replaceFunction,o,val=\"string\"==typeof str?0===str.indexOf(\"0x\")?parseInt(str.substr(2),16):parseInt(str):str,unit=0,units=[\"wei\",\"Kwei\",\"Mwei\",\"Gwei\",\"szabo\",\"finney\",\"ether\",\"grand\",\"Mether\",\"Gether\",\"Tether\",\"Pether\",\"Eether\",\"Zether\",\"Yether\",\"Nether\",\"Dether\",\"Vether\",\"Uether\"];val>3e3&&unit.\n*/\n/** @file abi.js\n * @authors:\n * Marek Kotewicz \n * Gav Wood \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var web3 = require('./web3'); // jshint ignore:line\n*/}\n\n// TODO: make these be actually accurate instead of falling back onto JS's doubles.\nvar hexToDec = function (hex) {\n return parseInt(hex, 16).toString();\n};\n\nvar decToHex = function (dec) {\n return parseInt(dec).toString(16);\n};\n\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\nvar findMethodIndex = function (json, methodName) {\n return findIndex(json, function (method) {\n return method.name === methodName;\n });\n};\n\nvar padLeft = function (string, chars) {\n return new Array(chars - string.length + 1).join(\"0\") + string;\n};\n\nvar calcBitPadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n return parseInt(value) / 8;\n};\n\nvar calcBytePadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n return parseInt(value);\n};\n\nvar calcRealPadding = function (type, expected) {\n var value = type.slice(expected.length);\n if (value === \"\") {\n return 32;\n }\n var sizes = value.split('x');\n for (var padding = 0, i = 0; i < sizes; i++) {\n padding += (sizes[i] / 8);\n }\n return padding;\n};\n\nvar setupInputTypes = function () {\n \n var prefixedType = function (prefix, calcPadding) {\n return function (type, value) {\n var expected = prefix;\n if (type.indexOf(expected) !== 0) {\n return false;\n }\n\n var padding = calcPadding(type, expected);\n if (typeof value === \"number\")\n value = value.toString(16);\n else if (typeof value === \"string\")\n value = web3.toHex(value); \n else if (value.indexOf('0x') === 0)\n value = value.substr(2);\n else\n value = (+value).toString(16);\n return padLeft(value, padding * 2);\n };\n };\n\n var namedType = function (name, padding, formatter) {\n return function (type, value) {\n if (type !== name) {\n return false;\n }\n\n return padLeft(formatter ? formatter(value) : value, padding * 2);\n };\n };\n\n var formatBool = function (value) {\n return value ? '0x1' : '0x0';\n };\n\n return [\n prefixedType('uint', calcBitPadding),\n prefixedType('int', calcBitPadding),\n prefixedType('hash', calcBitPadding),\n prefixedType('string', calcBytePadding),\n prefixedType('real', calcRealPadding),\n prefixedType('ureal', calcRealPadding),\n namedType('address', 20),\n namedType('bool', 1, formatBool),\n ];\n};\n\nvar inputTypes = setupInputTypes();\n\nvar toAbiInput = function (json, methodName, params) {\n var bytes = \"\";\n var index = findMethodIndex(json, methodName);\n\n if (index === -1) {\n return;\n }\n\n bytes = \"0x\" + padLeft(index.toString(16), 2);\n var method = json[index];\n\n for (var i = 0; i < method.inputs.length; i++) {\n var found = false;\n for (var j = 0; j < inputTypes.length && !found; j++) {\n found = inputTypes[j](method.inputs[i].type, params[i]);\n }\n if (!found) {\n console.error('unsupported json type: ' + method.inputs[i].type);\n }\n bytes += found;\n }\n return bytes;\n};\n\nvar setupOutputTypes = function () {\n\n var prefixedType = function (prefix, calcPadding) {\n return function (type) {\n var expected = prefix;\n if (type.indexOf(expected) !== 0) {\n return -1;\n }\n\n var padding = calcPadding(type, expected);\n return padding * 2;\n };\n };\n\n var namedType = function (name, padding) {\n return function (type) {\n return name === type ? padding * 2 : -1;\n };\n };\n\n var formatInt = function (value) {\n return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);\n };\n\n var formatHash = function (value) {\n return \"0x\" + value;\n };\n\n var formatBool = function (value) {\n return value === '1' ? true : false;\n };\n\n var formatString = function (value) {\n return web3.toAscii(value);\n };\n\n return [\n { padding: prefixedType('uint', calcBitPadding), format: formatInt },\n { padding: prefixedType('int', calcBitPadding), format: formatInt },\n { padding: prefixedType('hash', calcBitPadding), format: formatHash },\n { padding: prefixedType('string', calcBytePadding), format: formatString },\n { padding: prefixedType('real', calcRealPadding), format: formatInt },\n { padding: prefixedType('ureal', calcRealPadding), format: formatInt },\n { padding: namedType('address', 20) },\n { padding: namedType('bool', 1), format: formatBool }\n ];\n};\n\nvar outputTypes = setupOutputTypes();\n\nvar fromAbiOutput = function (json, methodName, output) {\n var index = findMethodIndex(json, methodName);\n\n if (index === -1) {\n return;\n }\n\n output = output.slice(2);\n\n var result = [];\n var method = json[index];\n for (var i = 0; i < method.outputs.length; i++) {\n var padding = -1;\n for (var j = 0; j < outputTypes.length && padding === -1; j++) {\n padding = outputTypes[j].padding(method.outputs[i].type);\n }\n\n if (padding === -1) {\n // not found output parsing\n continue;\n }\n var res = output.slice(0, padding);\n var formatter = outputTypes[j - 1].format;\n result.push(formatter ? formatter(res) : (\"0x\" + res));\n output = output.slice(padding);\n }\n\n return result;\n};\n\nvar inputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n parser[method.name] = function () {\n var params = Array.prototype.slice.call(arguments);\n return toAbiInput(json, method.name, params);\n };\n });\n\n return parser;\n};\n\nvar outputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n parser[method.name] = function (output) {\n return fromAbiOutput(json, method.name, output);\n };\n });\n\n return parser;\n};\n\nmodule.exports = {\n inputParser: inputParser,\n outputParser: outputParser\n};\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file autoprovider.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\n/*\n * @brief if qt object is available, uses QtProvider,\n * if not tries to connect over websockets\n * if it fails, it uses HttpRpcProvider\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var WebSocket = require('ws'); // jshint ignore:line\n var web3 = require('./main.js'); // jshint ignore:line\n*/}\n\nvar AutoProvider = function (userOptions) {\n if (web3.haveProvider()) {\n return;\n }\n\n // before we determine what provider we are, we have to cache request\n this.sendQueue = [];\n this.onmessageQueue = [];\n\n if (navigator.qt) {\n this.provider = new web3.providers.QtProvider();\n return;\n }\n\n userOptions = userOptions || {};\n var options = {\n httprpc: userOptions.httprpc || 'http://localhost:8080',\n websockets: userOptions.websockets || 'ws://localhost:40404/eth'\n };\n\n var self = this;\n var closeWithSuccess = function (success) {\n ws.close();\n if (success) {\n self.provider = new web3.providers.WebSocketProvider(options.websockets);\n } else {\n self.provider = new web3.providers.HttpRpcProvider(options.httprpc);\n self.poll = self.provider.poll.bind(self.provider);\n }\n self.sendQueue.forEach(function (payload) {\n self.provider(payload);\n });\n self.onmessageQueue.forEach(function (handler) {\n self.provider.onmessage = handler;\n });\n };\n\n var ws = new WebSocket(options.websockets);\n\n ws.onopen = function() {\n closeWithSuccess(true);\n };\n\n ws.onerror = function() {\n closeWithSuccess(false);\n };\n};\n\nAutoProvider.prototype.send = function (payload) {\n if (this.provider) {\n this.provider.send(payload);\n return;\n }\n this.sendQueue.push(payload);\n};\n\nObject.defineProperty(AutoProvider.prototype, 'onmessage', {\n set: function (handler) {\n if (this.provider) {\n this.provider.onmessage = handler;\n return;\n }\n this.onmessageQueue.push(handler);\n }\n});\n\nmodule.exports = AutoProvider;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var web3 = require('./web3'); // jshint ignore:line\n*/}\n\nvar abi = require('./abi');\n\nvar contract = function (address, desc) {\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n var contract = {};\n\n desc.forEach(function (method) {\n contract[method.name] = function () {\n var params = Array.prototype.slice.call(arguments);\n var parsed = inputParser[method.name].apply(null, params);\n\n var onSuccess = function (result) {\n return outputParser[method.name](result);\n };\n\n return {\n call: function (extra) {\n extra = extra || {};\n extra.to = address;\n extra.data = parsed;\n return web3.eth.call(extra).then(onSuccess);\n },\n transact: function (extra) {\n extra = extra || {};\n extra.to = address;\n extra.data = parsed;\n return web3.eth.transact(extra).then(onSuccess);\n }\n };\n };\n });\n\n return contract;\n};\n\nmodule.exports = contract;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file httprpc.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line\n*/}\n\nvar HttpRpcProvider = function (host) {\n this.handlers = [];\n this.host = host;\n};\n\nfunction formatJsonRpcObject(object) {\n return {\n jsonrpc: '2.0',\n method: object.call,\n params: object.args,\n id: object._id\n };\n}\n\nfunction formatJsonRpcMessage(message) {\n var object = JSON.parse(message);\n\n return {\n _id: object.id,\n data: object.result,\n error: object.error\n };\n}\n\nHttpRpcProvider.prototype.sendRequest = function (payload, cb) {\n var data = formatJsonRpcObject(payload);\n\n var request = new XMLHttpRequest();\n request.open(\"POST\", this.host, true);\n request.send(JSON.stringify(data));\n request.onreadystatechange = function () {\n if (request.readyState === 4 && cb) {\n cb(request);\n }\n };\n};\n\nHttpRpcProvider.prototype.send = function (payload) {\n var self = this;\n this.sendRequest(payload, function (request) {\n self.handlers.forEach(function (handler) {\n handler.call(self, formatJsonRpcMessage(request.responseText));\n });\n });\n};\n\nHttpRpcProvider.prototype.poll = function (payload, id) {\n var self = this;\n this.sendRequest(payload, function (request) {\n var parsed = JSON.parse(request.responseText);\n if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) {\n return;\n }\n self.handlers.forEach(function (handler) {\n handler.call(self, {_event: payload.call, _id: id, data: parsed.result});\n });\n });\n};\n\nObject.defineProperty(HttpRpcProvider.prototype, \"onmessage\", {\n set: function (handler) {\n this.handlers.push(handler);\n }\n});\n\nmodule.exports = HttpRpcProvider;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file main.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nfunction flattenPromise (obj) {\n if (obj instanceof Promise) {\n return Promise.resolve(obj);\n }\n\n if (obj instanceof Array) {\n return new Promise(function (resolve) {\n var promises = obj.map(function (o) {\n return flattenPromise(o);\n });\n\n return Promise.all(promises).then(function (res) {\n for (var i = 0; i < obj.length; i++) {\n obj[i] = res[i];\n }\n resolve(obj);\n });\n });\n }\n\n if (obj instanceof Object) {\n return new Promise(function (resolve) {\n var keys = Object.keys(obj);\n var promises = keys.map(function (key) {\n return flattenPromise(obj[key]);\n });\n\n return Promise.all(promises).then(function (res) {\n for (var i = 0; i < keys.length; i++) {\n obj[keys[i]] = res[i];\n }\n resolve(obj);\n });\n });\n }\n\n return Promise.resolve(obj);\n}\n\nvar web3Methods = function () {\n return [\n { name: 'sha3', call: 'web3_sha3' }\n ];\n};\n\nvar ethMethods = function () {\n var blockCall = function (args) {\n return typeof args[0] === \"string\" ? \"eth_blockByHash\" : \"eth_blockByNumber\";\n };\n\n var transactionCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_transactionByHash' : 'eth_transactionByNumber';\n };\n\n var uncleCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_uncleByHash' : 'eth_uncleByNumber';\n };\n\n var methods = [\n { name: 'balanceAt', call: 'eth_balanceAt' },\n { name: 'stateAt', call: 'eth_stateAt' },\n { name: 'storageAt', call: 'eth_storageAt' },\n { name: 'countAt', call: 'eth_countAt'},\n { name: 'codeAt', call: 'eth_codeAt' },\n { name: 'transact', call: 'eth_transact' },\n { name: 'call', call: 'eth_call' },\n { name: 'block', call: blockCall },\n { name: 'transaction', call: transactionCall },\n { name: 'uncle', call: uncleCall },\n { name: 'compilers', call: 'eth_compilers' },\n { name: 'lll', call: 'eth_lll' },\n { name: 'solidity', call: 'eth_solidity' },\n { name: 'serpent', call: 'eth_serpent' },\n { name: 'logs', call: 'eth_logs' }\n ];\n return methods;\n};\n\nvar ethProperties = function () {\n return [\n { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },\n { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },\n { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },\n { name: 'gasPrice', getter: 'eth_gasPrice' },\n { name: 'account', getter: 'eth_account' },\n { name: 'accounts', getter: 'eth_accounts' },\n { name: 'peerCount', getter: 'eth_peerCount' },\n { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },\n { name: 'number', getter: 'eth_number'}\n ];\n};\n\nvar dbMethods = function () {\n return [\n { name: 'put', call: 'db_put' },\n { name: 'get', call: 'db_get' },\n { name: 'putString', call: 'db_putString' },\n { name: 'getString', call: 'db_getString' }\n ];\n};\n\nvar shhMethods = function () {\n return [\n { name: 'post', call: 'shh_post' },\n { name: 'newIdentity', call: 'shh_newIdentity' },\n { name: 'haveIdentity', call: 'shh_haveIdentity' },\n { name: 'newGroup', call: 'shh_newGroup' },\n { name: 'addToGroup', call: 'shh_addToGroup' }\n ];\n};\n\nvar ethWatchMethods = function () {\n var newFilter = function (args) {\n return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';\n };\n\n return [\n { name: 'newFilter', call: newFilter },\n { name: 'uninstallFilter', call: 'eth_uninstallFilter' },\n { name: 'getMessages', call: 'eth_filterLogs' }\n ];\n};\n\nvar shhWatchMethods = function () {\n return [\n { name: 'newFilter', call: 'shh_newFilter' },\n { name: 'uninstallFilter', call: 'shh_uninstallFilter' },\n { name: 'getMessage', call: 'shh_getMessages' }\n ];\n};\n\nvar setupMethods = function (obj, methods) {\n methods.forEach(function (method) {\n obj[method.name] = function () {\n return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {\n var call = typeof method.call === \"function\" ? method.call(args) : method.call;\n return {call: call, args: args};\n }).then(function (request) {\n return new Promise(function (resolve, reject) {\n web3.provider.send(request, function (err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n }).catch(function(err) {\n console.error(err);\n });\n };\n });\n};\n\nvar setupProperties = function (obj, properties) {\n properties.forEach(function (property) {\n var proto = {};\n proto.get = function () {\n return new Promise(function(resolve, reject) {\n web3.provider.send({call: property.getter}, function(err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n };\n if (property.setter) {\n proto.set = function (val) {\n return flattenPromise([val]).then(function (args) {\n return new Promise(function (resolve) {\n web3.provider.send({call: property.setter, args: args}, function (err, result) {\n if (!err) {\n resolve(result);\n return;\n }\n reject(err);\n });\n });\n }).catch(function (err) {\n console.error(err);\n });\n };\n }\n Object.defineProperty(obj, property.name, proto);\n });\n};\n\n// TODO: import from a dependency, don't duplicate.\nvar hexToDec = function (hex) {\n return parseInt(hex, 16).toString();\n};\n\nvar decToHex = function (dec) {\n return parseInt(dec).toString(16);\n};\n\n\nvar web3 = {\n _callbacks: {},\n _events: {},\n providers: {},\n\n toHex: function(str) {\n var hex = \"\";\n for(var i = 0; i < str.length; i++) {\n var n = str.charCodeAt(i).toString(16);\n hex += n.length < 2 ? '0' + n : n;\n }\n\n return hex;\n },\n\n toAscii: function(hex) {\n // Find termination\n var str = \"\";\n var i = 0, l = hex.length;\n if (hex.substring(0, 2) === '0x')\n i = 2;\n for(; i < l; i+=2) {\n var code = hex.charCodeAt(i);\n if(code === 0) {\n break;\n }\n\n str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));\n }\n\n return str;\n },\n\n fromAscii: function(str, pad) {\n pad = pad === undefined ? 32 : pad;\n var hex = this.toHex(str);\n while(hex.length < pad*2)\n hex += \"00\";\n return \"0x\" + hex;\n },\n\n toDecimal: function (val) {\n return hexToDec(val.substring(2));\n },\n\n fromDecimal: function (val) {\n return \"0x\" + decToHex(val);\n },\n\n toEth: function(str) {\n var val = typeof str === \"string\" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;\n var unit = 0;\n var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];\n while (val > 3000 && unit < units.length - 1)\n {\n val /= 1000;\n unit++;\n }\n var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);\n var replaceFunction = function($0, $1, $2) {\n return $1 + ',' + $2;\n };\n\n while (true) {\n var o = s;\n s = s.replace(/(\\d)(\\d\\d\\d[\\.\\,])/, replaceFunction);\n if (o === s)\n break;\n }\n return s + ' ' + units[unit];\n },\n\n eth: {\n prototype: Object(), // jshint ignore:line\n watch: function (params) {\n return new Filter(params, ethWatch);\n }\n },\n\n db: {\n prototype: Object() // jshint ignore:line\n },\n\n shh: {\n prototype: Object(), // jshint ignore:line\n watch: function (params) {\n return new Filter(params, shhWatch);\n }\n },\n\n on: function(event, id, cb) {\n if(web3._events[event] === undefined) {\n web3._events[event] = {};\n }\n\n web3._events[event][id] = cb;\n return this;\n },\n\n off: function(event, id) {\n if(web3._events[event] !== undefined) {\n delete web3._events[event][id];\n }\n\n return this;\n },\n\n trigger: function(event, id, data) {\n var callbacks = web3._events[event];\n if (!callbacks || !callbacks[id]) {\n return;\n }\n var cb = callbacks[id];\n cb(data);\n }\n};\n\nsetupMethods(web3, web3Methods());\nsetupMethods(web3.eth, ethMethods());\nsetupProperties(web3.eth, ethProperties());\nsetupMethods(web3.db, dbMethods());\nsetupMethods(web3.shh, shhMethods());\n\nvar ethWatch = {\n changed: 'eth_changed'\n};\nsetupMethods(ethWatch, ethWatchMethods());\nvar shhWatch = {\n changed: 'shh_changed'\n};\nsetupMethods(shhWatch, shhWatchMethods());\n\nvar ProviderManager = function() {\n this.queued = [];\n this.polls = [];\n this.ready = false;\n this.provider = undefined;\n this.id = 1;\n\n var self = this;\n var poll = function () {\n if (self.provider && self.provider.poll) {\n self.polls.forEach(function (data) {\n data.data._id = self.id;\n self.id++;\n self.provider.poll(data.data, data.id);\n });\n }\n setTimeout(poll, 12000);\n };\n poll();\n};\n\nProviderManager.prototype.send = function(data, cb) {\n data._id = this.id;\n if (cb) {\n web3._callbacks[data._id] = cb;\n }\n\n data.args = data.args || [];\n this.id++;\n\n if(this.provider !== undefined) {\n this.provider.send(data);\n } else {\n console.warn(\"provider is not set\");\n this.queued.push(data);\n }\n};\n\nProviderManager.prototype.set = function(provider) {\n if(this.provider !== undefined && this.provider.unload !== undefined) {\n this.provider.unload();\n }\n\n this.provider = provider;\n this.ready = true;\n};\n\nProviderManager.prototype.sendQueued = function() {\n for(var i = 0; this.queued.length; i++) {\n // Resend\n this.send(this.queued[i]);\n }\n};\n\nProviderManager.prototype.installed = function() {\n return this.provider !== undefined;\n};\n\nProviderManager.prototype.startPolling = function (data, pollId) {\n if (!this.provider || !this.provider.poll) {\n return;\n }\n this.polls.push({data: data, id: pollId});\n};\n\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nweb3.provider = new ProviderManager();\n\nweb3.setProvider = function(provider) {\n provider.onmessage = messageHandler;\n web3.provider.set(provider);\n web3.provider.sendQueued();\n};\n\nweb3.haveProvider = function() {\n return !!web3.provider.provider;\n};\n\nvar Filter = function(options, impl) {\n this.impl = impl;\n this.callbacks = [];\n\n var self = this;\n this.promise = impl.newFilter(options);\n this.promise.then(function (id) {\n self.id = id;\n web3.on(impl.changed, id, self.trigger.bind(self));\n web3.provider.startPolling({call: impl.changed, args: [id]}, id);\n });\n};\n\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\n\nFilter.prototype.changed = function(callback) {\n var self = this;\n this.promise.then(function(id) {\n self.callbacks.push(callback);\n });\n};\n\nFilter.prototype.trigger = function(messages) {\n for(var i = 0; i < this.callbacks.length; i++) {\n this.callbacks[i].call(this, messages);\n }\n};\n\nFilter.prototype.uninstall = function() {\n var self = this;\n this.promise.then(function (id) {\n self.impl.uninstallFilter(id);\n web3.provider.stopPolling(id);\n web3.off(impl.changed, id);\n });\n};\n\nFilter.prototype.messages = function() {\n var self = this;\n return this.promise.then(function (id) {\n return self.impl.getMessages(id);\n });\n};\n\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nfunction messageHandler(data) {\n if(data._event !== undefined) {\n web3.trigger(data._event, data._id, data.data);\n return;\n }\n\n if(data._id) {\n var cb = web3._callbacks[data._id];\n if (cb) {\n cb.call(this, data.error, data.data);\n delete web3._callbacks[data._id];\n }\n }\n}\n\nmodule.exports = web3;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file qt.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * @date 2014\n */\n\nvar QtProvider = function() {\n this.handlers = [];\n\n var self = this;\n navigator.qt.onmessage = function (message) {\n self.handlers.forEach(function (handler) {\n handler.call(self, JSON.parse(message.data));\n });\n };\n};\n\nQtProvider.prototype.send = function(payload) {\n navigator.qt.postMessage(JSON.stringify(payload));\n};\n\nObject.defineProperty(QtProvider.prototype, \"onmessage\", {\n set: function(handler) {\n this.handlers.push(handler);\n }\n});\n\nmodule.exports = QtProvider;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file websocket.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\n// TODO: is these line is supposed to be here? \nif (\"build\" !== 'build') {/*\n var WebSocket = require('ws'); // jshint ignore:line\n*/}\n\nvar WebSocketProvider = function(host) {\n // onmessage handlers\n this.handlers = [];\n // queue will be filled with messages if send is invoked before the ws is ready\n this.queued = [];\n this.ready = false;\n\n this.ws = new WebSocket(host);\n\n var self = this;\n this.ws.onmessage = function(event) {\n for(var i = 0; i < self.handlers.length; i++) {\n self.handlers[i].call(self, JSON.parse(event.data), event);\n }\n };\n\n this.ws.onopen = function() {\n self.ready = true;\n\n for(var i = 0; i < self.queued.length; i++) {\n // Resend\n self.send(self.queued[i]);\n }\n };\n};\n\nWebSocketProvider.prototype.send = function(payload) {\n if(this.ready) {\n var data = JSON.stringify(payload);\n\n this.ws.send(data);\n } else {\n this.queued.push(payload);\n }\n};\n\nWebSocketProvider.prototype.onMessage = function(handler) {\n this.handlers.push(handler);\n};\n\nWebSocketProvider.prototype.unload = function() {\n this.ws.close();\n};\nObject.defineProperty(WebSocketProvider.prototype, \"onmessage\", {\n set: function(provider) { this.onMessage(provider); }\n});\n\nmodule.exports = WebSocketProvider;\n", + "var web3 = require('./lib/main');\nweb3.providers.WebSocketProvider = require('./lib/websocket');\nweb3.providers.HttpRpcProvider = require('./lib/httprpc');\nweb3.providers.QtProvider = require('./lib/qt');\nweb3.providers.AutoProvider = require('./lib/autoprovider');\nweb3.contract = require('./lib/contract');\n\nmodule.exports = web3;\n" ] } \ No newline at end of file diff --git a/dist/ethereum.min.js b/dist/ethereum.min.js index b1ecdfb83..f9eac6a9d 100644 --- a/dist/ethereum.min.js +++ b/dist/ethereum.min.js @@ -1 +1 @@ -require=function t(e,n,r){function o(s,a){if(!n[s]){if(!e[s]){var u="function"==typeof require&&require;if(!a&&u)return u(s,!0);if(i)return i(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[s]={exports:{}};e[s][0].call(l.exports,function(t){var n=e[s][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;so;o++)r+=n[o]/8;return r},l=function(){var t=function(t,e){return function(n,r){var o,i=t;return 0!==n.indexOf(i)?!1:(o=e(n,i),r="number"==typeof r?r.toString(16):"string"==typeof r?web3.toHex(r):0===r.indexOf("0x")?r.substr(2):(+r).toString(16),s(r,2*o))}},e=function(t,e,n){return function(r,o){return r!==t?!1:s(n?n(o):o,2*e)}},n=function(t){return t?"0x1":"0x0"};return[t("uint",a),t("int",a),t("hash",a),t("string",u),t("real",c),t("ureal",c),e("address",20),e("bool",1,n)]},h=l(),p=function(t,e,n){var r,o,a,u,c="",l=i(t,e);if(-1!==l){for(c="0x"+s(l.toString(16),2),r=t[l],o=0;or&&(e=t.charCodeAt(r),0!==e);r+=2)n+=String.fromCharCode(parseInt(t.substr(r,2),16));return n},fromAscii:function(t,e){e=void 0===e?32:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return m(t.substring(2))},fromDecimal:function(t){return"0x"+b(t)},toEth:function(t){for(var e,n,r,o="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,i=0,s=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];o>3e3&&ii;i++)o+=r[i]/8;return o},c=function(){var t=function(t,e){return function(n,r){var o=t;if(0!==n.indexOf(o))return!1;var a=e(n,o);return r="number"==typeof r?r.toString(16):"string"==typeof r?web3.toHex(r):0===r.indexOf("0x")?r.substr(2):(+r).toString(16),i(r,2*a)}},e=function(t,e,n){return function(r,o){return r!==t?!1:i(n?n(o):o,2*e)}},n=function(t){return t?"0x1":"0x0"};return[t("uint",a),t("int",a),t("hash",a),t("string",s),t("real",u),t("ureal",u),e("address",20),e("bool",1,n)]},l=c(),h=function(t,e,n){var r="",a=o(t,e);if(-1!==a){r="0x"+i(a.toString(16),2);for(var s=t[a],u=0;un;n+=2){var o=t.charCodeAt(n);if(0===o)break;e+=String.fromCharCode(parseInt(t.substr(n,2),16))}return e},fromAscii:function(t,e){e=void 0===e?32:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},toDecimal:function(t){return p(t.substring(2))},fromDecimal:function(t){return"0x"+d(t)},toEth:function(t){for(var e="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,n=0,r=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e>3e3&&n Date: Wed, 7 Jan 2015 15:37:00 +0100 Subject: [PATCH 48/54] add tests for OOG at max call depth --- test/stSystemOperationsTestFiller.json | 110 +++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/test/stSystemOperationsTestFiller.json b/test/stSystemOperationsTestFiller.json index 757c8a235..12f10fde5 100644 --- a/test/stSystemOperationsTestFiller.json +++ b/test/stSystemOperationsTestFiller.json @@ -33,6 +33,40 @@ } }, + "createNameRegistratorOOG_MemExpansionInsufficientBalance": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "10000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0x601080600c6000396000f3006000355415600957005b60203560003555) [[ 0 ]] (CREATE 11000 3 0xffffffffffffffffffffff) }", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "createNameRegistratorZeroMem": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -314,6 +348,48 @@ } }, + "CallToNameRegistratorMemOOGAndInsufficientBalance": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (MSTORE 32 0xaaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa ) [[ 0 ]] (CALL 0xffffffffff 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0xffffffffffff 64 0) }", + "storage": {} + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "23", + "code" : "0x6000355415600957005b60203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "CallToNameRegistratorAddressTooBigLeft": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -977,6 +1053,40 @@ } }, + "CallRecursiveBomb0_OOG_atMaxCallDepth": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "10000000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "20000000", + "nonce" : 0, + "code" : "{ [[ 0 ]] (+ (SLOAD 0) 1) [[ 2 ]] (MUL (DIV @@0 0x0402) 0xfffffffffffffffffff) [[ 1 ]] (CALL (- (GAS) 1024) (ADDRESS) 0 0 (MUL (DIV @@0 0x0402) 0xfffffffffffffffffff) 0 0) } ", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "1000000000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "CallRecursiveBomb1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From 0de9e2aebd43a9fe9e4fdac68d1915dcff7f0607 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 7 Jan 2015 12:50:23 +0100 Subject: [PATCH 49/54] Bugfix: Use parameter (not argument) type size on stack for function calls. --- libsolidity/ExpressionCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 6bf14f559..aa7406132 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -217,7 +217,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes()); // callee adds return parameters, but removes arguments and return label - m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1); + m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(function.getParameterTypes()) - 1); // @todo for now, the return value of a function is its first return value, so remove // all others From c6ae1754b955d50e893bb4f2e453ffd5c6c930e2 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 7 Jan 2015 15:41:11 +0100 Subject: [PATCH 50/54] Some changes to the optimizer. --- libevmcore/Assembly.cpp | 9 +++------ test/SolidityOptimizer.cpp | 14 +++++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index 5efbb9487..fd256d688 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -393,7 +393,6 @@ Assembly& Assembly::optimise(bool _enable) auto rw = r.second(vr); unsigned const vrSize = bytesRequiredBySlice(vr.begin(), vr.end()); unsigned const rwSize = bytesRequiredBySlice(rw.begin(), rw.end()); - //@todo check the actual size (including constant sizes) if (rwSize < vrSize || (rwSize == vrSize && popCountIncreased(vr, rw))) { copt << vr << "matches" << AssemblyItemsConstRef(&r.first) << "becomes..."; @@ -405,12 +404,10 @@ Assembly& Assembly::optimise(bool _enable) 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()); - for (unsigned j = 0; j < max(rw.size(), vr.size()); ++j) - if (j < rw.size()) - m_items[i + j] = rw[j]; - else - m_items.erase(m_items.begin() + i + rw.size()); + copy(rw.begin(), rw.end(), m_items.begin() + i); count++; copt << "Now:\n" << m_items; diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index ef5c6f9b5..7e06c2e30 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -48,7 +48,7 @@ public: m_optimize = true; bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName); int sizeDiff = nonOptimizedBytecode.size() - optimizedBytecode.size(); - BOOST_CHECK_MESSAGE(sizeDiff == int(_expectedSizeDecrease), "Bytecode did only shrink by " + BOOST_CHECK_MESSAGE(sizeDiff == int(_expectedSizeDecrease), "Bytecode shrank by " + boost::lexical_cast(sizeDiff) + " bytes, expected: " + boost::lexical_cast(_expectedSizeDecrease)); m_optimizedContract = m_contractAddress; @@ -91,10 +91,10 @@ BOOST_AUTO_TEST_CASE(large_integers) contract test { function f() returns (uint a, uint b) { a = 0x234234872642837426347000000; - b = 0x110000000000000000000000002; + b = 0x10000000000000000000000002; } })"; - compileBothVersions(28, sourceCode); + compileBothVersions(33, sourceCode); compareVersions(0); } @@ -102,8 +102,8 @@ BOOST_AUTO_TEST_CASE(invariants) { char const* sourceCode = R"( contract test { - function f(uint a) returns (uint b) { - return (((a + (1 - 1)) ^ 0) | 0) & (uint(0) - 1); + function f(int a) returns (int b) { + return int(0) | (int(1) * (int(0) ^ (0 + a))); } })"; compileBothVersions(28, sourceCode); @@ -127,8 +127,8 @@ BOOST_AUTO_TEST_CASE(unused_expressions) BOOST_AUTO_TEST_CASE(constant_folding_both_sides) { // if constants involving the same associative and commutative operator are applied from both - // sides, the operator should be applied only once, because the expression compiler - // (even in non-optimized mode) pushes literals as late as possible + // sides, the operator should be applied only once, because the expression compiler pushes + // literals as late as possible char const* sourceCode = R"( contract test { function f(uint x) returns (uint y) { From 460571bd7732d6829327c51c18c82cd705931345 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 7 Jan 2015 16:46:15 +0100 Subject: [PATCH 51/54] Fix some warnings about uninitialized members. --- libsolidity/Compiler.h | 2 +- libsolidity/CompilerStack.h | 2 +- libsolidity/ExpressionCompiler.h | 6 +++--- libsolidity/GlobalContext.h | 2 +- libsolidity/NameAndTypeResolver.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 8471fae2a..e83d1ed3a 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -30,7 +30,7 @@ namespace solidity { class Compiler: private ASTConstVisitor { public: - explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {} + explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), m_returnTag(m_context.newTag()) {} void compileContract(ContractDefinition const& _contract, std::vector const& _magicGlobals, std::map const& _contracts); diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 358c8fb77..e7143b7bb 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -113,7 +113,7 @@ private: struct Contract { - ContractDefinition const* contract; + ContractDefinition const* contract = nullptr; std::shared_ptr compiler; bytes bytecode; std::shared_ptr interfaceHandler; diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 67b16aac0..c0ee4ab48 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -146,12 +146,12 @@ private: private: CompilerContext* m_context; - LValueType m_type; + LValueType m_type = NONE; /// If m_type is STACK, this is base stack offset (@see /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. - unsigned m_baseStackOffset; + unsigned m_baseStackOffset = 0; /// Size of the value of this lvalue on the stack. - unsigned m_stackSize; + unsigned m_stackSize = 0; }; bool m_optimize; diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h index 50a21f702..c6e35f504 100644 --- a/libsolidity/GlobalContext.h +++ b/libsolidity/GlobalContext.h @@ -56,7 +56,7 @@ public: private: std::vector> m_magicVariables; - ContractDefinition const* m_currentContract; + ContractDefinition const* m_currentContract = nullptr; std::map> mutable m_thisPointer; }; diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 23ac5fe77..1032a87cf 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -69,7 +69,7 @@ private: /// not contain code. std::map m_scopes; - DeclarationContainer* m_currentScope; + DeclarationContainer* m_currentScope = nullptr; }; /** From bf8174ecee66f024bf46ef249dd70967aaeee9d9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 7 Jan 2015 16:58:09 +0100 Subject: [PATCH 52/54] Warnings fixes. Make Mix work with Qt 5.2 Minor other alterations. --- libevmcore/Assembly.cpp | 6 +++--- libsolidity/Types.h | 3 +++ mix/AssemblyDebuggerModel.cpp | 1 - mix/qml/StateDialog.qml | 2 +- mix/qml/StateList.qml | 6 +++--- mix/qml/TransactionDialog.qml | 2 +- mix/qml/main.qml | 4 ++-- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index fd256d688..f7a71102f 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -391,9 +391,9 @@ Assembly& Assembly::optimise(bool _enable) if (matches(vr, &r.first)) { auto rw = r.second(vr); - unsigned const vrSize = bytesRequiredBySlice(vr.begin(), vr.end()); - unsigned const rwSize = bytesRequiredBySlice(rw.begin(), rw.end()); - if (rwSize < vrSize || (rwSize == vrSize && popCountIncreased(vr, rw))) + unsigned const vrSizeInBytes = bytesRequiredBySlice(vr.begin(), vr.end()); + unsigned const rwSizeInBytes = bytesRequiredBySlice(rw.begin(), rw.end()); + if (rwSizeInBytes < vrSizeInBytes || (rwSizeInBytes == vrSizeInBytes && popCountIncreased(vr, rw))) { copt << vr << "matches" << AssemblyItemsConstRef(&r.first) << "becomes..."; copt << AssemblyItemsConstRef(&rw); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index ff8a48877..6f3ca6abf 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -387,6 +387,9 @@ public: protected: virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override { + (void)_operator; + (void)_this; + (void)_other; return TypePointer(); } }; diff --git a/mix/AssemblyDebuggerModel.cpp b/mix/AssemblyDebuggerModel.cpp index d09c2cd18..e822d0a3f 100644 --- a/mix/AssemblyDebuggerModel.cpp +++ b/mix/AssemblyDebuggerModel.cpp @@ -74,7 +74,6 @@ DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const& execution.go(onOp); execution.finalize(); - m_executiveState.completeMine(); DebuggingContent d; d.returnValue = execution.out().toVector(); diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index eb0b68ac8..862e6f854 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -1,5 +1,5 @@ import QtQuick 2.2 -import QtQuick.Controls 1.2 +import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.0 diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index 152a35671..0d4b6e5c6 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -1,7 +1,7 @@ import QtQuick 2.2 -import QtQuick.Controls.Styles 1.2 -import QtQuick.Controls 1.2 -import QtQuick.Dialogs 1.2 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 Rectangle { diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index b7c556272..54528f97f 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -1,5 +1,5 @@ import QtQuick 2.2 -import QtQuick.Controls 1.2 +import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.0 diff --git a/mix/qml/main.qml b/mix/qml/main.qml index bba7c9088..6e946cdfd 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -1,6 +1,6 @@ import QtQuick 2.2 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 From e9913a3d10136ce46090bdd94b0d0a4c870f69d8 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 6 Jan 2015 19:17:05 +0100 Subject: [PATCH 53/54] new vps for windows build --- extdep/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extdep/CMakeLists.txt b/extdep/CMakeLists.txt index 19cd00f7b..fdee34602 100644 --- a/extdep/CMakeLists.txt +++ b/extdep/CMakeLists.txt @@ -7,7 +7,7 @@ include(eth_download.cmake) # all dependencies will be installed into this directory, separated by platform string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name) set(ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/install/${_system_name}") -set(ETH_DEPENDENCY_SERVER "http://poc-7.ethdev.com/precompiled/${_system_name}") +set(ETH_DEPENDENCY_SERVER "http://build.ethdev.com/builds/${_system_name}-precompiled") file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/lib) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/include) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin) From 6cc8c9de6953db631f67888f5a5c0abdee307975 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 7 Jan 2015 20:23:33 +0100 Subject: [PATCH 54/54] VM skips push data when looking for JUMPDEST. Warnings fixes. --- libevm/VM.h | 3 ++- libsolidity/Types.h | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libevm/VM.h b/libevm/VM.h index cc9556c26..b8a33909c 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -86,7 +86,8 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st for (unsigned i = 0; i < _ext.code.size(); ++i) if (_ext.code[i] == (byte)Instruction::JUMPDEST) m_jumpDests.insert(i); - + else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32) + i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1; u256 nextPC = m_curPC + 1; auto osteps = _steps; for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 6f3ca6abf..a91a6c24e 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -415,6 +415,9 @@ public: protected: virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override { + (void)_operator; + (void)_this; + (void)_other; return TypePointer(); } @@ -445,6 +448,9 @@ public: protected: virtual TypePointer binaryOperatorResultImpl(Token::Value _operator, TypePointer const& _this, TypePointer const& _other) const override { + (void)_operator; + (void)_this; + (void)_other; return TypePointer(); }